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.Manifest.permission; 20 import android.app.Activity; 21 import android.app.KeyguardManager; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.pm.PackageManager; 25 import android.os.Build.VERSION; 26 import android.os.Bundle; 27 import android.os.Environment; 28 import android.os.Handler; 29 import android.util.Log; 30 import android.view.WindowManager.LayoutParams; 31 32 import java.io.File; 33 import java.util.concurrent.CountDownLatch; 34 35 /** 36 * Generates images by iterating through all themes and launching instances of 37 * {@link ThemeDeviceActivity}. 38 */ 39 public class GenerateImagesActivity extends Activity { 40 private static final String TAG = "GenerateImagesActivity"; 41 42 private static final String OUT_DIR = "cts-theme-assets"; 43 private static final int REQUEST_CODE = 1; 44 45 private final CountDownLatch mLatch = new CountDownLatch(1); 46 47 private File mOutputDir; 48 private int mCurrentTheme; 49 private String mFinishReason; 50 private boolean mFinishSuccess; 51 52 @Override onCreate(Bundle savedInstanceState)53 protected void onCreate(Bundle savedInstanceState) { 54 super.onCreate(savedInstanceState); 55 56 getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON 57 | LayoutParams.FLAG_TURN_SCREEN_ON 58 | LayoutParams.FLAG_DISMISS_KEYGUARD); 59 60 mOutputDir = new File(Environment.getExternalStorageDirectory(), OUT_DIR); 61 ThemeTestUtils.deleteDirectory(mOutputDir); 62 mOutputDir.mkdirs(); 63 64 if (!mOutputDir.exists()) { 65 finish("Failed to create output directory " + mOutputDir.getAbsolutePath(), false); 66 return; 67 } 68 69 final boolean canDisableKeyguard = checkCallingOrSelfPermission( 70 permission.DISABLE_KEYGUARD) == PackageManager.PERMISSION_GRANTED; 71 if (!canDisableKeyguard) { 72 finish("Not granted permission to disable keyguard", false); 73 return; 74 } 75 76 new KeyguardCheck(this) { 77 @Override 78 public void onSuccess() { 79 generateNextImage(); 80 } 81 82 @Override 83 public void onFailure() { 84 finish("Device is locked", false); 85 } 86 }.start(); 87 } 88 finish(String reason, boolean success)89 private void finish(String reason, boolean success) { 90 mFinishSuccess = success; 91 mFinishReason = reason; 92 93 Log.i(TAG, (success ? "OKAY" : "FAIL") + ":" + reason); 94 finish(); 95 } 96 isFinishSuccess()97 public boolean isFinishSuccess() { 98 return mFinishSuccess; 99 } 100 getFinishReason()101 public String getFinishReason() { 102 return mFinishReason; 103 } 104 105 static abstract class KeyguardCheck implements Runnable { 106 private static final int MAX_RETRIES = 3; 107 private static final int RETRY_DELAY = 500; 108 109 private final Handler mHandler; 110 private final KeyguardManager mKeyguard; 111 112 private int mRetries; 113 KeyguardCheck(Context context)114 public KeyguardCheck(Context context) { 115 mHandler = new Handler(context.getMainLooper()); 116 mKeyguard = (KeyguardManager) context.getSystemService(KEYGUARD_SERVICE); 117 } 118 start()119 public void start() { 120 mRetries = 0; 121 122 mHandler.removeCallbacks(this); 123 mHandler.post(this); 124 } 125 cancel()126 public void cancel() { 127 mHandler.removeCallbacks(this); 128 } 129 130 @Override run()131 public void run() { 132 if (!mKeyguard.isKeyguardLocked()) { 133 onSuccess(); 134 } else if (mRetries < MAX_RETRIES) { 135 mRetries++; 136 mHandler.postDelayed(this, RETRY_DELAY); 137 } else { 138 onFailure(); 139 } 140 141 } 142 onSuccess()143 public abstract void onSuccess(); onFailure()144 public abstract void onFailure(); 145 } 146 getOutputDir()147 public File getOutputDir() { 148 return mOutputDir; 149 } 150 151 /** 152 * Starts the activity to generate the next image. 153 */ generateNextImage()154 private boolean generateNextImage() { 155 final ThemeDeviceActivity.Theme theme = ThemeDeviceActivity.THEMES[mCurrentTheme]; 156 if (theme.apiLevel > VERSION.SDK_INT) { 157 Log.v(TAG, "Skipping theme \"" + theme.name 158 + "\" (requires API " + theme.apiLevel + ")"); 159 return false; 160 } 161 162 Log.v(TAG, "Generating images for theme \"" + theme.name + "\"..."); 163 164 final Intent intent = new Intent(this, ThemeDeviceActivity.class); 165 intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); 166 intent.putExtra(ThemeDeviceActivity.EXTRA_THEME, mCurrentTheme); 167 intent.putExtra(ThemeDeviceActivity.EXTRA_OUTPUT_DIR, mOutputDir.getAbsolutePath()); 168 startActivityForResult(intent, REQUEST_CODE); 169 return true; 170 } 171 172 @Override onActivityResult(int requestCode, int resultCode, Intent data)173 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 174 if (resultCode != RESULT_OK) { 175 Log.i(TAG, "FAIL:Failed to generate images for theme " + mCurrentTheme); 176 finish(); 177 return; 178 } 179 180 // Keep trying themes until one works. 181 boolean success = false; 182 while (++mCurrentTheme < ThemeDeviceActivity.THEMES.length && !success) { 183 success = generateNextImage(); 184 } 185 186 // If we ran out of themes, we're done. 187 if (!success) { 188 finish("Image generation complete!", true); 189 } 190 } 191 finish()192 public void finish() { 193 mLatch.countDown(); 194 super.finish(); 195 } 196 waitForCompletion()197 public void waitForCompletion() throws InterruptedException { 198 mLatch.await(); 199 } 200 } 201