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