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 * The duration of the CalendarView adjustment to settle to its final 48 * position. 49 */ 50 private static final long 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 Log.e(TAG, "Not all layouts got rendered: " + mLayoutIndex); 115 setResult(RESULT_CANCELED); 116 } 117 118 super.onDestroy(); 119 } 120 121 /** 122 * Sets the next layout in the UI. 123 */ setNextLayout()124 private void setNextLayout() { 125 if (mLayoutIndex >= LAYOUTS.length) { 126 setResult(RESULT_OK); 127 finish(); 128 return; 129 } 130 131 mViewGroup.removeAllViews(); 132 133 final Layout layout = LAYOUTS[mLayoutIndex++]; 134 final String layoutName = String.format("%s_%s", mTheme.name, layout.name); 135 final View view = getLayoutInflater().inflate(layout.id, mViewGroup, false); 136 if (layout.modifier != null) { 137 layout.modifier.modifyView(view); 138 } 139 140 mViewGroup.addView(view); 141 view.setFocusable(false); 142 143 Log.v(TAG, "Rendering layout " + layoutName 144 + " (" + mLayoutIndex + "/" + LAYOUTS.length + ")"); 145 146 final Runnable generateBitmapRunnable = new Runnable() { 147 @Override 148 public void run() { 149 new BitmapTask(view, layoutName).execute(); 150 } 151 }; 152 153 if (view instanceof DatePicker) { 154 // The Holo-styled DatePicker uses a CalendarView that has a 155 // non-configurable adjustment duration of 540ms. 156 view.postDelayed(generateBitmapRunnable, CALENDAR_VIEW_ADJUSTMENT_DURATION); 157 } else { 158 view.post(generateBitmapRunnable); 159 } 160 } 161 162 private class BitmapTask extends GenerateBitmapTask { BitmapTask(View view, String name)163 public BitmapTask(View view, String name) { 164 super(view, mOutputDir, name); 165 } 166 167 @Override onPostExecute(Boolean success)168 protected void onPostExecute(Boolean success) { 169 if (success && mIsRunning) { 170 setNextLayout(); 171 } else { 172 Log.e(TAG, "Failed to render view to bitmap: " + mName + " (activity running? " 173 + mIsRunning + ")"); 174 finish(); 175 } 176 } 177 } 178 179 /** 180 * A class to encapsulate information about a theme. 181 */ 182 static class Theme { 183 public final int id; 184 public final int apiLevel; 185 public final String name; 186 Theme(int id, int apiLevel, String name)187 private Theme(int id, int apiLevel, String name) { 188 this.id = id; 189 this.apiLevel = apiLevel; 190 this.name = name; 191 } 192 } 193 194 // List of themes to verify. 195 static final Theme[] THEMES = { 196 // Holo 197 new Theme(android.R.style.Theme_Holo, 198 Build.VERSION_CODES.HONEYCOMB, "holo"), 199 new Theme(android.R.style.Theme_Holo_Dialog, 200 Build.VERSION_CODES.HONEYCOMB, "holo_dialog"), 201 new Theme(android.R.style.Theme_Holo_Dialog_MinWidth, 202 Build.VERSION_CODES.HONEYCOMB, "holo_dialog_minwidth"), 203 new Theme(android.R.style.Theme_Holo_Dialog_NoActionBar, 204 Build.VERSION_CODES.HONEYCOMB, "holo_dialog_noactionbar"), 205 new Theme(android.R.style.Theme_Holo_Dialog_NoActionBar_MinWidth, 206 Build.VERSION_CODES.HONEYCOMB, "holo_dialog_noactionbar_minwidth"), 207 new Theme(android.R.style.Theme_Holo_DialogWhenLarge, 208 Build.VERSION_CODES.HONEYCOMB, "holo_dialogwhenlarge"), 209 new Theme(android.R.style.Theme_Holo_DialogWhenLarge_NoActionBar, 210 Build.VERSION_CODES.HONEYCOMB, "holo_dialogwhenlarge_noactionbar"), 211 new Theme(android.R.style.Theme_Holo_InputMethod, 212 Build.VERSION_CODES.HONEYCOMB, "holo_inputmethod"), 213 new Theme(android.R.style.Theme_Holo_NoActionBar, 214 Build.VERSION_CODES.HONEYCOMB, "holo_noactionbar"), 215 new Theme(android.R.style.Theme_Holo_NoActionBar_Fullscreen, 216 Build.VERSION_CODES.HONEYCOMB, "holo_noactionbar_fullscreen"), 217 new Theme(android.R.style.Theme_Holo_NoActionBar_Overscan, 218 Build.VERSION_CODES.JELLY_BEAN_MR2, "holo_noactionbar_overscan"), 219 new Theme(android.R.style.Theme_Holo_NoActionBar_TranslucentDecor, 220 Build.VERSION_CODES.KITKAT, "holo_noactionbar_translucentdecor"), 221 new Theme(android.R.style.Theme_Holo_Panel, 222 Build.VERSION_CODES.HONEYCOMB, "holo_panel"), 223 new Theme(android.R.style.Theme_Holo_Wallpaper, 224 Build.VERSION_CODES.HONEYCOMB, "holo_wallpaper"), 225 new Theme(android.R.style.Theme_Holo_Wallpaper_NoTitleBar, 226 Build.VERSION_CODES.HONEYCOMB, "holo_wallpaper_notitlebar"), 227 228 // Holo Light 229 new Theme(android.R.style.Theme_Holo_Light, 230 Build.VERSION_CODES.HONEYCOMB, "holo_light"), 231 new Theme(android.R.style.Theme_Holo_Light_DarkActionBar, 232 Build.VERSION_CODES.ICE_CREAM_SANDWICH, "holo_light_darkactionbar"), 233 new Theme(android.R.style.Theme_Holo_Light_Dialog, 234 Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog"), 235 new Theme(android.R.style.Theme_Holo_Light_Dialog_MinWidth, 236 Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_minwidth"), 237 new Theme(android.R.style.Theme_Holo_Light_Dialog_NoActionBar, 238 Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_noactionbar"), 239 new Theme(android.R.style.Theme_Holo_Light_Dialog_NoActionBar_MinWidth, 240 Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_noactionbar_minwidth"), 241 new Theme(android.R.style.Theme_Holo_Light_DialogWhenLarge, 242 Build.VERSION_CODES.HONEYCOMB, "holo_light_dialogwhenlarge"), 243 new Theme(android.R.style.Theme_Holo_Light_DialogWhenLarge_NoActionBar, 244 Build.VERSION_CODES.HONEYCOMB, "holo_light_dialogwhenlarge_noactionbar"), 245 new Theme(android.R.style.Theme_Holo_Light_NoActionBar, 246 Build.VERSION_CODES.HONEYCOMB_MR2, "holo_light_noactionbar"), 247 new Theme(android.R.style.Theme_Holo_Light_NoActionBar_Fullscreen, 248 Build.VERSION_CODES.HONEYCOMB_MR2, "holo_light_noactionbar_fullscreen"), 249 new Theme(android.R.style.Theme_Holo_Light_NoActionBar_Overscan, 250 Build.VERSION_CODES.JELLY_BEAN_MR2, "holo_light_noactionbar_overscan"), 251 new Theme(android.R.style.Theme_Holo_Light_NoActionBar_TranslucentDecor, 252 Build.VERSION_CODES.KITKAT, "holo_light_noactionbar_translucentdecor"), 253 new Theme(android.R.style.Theme_Holo_Light_Panel, 254 Build.VERSION_CODES.HONEYCOMB, "holo_light_panel"), 255 256 // Material 257 new Theme(android.R.style.Theme_Material, 258 Build.VERSION_CODES.LOLLIPOP, "material"), 259 new Theme(android.R.style.Theme_Material_Dialog, 260 Build.VERSION_CODES.LOLLIPOP, "material_dialog"), 261 new Theme(android.R.style.Theme_Material_Dialog_Alert, 262 Build.VERSION_CODES.LOLLIPOP, "material_dialog_alert"), 263 new Theme(android.R.style.Theme_Material_Dialog_MinWidth, 264 Build.VERSION_CODES.LOLLIPOP, "material_dialog_minwidth"), 265 new Theme(android.R.style.Theme_Material_Dialog_NoActionBar, 266 Build.VERSION_CODES.LOLLIPOP, "material_dialog_noactionbar"), 267 new Theme(android.R.style.Theme_Material_Dialog_NoActionBar_MinWidth, 268 Build.VERSION_CODES.LOLLIPOP, "material_dialog_noactionbar_minwidth"), 269 new Theme(android.R.style.Theme_Material_Dialog_Presentation, 270 Build.VERSION_CODES.LOLLIPOP, "material_dialog_presentation"), 271 new Theme(android.R.style.Theme_Material_DialogWhenLarge, 272 Build.VERSION_CODES.LOLLIPOP, "material_dialogwhenlarge"), 273 new Theme(android.R.style.Theme_Material_DialogWhenLarge_NoActionBar, 274 Build.VERSION_CODES.LOLLIPOP, "material_dialogwhenlarge_noactionbar"), 275 new Theme(android.R.style.Theme_Material_InputMethod, 276 Build.VERSION_CODES.LOLLIPOP, "material_inputmethod"), 277 new Theme(android.R.style.Theme_Material_NoActionBar, 278 Build.VERSION_CODES.LOLLIPOP, "material_noactionbar"), 279 new Theme(android.R.style.Theme_Material_NoActionBar_Fullscreen, 280 Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_fullscreen"), 281 new Theme(android.R.style.Theme_Material_NoActionBar_Overscan, 282 Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_overscan"), 283 new Theme(android.R.style.Theme_Material_NoActionBar_TranslucentDecor, 284 Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_translucentdecor"), 285 new Theme(android.R.style.Theme_Material_Panel, 286 Build.VERSION_CODES.LOLLIPOP, "material_panel"), 287 new Theme(android.R.style.Theme_Material_Settings, 288 Build.VERSION_CODES.LOLLIPOP, "material_settings"), 289 new Theme(android.R.style.Theme_Material_Voice, 290 Build.VERSION_CODES.LOLLIPOP, "material_voice"), 291 new Theme(android.R.style.Theme_Material_Wallpaper, 292 Build.VERSION_CODES.LOLLIPOP, "material_wallpaper"), 293 new Theme(android.R.style.Theme_Material_Wallpaper_NoTitleBar, 294 Build.VERSION_CODES.LOLLIPOP, "material_wallpaper_notitlebar"), 295 296 // Material Light 297 new Theme(android.R.style.Theme_Material_Light, 298 Build.VERSION_CODES.LOLLIPOP, "material_light"), 299 new Theme(android.R.style.Theme_Material_Light_DarkActionBar, 300 Build.VERSION_CODES.LOLLIPOP, "material_light_darkactionbar"), 301 new Theme(android.R.style.Theme_Material_Light_Dialog, 302 Build.VERSION_CODES.LOLLIPOP, "material_light_dialog"), 303 new Theme(android.R.style.Theme_Material_Light_Dialog_Alert, 304 Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_alert"), 305 new Theme(android.R.style.Theme_Material_Light_Dialog_MinWidth, 306 Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_minwidth"), 307 new Theme(android.R.style.Theme_Material_Light_Dialog_NoActionBar, 308 Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_noactionbar"), 309 new Theme(android.R.style.Theme_Material_Light_Dialog_NoActionBar_MinWidth, 310 Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_noactionbar_minwidth"), 311 new Theme(android.R.style.Theme_Material_Light_Dialog_Presentation, 312 Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_presentation"), 313 new Theme(android.R.style.Theme_Material_Light_DialogWhenLarge, 314 Build.VERSION_CODES.LOLLIPOP, "material_light_dialogwhenlarge"), 315 new Theme(android.R.style.Theme_Material_Light_DialogWhenLarge_NoActionBar, 316 Build.VERSION_CODES.LOLLIPOP, "material_light_dialogwhenlarge_noactionbar"), 317 new Theme(android.R.style.Theme_Material_Light_LightStatusBar, 318 Build.VERSION_CODES.M, "material_light_lightstatusbar"), 319 new Theme(android.R.style.Theme_Material_Light_NoActionBar, 320 Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar"), 321 new Theme(android.R.style.Theme_Material_Light_NoActionBar_Fullscreen, 322 Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_fullscreen"), 323 new Theme(android.R.style.Theme_Material_Light_NoActionBar_Overscan, 324 Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_overscan"), 325 new Theme(android.R.style.Theme_Material_Light_NoActionBar_TranslucentDecor, 326 Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_translucentdecor"), 327 new Theme(android.R.style.Theme_Material_Light_Panel, 328 Build.VERSION_CODES.LOLLIPOP, "material_light_panel"), 329 new Theme(android.R.style.Theme_Material_Light_Voice, 330 Build.VERSION_CODES.LOLLIPOP, "material_light_voice") 331 }; 332 333 /** 334 * A class to encapsulate information about a layout. 335 */ 336 private static class Layout { 337 public final int id; 338 public final String name; 339 public final LayoutModifier modifier; 340 Layout(int id, String name)341 private Layout(int id, String name) { 342 this(id, name, null); 343 } 344 Layout(int id, String name, LayoutModifier modifier)345 private Layout(int id, String name, LayoutModifier modifier) { 346 this.id = id; 347 this.name = name; 348 this.modifier = modifier; 349 } 350 } 351 352 // List of layouts to verify for each theme. 353 private static final Layout[] LAYOUTS = { 354 new Layout(R.layout.button, "button"), 355 new Layout(R.layout.button, "button_pressed", 356 new ViewPressedModifier()), 357 new Layout(R.layout.checkbox, "checkbox"), 358 new Layout(R.layout.checkbox, "checkbox_checked", 359 new ViewCheckedModifier()), 360 new Layout(R.layout.chronometer, "chronometer"), 361 new Layout(R.layout.color_blue_bright, "color_blue_bright"), 362 new Layout(R.layout.color_blue_dark, "color_blue_dark"), 363 new Layout(R.layout.color_blue_light, "color_blue_light"), 364 new Layout(R.layout.color_green_dark, "color_green_dark"), 365 new Layout(R.layout.color_green_light, "color_green_light"), 366 new Layout(R.layout.color_orange_dark, "color_orange_dark"), 367 new Layout(R.layout.color_orange_light, "color_orange_light"), 368 new Layout(R.layout.color_purple, "color_purple"), 369 new Layout(R.layout.color_red_dark, "color_red_dark"), 370 new Layout(R.layout.color_red_light, "color_red_light"), 371 new Layout(R.layout.datepicker, "datepicker", 372 new DatePickerModifier()), 373 new Layout(R.layout.display_info, "display_info"), 374 new Layout(R.layout.edittext, "edittext"), 375 new Layout(R.layout.progressbar_horizontal_0, "progressbar_horizontal_0"), 376 new Layout(R.layout.progressbar_horizontal_100, "progressbar_horizontal_100"), 377 new Layout(R.layout.progressbar_horizontal_50, "progressbar_horizontal_50"), 378 new Layout(R.layout.progressbar_large, "progressbar_large", 379 new ProgressBarModifier()), 380 new Layout(R.layout.progressbar_small, "progressbar_small", 381 new ProgressBarModifier()), 382 new Layout(R.layout.progressbar, "progressbar", 383 new ProgressBarModifier()), 384 new Layout(R.layout.radiobutton_checked, "radiobutton_checked"), 385 new Layout(R.layout.radiobutton, "radiobutton"), 386 new Layout(R.layout.radiogroup_horizontal, "radiogroup_horizontal"), 387 new Layout(R.layout.radiogroup_vertical, "radiogroup_vertical"), 388 new Layout(R.layout.ratingbar_0, "ratingbar_0"), 389 new Layout(R.layout.ratingbar_2point5, "ratingbar_2point5"), 390 new Layout(R.layout.ratingbar_5, "ratingbar_5"), 391 new Layout(R.layout.ratingbar_0, "ratingbar_0_pressed", 392 new ViewPressedModifier()), 393 new Layout(R.layout.ratingbar_2point5, "ratingbar_2point5_pressed", 394 new ViewPressedModifier()), 395 new Layout(R.layout.ratingbar_5, "ratingbar_5_pressed", 396 new ViewPressedModifier()), 397 new Layout(R.layout.searchview, "searchview_query", 398 new SearchViewModifier(SearchViewModifier.QUERY)), 399 new Layout(R.layout.searchview, "searchview_query_hint", 400 new SearchViewModifier(SearchViewModifier.QUERY_HINT)), 401 new Layout(R.layout.seekbar_0, "seekbar_0"), 402 new Layout(R.layout.seekbar_100, "seekbar_100"), 403 new Layout(R.layout.seekbar_50, "seekbar_50"), 404 new Layout(R.layout.spinner, "spinner"), 405 new Layout(R.layout.switch_button_checked, "switch_button_checked"), 406 new Layout(R.layout.switch_button, "switch_button"), 407 new Layout(R.layout.textview, "textview"), 408 new Layout(R.layout.timepicker, "timepicker", 409 new TimePickerModifier()), 410 new Layout(R.layout.togglebutton_checked, "togglebutton_checked"), 411 new Layout(R.layout.togglebutton, "togglebutton"), 412 new Layout(R.layout.zoomcontrols, "zoomcontrols"), 413 }; 414 } 415