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.Gravity; 23 import android.view.View; 24 import android.widget.LinearLayout; 25 import android.widget.Space; 26 27 import com.android.systemui.R; 28 import com.android.systemui.qs.QSTile.SignalState; 29 import com.android.systemui.qs.QSTile.State; 30 import com.android.systemui.qs.customize.QSCustomizer; 31 import com.android.systemui.statusbar.phone.QSTileHost; 32 import com.android.systemui.tuner.TunerService; 33 import com.android.systemui.tuner.TunerService.Tunable; 34 35 import java.util.ArrayList; 36 import java.util.Collection; 37 38 /** 39 * Version of QSPanel that only shows N Quick Tiles in the QS Header. 40 */ 41 public class QuickQSPanel extends QSPanel { 42 43 public static final String NUM_QUICK_TILES = "sysui_qqs_count"; 44 45 private int mMaxTiles; 46 private QSPanel mFullPanel; 47 private View mHeader; 48 49 public QuickQSPanel(Context context, AttributeSet attrs) { 50 super(context, attrs); 51 if (mTileLayout != null) { 52 for (int i = 0; i < mRecords.size(); i++) { 53 mTileLayout.removeTile(mRecords.get(i)); 54 } 55 removeView((View) mTileLayout); 56 } 57 mTileLayout = new HeaderTileLayout(context); 58 mTileLayout.setListening(mListening); 59 addView((View) mTileLayout, 1 /* Between brightness and footer */); 60 } 61 62 @Override 63 protected void onAttachedToWindow() { 64 super.onAttachedToWindow(); 65 TunerService.get(mContext).addTunable(mNumTiles, NUM_QUICK_TILES); 66 } 67 68 @Override 69 protected void onDetachedFromWindow() { 70 super.onDetachedFromWindow(); 71 TunerService.get(mContext).removeTunable(mNumTiles); 72 } 73 74 public void setQSPanelAndHeader(QSPanel fullPanel, View header) { 75 mFullPanel = fullPanel; 76 mHeader = header; 77 } 78 79 @Override 80 protected boolean shouldShowDetail() { 81 return !mExpanded; 82 } 83 84 @Override 85 protected void drawTile(TileRecord r, State state) { 86 if (state instanceof SignalState) { 87 State copy = r.tile.newTileState(); 88 state.copyTo(copy); 89 // No activity shown in the quick panel. 90 ((SignalState) copy).activityIn = false; 91 ((SignalState) copy).activityOut = false; 92 state = copy; 93 } 94 super.drawTile(r, state); 95 } 96 97 @Override 98 protected QSTileBaseView createTileView(QSTile<?> tile, boolean collapsedView) { 99 return new QSTileBaseView(mContext, tile.createTileView(mContext), collapsedView); 100 } 101 102 @Override 103 public void setHost(QSTileHost host, QSCustomizer customizer) { 104 super.setHost(host, customizer); 105 setTiles(mHost.getTiles()); 106 } 107 108 public void setMaxTiles(int maxTiles) { 109 mMaxTiles = maxTiles; 110 if (mHost != null) { 111 setTiles(mHost.getTiles()); 112 } 113 } 114 115 @Override 116 protected void onTileClick(QSTile<?> tile) { 117 tile.secondaryClick(); 118 } 119 120 @Override 121 public void onTuningChanged(String key, String newValue) { 122 // No tunings for you. 123 if (key.equals(QS_SHOW_BRIGHTNESS)) { 124 // No Brightness for you. 125 super.onTuningChanged(key, "0"); 126 } 127 } 128 129 @Override 130 public void setTiles(Collection<QSTile<?>> tiles) { 131 ArrayList<QSTile<?>> quickTiles = new ArrayList<>(); 132 for (QSTile<?> tile : tiles) { 133 quickTiles.add(tile); 134 if (quickTiles.size() == mMaxTiles) { 135 break; 136 } 137 } 138 super.setTiles(quickTiles, true); 139 } 140 141 private final Tunable mNumTiles = new Tunable() { 142 @Override 143 public void onTuningChanged(String key, String newValue) { 144 setMaxTiles(getNumQuickTiles(mContext)); 145 } 146 }; 147 148 public int getNumQuickTiles(Context context) { 149 return TunerService.get(context).getValue(NUM_QUICK_TILES, 5); 150 } 151 152 private static class HeaderTileLayout extends LinearLayout implements QSTileLayout { 153 154 private final Space mEndSpacer; 155 protected final ArrayList<TileRecord> mRecords = new ArrayList<>(); 156 private boolean mListening; 157 158 public HeaderTileLayout(Context context) { 159 super(context); 160 setClipChildren(false); 161 setClipToPadding(false); 162 setGravity(Gravity.CENTER_VERTICAL); 163 setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); 164 165 mEndSpacer = new Space(context); 166 mEndSpacer.setLayoutParams(generateLayoutParams()); 167 updateDownArrowMargin(); 168 addView(mEndSpacer); 169 setOrientation(LinearLayout.HORIZONTAL); 170 } 171 172 @Override 173 protected void onConfigurationChanged(Configuration newConfig) { 174 super.onConfigurationChanged(newConfig); 175 updateDownArrowMargin(); 176 } 177 178 private void updateDownArrowMargin() { 179 LayoutParams params = (LayoutParams) mEndSpacer.getLayoutParams(); 180 params.setMarginStart(mContext.getResources().getDimensionPixelSize( 181 R.dimen.qs_expand_margin)); 182 mEndSpacer.setLayoutParams(params); 183 } 184 185 @Override 186 public void setListening(boolean listening) { 187 if (mListening == listening) return; 188 mListening = listening; 189 for (TileRecord record : mRecords) { 190 record.tile.setListening(this, mListening); 191 } 192 } 193 194 @Override 195 public void addTile(TileRecord tile) { 196 addView(tile.tileView, getChildCount() - 1 /* Leave icon at end */, 197 generateLayoutParams()); 198 // Add a spacer. 199 addView(new Space(mContext), getChildCount() - 1 /* Leave icon at end */, 200 generateSpaceParams()); 201 mRecords.add(tile); 202 tile.tile.setListening(this, mListening); 203 } 204 205 private LayoutParams generateSpaceParams() { 206 int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size); 207 LayoutParams lp = new LayoutParams(0, size); 208 lp.weight = 1; 209 lp.gravity = Gravity.CENTER; 210 return lp; 211 } 212 213 private LayoutParams generateLayoutParams() { 214 int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size); 215 LayoutParams lp = new LayoutParams(size, size); 216 lp.gravity = Gravity.CENTER; 217 return lp; 218 } 219 220 @Override 221 public void removeTile(TileRecord tile) { 222 int childIndex = getChildIndex(tile.tileView); 223 // Remove the tile. 224 removeViewAt(childIndex); 225 // Remove its spacer as well. 226 removeViewAt(childIndex); 227 mRecords.remove(tile); 228 tile.tile.setListening(this, false); 229 } 230 231 private int getChildIndex(QSTileBaseView tileView) { 232 final int N = getChildCount(); 233 for (int i = 0; i < N; i++) { 234 if (getChildAt(i) == tileView) { 235 return i; 236 } 237 } 238 return -1; 239 } 240 241 @Override 242 public int getOffsetTop(TileRecord tile) { 243 return 0; 244 } 245 246 @Override 247 public boolean updateResources() { 248 // No resources here. 249 return false; 250 } 251 252 @Override 253 public boolean hasOverlappingRendering() { 254 return false; 255 } 256 257 @Override 258 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 259 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 260 if (mRecords != null && mRecords.size() > 0) { 261 View previousView = this; 262 for (TileRecord record : mRecords) { 263 if (record.tileView.getVisibility() == GONE) continue; 264 previousView = record.tileView.updateAccessibilityOrder(previousView); 265 } 266 mRecords.get(0).tileView.setAccessibilityTraversalAfter( 267 R.id.alarm_status_collapsed); 268 mRecords.get(mRecords.size() - 1).tileView.setAccessibilityTraversalBefore( 269 R.id.expand_indicator); 270 } 271 } 272 } 273 } 274