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.templates;
18 
19 import android.annotation.CallSuper;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.content.Context;
24 import android.os.Bundle;
25 import android.service.controls.Control;
26 import android.service.controls.actions.ControlAction;
27 import android.util.Log;
28 
29 import com.android.internal.util.Preconditions;
30 
31 import java.lang.annotation.Retention;
32 import java.lang.annotation.RetentionPolicy;
33 
34 /**
35  * An abstract input template for a {@link Control}.
36  *
37  * Specifies what layout is presented to the user for a given {@link Control}.
38  * <p>
39  * Some instances of {@link Control} can originate actions (via user interaction) to modify its
40  * associated state. The actions available to a given {@link Control} are determined by its
41  * {@link ControlTemplate}.
42  * @see ControlAction
43  */
44 public abstract class ControlTemplate {
45 
46     private static final String TAG = "ControlTemplate";
47 
48     private static final String KEY_TEMPLATE_ID = "key_template_id";
49     private static final String KEY_TEMPLATE_TYPE = "key_template_type";
50 
51     /**
52      * Singleton representing a {@link Control} with no input.
53      * @hide
54      */
55     public static final @NonNull ControlTemplate NO_TEMPLATE = new ControlTemplate("") {
56         @Override
57         public int getTemplateType() {
58             return TYPE_NO_TEMPLATE;
59         }
60     };
61 
62     /**
63      * Object returned when there is an unparcelling error.
64      * @hide
65      */
66     private static final @NonNull ControlTemplate ERROR_TEMPLATE = new ControlTemplate("") {
67         @Override
68         public int getTemplateType() {
69             return TYPE_ERROR;
70         }
71     };
72 
73     /**
74      * @hide
75      */
76     @Retention(RetentionPolicy.SOURCE)
77     @IntDef({
78             TYPE_ERROR,
79             TYPE_NO_TEMPLATE,
80             TYPE_TOGGLE,
81             TYPE_RANGE,
82             TYPE_THUMBNAIL,
83             TYPE_TOGGLE_RANGE,
84             TYPE_TEMPERATURE,
85             TYPE_STATELESS
86     })
87     public @interface TemplateType {}
88 
89     /**
90      * Type identifier of the template returned by {@link #getErrorTemplate()}.
91      */
92     public static final @TemplateType int TYPE_ERROR = -1;
93 
94     /**
95      * Type identifier of {@link ControlTemplate#getNoTemplateObject}.
96      */
97     public static final @TemplateType int TYPE_NO_TEMPLATE = 0;
98 
99     /**
100      * Type identifier of {@link ToggleTemplate}.
101      */
102     public static final @TemplateType int TYPE_TOGGLE = 1;
103 
104     /**
105      * Type identifier of {@link RangeTemplate}.
106      */
107     public static final @TemplateType int TYPE_RANGE = 2;
108 
109     /**
110      * Type identifier of {@link ThumbnailTemplate}.
111      */
112     public static final @TemplateType int TYPE_THUMBNAIL = 3;
113 
114     /**
115      * Type identifier of {@link ToggleRangeTemplate}.
116      */
117     public static final @TemplateType int TYPE_TOGGLE_RANGE = 6;
118 
119     /**
120      * Type identifier of {@link TemperatureControlTemplate}.
121      */
122     public static final @TemplateType int TYPE_TEMPERATURE = 7;
123 
124     /**
125      * Type identifier of {@link StatelessTemplate}.
126      */
127     public static final @TemplateType int TYPE_STATELESS = 8;
128 
129     private @NonNull final String mTemplateId;
130 
131     /**
132      * @return the identifier for this object.
133      */
134     @NonNull
getTemplateId()135     public String getTemplateId() {
136         return mTemplateId;
137     }
138 
139     /**
140      * The template type associated with this class.
141      */
getTemplateType()142     public abstract @TemplateType int getTemplateType();
143 
144     /**
145      * Obtain a {@link Bundle} describing this object populated with data.
146      * @return a {@link Bundle} containing the data that represents this object.
147      * @hide
148      */
149     @CallSuper
150     @NonNull
getDataBundle()151     Bundle getDataBundle() {
152         Bundle b = new Bundle();
153         b.putInt(KEY_TEMPLATE_TYPE, getTemplateType());
154         b.putString(KEY_TEMPLATE_ID, mTemplateId);
155         return b;
156     }
157 
ControlTemplate()158     private ControlTemplate() {
159         mTemplateId = "";
160     }
161 
162     /**
163      * @param b
164      * @hide
165      */
ControlTemplate(@onNull Bundle b)166     ControlTemplate(@NonNull Bundle b) {
167         mTemplateId = b.getString(KEY_TEMPLATE_ID);
168     }
169 
170     /**
171      * @hide
172      */
ControlTemplate(@onNull String templateId)173     ControlTemplate(@NonNull String templateId) {
174         Preconditions.checkNotNull(templateId);
175         mTemplateId = templateId;
176     }
177 
178     /**
179      * Call to prepare values for Binder transport.
180      *
181      * @hide
182      */
prepareTemplateForBinder(@onNull Context context)183     public void prepareTemplateForBinder(@NonNull Context context) {}
184 
185     /**
186      *
187      * @param bundle
188      * @return
189      * @hide
190      */
191     @NonNull
createTemplateFromBundle(@ullable Bundle bundle)192     static ControlTemplate createTemplateFromBundle(@Nullable Bundle bundle) {
193         if (bundle == null) {
194             Log.e(TAG, "Null bundle");
195             return ERROR_TEMPLATE;
196         }
197         int type = bundle.getInt(KEY_TEMPLATE_TYPE, TYPE_ERROR);
198         try {
199             switch (type) {
200                 case TYPE_TOGGLE:
201                     return new ToggleTemplate(bundle);
202                 case TYPE_RANGE:
203                     return new RangeTemplate(bundle);
204                 case TYPE_THUMBNAIL:
205                     return new ThumbnailTemplate(bundle);
206                 case TYPE_TOGGLE_RANGE:
207                     return new ToggleRangeTemplate(bundle);
208                 case TYPE_TEMPERATURE:
209                     return new TemperatureControlTemplate(bundle);
210                 case TYPE_STATELESS:
211                     return new StatelessTemplate(bundle);
212                 case TYPE_NO_TEMPLATE:
213                     return NO_TEMPLATE;
214                 case TYPE_ERROR:
215                 default:
216                     return ERROR_TEMPLATE;
217             }
218         } catch (Exception e) {
219             Log.e(TAG, "Error creating template", e);
220             return ERROR_TEMPLATE;
221         }
222     }
223 
224     /**
225      * @return a singleton {@link ControlTemplate} used for indicating an error in unparceling.
226      */
227     @NonNull
getErrorTemplate()228     public static ControlTemplate getErrorTemplate() {
229         return ERROR_TEMPLATE;
230     }
231 
232     /**
233      * Get a singleton {@link ControlTemplate}, which supports no direct user input.
234      *
235      * Used by {@link Control.StatelessBuilder} when there is no known state. Can also be used
236      * in {@link Control.StatefulBuilder} for conveying information to a user about the
237      * {@link Control} but direct user interaction is not desired. Since this template has no
238      * corresponding {@link ControlAction}, any user interaction will launch the
239      * {@link Control#getAppIntent()}.
240      *
241      * @return a singleton {@link ControlTemplate} to indicate no specific template is used by
242      *         this {@link Control}
243      */
244     @NonNull
getNoTemplateObject()245     public static ControlTemplate getNoTemplateObject() {
246         return NO_TEMPLATE;
247     }
248 
249 }
250