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 
17 package android.theme.app;
18 
19 import android.app.Activity;
20 import android.content.Intent;
21 import android.os.Build;
22 import android.os.Bundle;
23 import android.theme.app.modifiers.DatePickerModifier;
24 import android.theme.app.modifiers.ProgressBarModifier;
25 import android.theme.app.modifiers.SearchViewModifier;
26 import android.theme.app.modifiers.TimePickerModifier;
27 import android.theme.app.modifiers.ViewCheckedModifier;
28 import android.theme.app.modifiers.ViewPressedModifier;
29 import android.util.Log;
30 import android.view.View;
31 import android.view.WindowManager.LayoutParams;
32 import android.widget.DatePicker;
33 
34 import java.io.File;
35 import java.lang.Override;
36 
37 /**
38  * A activity which display various UI elements with non-modifiable themes.
39  */
40 public class ThemeDeviceActivity extends Activity {
41     public static final String EXTRA_THEME = "theme";
42     public static final String EXTRA_OUTPUT_DIR = "outputDir";
43 
44     private static final String TAG = "ThemeDeviceActivity";
45 
46     /**
47      * Delay that allows the Holo-style CalendarView to settle to its final
48      * position.
49      */
50     private static final long HOLO_CALENDAR_VIEW_ADJUSTMENT_DURATION = 540;
51 
52     private Theme mTheme;
53     private ReferenceViewGroup mViewGroup;
54     private File mOutputDir;
55     private int mLayoutIndex;
56     private boolean mIsRunning;
57 
58     @Override
onCreate(Bundle icicle)59     protected void onCreate(Bundle icicle) {
60         super.onCreate(icicle);
61 
62         final Intent intent = getIntent();
63         final int themeIndex = intent.getIntExtra(EXTRA_THEME, -1);
64         if (themeIndex < 0) {
65             Log.e(TAG, "No theme specified");
66             finish();
67         }
68 
69         final String outputDir = intent.getStringExtra(EXTRA_OUTPUT_DIR);
70         if (outputDir == null) {
71             Log.e(TAG, "No output directory specified");
72             finish();
73         }
74 
75         mOutputDir = new File(outputDir);
76         mTheme = THEMES[themeIndex];
77 
78         setTheme(mTheme.id);
79         setContentView(R.layout.theme_test);
80 
81         mViewGroup = (ReferenceViewGroup) findViewById(R.id.reference_view_group);
82 
83         getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON
84                 | LayoutParams.FLAG_TURN_SCREEN_ON
85                 | LayoutParams.FLAG_DISMISS_KEYGUARD );
86     }
87 
88     @Override
onResume()89     protected void onResume() {
90         super.onResume();
91 
92         mIsRunning = true;
93 
94         setNextLayout();
95     }
96 
97     @Override
onPause()98     protected void onPause() {
99         mIsRunning = false;
100 
101         if (!isFinishing()) {
102             // The activity paused for some reason, likely a system crash
103             // dialog. Finish it so we can move to the next theme.
104             Log.w(TAG, "onPause() called without a call to finish()", new RuntimeException());
105             finish();
106         }
107 
108         super.onPause();
109     }
110 
111     @Override
onDestroy()112     protected void onDestroy() {
113         if (mLayoutIndex < LAYOUTS.length) {
114             final Intent data = new Intent();
115             data.putExtra(GenerateImagesActivity.EXTRA_REASON, "Only rendered "
116                     + mLayoutIndex + "/" + LAYOUTS.length + " layouts");
117             setResult(RESULT_CANCELED, data);
118         }
119 
120         super.onDestroy();
121     }
122 
123     /**
124      * Sets the next layout in the UI.
125      */
setNextLayout()126     private void setNextLayout() {
127         if (mLayoutIndex >= LAYOUTS.length) {
128             setResult(RESULT_OK);
129             finish();
130             return;
131         }
132 
133         mViewGroup.removeAllViews();
134 
135         final Layout layout = LAYOUTS[mLayoutIndex++];
136         final String layoutName = String.format("%s_%s", mTheme.name, layout.name);
137         final View view = getLayoutInflater().inflate(layout.id, mViewGroup, false);
138         if (layout.modifier != null) {
139             layout.modifier.modifyView(view);
140         }
141 
142         mViewGroup.addView(view);
143         view.setFocusable(false);
144 
145         Log.v(TAG, "Rendering layout " + layoutName
146                 + " (" + mLayoutIndex + "/" + LAYOUTS.length + ")");
147 
148         final Runnable generateBitmapRunnable = new Runnable() {
149             @Override
150             public void run() {
151                 new BitmapTask(view, layoutName).execute();
152             }
153         };
154 
155         if (view instanceof DatePicker && mTheme.spec == Theme.HOLO) {
156             // The Holo-styled DatePicker uses a CalendarView that has a
157             // non-configurable adjustment duration of 540ms.
158             view.postDelayed(generateBitmapRunnable, HOLO_CALENDAR_VIEW_ADJUSTMENT_DURATION);
159         } else {
160             view.post(generateBitmapRunnable);
161         }
162     }
163 
164     private class BitmapTask extends GenerateBitmapTask {
BitmapTask(View view, String name)165         public BitmapTask(View view, String name) {
166             super(view, mOutputDir, name);
167         }
168 
169         @Override
onPostExecute(Boolean success)170         protected void onPostExecute(Boolean success) {
171             if (success && mIsRunning) {
172                 setNextLayout();
173             } else {
174                 Log.e(TAG, "Failed to render view to bitmap: " + mName + " (activity running? "
175                         + mIsRunning + ")");
176                 finish();
177             }
178         }
179     }
180 
181     /**
182      * A class to encapsulate information about a theme.
183      */
184     static class Theme {
185         public static final int HOLO = 0;
186         public static final int MATERIAL = 1;
187 
188         public final int spec;
189         public final int id;
190         public final int apiLevel;
191         public final String name;
192 
Theme(int spec, int id, int apiLevel, String name)193         private Theme(int spec, int id, int apiLevel, String name) {
194             this.spec = spec;
195             this.id = id;
196             this.apiLevel = apiLevel;
197             this.name = name;
198         }
199     }
200 
201     // List of themes to verify.
202     static final Theme[] THEMES = {
203             // Holo
204             new Theme(Theme.HOLO, android.R.style.Theme_Holo,
205                     Build.VERSION_CODES.HONEYCOMB, "holo"),
206             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Dialog,
207                     Build.VERSION_CODES.HONEYCOMB, "holo_dialog"),
208             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Dialog_MinWidth,
209                     Build.VERSION_CODES.HONEYCOMB, "holo_dialog_minwidth"),
210             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Dialog_NoActionBar,
211                     Build.VERSION_CODES.HONEYCOMB, "holo_dialog_noactionbar"),
212             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Dialog_NoActionBar_MinWidth,
213                     Build.VERSION_CODES.HONEYCOMB, "holo_dialog_noactionbar_minwidth"),
214             new Theme(Theme.HOLO, android.R.style.Theme_Holo_DialogWhenLarge,
215                     Build.VERSION_CODES.HONEYCOMB, "holo_dialogwhenlarge"),
216             new Theme(Theme.HOLO, android.R.style.Theme_Holo_DialogWhenLarge_NoActionBar,
217                     Build.VERSION_CODES.HONEYCOMB, "holo_dialogwhenlarge_noactionbar"),
218             new Theme(Theme.HOLO, android.R.style.Theme_Holo_InputMethod,
219                     Build.VERSION_CODES.HONEYCOMB, "holo_inputmethod"),
220             new Theme(Theme.HOLO, android.R.style.Theme_Holo_NoActionBar,
221                     Build.VERSION_CODES.HONEYCOMB, "holo_noactionbar"),
222             new Theme(Theme.HOLO, android.R.style.Theme_Holo_NoActionBar_Fullscreen,
223                     Build.VERSION_CODES.HONEYCOMB, "holo_noactionbar_fullscreen"),
224             new Theme(Theme.HOLO, android.R.style.Theme_Holo_NoActionBar_Overscan,
225                     Build.VERSION_CODES.JELLY_BEAN_MR2, "holo_noactionbar_overscan"),
226             new Theme(Theme.HOLO, android.R.style.Theme_Holo_NoActionBar_TranslucentDecor,
227                     Build.VERSION_CODES.KITKAT, "holo_noactionbar_translucentdecor"),
228             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Panel,
229                     Build.VERSION_CODES.HONEYCOMB, "holo_panel"),
230             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Wallpaper,
231                     Build.VERSION_CODES.HONEYCOMB, "holo_wallpaper"),
232             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Wallpaper_NoTitleBar,
233                     Build.VERSION_CODES.HONEYCOMB, "holo_wallpaper_notitlebar"),
234 
235             // Holo Light
236             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light,
237                     Build.VERSION_CODES.HONEYCOMB, "holo_light"),
238             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_DarkActionBar,
239                     Build.VERSION_CODES.ICE_CREAM_SANDWICH, "holo_light_darkactionbar"),
240             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_Dialog,
241                     Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog"),
242             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_Dialog_MinWidth,
243                     Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_minwidth"),
244             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_Dialog_NoActionBar,
245                     Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_noactionbar"),
246             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_Dialog_NoActionBar_MinWidth,
247                     Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_noactionbar_minwidth"),
248             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_DialogWhenLarge,
249                     Build.VERSION_CODES.HONEYCOMB, "holo_light_dialogwhenlarge"),
250             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_DialogWhenLarge_NoActionBar,
251                     Build.VERSION_CODES.HONEYCOMB, "holo_light_dialogwhenlarge_noactionbar"),
252             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_NoActionBar,
253                     Build.VERSION_CODES.HONEYCOMB_MR2, "holo_light_noactionbar"),
254             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_NoActionBar_Fullscreen,
255                     Build.VERSION_CODES.HONEYCOMB_MR2, "holo_light_noactionbar_fullscreen"),
256             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_NoActionBar_Overscan,
257                     Build.VERSION_CODES.JELLY_BEAN_MR2, "holo_light_noactionbar_overscan"),
258             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_NoActionBar_TranslucentDecor,
259                     Build.VERSION_CODES.KITKAT, "holo_light_noactionbar_translucentdecor"),
260             new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_Panel,
261                     Build.VERSION_CODES.HONEYCOMB, "holo_light_panel"),
262 
263             // Material
264             new Theme(Theme.MATERIAL, android.R.style.Theme_Material,
265                     Build.VERSION_CODES.LOLLIPOP, "material"),
266             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Dialog,
267                     Build.VERSION_CODES.LOLLIPOP, "material_dialog"),
268             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Dialog_Alert,
269                     Build.VERSION_CODES.LOLLIPOP, "material_dialog_alert"),
270             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Dialog_MinWidth,
271                     Build.VERSION_CODES.LOLLIPOP, "material_dialog_minwidth"),
272             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Dialog_NoActionBar,
273                     Build.VERSION_CODES.LOLLIPOP, "material_dialog_noactionbar"),
274             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Dialog_NoActionBar_MinWidth,
275                     Build.VERSION_CODES.LOLLIPOP, "material_dialog_noactionbar_minwidth"),
276             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Dialog_Presentation,
277                     Build.VERSION_CODES.LOLLIPOP, "material_dialog_presentation"),
278             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_DialogWhenLarge,
279                     Build.VERSION_CODES.LOLLIPOP, "material_dialogwhenlarge"),
280             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_DialogWhenLarge_NoActionBar,
281                     Build.VERSION_CODES.LOLLIPOP, "material_dialogwhenlarge_noactionbar"),
282             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_InputMethod,
283                     Build.VERSION_CODES.LOLLIPOP, "material_inputmethod"),
284             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_NoActionBar,
285                     Build.VERSION_CODES.LOLLIPOP, "material_noactionbar"),
286             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_NoActionBar_Fullscreen,
287                     Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_fullscreen"),
288             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_NoActionBar_Overscan,
289                     Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_overscan"),
290             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_NoActionBar_TranslucentDecor,
291                     Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_translucentdecor"),
292             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Panel,
293                     Build.VERSION_CODES.LOLLIPOP, "material_panel"),
294             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Settings,
295                     Build.VERSION_CODES.LOLLIPOP, "material_settings"),
296             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Voice,
297                     Build.VERSION_CODES.LOLLIPOP, "material_voice"),
298             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Wallpaper,
299                     Build.VERSION_CODES.LOLLIPOP, "material_wallpaper"),
300             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Wallpaper_NoTitleBar,
301                     Build.VERSION_CODES.LOLLIPOP, "material_wallpaper_notitlebar"),
302 
303             // Material Light
304             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light,
305                     Build.VERSION_CODES.LOLLIPOP, "material_light"),
306             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_DarkActionBar,
307                     Build.VERSION_CODES.LOLLIPOP, "material_light_darkactionbar"),
308             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Dialog,
309                     Build.VERSION_CODES.LOLLIPOP, "material_light_dialog"),
310             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Dialog_Alert,
311                     Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_alert"),
312             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Dialog_MinWidth,
313                     Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_minwidth"),
314             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Dialog_NoActionBar,
315                     Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_noactionbar"),
316             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Dialog_NoActionBar_MinWidth,
317                     Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_noactionbar_minwidth"),
318             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Dialog_Presentation,
319                     Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_presentation"),
320             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_DialogWhenLarge,
321                     Build.VERSION_CODES.LOLLIPOP, "material_light_dialogwhenlarge"),
322             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_DialogWhenLarge_NoActionBar,
323                     Build.VERSION_CODES.LOLLIPOP, "material_light_dialogwhenlarge_noactionbar"),
324             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_LightStatusBar,
325                     Build.VERSION_CODES.M, "material_light_lightstatusbar"),
326             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_NoActionBar,
327                     Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar"),
328             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_NoActionBar_Fullscreen,
329                     Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_fullscreen"),
330             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_NoActionBar_Overscan,
331                     Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_overscan"),
332             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_NoActionBar_TranslucentDecor,
333                     Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_translucentdecor"),
334             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Panel,
335                     Build.VERSION_CODES.LOLLIPOP, "material_light_panel"),
336             new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Voice,
337                     Build.VERSION_CODES.LOLLIPOP, "material_light_voice")
338     };
339 
340     /**
341      * A class to encapsulate information about a layout.
342      */
343     private static class Layout {
344         public final int id;
345         public final String name;
346         public final LayoutModifier modifier;
347 
Layout(int id, String name)348         private Layout(int id, String name) {
349             this(id, name, null);
350         }
351 
Layout(int id, String name, LayoutModifier modifier)352         private Layout(int id, String name, LayoutModifier modifier) {
353             this.id = id;
354             this.name = name;
355             this.modifier = modifier;
356         }
357     }
358 
359     // List of layouts to verify for each theme.
360     private static final Layout[] LAYOUTS = {
361             new Layout(R.layout.button, "button"),
362             new Layout(R.layout.button, "button_pressed",
363                     new ViewPressedModifier()),
364             new Layout(R.layout.checkbox, "checkbox"),
365             new Layout(R.layout.checkbox, "checkbox_checked",
366                     new ViewCheckedModifier()),
367             new Layout(R.layout.chronometer, "chronometer"),
368             new Layout(R.layout.color_blue_bright, "color_blue_bright"),
369             new Layout(R.layout.color_blue_dark, "color_blue_dark"),
370             new Layout(R.layout.color_blue_light, "color_blue_light"),
371             new Layout(R.layout.color_green_dark, "color_green_dark"),
372             new Layout(R.layout.color_green_light, "color_green_light"),
373             new Layout(R.layout.color_orange_dark, "color_orange_dark"),
374             new Layout(R.layout.color_orange_light, "color_orange_light"),
375             new Layout(R.layout.color_purple, "color_purple"),
376             new Layout(R.layout.color_red_dark, "color_red_dark"),
377             new Layout(R.layout.color_red_light, "color_red_light"),
378             new Layout(R.layout.datepicker, "datepicker",
379                     new DatePickerModifier()),
380             new Layout(R.layout.edittext, "edittext"),
381             new Layout(R.layout.progressbar_horizontal_0, "progressbar_horizontal_0"),
382             new Layout(R.layout.progressbar_horizontal_100, "progressbar_horizontal_100"),
383             new Layout(R.layout.progressbar_horizontal_50, "progressbar_horizontal_50"),
384             new Layout(R.layout.progressbar_large, "progressbar_large",
385                     new ProgressBarModifier()),
386             new Layout(R.layout.progressbar_small, "progressbar_small",
387                     new ProgressBarModifier()),
388             new Layout(R.layout.progressbar, "progressbar",
389                     new ProgressBarModifier()),
390             new Layout(R.layout.radiobutton_checked, "radiobutton_checked"),
391             new Layout(R.layout.radiobutton, "radiobutton"),
392             new Layout(R.layout.radiogroup_horizontal, "radiogroup_horizontal"),
393             new Layout(R.layout.radiogroup_vertical, "radiogroup_vertical"),
394             new Layout(R.layout.ratingbar_0, "ratingbar_0"),
395             new Layout(R.layout.ratingbar_2point5, "ratingbar_2point5"),
396             new Layout(R.layout.ratingbar_5, "ratingbar_5"),
397             new Layout(R.layout.ratingbar_0, "ratingbar_0_pressed",
398                     new ViewPressedModifier()),
399             new Layout(R.layout.ratingbar_2point5, "ratingbar_2point5_pressed",
400                     new ViewPressedModifier()),
401             new Layout(R.layout.ratingbar_5, "ratingbar_5_pressed",
402                     new ViewPressedModifier()),
403             new Layout(R.layout.searchview, "searchview_query",
404                     new SearchViewModifier(SearchViewModifier.QUERY)),
405             new Layout(R.layout.searchview, "searchview_query_hint",
406                     new SearchViewModifier(SearchViewModifier.QUERY_HINT)),
407             new Layout(R.layout.seekbar_0, "seekbar_0"),
408             new Layout(R.layout.seekbar_100, "seekbar_100"),
409             new Layout(R.layout.seekbar_50, "seekbar_50"),
410             new Layout(R.layout.spinner, "spinner"),
411             new Layout(R.layout.switch_button_checked, "switch_button_checked"),
412             new Layout(R.layout.switch_button, "switch_button"),
413             new Layout(R.layout.textview, "textview"),
414             new Layout(R.layout.timepicker, "timepicker",
415                     new TimePickerModifier()),
416             new Layout(R.layout.togglebutton_checked, "togglebutton_checked"),
417             new Layout(R.layout.togglebutton, "togglebutton"),
418             new Layout(R.layout.zoomcontrols, "zoomcontrols"),
419     };
420 }
421