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