Home | History | Annotate | Download | only in app
      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
     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
     89     protected void onResume() {
     90         super.onResume();
     91 
     92         mIsRunning = true;
     93 
     94         setNextLayout();
     95     }
     96 
     97     @Override
     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
    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      */
    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 {
    165         public BitmapTask(View view, String name) {
    166             super(view, mOutputDir, name);
    167         }
    168 
    169         @Override
    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 
    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 
    348         private Layout(int id, String name) {
    349             this(id, name, null);
    350         }
    351 
    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