1 /*
2  * Copyright (C) 2019 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 
17 package android.service.controls;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SuppressLint;
23 import android.app.PendingIntent;
24 import android.content.Intent;
25 import android.content.res.ColorStateList;
26 import android.graphics.drawable.Icon;
27 import android.os.Parcel;
28 import android.os.Parcelable;
29 import android.service.controls.actions.ControlAction;
30 import android.service.controls.templates.ControlTemplate;
31 import android.service.controls.templates.ControlTemplateWrapper;
32 import android.util.Log;
33 
34 import com.android.internal.util.Preconditions;
35 
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 
39 /**
40  * Represents a physical object that can be represented by a {@link ControlTemplate} and whose
41  * properties may be modified through a {@link ControlAction}.
42  *
43  * The information is provided by a {@link ControlsProviderService} and represents static
44  * information (not current status) about the device.
45  * <p>
46  * Each control needs a unique (per provider) identifier that is persistent across reboots of the
47  * system.
48  * <p>
49  * Each {@link Control} will have a name, a subtitle and will optionally belong to a structure
50  * and zone. Some of these values are defined by the user and/or the {@link ControlsProviderService}
51  * and will be used to display the control as well as group them for management.
52  * <p>
53  * Each object will have an associated {@link DeviceTypes.DeviceType}. This will determine the icons and colors
54  * used to display it.
55  * <p>
56  * An {@link Intent} linking to the provider Activity that expands on this {@link Control} and
57  * allows for further actions should be provided.
58  */
59 public final class Control implements Parcelable {
60     private static final String TAG = "Control";
61 
62     private static final int NUM_STATUS = 5;
63     /**
64      * @hide
65      */
66     @Retention(RetentionPolicy.SOURCE)
67     @IntDef({
68             STATUS_UNKNOWN,
69             STATUS_OK,
70             STATUS_NOT_FOUND,
71             STATUS_ERROR,
72             STATUS_DISABLED,
73     })
74     public @interface Status {};
75 
76     /**
77      * Reserved for use with the {@link StatelessBuilder}, and while loading. When state is
78      * requested via {@link ControlsProviderService#createPublisherFor}, use other status codes
79      * to indicate the proper device state.
80      */
81     public static final int STATUS_UNKNOWN = 0;
82 
83     /**
84      * Used to indicate that the state of the device was successfully retrieved. This includes
85      * all scenarios where the device may have a warning for the user, such as "Lock jammed",
86      * or "Vacuum stuck". Any information for the user should be set through
87      * {@link StatefulBuilder#setStatusText}.
88      */
89     public static final int STATUS_OK = 1;
90 
91     /**
92      * The device corresponding to the {@link Control} cannot be found or was removed. The user
93      * will be alerted and directed to the application to resolve.
94      */
95     public static final int STATUS_NOT_FOUND = 2;
96 
97     /**
98      * Used to indicate that there was a temporary error while loading the device state. A default
99      * error message will be displayed in place of any custom text that was set through
100      * {@link StatefulBuilder#setStatusText}.
101      */
102     public static final int STATUS_ERROR = 3;
103 
104     /**
105      * The {@link Control} is currently disabled.  A default error message will be displayed in
106      * place of any custom text that was set through {@link StatefulBuilder#setStatusText}.
107      */
108     public static final int STATUS_DISABLED = 4;
109 
110     private final @NonNull String mControlId;
111     private final @DeviceTypes.DeviceType int mDeviceType;
112     private final @NonNull CharSequence mTitle;
113     private final @NonNull CharSequence mSubtitle;
114     private final @Nullable CharSequence mStructure;
115     private final @Nullable CharSequence mZone;
116     private final @NonNull PendingIntent mAppIntent;
117 
118     private final @Nullable Icon mCustomIcon;
119     private final @Nullable ColorStateList mCustomColor;
120 
121     private final @Status int mStatus;
122     private final @NonNull ControlTemplate mControlTemplate;
123     private final @NonNull CharSequence mStatusText;
124 
125     /**
126      * @param controlId the unique persistent identifier for this object.
127      * @param deviceType the type of device for this control. This will determine icons and colors.
128      * @param title the user facing name of this control (e.g. "Bedroom thermostat").
129      * @param subtitle a user facing subtitle with extra information about this control
130      * @param structure a user facing name for the structure containing the device associated with
131      *                  this control.
132      * @param zone
133      * @param appIntent a {@link PendingIntent} linking to a page to interact with the
134      *                  corresponding device.
135      * @param customIcon
136      * @param customColor
137      * @param status
138      * @param controlTemplate
139      * @param statusText
140      */
Control(@onNull String controlId, @DeviceTypes.DeviceType int deviceType, @NonNull CharSequence title, @NonNull CharSequence subtitle, @Nullable CharSequence structure, @Nullable CharSequence zone, @NonNull PendingIntent appIntent, @Nullable Icon customIcon, @Nullable ColorStateList customColor, @Status int status, @NonNull ControlTemplate controlTemplate, @NonNull CharSequence statusText)141     Control(@NonNull String controlId,
142             @DeviceTypes.DeviceType int deviceType,
143             @NonNull CharSequence title,
144             @NonNull CharSequence subtitle,
145             @Nullable CharSequence structure,
146             @Nullable CharSequence zone,
147             @NonNull PendingIntent appIntent,
148             @Nullable Icon customIcon,
149             @Nullable ColorStateList customColor,
150             @Status int status,
151             @NonNull ControlTemplate controlTemplate,
152             @NonNull CharSequence statusText) {
153         Preconditions.checkNotNull(controlId);
154         Preconditions.checkNotNull(title);
155         Preconditions.checkNotNull(subtitle);
156         Preconditions.checkNotNull(appIntent);
157         Preconditions.checkNotNull(controlTemplate);
158         Preconditions.checkNotNull(statusText);
159         mControlId = controlId;
160         if (!DeviceTypes.validDeviceType(deviceType)) {
161             Log.e(TAG, "Invalid device type:" + deviceType);
162             mDeviceType = DeviceTypes.TYPE_UNKNOWN;
163         } else {
164             mDeviceType = deviceType;
165         }
166         mTitle = title;
167         mSubtitle = subtitle;
168         mStructure = structure;
169         mZone = zone;
170         mAppIntent = appIntent;
171 
172         mCustomColor = customColor;
173         mCustomIcon = customIcon;
174 
175         if (status < 0 || status >= NUM_STATUS) {
176             mStatus = STATUS_UNKNOWN;
177             Log.e(TAG, "Status unknown:" + status);
178         } else {
179             mStatus = status;
180         }
181         mControlTemplate = controlTemplate;
182         mStatusText = statusText;
183     }
184 
185     /**
186      * @param in
187      * @hide
188      */
Control(Parcel in)189     Control(Parcel in) {
190         mControlId = in.readString();
191         mDeviceType = in.readInt();
192         mTitle = in.readCharSequence();
193         mSubtitle = in.readCharSequence();
194         if (in.readByte() == (byte) 1) {
195             mStructure = in.readCharSequence();
196         } else {
197             mStructure = null;
198         }
199         if (in.readByte() == (byte) 1) {
200             mZone = in.readCharSequence();
201         } else {
202             mZone = null;
203         }
204         mAppIntent = PendingIntent.CREATOR.createFromParcel(in);
205 
206         if (in.readByte() == (byte) 1) {
207             mCustomIcon = Icon.CREATOR.createFromParcel(in);
208         } else {
209             mCustomIcon = null;
210         }
211 
212         if (in.readByte() == (byte) 1) {
213             mCustomColor = ColorStateList.CREATOR.createFromParcel(in);
214         } else {
215             mCustomColor = null;
216         }
217 
218         mStatus = in.readInt();
219         ControlTemplateWrapper wrapper = ControlTemplateWrapper.CREATOR.createFromParcel(in);
220         mControlTemplate = wrapper.getWrappedTemplate();
221         mStatusText = in.readCharSequence();
222     }
223 
224     /**
225      * @return the identifier for the {@link Control}
226      */
227     @NonNull
getControlId()228     public String getControlId() {
229         return mControlId;
230     }
231 
232 
233     /**
234      * @return type of device represented by this {@link Control}, used to determine the default
235      *         icon and color
236      */
237     @DeviceTypes.DeviceType
getDeviceType()238     public int getDeviceType() {
239         return mDeviceType;
240     }
241 
242     /**
243      * @return the user facing name of the {@link Control}
244      */
245     @NonNull
getTitle()246     public CharSequence getTitle() {
247         return mTitle;
248     }
249 
250     /**
251      * @return additional information about the {@link Control}, to appear underneath the title
252      */
253     @NonNull
getSubtitle()254     public CharSequence getSubtitle() {
255         return mSubtitle;
256     }
257 
258     /**
259      * Optional top-level group to help define the {@link Control}'s location, visible to the user.
260      * If not present, the application name will be used as the top-level group. A structure
261      * contains zones which contains controls.
262      *
263      * @return name of the structure containing the control
264      */
265     @Nullable
getStructure()266     public CharSequence getStructure() {
267         return mStructure;
268     }
269 
270     /**
271      * Optional group name to help define the {@link Control}'s location within a structure,
272      * visible to the user. A structure contains zones which contains controls.
273      *
274      * @return name of the zone containing the control
275      */
276     @Nullable
getZone()277     public CharSequence getZone() {
278         return mZone;
279     }
280 
281     /**
282      * @return a {@link PendingIntent} linking to an Activity for the {@link Control}
283      */
284     @NonNull
getAppIntent()285     public PendingIntent getAppIntent() {
286         return mAppIntent;
287     }
288 
289     /**
290      * Optional icon to be shown with the {@link Control}. It is highly recommended
291      * to let the system default the icon unless the default icon is not suitable.
292      *
293      * @return icon to show
294      */
295     @Nullable
getCustomIcon()296     public Icon getCustomIcon() {
297         return mCustomIcon;
298     }
299 
300     /**
301      * Optional color to be shown with the {@link Control}. It is highly recommended
302      * to let the system default the color unless the default is not suitable for the
303      * application.
304      *
305      * @return background color to use
306      */
307     @Nullable
getCustomColor()308     public ColorStateList getCustomColor() {
309         return mCustomColor;
310     }
311 
312     /**
313      * @return status of the {@link Control}, used to convey information about the attempt to
314      *         fetch the current state
315      */
316     @Status
getStatus()317     public int getStatus() {
318         return mStatus;
319     }
320 
321     /**
322      * @return instance of {@link ControlTemplate}, that defines how the {@link Control} will
323      *         behave and what interactions are available to the user
324      */
325     @NonNull
getControlTemplate()326     public ControlTemplate getControlTemplate() {
327         return mControlTemplate;
328     }
329 
330     /**
331      * @return user-facing text description of the {@link Control}'s status, describing its current
332      *         state
333      */
334     @NonNull
getStatusText()335     public CharSequence getStatusText() {
336         return mStatusText;
337     }
338 
339     @Override
describeContents()340     public int describeContents() {
341         return 0;
342     }
343 
344     @Override
writeToParcel(@onNull Parcel dest, int flags)345     public void writeToParcel(@NonNull Parcel dest, int flags) {
346         dest.writeString(mControlId);
347         dest.writeInt(mDeviceType);
348         dest.writeCharSequence(mTitle);
349         dest.writeCharSequence(mSubtitle);
350         if (mStructure != null) {
351             dest.writeByte((byte) 1);
352             dest.writeCharSequence(mStructure);
353         } else {
354             dest.writeByte((byte) 0);
355         }
356         if (mZone != null) {
357             dest.writeByte((byte) 1);
358             dest.writeCharSequence(mZone);
359         } else {
360             dest.writeByte((byte) 0);
361         }
362         mAppIntent.writeToParcel(dest, flags);
363         if (mCustomIcon != null) {
364             dest.writeByte((byte) 1);
365             mCustomIcon.writeToParcel(dest, flags);
366         } else {
367             dest.writeByte((byte) 0);
368         }
369         if (mCustomColor != null) {
370             dest.writeByte((byte) 1);
371             mCustomColor.writeToParcel(dest, flags);
372         } else {
373             dest.writeByte((byte) 0);
374         }
375 
376         dest.writeInt(mStatus);
377         new ControlTemplateWrapper(mControlTemplate).writeToParcel(dest, flags);
378         dest.writeCharSequence(mStatusText);
379     }
380 
381     public static final @NonNull Creator<Control> CREATOR = new Creator<Control>() {
382         @Override
383         public Control createFromParcel(@NonNull Parcel source) {
384             return new Control(source);
385         }
386 
387         @Override
388         public Control[] newArray(int size) {
389             return new Control[size];
390         }
391     };
392 
393     /**
394      * Builder class for {@link Control}.
395      *
396      * This class facilitates the creation of {@link Control} with no state. Must be used to
397      * provide controls for {@link ControlsProviderService#createPublisherForAllAvailable} and
398      * {@link ControlsProviderService#createPublisherForSuggested}.
399      *
400      * It provides the following defaults for non-optional parameters:
401      * <ul>
402      *     <li> Device type: {@link DeviceTypes#TYPE_UNKNOWN}
403      *     <li> Title: {@code ""}
404      *     <li> Subtitle: {@code ""}
405      * </ul>
406      * This fixes the values relating to state of the {@link Control} as required by
407      * {@link ControlsProviderService#createPublisherForAllAvailable}:
408      * <ul>
409      *     <li> Status: {@link Status#STATUS_UNKNOWN}
410      *     <li> Control template: {@link ControlTemplate#getNoTemplateObject}
411      *     <li> Status text: {@code ""}
412      * </ul>
413      */
414     @SuppressLint("MutableBareField")
415     public static final class StatelessBuilder {
416         private static final String TAG = "StatelessBuilder";
417         private @NonNull String mControlId;
418         private @DeviceTypes.DeviceType int mDeviceType = DeviceTypes.TYPE_UNKNOWN;
419         private @NonNull CharSequence mTitle = "";
420         private @NonNull CharSequence mSubtitle = "";
421         private @Nullable CharSequence mStructure;
422         private @Nullable CharSequence mZone;
423         private @NonNull PendingIntent mAppIntent;
424         private @Nullable Icon mCustomIcon;
425         private @Nullable ColorStateList mCustomColor;
426 
427         /**
428          * @param controlId the identifier for the {@link Control}
429          * @param appIntent the pending intent linking to the device Activity
430          */
StatelessBuilder(@onNull String controlId, @NonNull PendingIntent appIntent)431         public StatelessBuilder(@NonNull String controlId,
432                 @NonNull PendingIntent appIntent) {
433             Preconditions.checkNotNull(controlId);
434             Preconditions.checkNotNull(appIntent);
435             mControlId = controlId;
436             mAppIntent = appIntent;
437         }
438 
439         /**
440          * Creates a {@link StatelessBuilder} using an existing {@link Control} as a base.
441          *
442          * @param control base for the builder.
443          */
StatelessBuilder(@onNull Control control)444         public StatelessBuilder(@NonNull Control control) {
445             Preconditions.checkNotNull(control);
446             mControlId = control.mControlId;
447             mDeviceType = control.mDeviceType;
448             mTitle = control.mTitle;
449             mSubtitle = control.mSubtitle;
450             mStructure = control.mStructure;
451             mZone = control.mZone;
452             mAppIntent = control.mAppIntent;
453             mCustomIcon = control.mCustomIcon;
454             mCustomColor = control.mCustomColor;
455         }
456 
457         /**
458          * @param controlId the identifier for the {@link Control}
459          * @return {@code this}
460          */
461         @NonNull
setControlId(@onNull String controlId)462         public StatelessBuilder setControlId(@NonNull String controlId) {
463             Preconditions.checkNotNull(controlId);
464             mControlId = controlId;
465             return this;
466         }
467 
468         /**
469          * @param deviceType type of device represented by this {@link Control}, used to
470          *                   determine the default icon and color
471          * @return {@code this}
472          */
473         @NonNull
setDeviceType(@eviceTypes.DeviceType int deviceType)474         public StatelessBuilder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
475             if (!DeviceTypes.validDeviceType(deviceType)) {
476                 Log.e(TAG, "Invalid device type:" + deviceType);
477                 mDeviceType = DeviceTypes.TYPE_UNKNOWN;
478             } else {
479                 mDeviceType = deviceType;
480             }
481             return this;
482         }
483 
484         /**
485          * @param title the user facing name of the {@link Control}
486          * @return {@code this}
487          */
488         @NonNull
setTitle(@onNull CharSequence title)489         public StatelessBuilder setTitle(@NonNull CharSequence title) {
490             Preconditions.checkNotNull(title);
491             mTitle = title;
492             return this;
493         }
494 
495         /**
496          * @param subtitle additional information about the {@link Control}, to appear underneath
497          *                 the title
498          * @return {@code this}
499          */
500         @NonNull
setSubtitle(@onNull CharSequence subtitle)501         public StatelessBuilder setSubtitle(@NonNull CharSequence subtitle) {
502             Preconditions.checkNotNull(subtitle);
503             mSubtitle = subtitle;
504             return this;
505         }
506 
507         /**
508          * Optional top-level group to help define the {@link Control}'s location, visible to the
509          * user. If not present, the application name will be used as the top-level group. A
510          * structure contains zones which contains controls.
511          *
512          * @param structure name of the structure containing the control
513          * @return {@code this}
514          */
515         @NonNull
setStructure(@ullable CharSequence structure)516         public StatelessBuilder setStructure(@Nullable CharSequence structure) {
517             mStructure = structure;
518             return this;
519         }
520 
521         /**
522          * Optional group name to help define the {@link Control}'s location within a structure,
523          * visible to the user. A structure contains zones which contains controls.
524          *
525          * @param zone name of the zone containing the control
526          * @return {@code this}
527          */
528         @NonNull
setZone(@ullable CharSequence zone)529         public StatelessBuilder setZone(@Nullable CharSequence zone) {
530             mZone = zone;
531             return this;
532         }
533 
534         /**
535          * @param appIntent a {@link PendingIntent} linking to an Activity for the {@link Control}
536          * @return {@code this}
537          */
538         @NonNull
setAppIntent(@onNull PendingIntent appIntent)539         public StatelessBuilder setAppIntent(@NonNull PendingIntent appIntent) {
540             Preconditions.checkNotNull(appIntent);
541             mAppIntent = appIntent;
542             return this;
543         }
544 
545         /**
546          * Optional icon to be shown with the {@link Control}. It is highly recommended
547          * to let the system default the icon unless the default icon is not suitable.
548          *
549          * @param customIcon icon to show
550          * @return {@code this}
551          */
552         @NonNull
setCustomIcon(@ullable Icon customIcon)553         public StatelessBuilder setCustomIcon(@Nullable Icon customIcon) {
554             mCustomIcon = customIcon;
555             return this;
556         }
557 
558         /**
559          * Optional color to be shown with the {@link Control}. It is highly recommended
560          * to let the system default the color unless the default is not suitable for the
561          * application.
562          *
563          * @param customColor background color to use
564          * @return {@code this}
565          */
566         @NonNull
setCustomColor(@ullable ColorStateList customColor)567         public StatelessBuilder setCustomColor(@Nullable ColorStateList customColor) {
568             mCustomColor = customColor;
569             return this;
570         }
571 
572         /**
573          * @return a valid {@link Control}
574          */
575         @NonNull
build()576         public Control build() {
577             return new Control(mControlId,
578                     mDeviceType,
579                     mTitle,
580                     mSubtitle,
581                     mStructure,
582                     mZone,
583                     mAppIntent,
584                     mCustomIcon,
585                     mCustomColor,
586                     STATUS_UNKNOWN,
587                     ControlTemplate.NO_TEMPLATE,
588                     "");
589         }
590     }
591 
592     /**
593      * Builder class for {@link Control} that contains state information.
594      *
595      * State information is passed through an instance of a {@link ControlTemplate} and will
596      * determine how the user can interact with the {@link Control}. User interactions will
597      * be sent through the method call {@link ControlsProviderService#performControlAction}
598      * with an instance of {@link ControlAction} to convey any potential new value.
599      *
600      * Must be used to provide controls for {@link ControlsProviderService#createPublisherFor}.
601      *
602      * It provides the following defaults for non-optional parameters:
603      * <ul>
604      *     <li> Device type: {@link DeviceTypes#TYPE_UNKNOWN}
605      *     <li> Title: {@code ""}
606      *     <li> Subtitle: {@code ""}
607      *     <li> Status: {@link Status#STATUS_UNKNOWN}
608      *     <li> Control template: {@link ControlTemplate#getNoTemplateObject}
609      *     <li> Status text: {@code ""}
610      * </ul>
611      */
612     public static final class StatefulBuilder {
613         private static final String TAG = "StatefulBuilder";
614         private @NonNull String mControlId;
615         private @DeviceTypes.DeviceType int mDeviceType = DeviceTypes.TYPE_UNKNOWN;
616         private @NonNull CharSequence mTitle = "";
617         private @NonNull CharSequence mSubtitle = "";
618         private @Nullable CharSequence mStructure;
619         private @Nullable CharSequence mZone;
620         private @NonNull PendingIntent mAppIntent;
621         private @Nullable Icon mCustomIcon;
622         private @Nullable ColorStateList mCustomColor;
623         private @Status int mStatus = STATUS_UNKNOWN;
624         private @NonNull ControlTemplate mControlTemplate = ControlTemplate.NO_TEMPLATE;
625         private @NonNull CharSequence mStatusText = "";
626 
627         /**
628          * @param controlId the identifier for the {@link Control}.
629          * @param appIntent the pending intent linking to the device Activity.
630          */
StatefulBuilder(@onNull String controlId, @NonNull PendingIntent appIntent)631         public StatefulBuilder(@NonNull String controlId,
632                 @NonNull PendingIntent appIntent) {
633             Preconditions.checkNotNull(controlId);
634             Preconditions.checkNotNull(appIntent);
635             mControlId = controlId;
636             mAppIntent = appIntent;
637         }
638 
639         /**
640          * Creates a {@link StatelessBuilder} using an existing {@link Control} as a base.
641          *
642          * @param control base for the builder.
643          */
StatefulBuilder(@onNull Control control)644         public StatefulBuilder(@NonNull Control control) {
645             Preconditions.checkNotNull(control);
646             mControlId = control.mControlId;
647             mDeviceType = control.mDeviceType;
648             mTitle = control.mTitle;
649             mSubtitle = control.mSubtitle;
650             mStructure = control.mStructure;
651             mZone = control.mZone;
652             mAppIntent = control.mAppIntent;
653             mCustomIcon = control.mCustomIcon;
654             mCustomColor = control.mCustomColor;
655             mStatus = control.mStatus;
656             mControlTemplate = control.mControlTemplate;
657             mStatusText = control.mStatusText;
658         }
659 
660         /**
661          * @param controlId the identifier for the {@link Control}.
662          * @return {@code this}
663          */
664         @NonNull
setControlId(@onNull String controlId)665         public StatefulBuilder setControlId(@NonNull String controlId) {
666             Preconditions.checkNotNull(controlId);
667             mControlId = controlId;
668             return this;
669         }
670 
671         /**
672          * @param deviceType type of device represented by this {@link Control}, used to
673          *                   determine the default icon and color
674          * @return {@code this}
675          */
676         @NonNull
setDeviceType(@eviceTypes.DeviceType int deviceType)677         public StatefulBuilder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
678             if (!DeviceTypes.validDeviceType(deviceType)) {
679                 Log.e(TAG, "Invalid device type:" + deviceType);
680                 mDeviceType = DeviceTypes.TYPE_UNKNOWN;
681             } else {
682                 mDeviceType = deviceType;
683             }
684             return this;
685         }
686 
687         /**
688          * @param title the user facing name of the {@link Control}
689          * @return {@code this}
690          */
691         @NonNull
setTitle(@onNull CharSequence title)692         public StatefulBuilder setTitle(@NonNull CharSequence title) {
693             Preconditions.checkNotNull(title);
694             mTitle = title;
695             return this;
696         }
697 
698         /**
699          * @param subtitle additional information about the {@link Control}, to appear underneath
700          *                 the title
701          * @return {@code this}
702          */
703         @NonNull
setSubtitle(@onNull CharSequence subtitle)704         public StatefulBuilder setSubtitle(@NonNull CharSequence subtitle) {
705             Preconditions.checkNotNull(subtitle);
706             mSubtitle = subtitle;
707             return this;
708         }
709 
710         /**
711          * Optional top-level group to help define the {@link Control}'s location, visible to the
712          * user. If not present, the application name will be used as the top-level group. A
713          * structure contains zones which contains controls.
714          *
715          * @param structure name of the structure containing the control
716          * @return {@code this}
717          */
718         @NonNull
setStructure(@ullable CharSequence structure)719         public StatefulBuilder setStructure(@Nullable CharSequence structure) {
720             mStructure = structure;
721             return this;
722         }
723 
724         /**
725          * Optional group name to help define the {@link Control}'s location within a structure,
726          * visible to the user. A structure contains zones which contains controls.
727          *
728          * @param zone name of the zone containing the control
729          * @return {@code this}
730          */
731         @NonNull
setZone(@ullable CharSequence zone)732         public StatefulBuilder setZone(@Nullable CharSequence zone) {
733             mZone = zone;
734             return this;
735         }
736 
737         /**
738          * @param appIntent a {@link PendingIntent} linking to an Activity for the {@link Control}
739          * @return {@code this}
740          */
741         @NonNull
setAppIntent(@onNull PendingIntent appIntent)742         public StatefulBuilder setAppIntent(@NonNull PendingIntent appIntent) {
743             Preconditions.checkNotNull(appIntent);
744             mAppIntent = appIntent;
745             return this;
746         }
747 
748         /**
749          * Optional icon to be shown with the {@link Control}. It is highly recommended
750          * to let the system default the icon unless the default icon is not suitable.
751          *
752          * @param customIcon icon to show
753          * @return {@code this}
754          */
755         @NonNull
setCustomIcon(@ullable Icon customIcon)756         public StatefulBuilder setCustomIcon(@Nullable Icon customIcon) {
757             mCustomIcon = customIcon;
758             return this;
759         }
760 
761         /**
762          * Optional color to be shown with the {@link Control}. It is highly recommended
763          * to let the system default the color unless the default is not suitable for the
764          * application.
765          *
766          * @param customColor background color to use
767          * @return {@code this}
768          */
769         @NonNull
setCustomColor(@ullable ColorStateList customColor)770         public StatefulBuilder setCustomColor(@Nullable ColorStateList customColor) {
771             mCustomColor = customColor;
772             return this;
773         }
774 
775         /**
776          * @param status status of the {@link Control}, used to convey information about the
777          *               attempt to fetch the current state
778          * @return {@code this}
779          */
780         @NonNull
setStatus(@tatus int status)781         public StatefulBuilder setStatus(@Status int status) {
782             if (status < 0 || status >= NUM_STATUS) {
783                 mStatus = STATUS_UNKNOWN;
784                 Log.e(TAG, "Status unknown:" + status);
785             } else {
786                 mStatus = status;
787             }
788             return this;
789         }
790 
791         /**
792          * Set the {@link ControlTemplate} to define the primary user interaction
793          *
794          * Devices may support a variety of user interactions, and all interactions cannot be
795          * represented with a single {@link ControlTemplate}. Therefore, the selected template
796          * should be most closely aligned with what the expected primary device action will be.
797          * Any secondary interactions can be done via the {@link #setAppIntent(PendingIntent)}.
798          *
799          * @param controlTemplate instance of {@link ControlTemplate}, that defines how the
800          *                        {@link Control} will behave and what interactions are
801          *                        available to the user
802          * @return {@code this}
803          */
804         @NonNull
setControlTemplate(@onNull ControlTemplate controlTemplate)805         public StatefulBuilder setControlTemplate(@NonNull ControlTemplate controlTemplate) {
806             Preconditions.checkNotNull(controlTemplate);
807             mControlTemplate = controlTemplate;
808             return this;
809         }
810 
811         /**
812          * @param statusText user-facing text description of the {@link Control}'s status,
813          *                   describing its current state
814          * @return {@code this}
815          */
816         @NonNull
setStatusText(@onNull CharSequence statusText)817         public StatefulBuilder setStatusText(@NonNull CharSequence statusText) {
818             Preconditions.checkNotNull(statusText);
819             mStatusText = statusText;
820             return this;
821         }
822 
823         /**
824          * @return a valid {@link Control}
825          */
826         @NonNull
build()827         public Control build() {
828             return new Control(mControlId,
829                     mDeviceType,
830                     mTitle,
831                     mSubtitle,
832                     mStructure,
833                     mZone,
834                     mAppIntent,
835                     mCustomIcon,
836                     mCustomColor,
837                     mStatus,
838                     mControlTemplate,
839                     mStatusText);
840         }
841     }
842 }
843