TileService.java revision 161ccb5dfe2fb4dc8b1a41a6465fd0c6b839a294
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.app.Dialog;
19import android.app.Service;
20import android.content.Intent;
21import android.os.Handler;
22import android.os.IBinder;
23import android.os.Looper;
24import android.os.Message;
25import android.os.RemoteException;
26import android.view.WindowManager;
27
28/**
29 * A QSTileService provides the user a tile that can be added to Quick Settings.
30 * Quick Settings is a space provided that allows the user to change settings and
31 * take quick actions without leaving the context of their current app.
32 *
33 * <p>The lifecycle of a QSTileService is different from some other services in
34 * that it may be unbound during parts of its lifecycle.  Any of the following
35 * lifecycle events can happen indepently in a separate binding/creation of the
36 * service.</p>
37 *
38 * <ul>
39 * <li>When a tile is added by the user its QSTileService will be bound to and
40 * {@link #onTileAdded()} will be called.</li>
41 *
42 * <li>When a tile should be up to date and listing will be indicated by
43 * {@link #onStartListening()} and {@link #onStopListening()}.</li>
44 *
45 * <li>When the user removes a tile from Quick Settings {@link #onStopListening()}
46 * will be called.</li>
47 * </ul>
48 * <p>QSTileService will be detected by tiles that match the {@value #ACTION_QS_TILE}
49 * and require the permission "android.permission.BIND_QUICK_SETTINGS_TILE".
50 * The label and icon for the service will be used as the default label and
51 * icon for the tile. Here is an example QSTileService declaration.</p>
52 * <pre class="prettyprint">
53 * {@literal
54 * <service
55 *     android:name=".MyQSTileService"
56 *     android:label="@string/my_default_tile_label"
57 *     android:icon="@drawable/my_default_icon_label"
58 *     android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
59 *     <intent-filter>
60 *         <action android:name="android.service.quicksettings.action.QS_TILE" />
61 *     </intent-filter>
62 * </service>}
63 * </pre>
64 *
65 * @see Tile Tile for details about the UI of a Quick Settings Tile.
66 */
67public class TileService extends Service {
68
69    /**
70     * Action that identifies a Service as being a QSTileService.
71     */
72    public static final String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
73
74    private final H mHandler = new H(Looper.getMainLooper());
75
76    private boolean mListening = false;
77    private Tile mTile;
78    private IBinder mToken;
79
80    @Override
81    public void onDestroy() {
82        if (mListening) {
83            onStopListening();
84            mListening = false;
85        }
86        super.onDestroy();
87    }
88
89    /**
90     * Called when the user adds this tile to Quick Settings.
91     * <p/>
92     * Note that this is not guaranteed to be called between {@link #onCreate()}
93     * and {@link #onStartListening()}, it will only be called when the tile is added
94     * and not on subsequent binds.
95     */
96    public void onTileAdded() {
97    }
98
99    /**
100     * Called when the user removes this tile from Quick Settings.
101     */
102    public void onTileRemoved() {
103    }
104
105    /**
106     * Called when this tile moves into a listening state.
107     * <p/>
108     * When this tile is in a listening state it is expected to keep the
109     * UI up to date.  Any listeners or callbacks needed to keep this tile
110     * up to date should be registered here and unregistered in {@link #onStopListening()}.
111     *
112     * @see #getQsTile()
113     * @see Tile#updateTile()
114     */
115    public void onStartListening() {
116    }
117
118    /**
119     * Called when this tile moves out of the listening state.
120     */
121    public void onStopListening() {
122    }
123
124    /**
125     * Called when the user clicks on this tile.
126     */
127    public void onClick() {
128    }
129
130    /**
131     * Used to show a dialog.
132     *
133     * This will collapse the Quick Settings panel and show the dialog.
134     *
135     * @param dialog Dialog to show.
136     */
137    public final void showDialog(Dialog dialog) {
138        dialog.getWindow().getAttributes().token = mToken;
139        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_QS_DIALOG);
140        dialog.show();
141        getQsTile().onShowDialog();
142    }
143
144    /**
145     * Gets the {@link Tile} for this service.
146     * <p/>
147     * This tile may be used to get or set the current state for this
148     * tile. This tile is only valid for updates between {@link #onStartListening()}
149     * and {@link #onStopListening()}.
150     */
151    public final Tile getQsTile() {
152        return mTile;
153    }
154
155    @Override
156    public IBinder onBind(Intent intent) {
157        return new IQSTileService.Stub() {
158            @Override
159            public void setQSTile(Tile tile) throws RemoteException {
160                mHandler.obtainMessage(H.MSG_SET_TILE, tile).sendToTarget();
161            }
162
163            @Override
164            public void onTileRemoved() throws RemoteException {
165                mHandler.sendEmptyMessage(H.MSG_TILE_REMOVED);
166            }
167
168            @Override
169            public void onTileAdded() throws RemoteException {
170                mHandler.sendEmptyMessage(H.MSG_TILE_ADDED);
171            }
172
173            @Override
174            public void onStopListening() throws RemoteException {
175                mHandler.sendEmptyMessage(H.MSG_STOP_LISTENING);
176            }
177
178            @Override
179            public void onStartListening() throws RemoteException {
180                mHandler.sendEmptyMessage(H.MSG_START_LISTENING);
181            }
182
183            @Override
184            public void onClick(IBinder wtoken) throws RemoteException {
185                mHandler.obtainMessage(H.MSG_TILE_CLICKED, wtoken).sendToTarget();
186            }
187        };
188    }
189
190    private class H extends Handler {
191        private static final int MSG_SET_TILE = 1;
192        private static final int MSG_START_LISTENING = 2;
193        private static final int MSG_STOP_LISTENING = 3;
194        private static final int MSG_TILE_ADDED = 4;
195        private static final int MSG_TILE_REMOVED = 5;
196        private static final int MSG_TILE_CLICKED = 6;
197
198        public H(Looper looper) {
199            super(looper);
200        }
201
202        @Override
203        public void handleMessage(Message msg) {
204            switch (msg.what) {
205                case MSG_SET_TILE:
206                    mTile = (Tile) msg.obj;
207                    break;
208                case MSG_TILE_ADDED:
209                    TileService.this.onTileAdded();
210                    break;
211                case MSG_TILE_REMOVED:
212                    TileService.this.onTileRemoved();
213                    break;
214                case MSG_STOP_LISTENING:
215                    if (mListening) {
216                        mListening = false;
217                        TileService.this.onStopListening();
218                    }
219                    break;
220                case MSG_START_LISTENING:
221                    if (!mListening) {
222                        mListening = true;
223                        TileService.this.onStartListening();
224                    }
225                    break;
226                case MSG_TILE_CLICKED:
227                    mToken = (IBinder) msg.obj;
228                    TileService.this.onClick();
229                    break;
230            }
231        }
232    }
233}
234