1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.qs; 18 19 import android.content.Context; 20 import android.content.res.Configuration; 21 import android.util.AttributeSet; 22 import android.view.View; 23 import android.view.accessibility.AccessibilityNodeInfo; 24 import android.widget.LinearLayout; 25 26 import com.android.internal.logging.UiEventLogger; 27 import com.android.systemui.FontSizeUtils; 28 import com.android.systemui.plugins.qs.QSTile; 29 import com.android.systemui.res.R; 30 31 /** 32 * Version of QSPanel that only shows N Quick Tiles in the QS Header. 33 */ 34 public class QuickQSPanel extends QSPanel { 35 36 private static final String TAG = "QuickQSPanel"; 37 // A fallback value for max tiles number when setting via Tuner (parseNumTiles) 38 public static final int TUNER_MAX_TILES_FALLBACK = 6; 39 40 private boolean mDisabledByPolicy; 41 private int mMaxTiles; 42 QuickQSPanel(Context context, AttributeSet attrs)43 public QuickQSPanel(Context context, AttributeSet attrs) { 44 super(context, attrs); 45 mMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_tiles); 46 } 47 48 @Override setHorizontalContentContainerClipping()49 protected void setHorizontalContentContainerClipping() { 50 mHorizontalContentContainer.setClipToPadding(false); 51 mHorizontalContentContainer.setClipChildren(false); 52 } 53 54 @Override getOrCreateTileLayout()55 public TileLayout getOrCreateTileLayout() { 56 QQSSideLabelTileLayout layout = new QQSSideLabelTileLayout(mContext); 57 layout.setId(R.id.qqs_tile_layout); 58 return layout; 59 } 60 61 62 @Override displayMediaMarginsOnMedia()63 protected boolean displayMediaMarginsOnMedia() { 64 // Margins should be on the container to visually center the view 65 return false; 66 } 67 68 @Override mediaNeedsTopMargin()69 protected boolean mediaNeedsTopMargin() { 70 return true; 71 } 72 73 @Override updatePadding()74 protected void updatePadding() { 75 int bottomPadding = getResources().getDimensionPixelSize(R.dimen.qqs_layout_padding_bottom); 76 setPaddingRelative(getPaddingStart(), 77 getPaddingTop(), 78 getPaddingEnd(), 79 bottomPadding); 80 } 81 82 @Override getDumpableTag()83 protected String getDumpableTag() { 84 return TAG; 85 } 86 87 @Override shouldShowDetail()88 protected boolean shouldShowDetail() { 89 return !mExpanded; 90 } 91 setMaxTiles(int maxTiles)92 public void setMaxTiles(int maxTiles) { 93 mMaxTiles = maxTiles; 94 } 95 96 @Override onTuningChanged(String key, String newValue)97 public void onTuningChanged(String key, String newValue) { 98 if (QS_SHOW_BRIGHTNESS.equals(key)) { 99 // No Brightness or Tooltip for you! 100 super.onTuningChanged(key, "0"); 101 } 102 } 103 getNumQuickTiles()104 public int getNumQuickTiles() { 105 return mMaxTiles; 106 } 107 108 /** 109 * Parses the String setting into the number of tiles. Defaults to 110 * {@link #TUNER_MAX_TILES_FALLBACK} 111 * 112 * @param numTilesValue value of the setting to parse 113 * @return parsed value of numTilesValue OR {@link #TUNER_MAX_TILES_FALLBACK} on error 114 */ parseNumTiles(String numTilesValue)115 public static int parseNumTiles(String numTilesValue) { 116 try { 117 return Integer.parseInt(numTilesValue); 118 } catch (NumberFormatException e) { 119 // Couldn't read an int from the new setting value. Use default. 120 return TUNER_MAX_TILES_FALLBACK; 121 } 122 } 123 setDisabledByPolicy(boolean disabled)124 void setDisabledByPolicy(boolean disabled) { 125 if (disabled != mDisabledByPolicy) { 126 mDisabledByPolicy = disabled; 127 setVisibility(disabled ? View.GONE : View.VISIBLE); 128 } 129 } 130 131 /** 132 * Sets the visibility of this {@link QuickQSPanel}. This method has no effect when this panel 133 * is disabled by policy through {@link #setDisabledByPolicy(boolean)}, and in this case the 134 * visibility will always be {@link View#GONE}. This method is called externally by 135 * {@link QSAnimator} only. 136 */ 137 @Override setVisibility(int visibility)138 public void setVisibility(int visibility) { 139 if (mDisabledByPolicy) { 140 if (getVisibility() == View.GONE) { 141 return; 142 } 143 visibility = View.GONE; 144 } 145 super.setVisibility(visibility); 146 } 147 148 @Override openPanelEvent()149 protected QSEvent openPanelEvent() { 150 return QSEvent.QQS_PANEL_EXPANDED; 151 } 152 153 @Override closePanelEvent()154 protected QSEvent closePanelEvent() { 155 return QSEvent.QQS_PANEL_COLLAPSED; 156 } 157 158 @Override tileVisibleEvent()159 protected QSEvent tileVisibleEvent() { 160 return QSEvent.QQS_TILE_VISIBLE; 161 } 162 163 @Override onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)164 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 165 super.onInitializeAccessibilityNodeInfo(info); 166 // Remove the collapse action from QSPanel 167 info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE); 168 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND); 169 } 170 171 static class QQSSideLabelTileLayout extends SideLabelTileLayout { 172 173 private boolean mLastSelected; 174 QQSSideLabelTileLayout(Context context)175 QQSSideLabelTileLayout(Context context) { 176 super(context, null); 177 setClipChildren(false); 178 setClipToPadding(false); 179 LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, 180 LayoutParams.WRAP_CONTENT); 181 setLayoutParams(lp); 182 setMaxColumns(4); 183 } 184 185 @Override updateResources()186 public boolean updateResources() { 187 mResourceCellHeightResId = R.dimen.qs_quick_tile_size; 188 boolean b = super.updateResources(); 189 mMaxAllowedRows = getResources().getInteger(R.integer.quick_qs_panel_max_rows); 190 return b; 191 } 192 193 @Override estimateCellHeight()194 protected void estimateCellHeight() { 195 FontSizeUtils.updateFontSize(mTempTextView, R.dimen.qs_tile_text_size); 196 int unspecifiedSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 197 mTempTextView.measure(unspecifiedSpec, unspecifiedSpec); 198 int padding = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_padding); 199 // the QQS only have 1 label 200 mEstimatedCellHeight = mTempTextView.getMeasuredHeight() + padding * 2; 201 } 202 203 @Override onConfigurationChanged(Configuration newConfig)204 protected void onConfigurationChanged(Configuration newConfig) { 205 super.onConfigurationChanged(newConfig); 206 updateResources(); 207 } 208 209 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)210 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 211 // Make sure to always use the correct number of rows. As it's determined by the 212 // columns, just use as many as needed. 213 updateMaxRows(10000, mRecords.size()); 214 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 215 } 216 217 @Override setListening(boolean listening, UiEventLogger uiEventLogger)218 public void setListening(boolean listening, UiEventLogger uiEventLogger) { 219 boolean startedListening = !mListening && listening; 220 super.setListening(listening, uiEventLogger); 221 if (startedListening) { 222 // getNumVisibleTiles() <= mRecords.size() 223 for (int i = 0; i < getNumVisibleTiles(); i++) { 224 QSTile tile = mRecords.get(i).tile; 225 uiEventLogger.logWithInstanceId(QSEvent.QQS_TILE_VISIBLE, 0, 226 tile.getMetricsSpec(), tile.getInstanceId()); 227 } 228 } 229 } 230 231 @Override setExpansion(float expansion, float proposedTranslation)232 public void setExpansion(float expansion, float proposedTranslation) { 233 if (expansion > 0f && expansion < 1f) { 234 return; 235 } 236 // The cases we must set select for marquee when QQS/QS collapsed, and QS full expanded. 237 // Expansion == 0f is when QQS is fully showing (as opposed to 1f, which is QS). At this 238 // point we want them to be selected so the tiles will marquee (but not at other points 239 // of expansion. 240 boolean selected = (expansion == 1f || proposedTranslation < 0f); 241 if (mLastSelected == selected) { 242 return; 243 } 244 // We set it as not important while we change this, so setting each tile as selected 245 // will not cause them to announce themselves until the user has actually selected the 246 // item. 247 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); 248 for (int i = 0; i < getChildCount(); i++) { 249 getChildAt(i).setSelected(selected); 250 } 251 setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); 252 mLastSelected = selected; 253 } 254 } 255 } 256