2 * Copyright (C) 2015 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package android.service.quicksettings;
18 import android.Manifest;
19 import android.annotation.SdkConstant;
20 import android.annotation.SdkConstant.SdkConstantType;
21 import android.annotation.SystemApi;
22 import android.app.Dialog;
23 import android.app.Service;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.graphics.drawable.Icon;
28 import android.os.Handler;
29 import android.os.IBinder;
30 import android.os.Looper;
31 import android.os.Message;
32 import android.os.RemoteException;
33 import android.view.View;
34 import android.view.View.OnAttachStateChangeListener;
35 import android.view.WindowManager;
38 * A TileService provides the user a tile that can be added to Quick Settings.
39 * Quick Settings is a space provided that allows the user to change settings and
40 * take quick actions without leaving the context of their current app.
42 * <p>The lifecycle of a TileService is different from some other services in
43 * that it may be unbound during parts of its lifecycle. Any of the following
44 * lifecycle events can happen indepently in a separate binding/creation of the
48 * <li>When a tile is added by the user its TileService will be bound to and
49 * {@link #onTileAdded()} will be called.</li>
51 * <li>When a tile should be up to date and listing will be indicated by
52 * {@link #onStartListening()} and {@link #onStopListening()}.</li>
54 * <li>When the user removes a tile from Quick Settings {@link #onTileRemoved()}
55 * will be called.</li>
57 * <p>TileService will be detected by tiles that match the {@value #ACTION_QS_TILE}
58 * and require the permission "android.permission.BIND_QUICK_SETTINGS_TILE".
59 * The label and icon for the service will be used as the default label and
60 * icon for the tile. Here is an example TileService declaration.</p>
61 * <pre class="prettyprint">
64 * android:name=".MyQSTileService"
65 * android:label="@string/my_default_tile_label"
66 * android:icon="@drawable/my_default_icon_label"
67 * android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
69 * <action android:name="android.service.quicksettings.action.QS_TILE" />
74 * @see Tile Tile for details about the UI of a Quick Settings Tile.
76 public class TileService extends Service {
79 * An activity that provides a user interface for adjusting TileService preferences.
80 * Optional but recommended for apps that implement a TileService.
82 @SdkConstant(SdkConstantType.INTENT_CATEGORY)
83 public static final String ACTION_QS_TILE_PREFERENCES
84 = "android.service.quicksettings.action.QS_TILE_PREFERENCES";
87 * Action that identifies a Service as being a TileService.
89 public static final String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
92 * Meta-data for tile definition to set a tile into active mode.
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.
99 * To make a TileService an active tile, set this meta-data to true on the TileService's
100 * manifest declaration.
101 * <pre class="prettyprint">
103 * <meta-data android:name="android.service.quicksettings.ACTIVE_TILE"
104 * android:value="true" />
108 public static final String META_DATA_ACTIVE_TILE
109 = "android.service.quicksettings.ACTIVE_TILE";
112 * Used to notify SysUI that Listening has be requested.
115 public static final String ACTION_REQUEST_LISTENING
116 = "android.service.quicksettings.action.REQUEST_LISTENING";
121 public static final String EXTRA_SERVICE = "service";
126 public static final String EXTRA_TOKEN = "token";
131 public static final String EXTRA_COMPONENT = "android.service.quicksettings.extra.COMPONENT";
133 private final H mHandler = new H(Looper.getMainLooper());
135 private boolean mListening = false;
137 private IBinder mToken;
138 private IQSService mService;
139 private Runnable mUnlockRunnable;
140 private IBinder mTileToken;
143 public void onDestroy() {
152 * Called when the user adds this tile to Quick Settings.
154 * Note that this is not guaranteed to be called between {@link #onCreate()}
155 * and {@link #onStartListening()}, it will only be called when the tile is added
156 * and not on subsequent binds.
158 public void onTileAdded() {
162 * Called when the user removes this tile from Quick Settings.
164 public void onTileRemoved() {
168 * Called when this tile moves into a listening state.
170 * When this tile is in a listening state it is expected to keep the
171 * UI up to date. Any listeners or callbacks needed to keep this tile
172 * up to date should be registered here and unregistered in {@link #onStopListening()}.
175 * @see Tile#updateTile()
177 public void onStartListening() {
181 * Called when this tile moves out of the listening state.
183 public void onStopListening() {
187 * Called when the user clicks on this tile.
189 public void onClick() {
193 * Sets an icon to be shown in the status bar.
195 * The icon will be displayed before all other icons. Can only be called between
196 * {@link #onStartListening} and {@link #onStopListening}. Can only be called by system apps.
198 * @param icon The icon to be displayed, null to hide
199 * @param contentDescription Content description of the icon to be displayed
203 public final void setStatusIcon(Icon icon, String contentDescription) {
204 if (mService != null) {
206 mService.updateStatusIcon(mTileToken, icon, contentDescription);
207 } catch (RemoteException e) {
213 * Used to show a dialog.
215 * This will collapse the Quick Settings panel and show the dialog.
217 * @param dialog Dialog to show.
221 public final void showDialog(Dialog dialog) {
222 dialog.getWindow().getAttributes().token = mToken;
223 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_QS_DIALOG);
224 dialog.getWindow().getDecorView().addOnAttachStateChangeListener(
225 new OnAttachStateChangeListener() {
227 public void onViewAttachedToWindow(View v) {
231 public void onViewDetachedFromWindow(View v) {
233 mService.onDialogHidden(mTileToken);
234 } catch (RemoteException e) {
240 mService.onShowDialog(mTileToken);
241 } catch (RemoteException e) {
246 * Prompts the user to unlock the device before executing the Runnable.
248 * The user will be prompted for their current security method if applicable
249 * and if successful, runnable will be executed. The Runnable will not be
250 * executed if the user fails to unlock the device or cancels the operation.
252 public final void unlockAndRun(Runnable runnable) {
253 mUnlockRunnable = runnable;
255 mService.startUnlockAndRun(mTileToken);
256 } catch (RemoteException e) {
261 * Checks if the device is in a secure state.
263 * TileServices should detect when the device is secure and change their behavior
266 * @return true if the device is secure.
268 public final boolean isSecure() {
270 return mService.isSecure();
271 } catch (RemoteException e) {
277 * Checks if the lock screen is showing.
279 * When a device is locked, then {@link #showDialog} will not present a dialog, as it will
280 * be under the lock screen. If the behavior of the Tile is safe to do while locked,
281 * then the user should use {@link #startActivity} to launch an activity on top of the lock
282 * screen, otherwise the tile should use {@link #unlockAndRun(Runnable)} to give the
283 * user their security challenge.
285 * @return true if the device is locked.
287 public final boolean isLocked() {
289 return mService.isLocked();
290 } catch (RemoteException e) {
296 * Start an activity while collapsing the panel.
298 public final void startActivityAndCollapse(Intent intent) {
299 startActivity(intent);
301 mService.onStartActivity(mTileToken);
302 } catch (RemoteException e) {
307 * Gets the {@link Tile} for this service.
309 * This tile may be used to get or set the current state for this
310 * tile. This tile is only valid for updates between {@link #onStartListening()}
311 * and {@link #onStopListening()}.
313 public final Tile getQsTile() {
318 public IBinder onBind(Intent intent) {
319 mService = IQSService.Stub.asInterface(intent.getIBinderExtra(EXTRA_SERVICE));
320 mTileToken = intent.getIBinderExtra(EXTRA_TOKEN);
322 mTile = mService.getTile(mTileToken);
323 } catch (RemoteException e) {
324 throw new RuntimeException("Unable to reach IQSService", e);
327 mTile.setService(mService, mTileToken);
328 mHandler.sendEmptyMessage(H.MSG_START_SUCCESS);
330 return new IQSTileService.Stub() {
332 public void onTileRemoved() throws RemoteException {
333 mHandler.sendEmptyMessage(H.MSG_TILE_REMOVED);
337 public void onTileAdded() throws RemoteException {
338 mHandler.sendEmptyMessage(H.MSG_TILE_ADDED);
342 public void onStopListening() throws RemoteException {
343 mHandler.sendEmptyMessage(H.MSG_STOP_LISTENING);
347 public void onStartListening() throws RemoteException {
348 mHandler.sendEmptyMessage(H.MSG_START_LISTENING);
352 public void onClick(IBinder wtoken) throws RemoteException {
353 mHandler.obtainMessage(H.MSG_TILE_CLICKED, wtoken).sendToTarget();
357 public void onUnlockComplete() throws RemoteException{
358 mHandler.sendEmptyMessage(H.MSG_UNLOCK_COMPLETE);
363 private class H extends Handler {
364 private static final int MSG_START_LISTENING = 1;
365 private static final int MSG_STOP_LISTENING = 2;
366 private static final int MSG_TILE_ADDED = 3;
367 private static final int MSG_TILE_REMOVED = 4;
368 private static final int MSG_TILE_CLICKED = 5;
369 private static final int MSG_UNLOCK_COMPLETE = 6;
370 private static final int MSG_START_SUCCESS = 7;
372 public H(Looper looper) {
377 public void handleMessage(Message msg) {
380 TileService.this.onTileAdded();
382 case MSG_TILE_REMOVED:
385 TileService.this.onStopListening();
387 TileService.this.onTileRemoved();
389 case MSG_STOP_LISTENING:
392 TileService.this.onStopListening();
395 case MSG_START_LISTENING:
398 TileService.this.onStartListening();
401 case MSG_TILE_CLICKED:
402 mToken = (IBinder) msg.obj;
403 TileService.this.onClick();
405 case MSG_UNLOCK_COMPLETE:
406 if (mUnlockRunnable != null) {
407 mUnlockRunnable.run();
410 case MSG_START_SUCCESS:
412 mService.onStartSuccessful(mTileToken);
413 } catch (RemoteException e) {
421 * Requests that a tile be put in the listening state so it can send an update.
423 * This method is only applicable to tiles that have {@link #META_DATA_ACTIVE_TILE} defined
424 * as true on their TileService Manifest declaration, and will do nothing otherwise.
426 public static final void requestListeningState(Context context, ComponentName component) {
427 Intent intent = new Intent(ACTION_REQUEST_LISTENING);
428 intent.putExtra(EXTRA_COMPONENT, component);
429 context.sendBroadcast(intent, Manifest.permission.BIND_QUICK_SETTINGS_TILE);