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