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