• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 
15 package com.android.systemui.plugins.qs;
16 
17 import android.content.Context;
18 import android.content.res.Resources;
19 import android.graphics.drawable.Drawable;
20 import android.metrics.LogMaker;
21 import android.service.quicksettings.Tile;
22 import android.text.TextUtils;
23 
24 import androidx.annotation.NonNull;
25 import androidx.annotation.Nullable;
26 
27 import com.android.internal.logging.InstanceId;
28 import com.android.systemui.animation.Expandable;
29 import com.android.systemui.plugins.annotations.DependsOn;
30 import com.android.systemui.plugins.annotations.ProvidesInterface;
31 import com.android.systemui.plugins.qs.QSTile.Callback;
32 import com.android.systemui.plugins.qs.QSTile.Icon;
33 import com.android.systemui.plugins.qs.QSTile.State;
34 
35 import java.util.Objects;
36 import java.util.function.Consumer;
37 import java.util.function.Supplier;
38 
39 @ProvidesInterface(version = QSTile.VERSION)
40 @DependsOn(target = QSIconView.class)
41 @DependsOn(target = Callback.class)
42 @DependsOn(target = Icon.class)
43 @DependsOn(target = State.class)
44 public interface QSTile {
45     int VERSION = 5;
46 
getTileSpec()47     String getTileSpec();
48 
isAvailable()49     boolean isAvailable();
setTileSpec(String tileSpec)50     void setTileSpec(String tileSpec);
51 
clearState()52     @Deprecated default void clearState() {}
refreshState()53     void refreshState();
54 
addCallback(Callback callback)55     void addCallback(Callback callback);
removeCallback(Callback callback)56     void removeCallback(Callback callback);
removeCallbacks()57     void removeCallbacks();
58 
59     /**
60      * The tile was clicked.
61      *
62      * @param expandable {@link Expandable} that was clicked.
63      */
click(@ullable Expandable expandable)64     void click(@Nullable Expandable expandable);
65 
66     /**
67      * The tile secondary click was triggered.
68      *
69      * @param expandable {@link Expandable} that was clicked.
70      */
secondaryClick(@ullable Expandable expandable)71     void secondaryClick(@Nullable Expandable expandable);
72 
73     /**
74      * The tile was long clicked.
75      *
76      * @param expandable {@link Expandable} that was clicked.
77      */
longClick(@ullable Expandable expandable)78     void longClick(@Nullable Expandable expandable);
79 
userSwitch(int currentUser)80     void userSwitch(int currentUser);
getCurrentTileUser()81     int getCurrentTileUser();
82 
83     /**
84      * @deprecated not needed as {@link com.android.internal.logging.UiEvent} will use
85      * {@link #getMetricsSpec}
86      */
87     @Deprecated
getMetricsCategory()88     int getMetricsCategory();
89 
setListening(Object client, boolean listening)90     void setListening(Object client, boolean listening);
setDetailListening(boolean show)91     void setDetailListening(boolean show);
92 
destroy()93     void destroy();
94 
getTileLabel()95     CharSequence getTileLabel();
96 
97     @NonNull
getState()98     State getState();
99 
populate(LogMaker logMaker)100     default LogMaker populate(LogMaker logMaker) {
101         return logMaker;
102     }
103 
104     /**
105      * Return a string to be used to identify the tile in UiEvents.
106      */
getMetricsSpec()107     default String getMetricsSpec() {
108         return getClass().getSimpleName();
109     }
110 
111     /**
112      * Return an {@link InstanceId} to be used to identify the tile in UiEvents.
113      */
getInstanceId()114     InstanceId getInstanceId();
115 
isTileReady()116     default boolean isTileReady() {
117         return false;
118     }
119 
120     /**
121      * Return whether the tile is set to its listening state and therefore receiving updates and
122      * refreshes from controllers
123      */
isListening()124     boolean isListening();
125 
126     /**
127      * Get this tile's {@link TileDetailsViewModel} through a callback.
128      *
129      * Please only override this method if the tile can't get its {@link TileDetailsViewModel}
130      * synchronously and thus need a callback to defer it.
131      *
132      * @return a boolean indicating whether this tile has a {@link TileDetailsViewModel}. The tile's
133      * {@link TileDetailsViewModel} will be passed to the callback. Please always return true when
134      * overriding this method. Return false will make the tile display its dialog instead of details
135      * view, and it will not wait for the callback to be returned before proceeding to show the
136      * dialog.
137      */
getDetailsViewModel(Consumer<TileDetailsViewModel> callback)138     default boolean getDetailsViewModel(Consumer<TileDetailsViewModel> callback) {
139         TileDetailsViewModel tileDetailsViewModel = getDetailsViewModel();
140         callback.accept(tileDetailsViewModel);
141         return tileDetailsViewModel != null;
142     }
143 
144     /**
145      * Return this tile's {@link TileDetailsViewModel} to be used to render the TileDetailsView.
146      *
147      * Please only override this method if the tile doesn't need a callback to set its
148      * {@link TileDetailsViewModel}.
149      */
getDetailsViewModel()150     default TileDetailsViewModel getDetailsViewModel() {
151         return null;
152     }
153 
isDestroyed()154     boolean isDestroyed();
155 
156     @ProvidesInterface(version = Callback.VERSION)
157     interface Callback {
158         static final int VERSION = 2;
onStateChanged(State state)159         void onStateChanged(State state);
160     }
161 
162     @ProvidesInterface(version = Icon.VERSION)
163     public static abstract class Icon {
164         public static final int VERSION = 1;
getDrawable(Context context)165         abstract public Drawable getDrawable(Context context);
166 
getInvisibleDrawable(Context context)167         public Drawable getInvisibleDrawable(Context context) {
168             return getDrawable(context);
169         }
170 
171         @Override
hashCode()172         public int hashCode() {
173             return Icon.class.hashCode();
174         }
175 
getPadding()176         public int getPadding() {
177             return 0;
178         }
179 
180         @Override
181         @NonNull
toString()182         public String toString() {
183             return "Icon";
184         }
185     }
186 
187     @ProvidesInterface(version = State.VERSION)
188     public static class State {
189         public static final int VERSION = 1;
190         public static final int DEFAULT_STATE = Tile.STATE_ACTIVE;
191 
192         public Icon icon;
193         public Supplier<Icon> iconSupplier;
194         public int state = DEFAULT_STATE;
195         public CharSequence label;
196         @Nullable public CharSequence secondaryLabel;
197         public CharSequence contentDescription;
198         @Nullable public CharSequence stateDescription;
199         public CharSequence dualLabelContentDescription;
200         public boolean disabledByPolicy;
201         public boolean dualTarget = false;
202         public boolean isTransient = false;
203         public String expandedAccessibilityClassName;
204         public boolean handlesLongClick = true;
205         public boolean handlesSecondaryClick = false;
206         @Nullable
207         public Drawable sideViewCustomDrawable;
208         public String spec;
209 
210         /** Get the state text. */
getStateText(int arrayResId, Resources resources)211         public CharSequence getStateText(int arrayResId, Resources resources) {
212             if (state == Tile.STATE_UNAVAILABLE || this instanceof QSTile.BooleanState) {
213                 String[] array = resources.getStringArray(arrayResId);
214                 return array[state];
215             } else {
216                 return "";
217             }
218         }
219 
220         /**
221          *  If the current secondaryLabel value is not empty, ignore the given input and return
222          *  the current value. Otherwise return current value.
223          */
getSecondaryLabel(CharSequence stateText)224         public CharSequence getSecondaryLabel(CharSequence stateText) {
225             // Use a local reference as the value might change from other threads
226             CharSequence localSecondaryLabel = secondaryLabel;
227             if (TextUtils.isEmpty(localSecondaryLabel)) {
228                 return stateText;
229             }
230             return localSecondaryLabel;
231         }
232 
copyTo(State other)233         public boolean copyTo(State other) {
234             if (other == null) throw new IllegalArgumentException();
235             if (!other.getClass().equals(getClass())) throw new IllegalArgumentException();
236             final boolean changed = !Objects.equals(other.spec, spec)
237                     || !Objects.equals(other.icon, icon)
238                     || !Objects.equals(other.iconSupplier, iconSupplier)
239                     || !Objects.equals(other.label, label)
240                     || !Objects.equals(other.secondaryLabel, secondaryLabel)
241                     || !Objects.equals(other.contentDescription, contentDescription)
242                     || !Objects.equals(other.stateDescription, stateDescription)
243                     || !Objects.equals(other.dualLabelContentDescription,
244                             dualLabelContentDescription)
245                     || !Objects.equals(other.expandedAccessibilityClassName,
246                             expandedAccessibilityClassName)
247                     || !Objects.equals(other.disabledByPolicy, disabledByPolicy)
248                     || !Objects.equals(other.state, state)
249                     || !Objects.equals(other.isTransient, isTransient)
250                     || !Objects.equals(other.dualTarget, dualTarget)
251                     || !Objects.equals(other.handlesLongClick, handlesLongClick)
252                     || !Objects.equals(other.handlesSecondaryClick, handlesSecondaryClick)
253                     || !Objects.equals(other.sideViewCustomDrawable, sideViewCustomDrawable);
254             other.spec = spec;
255             other.icon = icon;
256             other.iconSupplier = iconSupplier;
257             other.label = label;
258             other.secondaryLabel = secondaryLabel;
259             other.contentDescription = contentDescription;
260             other.stateDescription = stateDescription;
261             other.dualLabelContentDescription = dualLabelContentDescription;
262             other.expandedAccessibilityClassName = expandedAccessibilityClassName;
263             other.disabledByPolicy = disabledByPolicy;
264             other.state = state;
265             other.dualTarget = dualTarget;
266             other.isTransient = isTransient;
267             other.handlesLongClick = handlesLongClick;
268             other.handlesSecondaryClick = handlesSecondaryClick;
269             other.sideViewCustomDrawable = sideViewCustomDrawable;
270             return changed;
271         }
272 
273         @Override
toString()274         public String toString() {
275             return toStringBuilder().toString();
276         }
277 
278         // Used in dumps to determine current state of a tile.
279         // This string may be used for CTS testing of tiles, so removing elements is discouraged.
toStringBuilder()280         protected StringBuilder toStringBuilder() {
281             final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
282             sb.append("spec=").append(spec);
283             sb.append(",icon=").append(icon);
284             sb.append(",iconSupplier=").append(iconSupplier);
285             sb.append(",label=").append(label);
286             sb.append(",secondaryLabel=").append(secondaryLabel);
287             sb.append(",contentDescription=").append(contentDescription);
288             sb.append(",stateDescription=").append(stateDescription);
289             sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription);
290             sb.append(",expandedAccessibilityClassName=").append(expandedAccessibilityClassName);
291             sb.append(",disabledByPolicy=").append(disabledByPolicy);
292             sb.append(",dualTarget=").append(dualTarget);
293             sb.append(",isTransient=").append(isTransient);
294             sb.append(",handlesSecondaryClick=").append(handlesSecondaryClick);
295             sb.append(",state=").append(state);
296             sb.append(",sideViewCustomDrawable=").append(sideViewCustomDrawable);
297             return sb.append(']');
298         }
299 
300         @NonNull
copy()301         public State copy() {
302             State state = new State();
303             copyTo(state);
304             return state;
305         }
306     }
307 
308     /**
309      * Distinguished from [BooleanState] for use-case purposes such as allowing null secondary label
310      */
311     @ProvidesInterface(version = AdapterState.VERSION)
312     class AdapterState extends State {
313         public static final int VERSION = 1;
314         public boolean value;
315         public boolean forceExpandIcon;
316 
317         @Override
copyTo(State other)318         public boolean copyTo(State other) {
319             final AdapterState o = (AdapterState) other;
320             final boolean changed = super.copyTo(other)
321                     || o.value != value
322                     || o.forceExpandIcon != forceExpandIcon;
323             o.value = value;
324             o.forceExpandIcon = forceExpandIcon;
325             return changed;
326         }
327 
328         @Override
toStringBuilder()329         protected StringBuilder toStringBuilder() {
330             final StringBuilder rt = super.toStringBuilder();
331             rt.insert(rt.length() - 1, ",value=" + value);
332             rt.insert(rt.length() - 1, ",forceExpandIcon=" + forceExpandIcon);
333             return rt;
334         }
335 
336         @androidx.annotation.NonNull
337         @Override
copy()338         public State copy() {
339             AdapterState state = new AdapterState();
340             copyTo(state);
341             return state;
342         }
343     }
344 
345     @ProvidesInterface(version = BooleanState.VERSION)
346     class BooleanState extends AdapterState {
347         public static final int VERSION = 1;
348 
349         @androidx.annotation.NonNull
350         @Override
copy()351         public State copy() {
352             BooleanState state = new BooleanState();
353             copyTo(state);
354             return state;
355         }
356     }
357 }
358