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 } 73 74 mOutputDir = setupOutputDirectory(); 75 if (mOutputDir == null) { 76 finish("Failed to create output directory " + OUT_DIR, false); 77 } else { 78 mOutputDir = setupOutputDirectory(); 79 if (mOutputDir == null) { 80 finish("Failed to create output directory: " + OUT_DIR, false); 81 } else { 82 generateNextImage(); 83 } 84 } 85 } 86 checkAssetDensity()87 private String checkAssetDensity() { 88 AssetBucketVerifier.Result result = AssetBucketVerifier.verifyAssetBucket(this); 89 90 String message; 91 if (result.foundAtDensity.contains(result.expectedAtDensity)) { 92 message = null; 93 } else if (result.foundAtDensity.isEmpty()) { 94 message = "Failed to find expected device assets at any density"; 95 } else { 96 StringBuilder foundAtDensityStr = new StringBuilder(result.foundAtDensity.get(0)); 97 for (int i = 1; i < result.foundAtDensity.size(); i++) { 98 foundAtDensityStr.append(", "); 99 foundAtDensityStr.append(result.foundAtDensity.get(i)); 100 } 101 message = "Failed to find device assets at expected density (" 102 + result.expectedAtDensity + "), but found at " + foundAtDensityStr; 103 } 104 105 return message; 106 } 107 setupOutputDirectory()108 private File setupOutputDirectory() { 109 mOutputDir = new File(Environment.getExternalStorageDirectory(), OUT_DIR); 110 ThemeTestUtils.deleteDirectory(mOutputDir); 111 mOutputDir.mkdirs(); 112 113 if (mOutputDir.exists()) { 114 return mOutputDir; 115 } 116 return null; 117 } 118 119 /** 120 * @return whether the test finished successfully 121 */ isFinishSuccess()122 public boolean isFinishSuccess() { 123 return mFinishSuccess; 124 } 125 126 /** 127 * @return user-visible string explaining why the test finished, may be {@code null} if the test 128 * finished unexpectedly 129 */ getFinishReason()130 public String getFinishReason() { 131 return mFinishReason; 132 } 133 134 /** 135 * Starts the activity to generate the next image. 136 */ generateNextImage()137 private void generateNextImage() { 138 // Keep trying themes until one works. 139 boolean success = false; 140 while (mCurrentTheme < THEMES.length && !success) { 141 success = launchThemeDeviceActivity(); 142 mCurrentTheme++; 143 } 144 145 // If we ran out of themes, we're done. 146 if (!success) { 147 CompressOutputThread compressOutputThread = new CompressOutputThread(); 148 compressOutputThread.start(); 149 } 150 } 151 launchThemeDeviceActivity()152 private boolean launchThemeDeviceActivity() { 153 final ThemeInfo theme = THEMES[mCurrentTheme]; 154 if (theme.apiLevel > VERSION.SDK_INT) { 155 Log.v(TAG, "Skipping theme \"" + theme.name 156 + "\" (requires API " + theme.apiLevel + ")"); 157 return false; 158 } 159 160 Log.v(TAG, "Generating images for theme \"" + theme.name + "\"..."); 161 162 final Intent intent = new Intent(this, ThemeDeviceActivity.class); 163 intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); 164 intent.putExtra(ThemeDeviceActivity.EXTRA_THEME, mCurrentTheme); 165 intent.putExtra(ThemeDeviceActivity.EXTRA_OUTPUT_DIR, mOutputDir.getAbsolutePath()); 166 startActivityForResult(intent, REQUEST_CODE); 167 return true; 168 } 169 170 @Override onActivityResult(int requestCode, int resultCode, Intent data)171 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 172 if (resultCode != RESULT_OK) { 173 finish("Failed to generate images for theme " + mCurrentTheme + " (" 174 + data.getStringExtra(EXTRA_REASON) + ")", false); 175 return; 176 } 177 178 generateNextImage(); 179 } 180 compressOutput()181 private void compressOutput() { 182 mOutputZip = new File(mOutputDir.getParentFile(), mOutputDir.getName() + ".zip"); 183 184 if (mOutputZip.exists()) { 185 // Remove any old test results. 186 mOutputZip.delete(); 187 } 188 189 try { 190 ThemeTestUtils.compressDirectory(mOutputDir, mOutputZip); 191 ThemeTestUtils.deleteDirectory(mOutputDir); 192 } catch (IOException e) { 193 e.printStackTrace(); 194 } 195 runOnUiThread(() -> { 196 finish("Image generation complete!", true); 197 }); 198 } 199 finish(String reason, boolean success)200 private void finish(String reason, boolean success) { 201 mFinishSuccess = success; 202 mFinishReason = reason; 203 204 finish(); 205 } 206 207 @Override finish()208 public void finish() { 209 mLatch.countDown(); 210 211 super.finish(); 212 } 213 getOutputZip()214 public File getOutputZip() { 215 return mOutputZip; 216 } 217 waitForCompletion(long timeoutMillis)218 public boolean waitForCompletion(long timeoutMillis) throws InterruptedException { 219 return mLatch.await(timeoutMillis, TimeUnit.MILLISECONDS); 220 } 221 } 222