TileService.java revision fe8f6826ce3c2beeb1fce54c67978ce69f849407
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 */ 16package android.service.quicksettings; 17 18import android.Manifest; 19import android.app.Dialog; 20import android.app.Service; 21import android.content.ComponentName; 22import android.content.Context; 23import android.content.Intent; 24import android.os.Handler; 25import android.os.IBinder; 26import android.os.Looper; 27import android.os.Message; 28import android.os.RemoteException; 29import android.view.WindowManager; 30 31/** 32 * A TileService provides the user a tile that can be added to Quick Settings. 33 * Quick Settings is a space provided that allows the user to change settings and 34 * take quick actions without leaving the context of their current app. 35 * 36 * <p>The lifecycle of a TileService is different from some other services in 37 * that it may be unbound during parts of its lifecycle. Any of the following 38 * lifecycle events can happen indepently in a separate binding/creation of the 39 * service.</p> 40 * 41 * <ul> 42 * <li>When a tile is added by the user its TileService will be bound to and 43 * {@link #onTileAdded()} will be called.</li> 44 * 45 * <li>When a tile should be up to date and listing will be indicated by 46 * {@link #onStartListening()} and {@link #onStopListening()}.</li> 47 * 48 * <li>When the user removes a tile from Quick Settings {@link #onTileRemoved()} 49 * will be called.</li> 50 * </ul> 51 * <p>TileService will be detected by tiles that match the {@value #ACTION_QS_TILE} 52 * and require the permission "android.permission.BIND_QUICK_SETTINGS_TILE". 53 * The label and icon for the service will be used as the default label and 54 * icon for the tile. Here is an example TileService declaration.</p> 55 * <pre class="prettyprint"> 56 * {@literal 57 * <service 58 * android:name=".MyQSTileService" 59 * android:label="@string/my_default_tile_label" 60 * android:icon="@drawable/my_default_icon_label" 61 * android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"> 62 * <intent-filter> 63 * <action android:name="android.service.quicksettings.action.QS_TILE" /> 64 * </intent-filter> 65 * </service>} 66 * </pre> 67 * 68 * @see Tile Tile for details about the UI of a Quick Settings Tile. 69 */ 70public class TileService extends Service { 71 72 /** 73 * Action that identifies a Service as being a TileService. 74 */ 75 public static final String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE"; 76 77 /** 78 * The tile mode hasn't been set yet. 79 * @hide 80 */ 81 public static final int TILE_MODE_UNSET = 0; 82 83 /** 84 * Constant to be returned by {@link #onTileAdded}. 85 * <p> 86 * Passive mode is the default mode for tiles. The System will tell the tile 87 * when it is most important to update by putting it in the listening state. 88 */ 89 public static final int TILE_MODE_PASSIVE = 1; 90 91 /** 92 * Constant to be returned by {@link #onTileAdded}. 93 * <p> 94 * Active mode is for tiles which already listen and keep track of their state in their 95 * own process. These tiles may request to send an update to the System while their process 96 * is alive using {@link #requestListeningState}. The System will only bind these tiles 97 * on its own when a click needs to occur. 98 */ 99 public static final int TILE_MODE_ACTIVE = 2; 100 101 /** 102 * Used to notify SysUI that Listening has be requested. 103 * @hide 104 */ 105 public static final String ACTION_REQUEST_LISTENING 106 = "android.service.quicksettings.action.REQUEST_LISTENING"; 107 108 /** 109 * @hide 110 */ 111 public static final String EXTRA_COMPONENT = "android.service.quicksettings.extra.COMPONENT"; 112 113 private final H mHandler = new H(Looper.getMainLooper()); 114 115 private boolean mListening = false; 116 private Tile mTile; 117 private IBinder mToken; 118 private IQSService mService; 119 120 @Override 121 public void onDestroy() { 122 if (mListening) { 123 onStopListening(); 124 mListening = false; 125 } 126 super.onDestroy(); 127 } 128 129 /** 130 * Called when the user adds this tile to Quick Settings. 131 * <p/> 132 * Note that this is not guaranteed to be called between {@link #onCreate()} 133 * and {@link #onStartListening()}, it will only be called when the tile is added 134 * and not on subsequent binds. 135 * 136 * @see #TILE_MODE_PASSIVE 137 * @see #TILE_MODE_ACTIVE 138 */ 139 public int onTileAdded() { 140 return TILE_MODE_PASSIVE; 141 } 142 143 /** 144 * Called when the user removes this tile from Quick Settings. 145 */ 146 public void onTileRemoved() { 147 } 148 149 /** 150 * Called when this tile moves into a listening state. 151 * <p/> 152 * When this tile is in a listening state it is expected to keep the 153 * UI up to date. Any listeners or callbacks needed to keep this tile 154 * up to date should be registered here and unregistered in {@link #onStopListening()}. 155 * 156 * @see #getQsTile() 157 * @see Tile#updateTile() 158 */ 159 public void onStartListening() { 160 } 161 162 /** 163 * Called when this tile moves out of the listening state. 164 */ 165 public void onStopListening() { 166 } 167 168 /** 169 * Called when the user clicks on this tile. 170 */ 171 public void onClick() { 172 } 173 174 /** 175 * Used to show a dialog. 176 * 177 * This will collapse the Quick Settings panel and show the dialog. 178 * 179 * @param dialog Dialog to show. 180 */ 181 public final void showDialog(Dialog dialog) { 182 dialog.getWindow().getAttributes().token = mToken; 183 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_QS_DIALOG); 184 dialog.show(); 185 try { 186 mService.onShowDialog(mTile); 187 } catch (RemoteException e) { 188 } 189 } 190 191 /** 192 * Gets the {@link Tile} for this service. 193 * <p/> 194 * This tile may be used to get or set the current state for this 195 * tile. This tile is only valid for updates between {@link #onStartListening()} 196 * and {@link #onStopListening()}. 197 */ 198 public final Tile getQsTile() { 199 return mTile; 200 } 201 202 @Override 203 public IBinder onBind(Intent intent) { 204 return new IQSTileService.Stub() { 205 @Override 206 public void setQSService(IQSService service) throws RemoteException { 207 mHandler.obtainMessage(H.MSG_SET_SERVICE, service).sendToTarget(); 208 } 209 210 @Override 211 public void setQSTile(Tile tile) throws RemoteException { 212 mHandler.obtainMessage(H.MSG_SET_TILE, tile).sendToTarget(); 213 } 214 215 @Override 216 public void onTileRemoved() throws RemoteException { 217 mHandler.sendEmptyMessage(H.MSG_TILE_REMOVED); 218 } 219 220 @Override 221 public void onTileAdded() throws RemoteException { 222 mHandler.sendEmptyMessage(H.MSG_TILE_ADDED); 223 } 224 225 @Override 226 public void onStopListening() throws RemoteException { 227 mHandler.sendEmptyMessage(H.MSG_STOP_LISTENING); 228 } 229 230 @Override 231 public void onStartListening() throws RemoteException { 232 mHandler.sendEmptyMessage(H.MSG_START_LISTENING); 233 } 234 235 @Override 236 public void onClick(IBinder wtoken) throws RemoteException { 237 mHandler.obtainMessage(H.MSG_TILE_CLICKED, wtoken).sendToTarget(); 238 } 239 }; 240 } 241 242 private class H extends Handler { 243 private static final int MSG_SET_TILE = 1; 244 private static final int MSG_START_LISTENING = 2; 245 private static final int MSG_STOP_LISTENING = 3; 246 private static final int MSG_TILE_ADDED = 4; 247 private static final int MSG_TILE_REMOVED = 5; 248 private static final int MSG_TILE_CLICKED = 6; 249 private static final int MSG_SET_SERVICE = 7; 250 251 public H(Looper looper) { 252 super(looper); 253 } 254 255 @Override 256 public void handleMessage(Message msg) { 257 switch (msg.what) { 258 case MSG_SET_SERVICE: 259 mService = (IQSService) msg.obj; 260 if (mTile != null) { 261 mTile.setService(mService); 262 } 263 break; 264 case MSG_SET_TILE: 265 mTile = (Tile) msg.obj; 266 if (mService != null && mTile != null) { 267 mTile.setService(mService); 268 } 269 break; 270 case MSG_TILE_ADDED: 271 int mode = TileService.this.onTileAdded(); 272 if (mService == null) { 273 return; 274 } 275 try { 276 mService.setTileMode(new ComponentName(TileService.this, 277 TileService.this.getClass()), mode); 278 } catch (RemoteException e) { 279 } 280 break; 281 case MSG_TILE_REMOVED: 282 TileService.this.onTileRemoved(); 283 break; 284 case MSG_STOP_LISTENING: 285 if (mListening) { 286 mListening = false; 287 TileService.this.onStopListening(); 288 } 289 break; 290 case MSG_START_LISTENING: 291 if (!mListening) { 292 mListening = true; 293 TileService.this.onStartListening(); 294 } 295 break; 296 case MSG_TILE_CLICKED: 297 mToken = (IBinder) msg.obj; 298 TileService.this.onClick(); 299 break; 300 } 301 } 302 } 303 304 /** 305 * Requests that a tile be put in the listening state so it can send an update. 306 * 307 * This method is only applicable to tiles that return {@link #TILE_MODE_ACTIVE} from 308 * {@link #onTileAdded()}, and will do nothing otherwise. 309 */ 310 public static final void requestListeningState(Context context, ComponentName component) { 311 Intent intent = new Intent(ACTION_REQUEST_LISTENING); 312 intent.putExtra(EXTRA_COMPONENT, component); 313 context.sendBroadcast(intent, Manifest.permission.BIND_QUICK_SETTINGS_TILE); 314 } 315} 316