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