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