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 static android.theme.app.TestConfiguration.THEMES; 20 21 import android.app.Activity; 22 import android.content.Intent; 23 import android.os.Build.VERSION; 24 import android.os.Bundle; 25 import android.os.Environment; 26 import android.util.Log; 27 import android.view.WindowManager.LayoutParams; 28 29 import java.io.File; 30 import java.io.IOException; 31 import java.util.concurrent.CountDownLatch; 32 import java.util.concurrent.TimeUnit; 33 34 /** 35 * Generates images by iterating through all themes and launching instances of 36 * {@link ThemeDeviceActivity}. 37 */ 38 public class GenerateImagesActivity extends Activity { 39 private static final String TAG = "GenerateImagesActivity"; 40 41 private static final String OUT_DIR = "cts-theme-assets"; 42 private static final int REQUEST_CODE = 1; 43 44 public static final String EXTRA_REASON = "reason"; 45 46 private final CountDownLatch mLatch = new CountDownLatch(1); 47 48 private File mOutputDir; 49 private File mOutputZip; 50 51 private int mCurrentTheme; 52 private String mFinishReason; 53 private boolean mFinishSuccess; 54 55 class CompressOutputThread extends Thread { run()56 public void run() { 57 compressOutput(); 58 } 59 } 60 61 @Override onCreate(Bundle savedInstanceState)62 protected void onCreate(Bundle savedInstanceState) { 63 super.onCreate(savedInstanceState); 64 65 // Useful for local testing. Not required for CTS harness. 66 getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON); 67 68 // Make sure the device has reasonable assets. 69 String assetDensityFailureMsg = checkAssetDensity(); 70 if (assetDensityFailureMsg != null) { 71 finish("Failed to verify device assets: "+ assetDensityFailureMsg, false); 72 } else { 73 mOutputDir = setupOutputDirectory(); 74 if (mOutputDir == null) { 75 finish("Failed to create output directory: " + OUT_DIR, false); 76 } else { 77 generateNextImage(); 78 } 79 } 80 } 81 checkAssetDensity()82 private String checkAssetDensity() { 83 AssetBucketVerifier.Result result = AssetBucketVerifier.verifyAssetBucket(this); 84 85 String message; 86 if (result.foundAtDensity.contains(result.expectedAtDensity)) { 87 message = null; 88 } else if (result.foundAtDensity.isEmpty()) { 89 message = "Failed to find expected device assets at any density"; 90 } else { 91 StringBuilder foundAtDensityStr = new StringBuilder(result.foundAtDensity.get(0)); 92 for (int i = 1; i < result.foundAtDensity.size(); i++) { 93 foundAtDensityStr.append(", "); 94 foundAtDensityStr.append(result.foundAtDensity.get(i)); 95 } 96 message = "Failed to find device assets at expected density (" 97 + result.expectedAtDensity + "), but found at " + foundAtDensityStr; 98 } 99 100 return message; 101 } 102 setupOutputDirectory()103 private File setupOutputDirectory() { 104 mOutputDir = new File(Environment.getExternalStorageDirectory(), OUT_DIR); 105 ThemeTestUtils.deleteDirectory(mOutputDir); 106 mOutputDir.mkdirs(); 107 108 if (mOutputDir.exists()) { 109 return mOutputDir; 110 } 111 return null; 112 } 113 114 /** 115 * @return whether the test finished successfully 116 */ isFinishSuccess()117 public boolean isFinishSuccess() { 118 return mFinishSuccess; 119 } 120 121 /** 122 * @return user-visible string explaining why the test finished, may be {@code null} if the test 123 * finished unexpectedly 124 */ getFinishReason()125 public String getFinishReason() { 126 return mFinishReason; 127 } 128 129 /** 130 * Starts the activity to generate the next image. 131 */ generateNextImage()132 private void generateNextImage() { 133 // Keep trying themes until one works. 134 boolean success = false; 135 while (mCurrentTheme < THEMES.length && !success) { 136 success = launchThemeDeviceActivity(); 137 mCurrentTheme++; 138 } 139 140 // If we ran out of themes, we're done. 141 if (!success) { 142 CompressOutputThread compressOutputThread = new CompressOutputThread(); 143 compressOutputThread.start(); 144 } 145 } 146 launchThemeDeviceActivity()147 private boolean launchThemeDeviceActivity() { 148 final ThemeInfo theme = THEMES[mCurrentTheme]; 149 if (theme.apiLevel > VERSION.SDK_INT) { 150 Log.v(TAG, "Skipping theme \"" + theme.name 151 + "\" (requires API " + theme.apiLevel + ")"); 152 return false; 153 } 154 155 Log.v(TAG, "Generating images for theme \"" + theme.name + "\"..."); 156 157 final Intent intent = new Intent(this, ThemeDeviceActivity.class); 158 intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); 159 intent.putExtra(ThemeDeviceActivity.EXTRA_THEME, mCurrentTheme); 160 intent.putExtra(ThemeDeviceActivity.EXTRA_OUTPUT_DIR, mOutputDir.getAbsolutePath()); 161 startActivityForResult(intent, REQUEST_CODE); 162 return true; 163 } 164 165 @Override onActivityResult(int requestCode, int resultCode, Intent data)166 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 167 if (resultCode != RESULT_OK) { 168 finish("Failed to generate images for theme " + mCurrentTheme + " (" 169 + data.getStringExtra(EXTRA_REASON) + ")", false); 170 return; 171 } 172 173 generateNextImage(); 174 } 175 compressOutput()176 private void compressOutput() { 177 mOutputZip = new File(mOutputDir.getParentFile(), mOutputDir.getName() + ".zip"); 178 179 if (mOutputZip.exists()) { 180 // Remove any old test results. 181 mOutputZip.delete(); 182 } 183 184 try { 185 ThemeTestUtils.compressDirectory(mOutputDir, mOutputZip); 186 ThemeTestUtils.deleteDirectory(mOutputDir); 187 } catch (IOException e) { 188 e.printStackTrace(); 189 } 190 runOnUiThread(() -> { 191 finish("Image generation complete!", true); 192 }); 193 } 194 finish(String reason, boolean success)195 private void finish(String reason, boolean success) { 196 mFinishSuccess = success; 197 mFinishReason = reason; 198 199 finish(); 200 } 201 202 @Override finish()203 public void finish() { 204 mLatch.countDown(); 205 206 super.finish(); 207 } 208 getOutputZip()209 public File getOutputZip() { 210 return mOutputZip; 211 } 212 waitForCompletion(long timeoutMillis)213 public boolean waitForCompletion(long timeoutMillis) throws InterruptedException { 214 return mLatch.await(timeoutMillis, TimeUnit.MILLISECONDS); 215 } 216 } 217