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