QSTile.java revision ccb6b9a90f228cc4e31a9442ed28756ff474c080
1/* 2 * Copyright (C) 2014 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 17package com.android.systemui.qs; 18 19import android.content.Context; 20import android.content.Intent; 21import android.graphics.drawable.Drawable; 22import android.graphics.drawable.VectorDrawable; 23import android.os.Handler; 24import android.os.Looper; 25import android.os.Message; 26import android.util.Log; 27import android.view.View; 28import android.view.ViewGroup; 29 30import com.android.systemui.qs.QSTile.State; 31import com.android.systemui.statusbar.policy.BluetoothController; 32import com.android.systemui.statusbar.policy.CastController; 33import com.android.systemui.statusbar.policy.Listenable; 34import com.android.systemui.statusbar.policy.LocationController; 35import com.android.systemui.statusbar.policy.NetworkController; 36import com.android.systemui.statusbar.policy.RotationLockController; 37import com.android.systemui.statusbar.policy.TetheringController; 38import com.android.systemui.statusbar.policy.ZenModeController; 39 40import java.util.List; 41import java.util.Objects; 42 43/** 44 * Base quick-settings tile, extend this to create a new tile. 45 * 46 * State management done on a looper provided by the host. Tiles should update state in 47 * handleUpdateState. Callbacks affecting state should use refreshState to trigger another 48 * state update pass on tile looper. 49 */ 50public abstract class QSTile<TState extends State> implements Listenable { 51 protected final String TAG = "QSTile." + getClass().getSimpleName(); 52 protected static final boolean DEBUG = false; 53 54 protected final Host mHost; 55 protected final Context mContext; 56 protected final H mHandler; 57 protected final Handler mUiHandler = new Handler(Looper.getMainLooper()); 58 59 private Callback mCallback; 60 protected final TState mState = newTileState(); 61 private final TState mTmpState = newTileState(); 62 63 abstract protected TState newTileState(); 64 abstract protected void handleClick(); 65 abstract protected void handleUpdateState(TState state, Object arg); 66 67 protected QSTile(Host host) { 68 mHost = host; 69 mContext = host.getContext(); 70 mHandler = new H(host.getLooper()); 71 } 72 73 public boolean supportsDualTargets() { 74 return false; 75 } 76 77 public Host getHost() { 78 return mHost; 79 } 80 81 public QSTileView createTileView(Context context) { 82 return new QSTileView(context); 83 } 84 85 public View createDetailView(Context context, ViewGroup root) { 86 return null; // optional 87 } 88 89 // safe to call from any thread 90 91 public void setCallback(Callback callback) { 92 mHandler.obtainMessage(H.SET_CALLBACK, callback).sendToTarget(); 93 } 94 95 public void click() { 96 mHandler.sendEmptyMessage(H.CLICK); 97 } 98 99 public void secondaryClick() { 100 mHandler.sendEmptyMessage(H.SECONDARY_CLICK); 101 } 102 103 public void showDetail(boolean show) { 104 mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0).sendToTarget(); 105 } 106 107 protected final void refreshState() { 108 refreshState(null); 109 } 110 111 protected final void refreshState(Object arg) { 112 mHandler.obtainMessage(H.REFRESH_STATE, arg).sendToTarget(); 113 } 114 115 public void userSwitch(int newUserId) { 116 mHandler.obtainMessage(H.USER_SWITCH, newUserId).sendToTarget(); 117 } 118 119 // call only on tile worker looper 120 121 private void handleSetCallback(Callback callback) { 122 mCallback = callback; 123 handleRefreshState(null); 124 } 125 126 protected void handleSecondaryClick() { 127 // optional 128 } 129 130 protected void handleRefreshState(Object arg) { 131 handleUpdateState(mTmpState, arg); 132 final boolean changed = mTmpState.copyTo(mState); 133 if (changed) { 134 handleStateChanged(); 135 } 136 } 137 138 private void handleStateChanged() { 139 if (mCallback != null) { 140 mCallback.onStateChanged(mState); 141 } 142 } 143 144 private void handleShowDetail(boolean show) { 145 if (mCallback != null) { 146 mCallback.onShowDetail(show); 147 } 148 } 149 150 protected void handleUserSwitch(int newUserId) { 151 handleRefreshState(null); 152 } 153 154 protected final class H extends Handler { 155 private static final int SET_CALLBACK = 1; 156 private static final int CLICK = 2; 157 private static final int SECONDARY_CLICK = 3; 158 private static final int REFRESH_STATE = 4; 159 private static final int SHOW_DETAIL = 5; 160 private static final int USER_SWITCH = 6; 161 162 private H(Looper looper) { 163 super(looper); 164 } 165 166 @Override 167 public void handleMessage(Message msg) { 168 String name = null; 169 try { 170 if (msg.what == SET_CALLBACK) { 171 name = "handleSetCallback"; 172 handleSetCallback((QSTile.Callback)msg.obj); 173 } else if (msg.what == CLICK) { 174 name = "handleClick"; 175 handleClick(); 176 } else if (msg.what == SECONDARY_CLICK) { 177 name = "handleSecondaryClick"; 178 handleSecondaryClick(); 179 } else if (msg.what == REFRESH_STATE) { 180 name = "handleRefreshState"; 181 handleRefreshState(msg.obj); 182 } else if (msg.what == SHOW_DETAIL) { 183 name = "handleShowDetail"; 184 handleShowDetail(msg.arg1 != 0); 185 } else if (msg.what == USER_SWITCH) { 186 name = "handleUserSwitch"; 187 handleUserSwitch(msg.arg1); 188 } 189 } catch (Throwable t) { 190 final String error = "Error in " + name; 191 Log.w(TAG, error, t); 192 mHost.warn(error, t); 193 } 194 } 195 } 196 197 public interface Callback { 198 void onStateChanged(State state); 199 void onShowDetail(boolean show); 200 } 201 202 public interface Host { 203 void startSettingsActivity(Intent intent); 204 void warn(String message, Throwable t); 205 void collapsePanels(); 206 Looper getLooper(); 207 Context getContext(); 208 VectorDrawable getVectorDrawable(int resId); 209 BluetoothController getBluetoothController(); 210 LocationController getLocationController(); 211 RotationLockController getRotationLockController(); 212 List<QSTile<?>> getTiles(); 213 NetworkController getNetworkController(); 214 ZenModeController getZenModeController(); 215 TetheringController getTetheringController(); 216 CastController getCastController(); 217 } 218 219 public static class State { 220 public boolean visible; 221 public int iconId; 222 public Drawable icon; 223 public String label; 224 public String contentDescription; 225 226 public boolean copyTo(State other) { 227 if (other == null) throw new IllegalArgumentException(); 228 if (!other.getClass().equals(getClass())) throw new IllegalArgumentException(); 229 final boolean changed = other.visible != visible 230 || other.iconId != iconId 231 || !Objects.equals(other.icon, icon) 232 || !Objects.equals(other.label, label) 233 || !Objects.equals(other.contentDescription, contentDescription); 234 other.visible = visible; 235 other.iconId = iconId; 236 other.icon = icon; 237 other.label = label; 238 other.contentDescription = contentDescription; 239 return changed; 240 } 241 242 @Override 243 public String toString() { 244 return toStringBuilder().toString(); 245 } 246 247 protected StringBuilder toStringBuilder() { 248 final StringBuilder sb = new StringBuilder( getClass().getSimpleName()).append('['); 249 sb.append("visible=").append(visible); 250 sb.append(",iconId=").append(iconId); 251 sb.append(",icon=").append(icon); 252 sb.append(",label=").append(label); 253 sb.append(",contentDescription=").append(contentDescription); 254 return sb.append(']'); 255 } 256 } 257 258 public static class BooleanState extends State { 259 public boolean value; 260 261 @Override 262 public boolean copyTo(State other) { 263 final BooleanState o = (BooleanState) other; 264 final boolean changed = super.copyTo(other) || o.value != value; 265 o.value = value; 266 return changed; 267 } 268 269 @Override 270 protected StringBuilder toStringBuilder() { 271 final StringBuilder rt = super.toStringBuilder(); 272 rt.insert(rt.length() - 1, ",value=" + value); 273 return rt; 274 } 275 } 276 277 public static final class SignalState extends State { 278 public boolean enabled; 279 public boolean connected; 280 public boolean activityIn; 281 public boolean activityOut; 282 public int overlayIconId; 283 284 @Override 285 public boolean copyTo(State other) { 286 final SignalState o = (SignalState) other; 287 final boolean changed = o.enabled != enabled 288 || o.connected != connected || o.activityIn != activityIn 289 || o.activityOut != activityOut 290 || o.overlayIconId != overlayIconId; 291 o.enabled = enabled; 292 o.connected = connected; 293 o.activityIn = activityIn; 294 o.activityOut = activityOut; 295 o.overlayIconId = overlayIconId; 296 return super.copyTo(other) || changed; 297 } 298 299 @Override 300 protected StringBuilder toStringBuilder() { 301 final StringBuilder rt = super.toStringBuilder(); 302 rt.insert(rt.length() - 1, ",enabled=" + enabled); 303 rt.insert(rt.length() - 1, ",connected=" + connected); 304 rt.insert(rt.length() - 1, ",activityIn=" + activityIn); 305 rt.insert(rt.length() - 1, ",activityOut=" + activityOut); 306 rt.insert(rt.length() - 1, ",overlayIconId=" + overlayIconId); 307 return rt; 308 } 309 } 310} 311