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, 6); 150 } 151 152 private static class HeaderTileLayout extends LinearLayout implements QSTileLayout { 153 154 protected final ArrayList<TileRecord> mRecords = new ArrayList<>(); 155 private boolean mListening; 156 157 public HeaderTileLayout(Context context) { 158 super(context); 159 setClipChildren(false); 160 setClipToPadding(false); 161 setGravity(Gravity.CENTER_VERTICAL); 162 setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); 163 } 164 165 @Override 166 public void setListening(boolean listening) { 167 if (mListening == listening) return; 168 mListening = listening; 169 for (TileRecord record : mRecords) { 170 record.tile.setListening(this, mListening); 171 } 172 } 173 174 @Override 175 public void addTile(TileRecord tile) { 176 if (getChildCount() != 0) { 177 // Add a spacer. 178 addView(new Space(mContext), getChildCount(), generateSpaceParams()); 179 } 180 addView(tile.tileView, getChildCount(), generateLayoutParams()); 181 mRecords.add(tile); 182 tile.tile.setListening(this, mListening); 183 } 184 185 private LayoutParams generateSpaceParams() { 186 int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size); 187 LayoutParams lp = new LayoutParams(0, size); 188 lp.weight = 1; 189 lp.gravity = Gravity.CENTER; 190 return lp; 191 } 192 193 private LayoutParams generateLayoutParams() { 194 int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size); 195 LayoutParams lp = new LayoutParams(size, size); 196 lp.gravity = Gravity.CENTER; 197 return lp; 198 } 199 200 @Override 201 public void removeTile(TileRecord tile) { 202 int childIndex = getChildIndex(tile.tileView); 203 // Remove the tile. 204 removeViewAt(childIndex); 205 if (getChildCount() != 0) { 206 // Remove its spacer as well. 207 removeViewAt(childIndex); 208 } 209 mRecords.remove(tile); 210 tile.tile.setListening(this, false); 211 } 212 213 private int getChildIndex(QSTileBaseView tileView) { 214 final int N = getChildCount(); 215 for (int i = 0; i < N; i++) { 216 if (getChildAt(i) == tileView) { 217 return i; 218 } 219 } 220 return -1; 221 } 222 223 @Override 224 public int getOffsetTop(TileRecord tile) { 225 return 0; 226 } 227 228 @Override 229 public boolean updateResources() { 230 // No resources here. 231 return false; 232 } 233 234 @Override 235 public boolean hasOverlappingRendering() { 236 return false; 237 } 238 239 @Override 240 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 241 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 242 if (mRecords != null && mRecords.size() > 0) { 243 View previousView = this; 244 for (TileRecord record : mRecords) { 245 if (record.tileView.getVisibility() == GONE) continue; 246 previousView = record.tileView.updateAccessibilityOrder(previousView); 247 } 248 mRecords.get(0).tileView.setAccessibilityTraversalAfter( 249 R.id.alarm_status_collapsed); 250 mRecords.get(mRecords.size() - 1).tileView.setAccessibilityTraversalBefore( 251 R.id.expand_indicator); 252 } 253 } 254 } 255 } 256