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