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  */
16 package android.service.quicksettings;
17 
18 import android.annotation.Nullable;
19 import android.app.PendingIntent;
20 import android.graphics.drawable.Icon;
21 import android.os.IBinder;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.os.RemoteException;
25 import android.text.TextUtils;
26 import android.util.Log;
27 
28 /**
29  * A Tile holds the state of a tile that will be displayed
30  * in Quick Settings.
31  *
32  * A tile in Quick Settings exists as an icon with an accompanied label.
33  * It also may have content description for accessibility usability.
34  * The style and layout of the tile may change to match a given
35  * device.
36  */
37 public final class Tile implements Parcelable {
38 
39     private static final String TAG = "Tile";
40 
41     /**
42      * An unavailable state indicates that for some reason this tile is not currently
43      * available to the user, and will have no click action.  The tile's icon will be
44      * tinted differently to reflect this state.
45      */
46     public static final int STATE_UNAVAILABLE = 0;
47 
48     /**
49      * This represents a tile that is currently in a disabled state but is still interactable.
50      *
51      * A disabled state indicates that the tile is not currently active (e.g. wifi disconnected or
52      * bluetooth disabled), but is still interactable by the user to modify this state.  Tiles
53      * that have boolean states should use this to represent one of their states.  The tile's
54      * icon will be tinted differently to reflect this state, but still be distinct from unavailable.
55      */
56     public static final int STATE_INACTIVE = 1;
57 
58     /**
59      * This represents a tile that is currently active. (e.g. wifi is connected, bluetooth is on,
60      * cast is casting).  This is the default state.
61      */
62     public static final int STATE_ACTIVE = 2;
63 
64     private IBinder mToken;
65     private Icon mIcon;
66     private CharSequence mLabel;
67     private CharSequence mSubtitle;
68     private CharSequence mContentDescription;
69     private CharSequence mStateDescription;
70     private PendingIntent mPendingIntent;
71     // Default to inactive until clients of the new API can update.
72     private int mState = STATE_INACTIVE;
73 
74     private IQSService mService;
75 
76     /**
77      * @hide
78      */
Tile(Parcel source)79     public Tile(Parcel source) {
80         readFromParcel(source);
81     }
82 
83     /**
84      * @hide
85      */
Tile()86     public Tile() {
87     }
88 
89     /**
90      * @hide
91      */
setService(IQSService service, IBinder stub)92     public void setService(IQSService service, IBinder stub) {
93         mService = service;
94         mToken = stub;
95     }
96 
97     /**
98      * The current state of the tile.
99      *
100      * @see #STATE_UNAVAILABLE
101      * @see #STATE_INACTIVE
102      * @see #STATE_ACTIVE
103      */
getState()104     public int getState() {
105         return mState;
106     }
107 
108     /**
109      * Sets the current state for the tile.
110      *
111      * Does not take effect until {@link #updateTile()} is called.
112      *
113      * @param state One of {@link #STATE_UNAVAILABLE}, {@link #STATE_INACTIVE},
114      * {@link #STATE_ACTIVE}
115      */
setState(int state)116     public void setState(int state) {
117         mState = state;
118     }
119 
120     /**
121      * Gets the current icon for the tile.
122      */
getIcon()123     public Icon getIcon() {
124         return mIcon;
125     }
126 
127     /**
128      * Sets the current icon for the tile.
129      *
130      * This icon is expected to be white on alpha, and may be
131      * tinted by the system to match it's theme.
132      *
133      * Does not take effect until {@link #updateTile()} is called.
134      *
135      * @param icon New icon to show.
136      */
setIcon(Icon icon)137     public void setIcon(Icon icon) {
138         this.mIcon = icon;
139     }
140 
141     /**
142      * Gets the current label for the tile.
143      */
getLabel()144     public CharSequence getLabel() {
145         return mLabel;
146     }
147 
148     /**
149      * Sets the current label for the tile.
150      *
151      * Does not take effect until {@link #updateTile()} is called.
152      *
153      * @param label New label to show.
154      */
setLabel(CharSequence label)155     public void setLabel(CharSequence label) {
156         this.mLabel = label;
157     }
158 
159     /**
160      * Gets the current subtitle for the tile.
161      */
162     @Nullable
getSubtitle()163     public CharSequence getSubtitle() {
164         return mSubtitle;
165     }
166 
167     /**
168      * Set the subtitle for the tile. Will be displayed as the secondary label.
169      * @param subtitle the subtitle to show.
170      */
setSubtitle(@ullable CharSequence subtitle)171     public void setSubtitle(@Nullable CharSequence subtitle) {
172         this.mSubtitle = subtitle;
173     }
174 
175     /**
176      * Gets the current content description for the tile.
177      */
getContentDescription()178     public CharSequence getContentDescription() {
179         return mContentDescription;
180     }
181 
182     /**
183      * Gets the current state description for the tile.
184      */
185     @Nullable
getStateDescription()186     public CharSequence getStateDescription() {
187         return mStateDescription;
188     }
189 
190     /**
191      * Sets the current content description for the tile.
192      *
193      * Does not take effect until {@link #updateTile()} is called.
194      *
195      * @param contentDescription New content description to use.
196      */
setContentDescription(CharSequence contentDescription)197     public void setContentDescription(CharSequence contentDescription) {
198         this.mContentDescription = contentDescription;
199     }
200 
201     /**
202      * Sets the current state description for the tile.
203      *
204      * Does not take effect until {@link #updateTile()} is called.
205      *
206      * @param stateDescription New state description to use.
207      */
setStateDescription(@ullable CharSequence stateDescription)208     public void setStateDescription(@Nullable CharSequence stateDescription) {
209         this.mStateDescription = stateDescription;
210     }
211 
212     @Override
describeContents()213     public int describeContents() {
214         return 0;
215     }
216 
217     /**
218      * Pushes the state of the Tile to Quick Settings to be displayed.
219      */
updateTile()220     public void updateTile() {
221         try {
222             mService.updateQsTile(this, mToken);
223         } catch (RemoteException e) {
224             Log.e(TAG, "Couldn't update tile");
225         }
226     }
227 
228     /**
229      * Gets the Activity {@link PendingIntent} to be launched when the tile is clicked.
230      */
231     @Nullable
getActivityLaunchForClick()232     public PendingIntent getActivityLaunchForClick() {
233         return mPendingIntent;
234     }
235 
236     /**
237      * Sets an Activity {@link PendingIntent} to be launched when the tile is clicked.
238      *
239      * The last value set here will be launched when the user clicks in the tile, instead of
240      * forwarding the `onClick` message to the {@link TileService}. Set to {@code null} to handle
241      * the `onClick` in the `TileService`
242      * (This is the default behavior if this method is never called.)
243      * @param pendingIntent a PendingIntent for an activity to be launched onclick, or {@code null}
244      *                      to handle the clicks in the `TileService`.
245      */
setActivityLaunchForClick(@ullable PendingIntent pendingIntent)246     public void setActivityLaunchForClick(@Nullable PendingIntent pendingIntent) {
247         if (pendingIntent != null && !pendingIntent.isActivity()) {
248             throw new IllegalArgumentException();
249         } else {
250             mPendingIntent = pendingIntent;
251         }
252     }
253 
254     @Override
writeToParcel(Parcel dest, int flags)255     public void writeToParcel(Parcel dest, int flags) {
256         if (mIcon != null) {
257             dest.writeByte((byte) 1);
258             mIcon.writeToParcel(dest, flags);
259         } else {
260             dest.writeByte((byte) 0);
261         }
262         if (mPendingIntent != null) {
263             dest.writeByte((byte) 1);
264             mPendingIntent.writeToParcel(dest, flags);
265         } else {
266             dest.writeByte((byte) 0);
267         }
268         dest.writeInt(mState);
269         TextUtils.writeToParcel(mLabel, dest, flags);
270         TextUtils.writeToParcel(mSubtitle, dest, flags);
271         TextUtils.writeToParcel(mContentDescription, dest, flags);
272         TextUtils.writeToParcel(mStateDescription, dest, flags);
273     }
274 
readFromParcel(Parcel source)275     private void readFromParcel(Parcel source) {
276         if (source.readByte() != 0) {
277             mIcon = Icon.CREATOR.createFromParcel(source);
278         } else {
279             mIcon = null;
280         }
281         if (source.readByte() != 0) {
282             mPendingIntent = PendingIntent.CREATOR.createFromParcel(source);
283         } else {
284             mPendingIntent = null;
285         }
286         mState = source.readInt();
287         mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
288         mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
289         mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
290         mStateDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
291     }
292 
293     public static final @android.annotation.NonNull Creator<Tile> CREATOR = new Creator<Tile>() {
294         @Override
295         public Tile createFromParcel(Parcel source) {
296             return new Tile(source);
297         }
298 
299         @Override
300         public Tile[] newArray(int size) {
301             return new Tile[size];
302         }
303     };
304 }