Merge "add unit test for DynamicConfigHandler"
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index c17b98b..07bc383 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -169,7 +169,6 @@
CtsLocationTestCases \
CtsLocation2TestCases \
CtsMediaStressTestCases \
- CtsMediaTestCases \
CtsMidiTestCases \
CtsNativeOpenGLTestCases \
CtsNdefTestCases \
diff --git a/apps/CameraITS/tests/scene1/test_exposure.py b/apps/CameraITS/tests/scene1/test_exposure.py
index d217bdb..26c398d 100644
--- a/apps/CameraITS/tests/scene1/test_exposure.py
+++ b/apps/CameraITS/tests/scene1/test_exposure.py
@@ -36,11 +36,13 @@
THRESHOLD_MIN_LEVEL = 0.1
THRESHOLD_MAX_LEVEL = 0.9
THRESHOLD_MAX_LEVEL_DIFF = 0.025
+ THRESHOLD_MAX_LEVEL_DIFF_WIDE_RANGE = 0.05
mults = []
r_means = []
g_means = []
b_means = []
+ threshold_max_level_diff = THRESHOLD_MAX_LEVEL_DIFF
with its.device.ItsSession() as cam:
props = cam.get_camera_properties()
@@ -57,13 +59,18 @@
req = its.objects.manual_capture_request(s*m, e/m)
cap = cam.do_capture(req)
img = its.image.convert_capture_to_rgb_image(cap)
- its.image.write_image(img, "%s_mult=%02d.jpg" % (NAME, m))
+ its.image.write_image(img, "%s_mult=%3.2f.jpg" % (NAME, m))
tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
rgb_means = its.image.compute_image_means(tile)
r_means.append(rgb_means[0])
g_means.append(rgb_means[1])
b_means.append(rgb_means[2])
- m = m + 4
+ # Test 3 steps per 2x gain
+ m = m * pow(2, 1.0 / 3)
+
+ # Allow more threshold for devices with wider exposure range
+ if m >= 64.0:
+ threshold_max_level_diff = THRESHOLD_MAX_LEVEL_DIFF_WIDE_RANGE
# Draw a plot.
pylab.plot(mults, r_means, 'r')
@@ -83,7 +90,7 @@
max_diff = max_val - min_val
print "Channel %d line fit (y = mx+b): m = %f, b = %f" % (chan, m, b)
print "Channel max %f min %f diff %f" % (max_val, min_val, max_diff)
- assert(max_diff < THRESHOLD_MAX_LEVEL_DIFF)
+ assert(max_diff < threshold_max_level_diff)
assert(b > THRESHOLD_MIN_LEVEL and b < THRESHOLD_MAX_LEVEL)
for v in values:
assert(v > THRESHOLD_MIN_LEVEL and v < THRESHOLD_MAX_LEVEL)
diff --git a/apps/CameraITS/tests/scene1/test_param_noise_reduction.py b/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
index 2c8d73b..35cfc07 100644
--- a/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
+++ b/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
@@ -36,11 +36,10 @@
NAME = os.path.basename(__file__).split(".")[0]
RELATIVE_ERROR_TOLERANCE = 0.1
-
- # List of variances for Y,U,V.
+ # List of variances for R,G,B.
variances = [[],[],[]]
- # Reference (baseline) variance for each of Y,U,V.
+ # Reference (baseline) variance for each of R,G,B.
ref_variance = []
nr_modes_reported = []
@@ -56,16 +55,15 @@
req = its.objects.manual_capture_request(s, e)
req["android.noiseReduction.mode"] = 0
cap = cam.do_capture(req)
+ rgb_image = its.image.convert_capture_to_rgb_image(cap)
its.image.write_image(
- its.image.convert_capture_to_rgb_image(cap),
+ rgb_image,
"%s_low_gain.jpg" % (NAME))
- planes = its.image.convert_capture_to_planes(cap)
- for j in range(3):
- img = planes[j]
- tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
- ref_variance.append(its.image.compute_image_variances(tile)[0])
+ rgb_tile = its.image.get_image_patch(rgb_image, 0.45, 0.45, 0.1, 0.1)
+ ref_variance = its.image.compute_image_variances(rgb_tile)
print "Ref variances:", ref_variance
+ e, s = its.target.get_target_exposure_combos(cam)["maxSensitivity"]
# NR modes 0, 1, 2, 3, 4 with high gain
for mode in range(5):
# Skip unavailable modes
@@ -75,22 +73,22 @@
variances[channel].append(0)
continue;
- e, s = its.target.get_target_exposure_combos(cam)["maxSensitivity"]
req = its.objects.manual_capture_request(s, e)
req["android.noiseReduction.mode"] = mode
cap = cam.do_capture(req)
+ rgb_image = its.image.convert_capture_to_rgb_image(cap)
nr_modes_reported.append(
cap["metadata"]["android.noiseReduction.mode"])
its.image.write_image(
- its.image.convert_capture_to_rgb_image(cap),
+ rgb_image,
"%s_high_gain_nr=%d.jpg" % (NAME, mode))
- planes = its.image.convert_capture_to_planes(cap)
- for j in range(3):
- img = planes[j]
- tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
- variance = its.image.compute_image_variances(tile)[0]
- variances[j].append(variance / ref_variance[j])
- print "Variances with NR mode [0,1,2,3,4]:", variances
+ rgb_tile = its.image.get_image_patch(
+ rgb_image, 0.45, 0.45, 0.1, 0.1)
+ rgb_vars = its.image.compute_image_variances(rgb_tile)
+ for chan in range(3):
+ variance = rgb_vars[chan]
+ variances[chan].append(variance / ref_variance[chan])
+ print "Variances with NR mode [0,1,2]:", variances
# Draw a plot.
for j in range(3):
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
index f8f1a9a..6dbf8cc 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
@@ -302,8 +302,9 @@
* It cannot be reused.
*/
private class TriggerVerifier extends TriggerEventListener {
- private volatile CountDownLatch mCountDownLatch = new CountDownLatch(1);
+ private volatile CountDownLatch mCountDownLatch;
private volatile TriggerEventRegistry mEventRegistry;
+ private volatile long mTimestampForTriggeredEvent = 0;
// TODO: refactor out if needed
private class TriggerEventRegistry {
@@ -329,10 +330,7 @@
}
public long getTimeStampForTriggerEvent() {
- if (mEventRegistry != null && mEventRegistry.triggerEvent != null) {
- return mEventRegistry.triggerEvent.timestamp;
- }
- return 0;
+ return mTimestampForTriggeredEvent;
}
public String verifyEventTriggered() throws Throwable {
@@ -388,9 +386,16 @@
}
private TriggerEventRegistry awaitForEvent() throws InterruptedException {
+ mCountDownLatch = new CountDownLatch(1);
mCountDownLatch.await(TRIGGER_MAX_DELAY_SECONDS, TimeUnit.SECONDS);
TriggerEventRegistry registry = mEventRegistry;
+ // Save the last timestamp when the event triggered.
+ if (mEventRegistry != null && mEventRegistry.triggerEvent != null) {
+ mTimestampForTriggeredEvent = mEventRegistry.triggerEvent.timestamp;
+ }
+
+ mEventRegistry = null;
playSound();
return registry != null ? registry : new TriggerEventRegistry(null, 0);
}
diff --git a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
index 78da8ac..c8a2d8e 100644
--- a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
+++ b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
@@ -208,7 +208,8 @@
installResult);
// capture a launch of the app with async tracing
- String atraceArgs = "-a " + TEST_PKG + " -c -b 16000 view"; // TODO: zipping
+ // content traced by 'view' tag tested below, 'sched' used to ensure tgid printed
+ String atraceArgs = "-a " + TEST_PKG + " -c -b 16000 view sched"; // TODO: zipping
getDevice().executeShellCommand("atrace --async_stop " + atraceArgs);
getDevice().executeShellCommand("atrace --async_start " + atraceArgs);
getDevice().executeShellCommand("am start " + TEST_PKG);
diff --git a/hostsidetests/theme/Android.mk b/hostsidetests/theme/Android.mk
index 71027c7..188bf7a 100644
--- a/hostsidetests/theme/Android.mk
+++ b/hostsidetests/theme/Android.mk
@@ -29,6 +29,8 @@
LOCAL_CTS_TEST_PACKAGE := android.host.theme
+LOCAL_SDK_VERSION := current
+
include $(BUILD_CTS_HOST_JAVA_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/theme/README b/hostsidetests/theme/README
new file mode 100644
index 0000000..bce711a
--- /dev/null
+++ b/hostsidetests/theme/README
@@ -0,0 +1,73 @@
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+
+
+INTRODUCTION
+
+The Android theme tests ensure that the Holo and Material themes have not been
+modified. They consist of API-specific sets of reference images representing
+specific themes and widgets that must be identical across devices. To pass the
+theme tests, a device must be able to generate images that are identical to the
+reference images.
+
+NOTE: Reference images should only be updated by the CTS test maintainers. Any
+ modifications to the reference images will invalidate the test results.
+
+
+INSTRUCTIONS
+
+I. Generating reference images (CTS maintainers only)
+
+Reference images are typically only generated for new API revisions. To
+generate a new set of reference images, do the following:
+
+ 1. Connect one device for each DPI bucket (ldpi, xxxhdpi, etc.) that you wish
+ to generate references images for. Confirm that all devices are connected
+ with:
+
+ adb devices
+
+ 2. Image generation occurs on all devices in parallel. Resulting sets of
+ reference images are saved in assets/<api>/<dpi>.zip and will overwrite
+ any existing sets. Image generation may be started using:
+
+ .cts/hostsidetests/theme/generate_images.sh
+
+A complete collection of reference images for a given API revision must include
+a set for each possible DPI bucket (tvdpi, xxhdpi, etc.) that may be tested.
+
+
+II. Building theme tests
+
+1. If you have not already built the CTS tests, run an initial make:
+
+ make cts -j32
+
+2. Subsequent changes to the theme tests, including changes to the reference
+ images, may be built using mmm:
+
+ mmm cts/hostsidetests/theme -j32
+
+
+III. Running theme tests
+
+1. Connect the device that you wish to test. Confirm that is is connected with:
+
+ adb devices
+
+2. Run the theme tests using cts-tradefed:
+
+ cts-tradefed run cts -c android.theme.cts.ThemeHostTest
+
+3. Wait for the tests to complete. This should take less than five minutes.
diff --git a/hostsidetests/theme/android_device.py b/hostsidetests/theme/android_device.py
new file mode 100644
index 0000000..97b5fdd
--- /dev/null
+++ b/hostsidetests/theme/android_device.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import os
+import re
+import sys
+import threading
+import subprocess
+import time
+
+# class for running android device from python
+# it will fork the device processor
+class androidDevice(object):
+ def __init__(self, adbDevice):
+ self._adbDevice = adbDevice
+
+ def runAdbCommand(self, cmd):
+ self.waitForAdbDevice()
+ adbCmd = "adb -s %s %s" %(self._adbDevice, cmd)
+ adbProcess = subprocess.Popen(adbCmd.split(" "), bufsize = -1, stdout = subprocess.PIPE)
+ return adbProcess.communicate()
+
+ def runShellCommand(self, cmd):
+ return self.runAdbCommand("shell " + cmd)
+
+ def waitForAdbDevice(self):
+ os.system("adb -s %s wait-for-device" %self._adbDevice)
+
+ def waitForBootComplete(self, timeout = 240):
+ boot_complete = False
+ attempts = 0
+ wait_period = 5
+ while not boot_complete and (attempts*wait_period) < timeout:
+ (output, err) = self.runShellCommand("getprop dev.bootcomplete")
+ output = output.strip()
+ if output == "1":
+ boot_complete = True
+ else:
+ time.sleep(wait_period)
+ attempts += 1
+ if not boot_complete:
+ print "***boot not complete within timeout. will proceed to the next step"
+ return boot_complete
+
+ def installApk(self, apkPath):
+ (out, err) = self.runAdbCommand("install -r -d -g " + apkPath)
+ result = out.split()
+ return (out, err, "Success" in result)
+
+ def uninstallApk(self, package):
+ (out, err) = self.runAdbCommand("uninstall " + package)
+ result = out.split()
+ return "Success" in result
+
+ def runInstrumentationTest(self, option):
+ return self.runShellCommand("am instrument -w " + option)
+
+ def isProcessAlive(self, processName):
+ (out, err) = self.runShellCommand("ps")
+ names = out.split()
+ # very lazy implementation as it does not filter out things like uid
+ # should work mostly unless processName is too simple to overlap with
+ # uid. So only use name like com.android.xyz
+ return processName in names
+
+ def getDensity(self):
+ if "emulator" in self._adbDevice:
+ return int(self.runShellCommand("getprop qemu.sf.lcd_density")[0])
+ else:
+ return int(self.runShellCommand("getprop ro.sf.lcd_density")[0])
+
+ def getSdkLevel(self):
+ return int(self.runShellCommand("getprop ro.build.version.sdk")[0])
+
+ def getOrientation(self):
+ return int(self.runShellCommand("dumpsys | grep SurfaceOrientation")[0].split()[1])
+
+def runAdbDevices():
+ devices = subprocess.check_output(["adb", "devices"])
+ devices = devices.split('\n')[1:]
+
+ deviceSerial = []
+
+ for device in devices:
+ if device is not "":
+ info = device.split('\t')
+ if info[1] == "device":
+ deviceSerial.append(info[0])
+
+ return deviceSerial
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/hostsidetests/theme/app/Android.mk b/hostsidetests/theme/app/Android.mk
index 70623cb..1be2983 100644
--- a/hostsidetests/theme/app/Android.mk
+++ b/hostsidetests/theme/app/Android.mk
@@ -26,6 +26,8 @@
LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
LOCAL_SRC_FILES := $(call all-java-files-under, src)
#Flags to tell the Android Asset Packaging Tool not to strip for some densities
diff --git a/hostsidetests/theme/app/AndroidManifest.xml b/hostsidetests/theme/app/AndroidManifest.xml
index 81a4d9d..4e81ab0 100755
--- a/hostsidetests/theme/app/AndroidManifest.xml
+++ b/hostsidetests/theme/app/AndroidManifest.xml
@@ -22,9 +22,10 @@
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
<application>
<uses-library android:name="android.test.runner" />
- <activity android:name=".HoloDeviceActivity">
+ <activity android:name=".ThemeDeviceActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -37,6 +38,13 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+ <activity android:name=".GenerateImagesActivity"
+ android:exported="true" />
</application>
+ <!-- self-instrumenting test package. -->
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.theme.app"
+ android:label="Generates Theme reference images"/>
+
</manifest>
diff --git a/hostsidetests/theme/app/res/layout/holo_test.xml b/hostsidetests/theme/app/res/layout/theme_test.xml
similarity index 100%
rename from hostsidetests/theme/app/res/layout/holo_test.xml
rename to hostsidetests/theme/app/res/layout/theme_test.xml
diff --git a/hostsidetests/theme/app/res/values/strings.xml b/hostsidetests/theme/app/res/values/strings.xml
index a69a2e0..d9d6602 100644
--- a/hostsidetests/theme/app/res/values/strings.xml
+++ b/hostsidetests/theme/app/res/values/strings.xml
@@ -14,8 +14,6 @@
limitations under the License.
-->
<resources>
- <string name="holo_test_utilities">Holo Test Utilities</string>
-
<string name="display_info">Display Info</string>
<string name="display_info_text">Density DPI: %1$d\nDensity Bucket: %2$s\nWidth DP: %3$d\nHeight DP: %4$d</string>
diff --git a/hostsidetests/theme/app/src/android/theme/app/DisplayInfoActivity.java b/hostsidetests/theme/app/src/android/theme/app/DisplayInfoActivity.java
index 5255698..530675d 100644
--- a/hostsidetests/theme/app/src/android/theme/app/DisplayInfoActivity.java
+++ b/hostsidetests/theme/app/src/android/theme/app/DisplayInfoActivity.java
@@ -25,7 +25,8 @@
import android.widget.TextView;
/**
- * An activity to display information about the device, including density bucket and dimensions.
+ * An activity to display information about the device, including density
+ * bucket and dimensions.
*/
public class DisplayInfoActivity extends Activity {
diff --git a/hostsidetests/theme/app/src/android/theme/app/GenerateBitmapTask.java b/hostsidetests/theme/app/src/android/theme/app/GenerateBitmapTask.java
new file mode 100644
index 0000000..05b6dcf
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/GenerateBitmapTask.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.theme.app;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.Canvas;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.util.Log;
+import android.view.View;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+/**
+ * A task which gets the UI element to render to a bitmap and then saves that
+ * as a PNG asynchronously.
+ */
+class GenerateBitmapTask extends AsyncTask<Void, Void, Boolean> {
+ private static final String TAG = "GenerateBitmapTask";
+
+ private final View mView;
+ private final File mOutDir;
+
+ private Bitmap mBitmap;
+
+ protected final String mName;
+
+ public GenerateBitmapTask(View view, File outDir, String name) {
+ mView = view;
+ mOutDir = outDir;
+ mName = name;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ if (mView.getWidth() == 0 || mView.getHeight() == 0) {
+ Log.e(TAG, "Unable to draw view due to incorrect size: " + mName);
+ return;
+ }
+
+ mBitmap = Bitmap.createBitmap(mView.getWidth(), mView.getHeight(),
+ Bitmap.Config.ARGB_8888);
+
+ final Canvas canvas = new Canvas(mBitmap);
+ mView.draw(canvas);
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... ignored) {
+ final Bitmap bitmap = mBitmap;
+ if (bitmap == null) {
+ return false;
+ }
+
+ final File file = new File(mOutDir, mName + ".png");
+ if (file.exists() && !file.canWrite()) {
+ Log.e(TAG, "Unable to write file: " + file.getAbsolutePath());
+ return false;
+ }
+
+ boolean success = false;
+ try {
+ FileOutputStream stream = null;
+ try {
+ stream = new FileOutputStream(file);
+ success = bitmap.compress(CompressFormat.PNG, 100, stream);
+ } finally {
+ if (stream != null) {
+ stream.flush();
+ stream.close();
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, e.getMessage());
+ } finally {
+ bitmap.recycle();
+ }
+
+ return success;
+ }
+}
diff --git a/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java b/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java
new file mode 100644
index 0000000..e7f5aa2
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.theme.app;
+
+import android.Manifest.permission;
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Build.VERSION;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.util.Log;
+import android.view.WindowManager.LayoutParams;
+
+import java.io.File;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Generates images by iterating through all themes and launching instances of
+ * {@link ThemeDeviceActivity}.
+ */
+public class GenerateImagesActivity extends Activity {
+ private static final String TAG = "GenerateImagesActivity";
+
+ private static final String OUT_DIR = "cts-theme-assets";
+ private static final int REQUEST_CODE = 1;
+
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+
+ private File mOutputDir;
+ private int mCurrentTheme;
+ private String mFinishReason;
+ private boolean mFinishSuccess;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON
+ | LayoutParams.FLAG_TURN_SCREEN_ON
+ | LayoutParams.FLAG_DISMISS_KEYGUARD);
+
+ mOutputDir = new File(Environment.getExternalStorageDirectory(), OUT_DIR);
+ ThemeTestUtils.deleteDirectory(mOutputDir);
+ mOutputDir.mkdirs();
+
+ if (!mOutputDir.exists()) {
+ finish("Failed to create output directory " + mOutputDir.getAbsolutePath(), false);
+ return;
+ }
+
+ final boolean canDisableKeyguard = checkCallingOrSelfPermission(
+ permission.DISABLE_KEYGUARD) == PackageManager.PERMISSION_GRANTED;
+ if (!canDisableKeyguard) {
+ finish("Not granted permission to disable keyguard", false);
+ return;
+ }
+
+ new KeyguardCheck(this) {
+ @Override
+ public void onSuccess() {
+ generateNextImage();
+ }
+
+ @Override
+ public void onFailure() {
+ finish("Device is locked", false);
+ }
+ }.start();
+ }
+
+ private void finish(String reason, boolean success) {
+ mFinishSuccess = success;
+ mFinishReason = reason;
+
+ Log.i(TAG, (success ? "OKAY" : "FAIL") + ":" + reason);
+ finish();
+ }
+
+ public boolean isFinishSuccess() {
+ return mFinishSuccess;
+ }
+
+ public String getFinishReason() {
+ return mFinishReason;
+ }
+
+ static abstract class KeyguardCheck implements Runnable {
+ private static final int MAX_RETRIES = 3;
+ private static final int RETRY_DELAY = 500;
+
+ private final Handler mHandler;
+ private final KeyguardManager mKeyguard;
+
+ private int mRetries;
+
+ public KeyguardCheck(Context context) {
+ mHandler = new Handler(context.getMainLooper());
+ mKeyguard = (KeyguardManager) context.getSystemService(KEYGUARD_SERVICE);
+ }
+
+ public void start() {
+ mRetries = 0;
+
+ mHandler.removeCallbacks(this);
+ mHandler.post(this);
+ }
+
+ public void cancel() {
+ mHandler.removeCallbacks(this);
+ }
+
+ @Override
+ public void run() {
+ if (!mKeyguard.isKeyguardLocked()) {
+ onSuccess();
+ } else if (mRetries < MAX_RETRIES) {
+ mRetries++;
+ mHandler.postDelayed(this, RETRY_DELAY);
+ } else {
+ onFailure();
+ }
+
+ }
+
+ public abstract void onSuccess();
+ public abstract void onFailure();
+ }
+
+ public File getOutputDir() {
+ return mOutputDir;
+ }
+
+ /**
+ * Starts the activity to generate the next image.
+ */
+ private boolean generateNextImage() {
+ final ThemeDeviceActivity.Theme theme = ThemeDeviceActivity.THEMES[mCurrentTheme];
+ if (theme.apiLevel > VERSION.SDK_INT) {
+ Log.v(TAG, "Skipping theme \"" + theme.name
+ + "\" (requires API " + theme.apiLevel + ")");
+ return false;
+ }
+
+ Log.v(TAG, "Generating images for theme \"" + theme.name + "\"...");
+
+ final Intent intent = new Intent(this, ThemeDeviceActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ intent.putExtra(ThemeDeviceActivity.EXTRA_THEME, mCurrentTheme);
+ intent.putExtra(ThemeDeviceActivity.EXTRA_OUTPUT_DIR, mOutputDir.getAbsolutePath());
+ startActivityForResult(intent, REQUEST_CODE);
+ return true;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != RESULT_OK) {
+ Log.i(TAG, "FAIL:Failed to generate images for theme " + mCurrentTheme);
+ finish();
+ return;
+ }
+
+ // Keep trying themes until one works.
+ boolean success = false;
+ while (++mCurrentTheme < ThemeDeviceActivity.THEMES.length && !success) {
+ success = generateNextImage();
+ }
+
+ // If we ran out of themes, we're done.
+ if (!success) {
+ finish("Image generation complete!", true);
+ }
+ }
+
+ public void finish() {
+ mLatch.countDown();
+ super.finish();
+ }
+
+ public void waitForCompletion() throws InterruptedException {
+ mLatch.await();
+ }
+}
diff --git a/hostsidetests/theme/app/src/android/theme/app/HoloDeviceActivity.java b/hostsidetests/theme/app/src/android/theme/app/HoloDeviceActivity.java
deleted file mode 100644
index 8ae9fc8..0000000
--- a/hostsidetests/theme/app/src/android/theme/app/HoloDeviceActivity.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.theme.app;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.CompressFormat;
-import android.graphics.Canvas;
-import android.os.AsyncTask;
-import android.os.Environment;
-import android.os.Bundle;
-import android.os.Handler;
-import android.theme.app.modifiers.DatePickerModifier;
-import android.theme.app.modifiers.ProgressBarModifier;
-import android.theme.app.modifiers.SearchViewModifier;
-import android.theme.app.modifiers.TimePickerModifier;
-import android.theme.app.modifiers.ViewCheckedModifier;
-import android.theme.app.modifiers.ViewPressedModifier;
-import android.theme.app.R;
-import android.theme.app.ReferenceViewGroup;
-import android.util.Log;
-import android.view.View;
-import android.widget.CheckBox;
-import android.widget.DatePicker;
-import android.widget.LinearLayout;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.lang.Override;
-
-/**
- * A activity which display various UI elements with Holo theme.
- */
-public class HoloDeviceActivity extends Activity {
-
- public static final String EXTRA_THEME = "holo_theme_extra";
-
- private static final String TAG = HoloDeviceActivity.class.getSimpleName();
-
- /**
- * The duration of the CalendarView adjustement to settle to its final position.
- */
- private static final long CALENDAR_VIEW_ADJUSTMENT_DURATION = 540;
-
- private Theme mTheme;
-
- private ReferenceViewGroup mViewGroup;
-
- private int mLayoutIndex;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- mTheme = THEMES[getIntent().getIntExtra(EXTRA_THEME, 0)];
- setTheme(mTheme.mId);
- setContentView(R.layout.holo_test);
- mViewGroup = (ReferenceViewGroup) findViewById(R.id.reference_view_group);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- setNextLayout();
- }
-
- @Override
- protected void onPause() {
- if (!isFinishing()) {
- // The Activity got paused for some reasons, for finish it as the host won't move on to
- // the next theme otherwise.
- Log.w(TAG, "onPause called without a call to finish().");
- finish();
- }
- super.onPause();
- }
-
- @Override
- protected void onDestroy() {
- if (mLayoutIndex != LAYOUTS.length) {
- Log.w(TAG, "Not all layouts got rendered: " + mLayoutIndex);
- }
- Log.i(TAG, "OKAY:" + mTheme.mName);
- super.onDestroy();
- }
-
- /**
- * Sets the next layout in the UI.
- */
- private void setNextLayout() {
- if (mLayoutIndex >= LAYOUTS.length) {
- finish();
- return;
- }
- final Layout layout = LAYOUTS[mLayoutIndex++];
- final String layoutName = String.format("%s_%s", mTheme.mName, layout.mName);
-
- mViewGroup.removeAllViews();
- final View view = getLayoutInflater().inflate(layout.mId, mViewGroup, false);
- if (layout.mModifier != null) {
- layout.mModifier.modifyView(view);
- }
- mViewGroup.addView(view);
- view.setFocusable(false);
-
- final Runnable generateBitmapRunnable = new Runnable() {
- @Override
- public void run() {
- new GenerateBitmapTask(view, layoutName).execute();
- }
- };
-
- if (view instanceof DatePicker) {
- // DatePicker uses a CalendarView that has a non-configurable adjustment duration of
- // 540ms
- view.postDelayed(generateBitmapRunnable, CALENDAR_VIEW_ADJUSTMENT_DURATION);
- } else {
- view.post(generateBitmapRunnable);
- }
- }
-
- /**
- * A task which gets the UI element to render to a bitmap and then saves that as a png
- * asynchronously
- */
- private class GenerateBitmapTask extends AsyncTask<Void, Void, Boolean> {
-
- private final View mView;
-
- private final String mName;
-
- public GenerateBitmapTask(final View view, final String name) {
- super();
- mView = view;
- mName = name;
- }
-
- @Override
- protected Boolean doInBackground(Void... ignored) {
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- Log.i(TAG, "External storage for saving bitmaps is not mounted");
- return false;
- }
- if (mView.getWidth() == 0 || mView.getHeight() == 0) {
- Log.w(TAG, "Unable to draw View due to incorrect size: " + mName);
- return false;
- }
-
- final Bitmap bitmap = Bitmap.createBitmap(
- mView.getWidth(), mView.getHeight(), Bitmap.Config.ARGB_8888);
- final Canvas canvas = new Canvas(bitmap);
-
- mView.draw(canvas);
- final File dir = new File(Environment.getExternalStorageDirectory(), "cts-holo-assets");
- dir.mkdirs();
- boolean success = false;
- try {
- final File file = new File(dir, mName + ".png");
- FileOutputStream stream = null;
- try {
- stream = new FileOutputStream(file);
- success = bitmap.compress(CompressFormat.PNG, 100, stream);
- } finally {
- if (stream != null) {
- stream.close();
- }
- }
- } catch (Exception e) {
- Log.e(TAG, e.getMessage());
- } finally {
- bitmap.recycle();
- }
- return success;
- }
-
- @Override
- protected void onPostExecute(Boolean success) {
- setNextLayout();
- }
- }
-
- /**
- * A class to encapsulate information about a holo theme.
- */
- private static class Theme {
-
- public final int mId;
-
- public final String mName;
-
- private Theme(int id, String name) {
- mId = id;
- mName = name;
- }
- }
-
- private static final Theme[] THEMES = {
- new Theme(android.R.style.Theme_Holo,
- "holo"),
- new Theme(android.R.style.Theme_Holo_Dialog,
- "holo_dialog"),
- new Theme(android.R.style.Theme_Holo_Dialog_MinWidth,
- "holo_dialog_minwidth"),
- new Theme(android.R.style.Theme_Holo_Dialog_NoActionBar,
- "holo_dialog_noactionbar"),
- new Theme(android.R.style.Theme_Holo_Dialog_NoActionBar_MinWidth,
- "holo_dialog_noactionbar_minwidth"),
- new Theme(android.R.style.Theme_Holo_DialogWhenLarge,
- "holo_dialogwhenlarge"),
- new Theme(android.R.style.Theme_Holo_DialogWhenLarge_NoActionBar,
- "holo_dialogwhenlarge_noactionbar"),
- new Theme(android.R.style.Theme_Holo_InputMethod,
- "holo_inputmethod"),
- new Theme(android.R.style.Theme_Holo_Light,
- "holo_light"),
- new Theme(android.R.style.Theme_Holo_Light_DarkActionBar,
- "holo_light_darkactionbar"),
- new Theme(android.R.style.Theme_Holo_Light_Dialog,
- "holo_light_dialog"),
- new Theme(android.R.style.Theme_Holo_Light_Dialog_MinWidth,
- "holo_light_dialog_minwidth"),
- new Theme(android.R.style.Theme_Holo_Light_Dialog_NoActionBar,
- "holo_light_dialog_noactionbar"),
- new Theme(android.R.style.Theme_Holo_Light_Dialog_NoActionBar_MinWidth,
- "holo_light_dialog_noactionbar_minwidth"),
- new Theme(android.R.style.Theme_Holo_Light_DialogWhenLarge,
- "holo_light_dialogwhenlarge"),
- new Theme(android.R.style.Theme_Holo_Light_DialogWhenLarge_NoActionBar,
- "holo_light_dialogwhenlarge_noactionbar"),
- new Theme(android.R.style.Theme_Holo_Light_NoActionBar,
- "holo_light_noactionbar"),
- new Theme(android.R.style.Theme_Holo_Light_NoActionBar_Fullscreen,
- "holo_light_noactionbar_fullscreen"),
- new Theme(android.R.style.Theme_Holo_Light_Panel,
- "holo_light_panel"),
- new Theme(android.R.style.Theme_Holo_NoActionBar,
- "holo_noactionbar"),
- new Theme(android.R.style.Theme_Holo_NoActionBar_Fullscreen,
- "holo_noactionbar_fullscreen"),
- new Theme(android.R.style.Theme_Holo_Panel,
- "holo_panel"),
- new Theme(android.R.style.Theme_Holo_Wallpaper,
- "holo_wallpaper"),
- new Theme(android.R.style.Theme_Holo_Wallpaper_NoTitleBar,
- "holo_wallpaper_notitlebar")
- };
-
- /**
- * A class to encapsulate information about a holo layout.
- */
- private static class Layout {
-
- public final int mId;
-
- public final String mName;
-
- public final LayoutModifier mModifier;
-
- private Layout(int id, String name, LayoutModifier modifier) {
- mId = id;
- mName = name;
- mModifier = modifier;
- }
- }
-
- private static final Layout[] LAYOUTS = {
- new Layout(R.layout.button, "button", null),
- new Layout(R.layout.button, "button_pressed", new ViewPressedModifier()),
- new Layout(R.layout.checkbox, "checkbox", null),
- new Layout(R.layout.checkbox, "checkbox_checked", new ViewCheckedModifier()),
- new Layout(R.layout.chronometer, "chronometer", null),
- new Layout(R.layout.color_blue_bright, "color_blue_bright", null),
- new Layout(R.layout.color_blue_dark, "color_blue_dark", null),
- new Layout(R.layout.color_blue_light, "color_blue_light", null),
- new Layout(R.layout.color_green_dark, "color_green_dark", null),
- new Layout(R.layout.color_green_light, "color_green_light", null),
- new Layout(R.layout.color_orange_dark, "color_orange_dark", null),
- new Layout(R.layout.color_orange_light, "color_orange_light", null),
- new Layout(R.layout.color_purple, "color_purple", null),
- new Layout(R.layout.color_red_dark, "color_red_dark", null),
- new Layout(R.layout.color_red_light, "color_red_light", null),
- new Layout(R.layout.datepicker, "datepicker", new DatePickerModifier()),
- new Layout(R.layout.display_info, "display_info", null),
- new Layout(R.layout.edittext, "edittext", null),
- new Layout(R.layout.progressbar_horizontal_0, "progressbar_horizontal_0", null),
- new Layout(R.layout.progressbar_horizontal_100, "progressbar_horizontal_100", null),
- new Layout(R.layout.progressbar_horizontal_50, "progressbar_horizontal_50", null),
- new Layout(R.layout.progressbar_large, "progressbar_large", new ProgressBarModifier()),
- new Layout(R.layout.progressbar_small, "progressbar_small", new ProgressBarModifier()),
- new Layout(R.layout.progressbar, "progressbar", new ProgressBarModifier()),
- new Layout(R.layout.radiobutton_checked, "radiobutton_checked", null),
- new Layout(R.layout.radiobutton, "radiobutton", null),
- new Layout(R.layout.radiogroup_horizontal, "radiogroup_horizontal", null),
- new Layout(R.layout.radiogroup_vertical, "radiogroup_vertical", null),
- new Layout(R.layout.ratingbar_0, "ratingbar_0", null),
- new Layout(R.layout.ratingbar_2point5, "ratingbar_2point5", null),
- new Layout(R.layout.ratingbar_5, "ratingbar_5", null),
- new Layout(R.layout.ratingbar_0, "ratingbar_0_pressed", new ViewPressedModifier()),
- new Layout(R.layout.ratingbar_2point5, "ratingbar_2point5_pressed", new ViewPressedModifier()),
- new Layout(R.layout.ratingbar_5, "ratingbar_5_pressed", new ViewPressedModifier()),
- new Layout(R.layout.searchview, "searchview_query", new SearchViewModifier(SearchViewModifier.QUERY)),
- new Layout(R.layout.searchview, "searchview_query_hint", new SearchViewModifier(SearchViewModifier.QUERY_HINT)),
- new Layout(R.layout.seekbar_0, "seekbar_0", null),
- new Layout(R.layout.seekbar_100, "seekbar_100", null),
- new Layout(R.layout.seekbar_50, "seekbar_50", null),
- new Layout(R.layout.spinner, "spinner", null),
- new Layout(R.layout.switch_button_checked, "switch_button_checked", null),
- new Layout(R.layout.switch_button, "switch_button", null),
- new Layout(R.layout.textview, "textview", null),
- new Layout(R.layout.timepicker, "timepicker", new TimePickerModifier()),
- new Layout(R.layout.togglebutton_checked, "togglebutton_checked", null),
- new Layout(R.layout.togglebutton, "togglebutton", null),
- new Layout(R.layout.zoomcontrols, "zoomcontrols", null),
- };
-}
diff --git a/hostsidetests/theme/app/src/android/theme/app/ReferenceImagesTest.java b/hostsidetests/theme/app/src/android/theme/app/ReferenceImagesTest.java
new file mode 100644
index 0000000..7569252
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/ReferenceImagesTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.theme.app;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+import java.io.File;
+
+/**
+ * Activity test case used to instrument generation of reference images.
+ */
+public class ReferenceImagesTest extends ActivityInstrumentationTestCase2<GenerateImagesActivity> {
+
+ public ReferenceImagesTest() {
+ super(GenerateImagesActivity.class);
+ }
+
+ public void testGenerateReferenceImages() throws Exception {
+ setActivityInitialTouchMode(true);
+
+ final GenerateImagesActivity activity = getActivity();
+ activity.waitForCompletion();
+
+ assertTrue(activity.getFinishReason(), activity.isFinishSuccess());
+
+ final File outputDir = activity.getOutputDir();
+ final File outputZip = new File(outputDir.getParentFile(), outputDir.getName() + ".zip");
+ if (outputZip.exists()) {
+ // Remove any old test results.
+ outputZip.delete();
+ }
+
+ ThemeTestUtils.compressDirectory(outputDir, outputZip);
+ ThemeTestUtils.deleteDirectory(outputDir);
+
+ assertTrue("Generated reference image ZIP", outputZip.exists());
+ }
+}
diff --git a/hostsidetests/theme/app/src/android/theme/app/ThemeDeviceActivity.java b/hostsidetests/theme/app/src/android/theme/app/ThemeDeviceActivity.java
new file mode 100644
index 0000000..d8b1f30
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/ThemeDeviceActivity.java
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.theme.app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.theme.app.modifiers.DatePickerModifier;
+import android.theme.app.modifiers.ProgressBarModifier;
+import android.theme.app.modifiers.SearchViewModifier;
+import android.theme.app.modifiers.TimePickerModifier;
+import android.theme.app.modifiers.ViewCheckedModifier;
+import android.theme.app.modifiers.ViewPressedModifier;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager.LayoutParams;
+import android.widget.DatePicker;
+
+import java.io.File;
+import java.lang.Override;
+
+/**
+ * A activity which display various UI elements with non-modifiable themes.
+ */
+public class ThemeDeviceActivity extends Activity {
+ public static final String EXTRA_THEME = "theme";
+ public static final String EXTRA_OUTPUT_DIR = "outputDir";
+
+ private static final String TAG = "ThemeDeviceActivity";
+
+ /**
+ * The duration of the CalendarView adjustment to settle to its final
+ * position.
+ */
+ private static final long CALENDAR_VIEW_ADJUSTMENT_DURATION = 540;
+
+ private Theme mTheme;
+ private ReferenceViewGroup mViewGroup;
+ private File mOutputDir;
+ private int mLayoutIndex;
+ private boolean mIsRunning;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ final Intent intent = getIntent();
+ final int themeIndex = intent.getIntExtra(EXTRA_THEME, -1);
+ if (themeIndex < 0) {
+ Log.e(TAG, "No theme specified");
+ finish();
+ }
+
+ final String outputDir = intent.getStringExtra(EXTRA_OUTPUT_DIR);
+ if (outputDir == null) {
+ Log.e(TAG, "No output directory specified");
+ finish();
+ }
+
+ mOutputDir = new File(outputDir);
+ mTheme = THEMES[themeIndex];
+
+ setTheme(mTheme.id);
+ setContentView(R.layout.theme_test);
+
+ mViewGroup = (ReferenceViewGroup) findViewById(R.id.reference_view_group);
+
+ getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON
+ | LayoutParams.FLAG_TURN_SCREEN_ON
+ | LayoutParams.FLAG_DISMISS_KEYGUARD );
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ mIsRunning = true;
+
+ setNextLayout();
+ }
+
+ @Override
+ protected void onPause() {
+ mIsRunning = false;
+
+ if (!isFinishing()) {
+ // The activity paused for some reason, likely a system crash
+ // dialog. Finish it so we can move to the next theme.
+ Log.w(TAG, "onPause() called without a call to finish()", new RuntimeException());
+ finish();
+ }
+
+ super.onPause();
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (mLayoutIndex < LAYOUTS.length) {
+ Log.e(TAG, "Not all layouts got rendered: " + mLayoutIndex);
+ setResult(RESULT_CANCELED);
+ }
+
+ super.onDestroy();
+ }
+
+ /**
+ * Sets the next layout in the UI.
+ */
+ private void setNextLayout() {
+ if (mLayoutIndex >= LAYOUTS.length) {
+ setResult(RESULT_OK);
+ finish();
+ return;
+ }
+
+ mViewGroup.removeAllViews();
+
+ final Layout layout = LAYOUTS[mLayoutIndex++];
+ final String layoutName = String.format("%s_%s", mTheme.name, layout.name);
+ final View view = getLayoutInflater().inflate(layout.id, mViewGroup, false);
+ if (layout.modifier != null) {
+ layout.modifier.modifyView(view);
+ }
+
+ mViewGroup.addView(view);
+ view.setFocusable(false);
+
+ Log.v(TAG, "Rendering layout " + layoutName
+ + " (" + mLayoutIndex + "/" + LAYOUTS.length + ")");
+
+ final Runnable generateBitmapRunnable = new Runnable() {
+ @Override
+ public void run() {
+ new BitmapTask(view, layoutName).execute();
+ }
+ };
+
+ if (view instanceof DatePicker) {
+ // The Holo-styled DatePicker uses a CalendarView that has a
+ // non-configurable adjustment duration of 540ms.
+ view.postDelayed(generateBitmapRunnable, CALENDAR_VIEW_ADJUSTMENT_DURATION);
+ } else {
+ view.post(generateBitmapRunnable);
+ }
+ }
+
+ private class BitmapTask extends GenerateBitmapTask {
+ public BitmapTask(View view, String name) {
+ super(view, mOutputDir, name);
+ }
+
+ @Override
+ protected void onPostExecute(Boolean success) {
+ if (success && mIsRunning) {
+ setNextLayout();
+ } else {
+ Log.e(TAG, "Failed to render view to bitmap: " + mName + " (activity running? "
+ + mIsRunning + ")");
+ finish();
+ }
+ }
+ }
+
+ /**
+ * A class to encapsulate information about a theme.
+ */
+ static class Theme {
+ public final int id;
+ public final int apiLevel;
+ public final String name;
+
+ private Theme(int id, int apiLevel, String name) {
+ this.id = id;
+ this.apiLevel = apiLevel;
+ this.name = name;
+ }
+ }
+
+ // List of themes to verify.
+ static final Theme[] THEMES = {
+ // Holo
+ new Theme(android.R.style.Theme_Holo,
+ Build.VERSION_CODES.HONEYCOMB, "holo"),
+ new Theme(android.R.style.Theme_Holo_Dialog,
+ Build.VERSION_CODES.HONEYCOMB, "holo_dialog"),
+ new Theme(android.R.style.Theme_Holo_Dialog_MinWidth,
+ Build.VERSION_CODES.HONEYCOMB, "holo_dialog_minwidth"),
+ new Theme(android.R.style.Theme_Holo_Dialog_NoActionBar,
+ Build.VERSION_CODES.HONEYCOMB, "holo_dialog_noactionbar"),
+ new Theme(android.R.style.Theme_Holo_Dialog_NoActionBar_MinWidth,
+ Build.VERSION_CODES.HONEYCOMB, "holo_dialog_noactionbar_minwidth"),
+ new Theme(android.R.style.Theme_Holo_DialogWhenLarge,
+ Build.VERSION_CODES.HONEYCOMB, "holo_dialogwhenlarge"),
+ new Theme(android.R.style.Theme_Holo_DialogWhenLarge_NoActionBar,
+ Build.VERSION_CODES.HONEYCOMB, "holo_dialogwhenlarge_noactionbar"),
+ new Theme(android.R.style.Theme_Holo_InputMethod,
+ Build.VERSION_CODES.HONEYCOMB, "holo_inputmethod"),
+ new Theme(android.R.style.Theme_Holo_NoActionBar,
+ Build.VERSION_CODES.HONEYCOMB, "holo_noactionbar"),
+ new Theme(android.R.style.Theme_Holo_NoActionBar_Fullscreen,
+ Build.VERSION_CODES.HONEYCOMB, "holo_noactionbar_fullscreen"),
+ new Theme(android.R.style.Theme_Holo_NoActionBar_Overscan,
+ Build.VERSION_CODES.JELLY_BEAN_MR2, "holo_noactionbar_overscan"),
+ new Theme(android.R.style.Theme_Holo_NoActionBar_TranslucentDecor,
+ Build.VERSION_CODES.KITKAT, "holo_noactionbar_translucentdecor"),
+ new Theme(android.R.style.Theme_Holo_Panel,
+ Build.VERSION_CODES.HONEYCOMB, "holo_panel"),
+ new Theme(android.R.style.Theme_Holo_Wallpaper,
+ Build.VERSION_CODES.HONEYCOMB, "holo_wallpaper"),
+ new Theme(android.R.style.Theme_Holo_Wallpaper_NoTitleBar,
+ Build.VERSION_CODES.HONEYCOMB, "holo_wallpaper_notitlebar"),
+
+ // Holo Light
+ new Theme(android.R.style.Theme_Holo_Light,
+ Build.VERSION_CODES.HONEYCOMB, "holo_light"),
+ new Theme(android.R.style.Theme_Holo_Light_DarkActionBar,
+ Build.VERSION_CODES.ICE_CREAM_SANDWICH, "holo_light_darkactionbar"),
+ new Theme(android.R.style.Theme_Holo_Light_Dialog,
+ Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog"),
+ new Theme(android.R.style.Theme_Holo_Light_Dialog_MinWidth,
+ Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_minwidth"),
+ new Theme(android.R.style.Theme_Holo_Light_Dialog_NoActionBar,
+ Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_noactionbar"),
+ new Theme(android.R.style.Theme_Holo_Light_Dialog_NoActionBar_MinWidth,
+ Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_noactionbar_minwidth"),
+ new Theme(android.R.style.Theme_Holo_Light_DialogWhenLarge,
+ Build.VERSION_CODES.HONEYCOMB, "holo_light_dialogwhenlarge"),
+ new Theme(android.R.style.Theme_Holo_Light_DialogWhenLarge_NoActionBar,
+ Build.VERSION_CODES.HONEYCOMB, "holo_light_dialogwhenlarge_noactionbar"),
+ new Theme(android.R.style.Theme_Holo_Light_NoActionBar,
+ Build.VERSION_CODES.HONEYCOMB_MR2, "holo_light_noactionbar"),
+ new Theme(android.R.style.Theme_Holo_Light_NoActionBar_Fullscreen,
+ Build.VERSION_CODES.HONEYCOMB_MR2, "holo_light_noactionbar_fullscreen"),
+ new Theme(android.R.style.Theme_Holo_Light_NoActionBar_Overscan,
+ Build.VERSION_CODES.JELLY_BEAN_MR2, "holo_light_noactionbar_overscan"),
+ new Theme(android.R.style.Theme_Holo_Light_NoActionBar_TranslucentDecor,
+ Build.VERSION_CODES.KITKAT, "holo_light_noactionbar_translucentdecor"),
+ new Theme(android.R.style.Theme_Holo_Light_Panel,
+ Build.VERSION_CODES.HONEYCOMB, "holo_light_panel"),
+
+ // Material
+ new Theme(android.R.style.Theme_Material,
+ Build.VERSION_CODES.LOLLIPOP, "material"),
+ new Theme(android.R.style.Theme_Material_Dialog,
+ Build.VERSION_CODES.LOLLIPOP, "material_dialog"),
+ new Theme(android.R.style.Theme_Material_Dialog_Alert,
+ Build.VERSION_CODES.LOLLIPOP, "material_dialog_alert"),
+ new Theme(android.R.style.Theme_Material_Dialog_MinWidth,
+ Build.VERSION_CODES.LOLLIPOP, "material_dialog_minwidth"),
+ new Theme(android.R.style.Theme_Material_Dialog_NoActionBar,
+ Build.VERSION_CODES.LOLLIPOP, "material_dialog_noactionbar"),
+ new Theme(android.R.style.Theme_Material_Dialog_NoActionBar_MinWidth,
+ Build.VERSION_CODES.LOLLIPOP, "material_dialog_noactionbar_minwidth"),
+ new Theme(android.R.style.Theme_Material_Dialog_Presentation,
+ Build.VERSION_CODES.LOLLIPOP, "material_dialog_presentation"),
+ new Theme(android.R.style.Theme_Material_DialogWhenLarge,
+ Build.VERSION_CODES.LOLLIPOP, "material_dialogwhenlarge"),
+ new Theme(android.R.style.Theme_Material_DialogWhenLarge_NoActionBar,
+ Build.VERSION_CODES.LOLLIPOP, "material_dialogwhenlarge_noactionbar"),
+ new Theme(android.R.style.Theme_Material_InputMethod,
+ Build.VERSION_CODES.LOLLIPOP, "material_inputmethod"),
+ new Theme(android.R.style.Theme_Material_NoActionBar,
+ Build.VERSION_CODES.LOLLIPOP, "material_noactionbar"),
+ new Theme(android.R.style.Theme_Material_NoActionBar_Fullscreen,
+ Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_fullscreen"),
+ new Theme(android.R.style.Theme_Material_NoActionBar_Overscan,
+ Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_overscan"),
+ new Theme(android.R.style.Theme_Material_NoActionBar_TranslucentDecor,
+ Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_translucentdecor"),
+ new Theme(android.R.style.Theme_Material_Panel,
+ Build.VERSION_CODES.LOLLIPOP, "material_panel"),
+ new Theme(android.R.style.Theme_Material_Settings,
+ Build.VERSION_CODES.LOLLIPOP, "material_settings"),
+ new Theme(android.R.style.Theme_Material_Voice,
+ Build.VERSION_CODES.LOLLIPOP, "material_voice"),
+ new Theme(android.R.style.Theme_Material_Wallpaper,
+ Build.VERSION_CODES.LOLLIPOP, "material_wallpaper"),
+ new Theme(android.R.style.Theme_Material_Wallpaper_NoTitleBar,
+ Build.VERSION_CODES.LOLLIPOP, "material_wallpaper_notitlebar"),
+
+ // Material Light
+ new Theme(android.R.style.Theme_Material_Light,
+ Build.VERSION_CODES.LOLLIPOP, "material_light"),
+ new Theme(android.R.style.Theme_Material_Light_DarkActionBar,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_darkactionbar"),
+ new Theme(android.R.style.Theme_Material_Light_Dialog,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_dialog"),
+ new Theme(android.R.style.Theme_Material_Light_Dialog_Alert,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_alert"),
+ new Theme(android.R.style.Theme_Material_Light_Dialog_MinWidth,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_minwidth"),
+ new Theme(android.R.style.Theme_Material_Light_Dialog_NoActionBar,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_noactionbar"),
+ new Theme(android.R.style.Theme_Material_Light_Dialog_NoActionBar_MinWidth,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_noactionbar_minwidth"),
+ new Theme(android.R.style.Theme_Material_Light_Dialog_Presentation,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_presentation"),
+ new Theme(android.R.style.Theme_Material_Light_DialogWhenLarge,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_dialogwhenlarge"),
+ new Theme(android.R.style.Theme_Material_Light_DialogWhenLarge_NoActionBar,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_dialogwhenlarge_noactionbar"),
+ new Theme(android.R.style.Theme_Material_Light_LightStatusBar,
+ Build.VERSION_CODES.M, "material_light_lightstatusbar"),
+ new Theme(android.R.style.Theme_Material_Light_NoActionBar,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar"),
+ new Theme(android.R.style.Theme_Material_Light_NoActionBar_Fullscreen,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_fullscreen"),
+ new Theme(android.R.style.Theme_Material_Light_NoActionBar_Overscan,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_overscan"),
+ new Theme(android.R.style.Theme_Material_Light_NoActionBar_TranslucentDecor,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_translucentdecor"),
+ new Theme(android.R.style.Theme_Material_Light_Panel,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_panel"),
+ new Theme(android.R.style.Theme_Material_Light_Voice,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_voice")
+ };
+
+ /**
+ * A class to encapsulate information about a layout.
+ */
+ private static class Layout {
+ public final int id;
+ public final String name;
+ public final LayoutModifier modifier;
+
+ private Layout(int id, String name) {
+ this(id, name, null);
+ }
+
+ private Layout(int id, String name, LayoutModifier modifier) {
+ this.id = id;
+ this.name = name;
+ this.modifier = modifier;
+ }
+ }
+
+ // List of layouts to verify for each theme.
+ private static final Layout[] LAYOUTS = {
+ new Layout(R.layout.button, "button"),
+ new Layout(R.layout.button, "button_pressed",
+ new ViewPressedModifier()),
+ new Layout(R.layout.checkbox, "checkbox"),
+ new Layout(R.layout.checkbox, "checkbox_checked",
+ new ViewCheckedModifier()),
+ new Layout(R.layout.chronometer, "chronometer"),
+ new Layout(R.layout.color_blue_bright, "color_blue_bright"),
+ new Layout(R.layout.color_blue_dark, "color_blue_dark"),
+ new Layout(R.layout.color_blue_light, "color_blue_light"),
+ new Layout(R.layout.color_green_dark, "color_green_dark"),
+ new Layout(R.layout.color_green_light, "color_green_light"),
+ new Layout(R.layout.color_orange_dark, "color_orange_dark"),
+ new Layout(R.layout.color_orange_light, "color_orange_light"),
+ new Layout(R.layout.color_purple, "color_purple"),
+ new Layout(R.layout.color_red_dark, "color_red_dark"),
+ new Layout(R.layout.color_red_light, "color_red_light"),
+ new Layout(R.layout.datepicker, "datepicker",
+ new DatePickerModifier()),
+ new Layout(R.layout.display_info, "display_info"),
+ new Layout(R.layout.edittext, "edittext"),
+ new Layout(R.layout.progressbar_horizontal_0, "progressbar_horizontal_0"),
+ new Layout(R.layout.progressbar_horizontal_100, "progressbar_horizontal_100"),
+ new Layout(R.layout.progressbar_horizontal_50, "progressbar_horizontal_50"),
+ new Layout(R.layout.progressbar_large, "progressbar_large",
+ new ProgressBarModifier()),
+ new Layout(R.layout.progressbar_small, "progressbar_small",
+ new ProgressBarModifier()),
+ new Layout(R.layout.progressbar, "progressbar",
+ new ProgressBarModifier()),
+ new Layout(R.layout.radiobutton_checked, "radiobutton_checked"),
+ new Layout(R.layout.radiobutton, "radiobutton"),
+ new Layout(R.layout.radiogroup_horizontal, "radiogroup_horizontal"),
+ new Layout(R.layout.radiogroup_vertical, "radiogroup_vertical"),
+ new Layout(R.layout.ratingbar_0, "ratingbar_0"),
+ new Layout(R.layout.ratingbar_2point5, "ratingbar_2point5"),
+ new Layout(R.layout.ratingbar_5, "ratingbar_5"),
+ new Layout(R.layout.ratingbar_0, "ratingbar_0_pressed",
+ new ViewPressedModifier()),
+ new Layout(R.layout.ratingbar_2point5, "ratingbar_2point5_pressed",
+ new ViewPressedModifier()),
+ new Layout(R.layout.ratingbar_5, "ratingbar_5_pressed",
+ new ViewPressedModifier()),
+ new Layout(R.layout.searchview, "searchview_query",
+ new SearchViewModifier(SearchViewModifier.QUERY)),
+ new Layout(R.layout.searchview, "searchview_query_hint",
+ new SearchViewModifier(SearchViewModifier.QUERY_HINT)),
+ new Layout(R.layout.seekbar_0, "seekbar_0"),
+ new Layout(R.layout.seekbar_100, "seekbar_100"),
+ new Layout(R.layout.seekbar_50, "seekbar_50"),
+ new Layout(R.layout.spinner, "spinner"),
+ new Layout(R.layout.switch_button_checked, "switch_button_checked"),
+ new Layout(R.layout.switch_button, "switch_button"),
+ new Layout(R.layout.textview, "textview"),
+ new Layout(R.layout.timepicker, "timepicker",
+ new TimePickerModifier()),
+ new Layout(R.layout.togglebutton_checked, "togglebutton_checked"),
+ new Layout(R.layout.togglebutton, "togglebutton"),
+ new Layout(R.layout.zoomcontrols, "zoomcontrols"),
+ };
+}
diff --git a/hostsidetests/theme/app/src/android/theme/app/ThemeTestUtils.java b/hostsidetests/theme/app/src/android/theme/app/ThemeTestUtils.java
new file mode 100644
index 0000000..4daca6c
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/ThemeTestUtils.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.theme.app;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+public class ThemeTestUtils {
+
+ /**
+ * Compresses the contents of a directory to a ZIP file.
+ *
+ * @param dir the directory to compress
+ * @return {@code true} on success, {@code false} on failure
+ */
+ public static boolean compressDirectory(File dir, File file) throws IOException {
+ if (dir == null || !dir.exists() || file == null || file.exists()) {
+ return false;
+ }
+
+ final File[] srcFiles = dir.listFiles();
+ if (srcFiles.length == 0) {
+ return false;
+ }
+
+ final ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(file));
+ final byte[] data = new byte[4096];
+ for (int i = 0; i < srcFiles.length; i++) {
+ final FileInputStream fileIn = new FileInputStream(srcFiles[i]);
+ final ZipEntry entry = new ZipEntry(srcFiles[i].getName());
+ zipOut.putNextEntry(entry);
+
+ int count;
+ while ((count = fileIn.read(data, 0, data.length)) != -1) {
+ zipOut.write(data, 0, count);
+ zipOut.flush();
+ }
+
+ zipOut.closeEntry();
+ fileIn.close();
+ }
+
+ zipOut.close();
+ return true;
+ }
+
+ /**
+ * Recursively deletes a directory and its contents.
+ *
+ * @param dir the directory to delete
+ * @return {@code true} on success, {@code false} on failure
+ */
+ public static boolean deleteDirectory(File dir) {
+ final File files[] = dir.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ deleteDirectory(file);
+ }
+ }
+ return dir.delete();
+ }
+}
diff --git a/hostsidetests/theme/assets/23/400dpi.zip b/hostsidetests/theme/assets/23/400dpi.zip
new file mode 100644
index 0000000..be0891f
--- /dev/null
+++ b/hostsidetests/theme/assets/23/400dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/560dpi.zip b/hostsidetests/theme/assets/23/560dpi.zip
new file mode 100644
index 0000000..cf0a559
--- /dev/null
+++ b/hostsidetests/theme/assets/23/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/hdpi.zip b/hostsidetests/theme/assets/23/hdpi.zip
new file mode 100644
index 0000000..80c12a7
--- /dev/null
+++ b/hostsidetests/theme/assets/23/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/ldpi.zip b/hostsidetests/theme/assets/23/ldpi.zip
new file mode 100644
index 0000000..937914a
--- /dev/null
+++ b/hostsidetests/theme/assets/23/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/mdpi.zip b/hostsidetests/theme/assets/23/mdpi.zip
new file mode 100644
index 0000000..f842676
--- /dev/null
+++ b/hostsidetests/theme/assets/23/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/tvdpi.zip b/hostsidetests/theme/assets/23/tvdpi.zip
new file mode 100644
index 0000000..77386e5
--- /dev/null
+++ b/hostsidetests/theme/assets/23/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/xhdpi.zip b/hostsidetests/theme/assets/23/xhdpi.zip
new file mode 100644
index 0000000..a8310d5
--- /dev/null
+++ b/hostsidetests/theme/assets/23/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/xxhdpi.zip b/hostsidetests/theme/assets/23/xxhdpi.zip
new file mode 100644
index 0000000..f88711f
--- /dev/null
+++ b/hostsidetests/theme/assets/23/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/generate_images.sh b/hostsidetests/theme/generate_images.sh
new file mode 100755
index 0000000..9bcc3e5
--- /dev/null
+++ b/hostsidetests/theme/generate_images.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script is used to generate reference images for the CTS theme tests.
+# See the accompanying README file for more information.
+
+# retry <command> <tries> <message> <delay>
+function retry {
+ RETRY="0"
+ while true; do
+ if (("$RETRY" >= "$2")); then
+ echo $OUTPUT
+ exit
+ fi
+
+ OUTPUT=`$1 |& grep error`
+
+ if [ -z "$OUTPUT" ]; then
+ break
+ fi
+
+ echo $3
+ sleep $4
+ RETRY=$[$RETRY + 1]
+ done
+}
+
+themeApkPath="$ANDROID_HOST_OUT/cts/android-cts/repository/testcases/CtsThemeDeviceApp.apk"
+outDir="$ANDROID_BUILD_TOP/cts/hostsidetests/theme/assets"
+exe="$ANDROID_BUILD_TOP/cts/hostsidetests/theme/run_theme_capture_device.py"
+
+if [ -z "$ANDROID_BUILD_TOP" ]; then
+ echo "Missing environment variables. Did you run build/envsetup.sh and lunch?"
+ exit
+fi
+
+if [ ! -e "$themeApkPath" ]; then
+ echo "Couldn't find test APK. Did you run make cts?"
+ exit
+fi
+
+adb devices
+python $exe $themeApkPath $outDir
diff --git a/hostsidetests/theme/run_theme_capture_device.py b/hostsidetests/theme/run_theme_capture_device.py
new file mode 100755
index 0000000..23171db
--- /dev/null
+++ b/hostsidetests/theme/run_theme_capture_device.py
@@ -0,0 +1,170 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import os
+import sys
+import threading
+import time
+import Queue
+sys.path.append(sys.path[0])
+from android_device import *
+
+CTS_THEME_dict = {
+ 120 : "ldpi",
+ 160 : "mdpi",
+ 213 : "tvdpi",
+ 240 : "hdpi",
+ 320 : "xhdpi",
+ 400 : "400dpi",
+ 480 : "xxhdpi",
+ 560 : "560dpi",
+ 640 : "xxxhdpi",
+}
+
+OUT_FILE = "/sdcard/cts-theme-assets.zip"
+
+# pass a function with number of instances to be executed in parallel
+# each thread continues until config q is empty.
+def executeParallel(tasks, setup, q, numberThreads):
+ class ParallelExecutor(threading.Thread):
+ def __init__(self, tasks, q):
+ threading.Thread.__init__(self)
+ self._q = q
+ self._tasks = tasks
+ self._setup = setup
+ self._result = 0
+
+ def run(self):
+ try:
+ while True:
+ config = q.get(block=True, timeout=2)
+ for t in self._tasks:
+ try:
+ if t(self._setup, config):
+ self._result += 1
+ except KeyboardInterrupt:
+ raise
+ except:
+ print "Failed to execute thread:", sys.exc_info()[0]
+ q.task_done()
+ except KeyboardInterrupt:
+ raise
+ except Queue.Empty:
+ pass
+
+ def getResult(self):
+ return self._result
+
+ result = 0;
+ threads = []
+ for i in range(numberThreads):
+ t = ParallelExecutor(tasks, q)
+ t.start()
+ threads.append(t)
+ for t in threads:
+ t.join()
+ result += t.getResult()
+ return result;
+
+def printAdbResult(device, out, err):
+ print "device: " + device
+ if out is not None:
+ print "out:\n" + out
+ if err is not None:
+ print "err:\n" + err
+
+def getResDir(outPath, resName):
+ resDir = outPath + "/" + resName
+ return resDir
+
+def doCapturing(setup, deviceSerial):
+ (themeApkPath, outPath) = setup
+
+ print "Found device: " + deviceSerial
+ device = androidDevice(deviceSerial)
+
+ # outPath = outPath + "/%d/" % (device.getSdkLevel()) + deviceSerial
+ outPath = outPath + "/%d" % (device.getSdkLevel())
+ density = device.getDensity()
+ resName = CTS_THEME_dict[density]
+
+ device.uninstallApk("android.theme.app")
+
+ (out, err, success) = device.installApk(themeApkPath)
+ if not success:
+ print "Failed to install APK on " + deviceSerial
+ printAdbResult(device, out, err)
+ return False
+
+ print "Generating images on " + deviceSerial + "..."
+ try:
+ (out, err) = device.runInstrumentationTest("android.theme.app/android.support.test.runner.AndroidJUnitRunner")
+ except KeyboardInterrupt:
+ raise
+ except:
+ (out, err) = device.runInstrumentationTest("android.theme.app/android.test.InstrumentationTestRunner")
+
+ # Detect test failure and abort.
+ if "FAILURES!!!" in out.split():
+ printAdbResult(deviceSerial, out, err)
+ return False
+
+ # Make sure that the run is complete by checking the process itself
+ print "Waiting for " + deviceSerial + "..."
+ waitTime = 0
+ while device.isProcessAlive("android.theme.app"):
+ time.sleep(1)
+ waitTime = waitTime + 1
+ if waitTime > 180:
+ print "Timed out"
+ break
+
+ time.sleep(10)
+ resDir = getResDir(outPath, resName)
+
+ print "Pulling images from " + deviceSerial + " to " + resDir + ".zip"
+ device.runAdbCommand("pull " + OUT_FILE + " " + resDir + ".zip")
+ device.runAdbCommand("shell rm -rf " + OUT_FILE)
+ return True
+
+def main(argv):
+ if len(argv) < 3:
+ print "run_theme_capture_device.py themeApkPath outDir"
+ sys.exit(1)
+ themeApkPath = argv[1]
+ outPath = os.path.abspath(argv[2])
+ os.system("mkdir -p " + outPath)
+
+ tasks = []
+ tasks.append(doCapturing)
+
+ devices = runAdbDevices();
+ numberThreads = len(devices)
+
+ configQ = Queue.Queue()
+ for device in devices:
+ configQ.put(device)
+ setup = (themeApkPath, outPath)
+ result = executeParallel(tasks, setup, configQ, numberThreads)
+
+ if result > 0:
+ print 'Generated reference images for %(count)d devices' % {"count": result}
+ else:
+ print 'Failed to generate reference images'
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java b/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
index ba880d7..63c7472 100644
--- a/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
+++ b/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
@@ -23,6 +23,7 @@
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
+import java.io.IOException;
import java.lang.String;
import java.util.concurrent.Callable;
@@ -32,70 +33,62 @@
* Compares the images generated by the device with the reference images.
*/
public class ComparisonTask implements Callable<Boolean> {
-
- private static final String TAG = ComparisonTask.class.getSimpleName();
+ private static final String TAG = "ComparisonTask";
private static final int IMAGE_THRESHOLD = 2;
- private static final String STORAGE_PATH_DEVICE = "/sdcard/cts-holo-assets/%s.png";
-
private final ITestDevice mDevice;
+ private final File mExpected;
+ private final File mActual;
- private final File mReference;
-
- private final String mName;
-
- public ComparisonTask(ITestDevice device, File reference, String name) {
+ public ComparisonTask(ITestDevice device, File expected, File actual) {
mDevice = device;
- mReference = reference;
- mName = name;
+ mExpected = expected;
+ mActual = actual;
}
public Boolean call() {
boolean success = false;
- File generated = null;
+
try {
- generated = File.createTempFile("gen_" + mName, ".png");
-
- final String remoteGenerated = String.format(STORAGE_PATH_DEVICE, mName);
- if (!mDevice.doesFileExist(remoteGenerated)) {
- Log.logAndDisplay(LogLevel.ERROR, TAG, "File " + remoteGenerated + " have not been saved on device");
- return false;
- }
- mDevice.pullFile(remoteGenerated, generated);
-
- final BufferedImage ref = ImageIO.read(mReference);
- final BufferedImage gen = ImageIO.read(generated);
- if (compare(ref, gen, IMAGE_THRESHOLD)) {
+ final BufferedImage expected = ImageIO.read(mExpected);
+ final BufferedImage actual = ImageIO.read(mActual);
+ if (compare(expected, actual, IMAGE_THRESHOLD)) {
success = true;
} else {
- File diff = File.createTempFile("diff_" + mName, ".png");
- createDiff(ref, gen, diff);
+ final File diff = File.createTempFile("diff_" + mExpected.getName(), ".png");
+ createDiff(expected, actual, diff);
Log.logAndDisplay(LogLevel.INFO, TAG, "Diff created: " + diff.getPath());
}
- } catch (Exception e) {
- Log.logAndDisplay(LogLevel.ERROR, TAG, String.format(STORAGE_PATH_DEVICE, mName));
+ } catch (IOException e) {
Log.logAndDisplay(LogLevel.ERROR, TAG, e.toString());
e.printStackTrace();
- } finally {
- if (generated != null) {
- generated.delete();
- }
}
+
return success;
}
- private static boolean compare(BufferedImage reference, BufferedImage generated, int threshold) {
- final int w = generated.getWidth();
- final int h = generated.getHeight();
- if (w != reference.getWidth() || h != reference.getHeight()) {
+ /**
+ * Verifies that the pixels of reference and generated images are similar
+ * within a specified threshold.
+ *
+ * @param expected expected image
+ * @param actual actual image
+ * @param threshold maximum difference per channel
+ * @return {@code true} if the images are similar, false otherwise
+ */
+ private static boolean compare(BufferedImage expected, BufferedImage actual,
+ int threshold) {
+ final int w = actual.getWidth();
+ final int h = actual.getHeight();
+ if (w != expected.getWidth() || h != expected.getHeight()) {
return false;
}
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
- final int p1 = reference.getRGB(i, j);
- final int p2 = generated.getRGB(i, j);
+ final int p1 = expected.getRGB(i, j);
+ final int p2 = actual.getRGB(i, j);
final int dr = (p1 & 0x000000FF) - (p2 & 0x000000FF);
final int dg = ((p1 & 0x0000FF00) - (p2 & 0x0000FF00)) >> 8;
final int db = ((p1 & 0x00FF0000) - (p2 & 0x00FF0000)) >> 16;
@@ -112,45 +105,49 @@
return true;
}
- private static void createDiff(BufferedImage image1, BufferedImage image2, File out)
- throws Exception {
- final int w1 = image1.getWidth();
- final int h1 = image1.getHeight();
- final int w2 = image2.getWidth();
- final int h2 = image2.getHeight();
+ private static void createDiff(BufferedImage expected, BufferedImage actual, File out)
+ throws IOException {
+ final int w1 = expected.getWidth();
+ final int h1 = expected.getHeight();
+ final int w2 = actual.getWidth();
+ final int h2 = actual.getHeight();
final int width = Math.max(w1, w2);
final int height = Math.max(h1, h2);
+
// The diff will contain image1, image2 and the difference between the two.
- final BufferedImage diff = new BufferedImage(width * 3, height, BufferedImage.TYPE_INT_ARGB);
+ final BufferedImage diff = new BufferedImage(
+ width * 3, height, BufferedImage.TYPE_INT_ARGB);
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
final boolean inBounds1 = i < w1 && j < h1;
final boolean inBounds2 = i < w2 && j < h2;
- int color1 = Color.WHITE.getRGB();
- int color2 = Color.WHITE.getRGB();
- int color3;
+ int colorExpected = Color.WHITE.getRGB();
+ int colorActual = Color.WHITE.getRGB();
+ int colorDiff;
if (inBounds1 && inBounds2) {
- color1 = image1.getRGB(i, j);
- color2 = image2.getRGB(i, j);
- color3 = color1 == color2 ? color1 : Color.RED.getRGB();
+ colorExpected = expected.getRGB(i, j);
+ colorActual = actual.getRGB(i, j);
+ colorDiff = colorExpected == colorActual ? colorExpected : Color.RED.getRGB();
} else if (inBounds1 && !inBounds2) {
- color1 = image1.getRGB(i, j);
- color3 = Color.BLUE.getRGB();
+ colorExpected = expected.getRGB(i, j);
+ colorDiff = Color.BLUE.getRGB();
} else if (!inBounds1 && inBounds2) {
- color2 = image2.getRGB(i, j);
- color3 = Color.GREEN.getRGB();
+ colorActual = actual.getRGB(i, j);
+ colorDiff = Color.GREEN.getRGB();
} else {
- color3 = Color.MAGENTA.getRGB();
+ colorDiff = Color.MAGENTA.getRGB();
}
+
int x = i;
- diff.setRGB(x, j, color1);
+ diff.setRGB(x, j, colorExpected);
x += width;
- diff.setRGB(x, j, color2);
+ diff.setRGB(x, j, colorActual);
x += width;
- diff.setRGB(x, j, color3);
+ diff.setRGB(x, j, colorDiff);
}
}
+
ImageIO.write(diff, "png", out);
}
diff --git a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
index 7c4d63a..e17e205 100644
--- a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
+++ b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
@@ -21,8 +21,8 @@
import com.android.cts.util.TimeoutReq;
import com.android.ddmlib.Log;
import com.android.ddmlib.Log.LogLevel;
-import com.android.ddmlib.IShellOutputReceiver;
import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IAbi;
@@ -30,145 +30,44 @@
import com.android.tradefed.testtype.IBuildReceiver;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.lang.String;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;
-import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
- * Test to check the Holo theme has not been changed.
+ * Test to check non-modifiable themes have not been changed.
*/
public class ThemeHostTest extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+ private static final String LOG_TAG = "ThemeHostTest";
+ private static final String APK_NAME = "CtsThemeDeviceApp";
+ private static final String APP_PACKAGE_NAME = "android.theme.app";
- private static final String TAG = ThemeHostTest.class.getSimpleName();
-
- private static final int ADB_TIMEOUT = 60 * 60 * 1000;//60mins in ms
-
- /** The package name of the APK. */
- private static final String PACKAGE = "android.theme.app";
-
- /** The file name of the APK. */
- private static final String APK = "CtsThemeDeviceApp.apk";
+ private static final String GENERATED_ASSETS_ZIP = "/sdcard/cts-theme-assets.zip";
/** The class name of the main activity in the APK. */
- private static final String CLASS = "HoloDeviceActivity";
+ private static final String CLASS = "GenerateImagesActivity";
/** The command to launch the main activity. */
private static final String START_CMD = String.format(
- "am start -W -a android.intent.action.MAIN -n %s/%s.%s", PACKAGE, PACKAGE, CLASS);
+ "am start -W -a android.intent.action.MAIN -n %s/%s.%s", APP_PACKAGE_NAME,
+ APP_PACKAGE_NAME, CLASS);
- private static final String CLEAR_GENERATED_CMD = "rm -rf /sdcard/cts-holo-assets/*.png";
-
- private static final String STOP_CMD = String.format("am force-stop %s", PACKAGE);
-
+ private static final String CLEAR_GENERATED_CMD = "rm -rf %s/*.png";
+ private static final String STOP_CMD = String.format("am force-stop %s", APP_PACKAGE_NAME);
private static final String HARDWARE_TYPE_CMD = "dumpsys | grep android.hardware.type";
-
private static final String DENSITY_PROP_DEVICE = "ro.sf.lcd_density";
-
private static final String DENSITY_PROP_EMULATOR = "qemu.sf.lcd_density";
- // Intent extras
- protected final static String INTENT_STRING_EXTRA = " --es %s %s";
-
- protected final static String INTENT_BOOLEAN_EXTRA = " --ez %s %b";
-
- protected final static String INTENT_INTEGER_EXTRA = " --ei %s %d";
-
- // Intent extra keys
- private static final String EXTRA_THEME = "holo_theme_extra";
-
- private static final String[] THEMES = {
- "holo",
- "holo_dialog",
- "holo_dialog_minwidth",
- "holo_dialog_noactionbar",
- "holo_dialog_noactionbar_minwidth",
- "holo_dialogwhenlarge",
- "holo_dialogwhenlarge_noactionbar",
- "holo_inputmethod",
- "holo_light",
- "holo_light_darkactionbar",
- "holo_light_dialog",
- "holo_light_dialog_minwidth",
- "holo_light_dialog_noactionbar",
- "holo_light_dialog_noactionbar_minwidth",
- "holo_light_dialogwhenlarge",
- "holo_light_dialogwhenlarge_noactionbar",
- "holo_light_noactionbar",
- "holo_light_noactionbar_fullscreen",
- "holo_light_panel",
- "holo_noactionbar",
- "holo_noactionbar_fullscreen",
- "holo_panel",
- "holo_wallpaper",
- "holo_wallpaper_notitlebar",
- };
-
- private final int NUM_THEMES = THEMES.length;
-
- private static final String[] LAYOUTS = {
- "button",
- "button_pressed",
- "checkbox",
- "checkbox_checked",
- "chronometer",
- "color_blue_bright",
- "color_blue_dark",
- "color_blue_light",
- "color_green_dark",
- "color_green_light",
- "color_orange_dark",
- "color_orange_light",
- "color_purple",
- "color_red_dark",
- "color_red_light",
- "datepicker",
- "display_info",
- "edittext",
- "progressbar_horizontal_0",
- "progressbar_horizontal_100",
- "progressbar_horizontal_50",
- "progressbar_large",
- "progressbar_small",
- "progressbar",
- "radiobutton_checked",
- "radiobutton",
- "radiogroup_horizontal",
- "radiogroup_vertical",
- "ratingbar_0",
- "ratingbar_2point5",
- "ratingbar_5",
- "ratingbar_0_pressed",
- "ratingbar_2point5_pressed",
- "ratingbar_5_pressed",
- "searchview_query",
- "searchview_query_hint",
- "seekbar_0",
- "seekbar_100",
- "seekbar_50",
- "spinner",
- "switch_button_checked",
- "switch_button",
- "textview",
- "timepicker",
- "togglebutton_checked",
- "togglebutton",
- "zoomcontrols",
- };
-
- private final int NUM_LAYOUTS = LAYOUTS.length;
-
- private final HashMap<String, File> mReferences = new HashMap<String, File>();
+ private final HashMap<String, File> mReferences = new HashMap<>();
/** The ABI to use. */
private IAbi mAbi;
@@ -197,32 +96,21 @@
@Override
protected void setUp() throws Exception {
super.setUp();
- // Get the device, this gives a handle to run commands and install APKs.
+
mDevice = getDevice();
- // Remove any previously installed versions of this APK.
- mDevice.uninstallPackage(PACKAGE);
+ mDevice.uninstallPackage(APP_PACKAGE_NAME);
+
// Get the APK from the build.
- File app = mBuild.getTestApp(APK);
- // Get the ABI flag.
- String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
- // Install the APK on the device.
+ final File app = mBuild.getTestApp(String.format("%s.apk", APK_NAME));
+ final String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+
mDevice.installPackage(app, false, options);
- // Remove previously generated images.
- mDevice.executeShellCommand(CLEAR_GENERATED_CMD);
- final String densityProp;
- if (mDevice.getSerialNumber().startsWith("emulator-")) {
- densityProp = DENSITY_PROP_EMULATOR;
- } else {
- densityProp = DENSITY_PROP_DEVICE;
- }
+ final String density = getDensityBucketForDevice(mDevice);
+ final String zipFile = String.format("/%s.zip", density);
+ Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Loading resources from " + zipFile);
- final String zip = String.format("/%s.zip",
- getDensityBucket(Integer.parseInt(mDevice.getProperty(densityProp))));
- Log.logAndDisplay(LogLevel.INFO, TAG, "Loading resources from " + zip);
-
-
- final InputStream zipStream = this.getClass().getResourceAsStream(zip);
+ final InputStream zipStream = ThemeHostTest.class.getResourceAsStream(zipFile);
if (zipStream != null) {
final ZipInputStream in = new ZipInputStream(zipStream);
try {
@@ -232,21 +120,28 @@
final String name = ze.getName();
final File tmp = File.createTempFile("ref_" + name, ".png");
final FileOutputStream out = new FileOutputStream(tmp);
+
int count;
while ((count = in.read(buffer)) != -1) {
out.write(buffer, 0, count);
}
+
out.flush();
out.close();
mReferences.put(name, tmp);
}
+ } catch (IOException e) {
+ Log.logAndDisplay(LogLevel.ERROR, LOG_TAG, "Failed to unzip assets: " + zipFile);
} finally {
in.close();
}
+ } else {
+ Log.logAndDisplay(LogLevel.ERROR, LOG_TAG, "Failed to get resource: " + zipFile);
}
- mExecutionService = Executors.newFixedThreadPool(2);// 2 worker threads
- mCompletionService = new ExecutorCompletionService<Boolean>(mExecutionService);
+ final int numCores = Runtime.getRuntime().availableProcessors();
+ mExecutionService = Executors.newFixedThreadPool(numCores * 2);
+ mCompletionService = new ExecutorCompletionService<>(mExecutionService);
}
@Override
@@ -255,86 +150,148 @@
for (File ref : mReferences.values()) {
ref.delete();
}
+
mExecutionService.shutdown();
+
// Remove the APK.
- mDevice.uninstallPackage(PACKAGE);
+ mDevice.uninstallPackage(APP_PACKAGE_NAME);
+
// Remove generated images.
mDevice.executeShellCommand(CLEAR_GENERATED_CMD);
+
super.tearDown();
}
@TimeoutReq(minutes = 60)
- public void testHoloThemes() throws Exception {
- if (checkHardwareTypeSkipTest(
- mDevice.executeShellCommand(HARDWARE_TYPE_CMD).trim())) {
- Log.logAndDisplay(LogLevel.INFO, TAG, "Skipped HoloThemes test for watch and TV");
+ public void testThemes() throws Exception {
+ if (checkHardwareTypeSkipTest(mDevice.executeShellCommand(HARDWARE_TYPE_CMD).trim())) {
+ Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Skipped themes test for watch");
return;
}
if (mReferences.isEmpty()) {
- Log.logAndDisplay(LogLevel.INFO, TAG,
- "Skipped HoloThemes test due to no reference images");
+ Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Skipped themes test due to no reference images");
return;
}
+ Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Generating device images...");
+
+ assertTrue("Aborted image generation", generateDeviceImages());
+
+ // Pull ZIP file from remote device.
+ final File localZip = File.createTempFile("generated", ".zip");
+ mDevice.pullFile(GENERATED_ASSETS_ZIP, localZip);
+
int numTasks = 0;
- for (int i = 0; i < NUM_THEMES; i++) {
- final String themeName = THEMES[i];
- runCapture(i, themeName);
- for (int j = 0; j < NUM_LAYOUTS; j++) {
- final String name = String.format("%s_%s", themeName, LAYOUTS[j]);
- final File ref = mReferences.get(name + ".png");
- if (!ref.exists()) {
- Log.logAndDisplay(LogLevel.INFO, TAG,
- "Skipping theme test due to missing reference for reference image " +
- name);
- continue;
+
+ Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Extracting generated images...");
+
+ // Extract generated images to temporary files.
+ final byte[] data = new byte[4096];
+ final ZipInputStream zipInput = new ZipInputStream(new FileInputStream(localZip));
+ ZipEntry entry;
+ while ((entry = zipInput.getNextEntry()) != null) {
+ final String name = entry.getName();
+ final File expected = mReferences.get(name);
+ if (expected != null && expected.exists()) {
+ final File actual = File.createTempFile("actual_" + name, ".png");
+ final FileOutputStream pngOutput = new FileOutputStream(actual);
+
+ int count;
+ while ((count = zipInput.read(data, 0, data.length)) != -1) {
+ pngOutput.write(data, 0, count);
}
- mCompletionService.submit(new ComparisonTask(mDevice, ref, name));
+
+ pngOutput.flush();
+ pngOutput.close();
+
+ mCompletionService.submit(new ComparisonTask(mDevice, expected, actual));
numTasks++;
+ } else {
+ Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Missing reference image for " + name);
}
+
+ zipInput.closeEntry();
}
+
+ zipInput.close();
+
+ Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Waiting for comparison tasks...");
+
int failures = 0;
- for (int i = 0; i < numTasks; i++) {
+ for (int i = numTasks; i > 0; i--) {
failures += mCompletionService.take().get() ? 0 : 1;
}
+
assertTrue(failures + " failures in theme test", failures == 0);
+
+ Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Finished!");
}
- private void runCapture(int themeId, String themeName) throws Exception {
- final StringBuilder sb = new StringBuilder(START_CMD);
- sb.append(String.format(INTENT_INTEGER_EXTRA, EXTRA_THEME, themeId));
- final String startCommand = sb.toString();
+ private boolean generateDeviceImages() throws Exception {
// Clear logcat
mDevice.executeAdbCommand("logcat", "-c");
+
// Stop any existing instances
mDevice.executeShellCommand(STOP_CMD);
- // Start activity
- mDevice.executeShellCommand(startCommand);
+ // Start activity
+ mDevice.executeShellCommand(START_CMD);
+
+ Log.logAndDisplay(LogLevel.VERBOSE, LOG_TAG, "Starting image generation...");
+
+ boolean aborted = false;
boolean waiting = true;
do {
// Dump logcat.
final String logs = mDevice.executeAdbCommand(
"logcat", "-v", "brief", "-d", CLASS + ":I", "*:S");
+
// Search for string.
final Scanner in = new Scanner(logs);
while (in.hasNextLine()) {
final String line = in.nextLine();
if (line.startsWith("I/" + CLASS)) {
final String[] lineSplit = line.split(":");
- final String s = lineSplit[1].trim();
- final String themeNameGenerated = lineSplit[2].trim();
- if (s.equals("OKAY") && themeNameGenerated.equals(themeName)) {
- waiting = false;
+ if (lineSplit.length >= 3) {
+ final String cmd = lineSplit[1].trim();
+ final String arg = lineSplit[2].trim();
+ switch (cmd) {
+ case "FAIL":
+ Log.logAndDisplay(LogLevel.WARN, LOG_TAG, line);
+ Log.logAndDisplay(LogLevel.WARN, LOG_TAG, "Aborting! Check host logs for details.");
+ aborted = true;
+ // fall-through
+ case "OKAY":
+ waiting = false;
+ break;
+ }
}
}
}
in.close();
- } while (waiting);
+ } while (waiting && !aborted);
+
+ Log.logAndDisplay(LogLevel.VERBOSE, LOG_TAG, "Image generation completed!");
+
+ return !aborted;
}
- private static String getDensityBucket(int density) {
+ private static String getDensityBucketForDevice(ITestDevice device) {
+ final String densityProp;
+ if (device.getSerialNumber().startsWith("emulator-")) {
+ densityProp = DENSITY_PROP_EMULATOR;
+ } else {
+ densityProp = DENSITY_PROP_DEVICE;
+ }
+
+ final int density;
+ try {
+ density = Integer.parseInt(device.getProperty(densityProp));
+ } catch (DeviceNotAvailableException e) {
+ return "unknown";
+ }
+
switch (density) {
case 120:
return "ldpi";
@@ -363,9 +320,7 @@
if (hardwareTypeString.contains("android.hardware.type.watch")) {
return true;
}
- if (hardwareTypeString.contains("android.hardware.type.television")) {
- return true;
- }
+
return false;
}
}
diff --git a/tests/sample/src/android/sample/cts/SampleJUnit4DeviceTest.java b/tests/sample/src/android/sample/cts/SampleJUnit4DeviceTest.java
new file mode 100755
index 0000000..c8863b3
--- /dev/null
+++ b/tests/sample/src/android/sample/cts/SampleJUnit4DeviceTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.sample.cts;
+
+import org.junit.Assert;
+import org.junit.runner.RunWith;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import android.app.Activity;
+import android.sample.SampleDeviceActivity;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+ * A simple compatibility test which tests the SharedPreferences API.
+ *
+ * This test uses {@link ActivityTestRule} to instrument the
+ * {@link android.sample.SampleDeviceActivity}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class SampleJUnit4DeviceTest {
+
+ private static final String KEY = "foo";
+
+ private static final String VALUE = "bar";
+
+ @Rule
+ public ActivityTestRule<SampleDeviceActivity> mActivityRule =
+ new ActivityTestRule(SampleDeviceActivity.class);
+
+
+ /**
+ * This inserts the key value pair and assert they can be retrieved. Then it clears the
+ * preferences and asserts they can no longer be retrieved.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void shouldSaveSharedPreferences() throws Exception {
+ // Save the key value pair to the preferences and assert they were saved.
+ mActivityRule.getActivity().savePreference(KEY, VALUE);
+ Assert.assertEquals("Preferences were not saved", VALUE,
+ mActivityRule.getActivity().getPreference(KEY));
+
+ // Clear the shared preferences and assert the data was removed.
+ mActivityRule.getActivity().clearPreferences();
+ Assert.assertNull("Preferences were not cleared",
+ mActivityRule.getActivity().getPreference(KEY));
+ }
+}
diff --git a/tests/tests/assist/AndroidManifest.xml b/tests/tests/assist/AndroidManifest.xml
index 97bd874..fefdf54 100644
--- a/tests/tests/assist/AndroidManifest.xml
+++ b/tests/tests/assist/AndroidManifest.xml
@@ -23,8 +23,9 @@
<application>
<uses-library android:name="android.test.runner" />
- <activity android:name="android.assist.TestStartActivity"
- android:label="Assist Target">
+ <activity android:name="android.assist.cts.TestStartActivity"
+ android:label="Assist Structure Test App"
+ android:theme="@android:style/Theme.Material.Light">
<intent-filter>
<action android:name="android.intent.action.TEST_START_ACTIVITY_ASSIST_STRUCTURE" />
<action android:name="android.intent.action.TEST_START_ACTIVITY_DISABLE_CONTEXT" />
diff --git a/tests/tests/assist/common/src/android/assist/common/Utils.java b/tests/tests/assist/common/src/android/assist/common/Utils.java
index 8d555da..4bf9412 100644
--- a/tests/tests/assist/common/src/android/assist/common/Utils.java
+++ b/tests/tests/assist/common/src/android/assist/common/Utils.java
@@ -46,9 +46,11 @@
/** Flag Secure Test intent constants */
public static final String FLAG_SECURE_HASRESUMED = ACTION_PREFIX + "flag_secure_hasResumed";
+ public static final String ASSIST_STRUCTURE_HASRESUMED = ACTION_PREFIX
+ + "assist_structure_hasResumed";
/** Two second timeout for getting back assist context */
- public static final int TIMEOUT_MS = 2 * 1000; // TODO(awlee): what is the timeout
+ public static final int TIMEOUT_MS = 2 * 1000;
public static final int ACTIVITY_ONRESUME_TIMEOUT_MS = 4000;
public static final String EXTRA_REGISTER_RECEIVER = "register_receiver";
@@ -66,10 +68,9 @@
*/
public static final String getTestActivity(String testCaseType) {
switch (testCaseType) {
- case ASSIST_STRUCTURE:
- return "service.AssistStructureActivity";
case DISABLE_CONTEXT:
return "service.DisableContextActivity";
+ case ASSIST_STRUCTURE:
case FLAG_SECURE:
case LIFECYCLE:
return "service.DelayedAssistantActivity";
@@ -84,9 +85,11 @@
public static final ComponentName getTestAppComponent(String testCaseType) {
switch (testCaseType) {
case ASSIST_STRUCTURE:
- case DISABLE_CONTEXT:
return new ComponentName(
"android.assist.testapp", "android.assist.testapp.TestApp");
+ case DISABLE_CONTEXT:
+ return new ComponentName(
+ "android.assist.testapp", "android.assist.testapp.DisableContextActivity");
case FLAG_SECURE:
return new ComponentName(
"android.assist.testapp", "android.assist.testapp.SecureActivity");
diff --git a/tests/tests/assist/res/layout/test_app.xml b/tests/tests/assist/res/layout/test_app.xml
new file mode 100644
index 0000000..3fbfd6d
--- /dev/null
+++ b/tests/tests/assist/res/layout/test_app.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/welcome" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="350dp"
+ android:orientation="vertical"
+ android:layout_gravity="bottom">
+ <FrameLayout
+ android:id="@+id/card1"
+ android:layout_width="match_parent"
+ android:layout_height="150dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginTop="16dp"
+ android:elevation="3dp">
+ </FrameLayout>
+ <View
+ android:id="@+id/card2"
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginTop="16dp"
+ android:elevation="3dp"/>
+ </LinearLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/res/values/strings.xml b/tests/tests/assist/res/values/strings.xml
new file mode 100644
index 0000000..ae4f16e
--- /dev/null
+++ b/tests/tests/assist/res/values/strings.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <string name="welcome">Hello there!</string>
+</resources>
diff --git a/tests/tests/assist/service/AndroidManifest.xml b/tests/tests/assist/service/AndroidManifest.xml
index cdbeef0..5a22d31 100644
--- a/tests/tests/assist/service/AndroidManifest.xml
+++ b/tests/tests/assist/service/AndroidManifest.xml
@@ -31,14 +31,7 @@
<action android:name="android.service.voice.VoiceInteractionService" />
</intent-filter>
</service>
- <activity android:name=".AssistStructureActivity" >
- <intent-filter>
- <action android:name="android.intent.action.START_TEST_ASSIST_STRUCTURE" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
- <activity android:name=".DisableContextActivity"
- android:label="Disabled Context Activity">
+ <activity android:name=".DisableContextActivity" >
<intent-filter>
<action android:name="android.intent.action.START_TEST_DISABLE_CONTEXT" />
<category android:name="android.intent.category.DEFAULT" />
@@ -47,6 +40,7 @@
<activity android:name=".DelayedAssistantActivity"
android:label="Delay Assistant Start Activity">
<intent-filter>
+ <action android:name="android.intent.action.START_TEST_ASSIST_STRUCTURE" />
<action android:name="android.intent.action.START_TEST_LIFECYCLE" />
<action android:name="android.intent.action.START_TEST_FLAG_SECURE" />
<category android:name="android.intent.category.DEFAULT" />
diff --git a/tests/tests/assist/service/src/android/voiceinteraction/service/DelayedAssistantActivity.java b/tests/tests/assist/service/src/android/voiceinteraction/service/DelayedAssistantActivity.java
index 31d1694..b7d67f1 100644
--- a/tests/tests/assist/service/src/android/voiceinteraction/service/DelayedAssistantActivity.java
+++ b/tests/tests/assist/service/src/android/voiceinteraction/service/DelayedAssistantActivity.java
@@ -24,7 +24,7 @@
import android.util.Log;
public class DelayedAssistantActivity extends Activity {
- static final String TAG = "DelatyedAssistantActivity";
+ static final String TAG = "DelayedAssistantActivity";
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
diff --git a/tests/tests/assist/service/src/android/voiceinteraction/service/DisableContextActivity.java b/tests/tests/assist/service/src/android/voiceinteraction/service/DisableContextActivity.java
index 52ba7ac..2fd540b 100644
--- a/tests/tests/assist/service/src/android/voiceinteraction/service/DisableContextActivity.java
+++ b/tests/tests/assist/service/src/android/voiceinteraction/service/DisableContextActivity.java
@@ -22,9 +22,6 @@
import android.os.Bundle;
import android.util.Log;
-/**
- * TODO(awlee): Change context on/off settings and test
- */
public class DisableContextActivity extends Activity {
static final String TAG = "DisableContextActivity";
@@ -38,8 +35,8 @@
super.onStart();
Intent intent = new Intent();
intent.setComponent(new ComponentName(this, MainInteractionService.class));
+ Log.i(TAG, "Starting service.");
finish();
- ComponentName serviceName = startService(intent);
- Log.i(TAG, "Started service: " + serviceName);
+ startService(intent);
}
}
diff --git a/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionService.java b/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionService.java
index 7530933..fc19e1c 100644
--- a/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionService.java
+++ b/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionService.java
@@ -51,7 +51,7 @@
}
private void maybeStart() {
- if (mIntent == null || !mReady) {
+ if (mIntent == null || !mReady) {
Log.wtf(TAG, "Can't start session because either intent is null or onReady() "
+ "has not been called yet. mIntent = " + mIntent + ", mReady = " + mReady);
} else {
@@ -60,18 +60,18 @@
Log.i(TAG, "Registering receiver to start session later");
if (mBroadcastReceiver == null) {
mBroadcastReceiver = new MainInteractionServiceBroadcastReceiver();
- registerReceiver(mBroadcastReceiver,
+ registerReceiver(mBroadcastReceiver,
new IntentFilter(Utils.BROADCAST_INTENT_START_ASSIST));
}
sendBroadcast(new Intent(Utils.ASSIST_RECEIVER_REGISTERED));
- } else {
- Log.i(TAG, "Yay! about to start session");
- showSession(new Bundle(), VoiceInteractionSession.SHOW_WITH_ASSIST |
- VoiceInteractionSession.SHOW_WITH_SCREENSHOT);
- }
+ } else {
+ Log.i(TAG, "Yay! about to start session");
+ showSession(new Bundle(), VoiceInteractionSession.SHOW_WITH_ASSIST |
+ VoiceInteractionSession.SHOW_WITH_SCREENSHOT);
+ }
} else {
Log.wtf(TAG, "**** Not starting MainInteractionService because" +
- " it is not set as the current voice interaction service");
+ " it is not set as the current voice interaction service");
}
}
}
@@ -79,6 +79,7 @@
private class MainInteractionServiceBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
+ Log.i(MainInteractionService.TAG, "Recieved broadcast to start session now.");
if (intent.getAction().equals(Utils.BROADCAST_INTENT_START_ASSIST)) {
showSession(new Bundle(), SHOW_WITH_ASSIST | SHOW_WITH_SCREENSHOT);
}
@@ -91,4 +92,4 @@
unregisterReceiver(mBroadcastReceiver);
}
}
-}
+}
\ No newline at end of file
diff --git a/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionSession.java b/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionSession.java
index f297b3e..38d03f8 100644
--- a/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionSession.java
+++ b/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionSession.java
@@ -92,7 +92,7 @@
/*@Nullable*/ AssistContent content) {
Log.i(TAG, "onHandleAssist");
Log.i(TAG,
- String.format("Bundle: %s, Structure: %s, Content: %s", data, structure, content));
+ String.format("Bundle: %s, Structure: %s, Content: %s", data, structure, content));
super.onHandleAssist(data, structure, content);
// send to test to verify that this is accurate.
diff --git a/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java b/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
index 763ecef..8ff235b 100644
--- a/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
@@ -18,7 +18,15 @@
import android.assist.common.Utils;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.provider.Settings;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
@@ -26,10 +34,11 @@
*/
public class AssistStructureTest extends AssistTestBase {
- static final String TAG = "AssistStructureTest";
-
private static final String TEST_CASE_TYPE = Utils.ASSIST_STRUCTURE;
+ private BroadcastReceiver mReceiver;
+ private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
+
public AssistStructureTest() {
super();
}
@@ -37,13 +46,59 @@
@Override
protected void setUp() throws Exception {
super.setUp();
+ setUpAndRegisterReceiver();
startTestActivity(TEST_CASE_TYPE);
- waitForBroadcast();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+ }
+
+ private void setUpAndRegisterReceiver() {
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ mReceiver = new AssistStructureTestBroadcastReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Utils.ASSIST_STRUCTURE_HASRESUMED);
+ filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ private void waitForOnResume() throws Exception {
+ Log.i(TAG, "waiting for onResume() before continuing");
+ if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("Activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+ }
}
public void testAssistStructure() throws Exception {
+ mTestActivity.start3pApp(TEST_CASE_TYPE);
+ mTestActivity.startTest(TEST_CASE_TYPE);
+ waitForAssistantToBeReady();
+ waitForOnResume();
+ startSession();
+ waitForContext();
verifyAssistDataNullness(false, false, false, false);
+
verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE),
- false /*FLAG_SECURE set*/);
+ false /*FLAG_SECURE set*/);
+ }
+
+ private class AssistStructureTestBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Utils.ASSIST_STRUCTURE_HASRESUMED)) {
+ mHasResumedLatch.countDown();
+ } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
+ mAssistantReadyLatch.countDown();
+ }
+ }
}
}
\ No newline at end of file
diff --git a/tests/tests/assist/src/android/assist/cts/AssistTest.java b/tests/tests/assist/src/android/assist/cts/AssistTest.java
deleted file mode 100644
index 5241c4e..0000000
--- a/tests/tests/assist/src/android/assist/cts/AssistTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.assist.cts;
-
-import android.assist.TestStartActivity;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.test.ActivityInstrumentationTestCase2;
-import android.util.Log;
-
-import junit.framework.Assert;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import android.assist.common.Utils;
-
-public class AssistTest extends ActivityInstrumentationTestCase2<TestStartActivity> {
- static final String TAG = "AssistTest";
- private static final int TIMEOUT_MS = 2 * 1000;
-
- public AssistTest() {
- super(TestStartActivity.class);
- }
-}
diff --git a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
index a7e7087..25460e5 100644
--- a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
@@ -16,16 +16,19 @@
package android.assist.cts;
-import android.assist.TestStartActivity;
+import android.assist.cts.TestStartActivity;
import android.assist.common.Utils;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
+import android.app.assist.AssistStructure.ViewNode;
+import android.app.assist.AssistStructure.WindowNode;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.cts.util.SystemUtil;
import android.graphics.Bitmap;
@@ -34,6 +37,11 @@
import android.provider.Settings;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.TextView;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -49,7 +57,9 @@
protected Bundle mAssistBundle;
protected Context mContext;
protected CountDownLatch mLatch, mAssistantReadyLatch;
+
private String mTestName;
+ private View mView;
public AssistTestBase() {
super(TestStartActivity.class);
@@ -61,9 +71,9 @@
mAssistantReadyLatch = new CountDownLatch(1);
mContext = getInstrumentation().getTargetContext();
SystemUtil.runShellCommand(getInstrumentation(),
- "settings put secure assist_structure_enabled 1");
+ "settings put secure assist_structure_enabled 1");
SystemUtil.runShellCommand(getInstrumentation(),
- "settings put secure assist_screenshot_enabled 1");
+ "settings put secure assist_screenshot_enabled 1");
logContextAndScreenshotSetting();
}
@@ -80,7 +90,7 @@
mTestName = testName;
intent.setAction("android.intent.action.TEST_START_ACTIVITY_" + testName);
intent.setComponent(new ComponentName(getInstrumentation().getContext(),
- TestStartActivity.class));
+ TestStartActivity.class));
setActivityIntent(intent);
mTestActivity = getActivity();
}
@@ -122,13 +132,14 @@
if (!mLatch.await(Utils.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
fail("Failed to receive broadcast in " + Utils.TIMEOUT_MS + "msec");
- return false;
}
+ Log.i(TAG, "Received broadcast with all information.");
return true;
}
/**
* Checks that the nullness of values are what we expect.
+ *
* @param isBundleNull True if assistBundle should be null.
* @param isStructureNull True if assistStructure should be null.
* @param isContentNull True if assistContent should be null.
@@ -159,7 +170,7 @@
}
/**
- * Traverses and compares the view heirarchy of the backgroundApp and the view we expect.
+ * Verifies the view hierarchy of the backgroundApp matches the assist structure.
*
* @param backgroundApp ComponentName of app the assistant is invoked upon
* @param isSecureWindow Denotes whether the activity has FLAG_SECURE set
@@ -169,26 +180,154 @@
assertEquals(backgroundApp.flattenToString(),
mAssistStructure.getActivityComponent().flattenToString());
- int numWindows = mAssistStructure.getWindowNodeCount();
- assertEquals(1, numWindows);
- for (int i = 0; i < numWindows; i++) {
- AssistStructure.ViewNode node = mAssistStructure.getWindowNodeAt(i).getRootViewNode();
- // TODO: Actually traverse the view heirarchy and verify it matches what we expect
- // If isSecureWindow, will not have any children.
- }
+ Log.i(TAG, "Traversing down structure for: " + backgroundApp.flattenToString());
+ mView = mTestActivity.findViewById(android.R.id.content).getRootView();
+ verifyHierarchy(mAssistStructure, isSecureWindow);
}
protected void logContextAndScreenshotSetting() {
Log.i(TAG, "Context is: " + Settings.Secure.getString(
mContext.getContentResolver(), "assist_structure_enabled"));
Log.i(TAG, "Screenshot is: " + Settings.Secure.getString(
- mContext.getContentResolver(), "assist_screenshot_enabled"));
+ mContext.getContentResolver(), "assist_screenshot_enabled"));
+ }
+
+ /**
+ * Recursively traverse and compare properties in the View hierarchy with the Assist Structure.
+ */
+ public void verifyHierarchy(AssistStructure structure, boolean isSecureWindow) {
+ Log.i(TAG, "verifyHierarchy");
+ Window mWindow = mTestActivity.getWindow();
+
+ int numWindows = structure.getWindowNodeCount();
+ // TODO: multiple windows?
+ assertEquals("Number of windows don't match", 1, numWindows);
+
+ for (int i = 0; i < numWindows; i++) {
+ AssistStructure.WindowNode windowNode = structure.getWindowNodeAt(i);
+ Log.i(TAG, "Title: " + windowNode.getTitle());
+ // verify top level window bounds are as big as the screen and pinned to 0,0
+ assertEquals("Window left position wrong: was " + windowNode.getLeft(),
+ windowNode.getLeft(), 0);
+ assertEquals("Window top position wrong: was " + windowNode.getTop(),
+ windowNode.getTop(), 0);
+
+ traverseViewAndStructure(
+ mView,
+ windowNode.getRootViewNode(),
+ isSecureWindow);
+ }
+ }
+
+ private void traverseViewAndStructure(View parentView, ViewNode parentNode,
+ boolean isSecureWindow) {
+ ViewGroup parentGroup;
+
+ if (parentView == null && parentNode == null) {
+ Log.i(TAG, "Views are null, done traversing this branch.");
+ return;
+ } else if (parentNode == null || parentView == null) {
+ fail(String.format("Views don't match. View: %s, Node: %s", parentView, parentNode));
+ }
+
+ // Debugging
+ Log.i(TAG, "parentView is of type: " + parentView.getClass().getName());
+ if (parentView instanceof ViewGroup) {
+ for (int childInt = 0; childInt < ((ViewGroup) parentView).getChildCount();
+ childInt++) {
+ Log.i(TAG,
+ "viewchild" + childInt + " is of type: "
+ + ((ViewGroup) parentView).getChildAt(childInt).getClass().getName());
+ }
+ }
+ if (parentView.getId() > 0) {
+ Log.i(TAG, "View ID: "
+ + mTestActivity.getResources().getResourceEntryName(parentView.getId()));
+ }
+
+ Log.i(TAG, "parentNode is of type: " + parentNode.getClassName());
+ for (int nodeInt = 0; nodeInt < parentNode.getChildCount(); nodeInt++) {
+ Log.i(TAG,
+ "nodechild" + nodeInt + " is of type: "
+ + parentNode.getChildAt(nodeInt).getClassName());
+ }
+ Log.i(TAG, "Node ID: " + parentNode.getIdEntry());
+
+ int numViewChildren = 0;
+ int numNodeChildren = 0;
+ if (parentView instanceof ViewGroup) {
+ numViewChildren = ((ViewGroup) parentView).getChildCount();
+ }
+ numNodeChildren = parentNode.getChildCount();
+
+ if (isSecureWindow) {
+ assertEquals("Secure window should only traverse root node.", 0, numNodeChildren);
+ isSecureWindow = false;
+ return;
+ } else {
+ assertEquals("Number of children did not match.", numViewChildren, numNodeChildren);
+ }
+
+ verifyViewProperties(parentView, parentNode);
+
+ if (parentView instanceof ViewGroup) {
+ parentGroup = (ViewGroup) parentView;
+
+ // TODO: set a max recursion level
+ for (int i = numNodeChildren - 1; i >= 0; i--) {
+ View childView = parentGroup.getChildAt(i);
+ ViewNode childNode = parentNode.getChildAt(i);
+
+ // if isSecureWindow, should not have reached this point.
+ assertFalse(isSecureWindow);
+ traverseViewAndStructure(childView, childNode, isSecureWindow);
+ }
+ }
+ }
+
+ /**
+ * Compare view properties of the view hierarchy with that reported in the assist structure.
+ */
+ private void verifyViewProperties(View parentView, ViewNode parentNode) {
+ assertEquals("Left positions do not match.", parentView.getLeft(), parentNode.getLeft());
+ assertEquals("Top positions do not match.", parentView.getTop(), parentNode.getTop());
+
+ int viewId = parentView.getId();
+
+ if (viewId > 0) {
+ if (parentNode.getIdEntry() != null) {
+ assertEquals("View IDs do not match.",
+ mTestActivity.getResources().getResourceEntryName(viewId),
+ parentNode.getIdEntry());
+ }
+ } else {
+ assertNull("View Node should not have an ID.", parentNode.getIdEntry());
+ }
+
+ assertEquals("Scroll X does not match.",
+ parentView.getScrollX(), parentNode.getScrollX());
+
+ assertEquals("Scroll Y does not match.",
+ parentView.getScrollY(), parentNode.getScrollY());
+
+ assertEquals("Heights do not match.", parentView.getHeight(),
+ parentNode.getHeight());
+
+ assertEquals("Widths do not match.", parentView.getWidth(), parentNode.getWidth());
+
+ // TODO: handle unicode/i18n
+ if (parentView instanceof TextView) {
+ assertEquals("Text in TextView does not match.",
+ ((TextView) parentView).getText().toString(), parentNode.getText());
+ } else {
+ assertNull(parentNode.getText());
+ }
}
class TestResultsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equalsIgnoreCase(Utils.BROADCAST_ASSIST_DATA_INTENT)) { // not necessary?
+ if (intent.getAction().equalsIgnoreCase(Utils.BROADCAST_ASSIST_DATA_INTENT)) {
Log.i(TAG, "Received broadcast with assist data.");
Bundle assistData = intent.getExtras();
AssistTestBase.this.mAssistBundle = assistData.getBundle(Utils.ASSIST_BUNDLE_KEY);
diff --git a/tests/tests/assist/src/android/assist/cts/DisableContextTest.java b/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
index 9b29407..dc28879 100644
--- a/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
+++ b/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
@@ -16,7 +16,6 @@
package android.assist.cts;
-import android.assist.TestStartActivity;
import android.assist.common.Utils;
import android.app.Activity;
@@ -31,7 +30,6 @@
import android.os.Bundle;
import android.provider.Settings;
import android.test.ActivityInstrumentationTestCase2;
-import android.util.Log;
import java.lang.Override;
import java.util.concurrent.CountDownLatch;
@@ -97,4 +95,4 @@
verifyAssistDataNullness(true, true, true, true);
}
-}
+}
\ No newline at end of file
diff --git a/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java b/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
index 40bf7a7..2e9932e 100644
--- a/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
+++ b/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
@@ -31,7 +31,6 @@
* invoked on an app with FLAG_SECURE set.
*/
public class FlagSecureTest extends AssistTestBase {
-
static final String TAG = "FlagSecureTest";
private static final String TEST_CASE_TYPE = Utils.FLAG_SECURE;
@@ -78,8 +77,13 @@
}
public void testSecureActivity() throws Exception {
+ mTestActivity.startTest(TEST_CASE_TYPE);
+ waitForAssistantToBeReady();
+ mTestActivity.start3pApp(TEST_CASE_TYPE);
+ waitForOnResume();
+ startSession();
+ waitForContext();
verifyAssistDataNullness(false, false, false, false);
-
// verify that we have only the root window and not its children.
verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), true);
}
diff --git a/tests/tests/assist/src/android/assist/cts/LifecycleTest.java b/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
index 19a1be5..7451017 100644
--- a/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
+++ b/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
@@ -16,7 +16,7 @@
package android.assist.cts;
-import android.assist.TestStartActivity;
+import android.assist.cts.TestStartActivity;
import android.assist.common.Utils;
import android.app.Activity;
diff --git a/tests/tests/assist/src/android/assist/TestStartActivity.java b/tests/tests/assist/src/android/assist/cts/TestStartActivity.java
similarity index 89%
rename from tests/tests/assist/src/android/assist/TestStartActivity.java
rename to tests/tests/assist/src/android/assist/cts/TestStartActivity.java
index df9b534..16c924f 100644
--- a/tests/tests/assist/src/android/assist/TestStartActivity.java
+++ b/tests/tests/assist/src/android/assist/cts/TestStartActivity.java
@@ -14,15 +14,21 @@
* limitations under the License.
*/
-package android.assist;
+package android.assist.cts;
import android.assist.common.Utils;
import android.app.Activity;
+import android.app.assist.AssistStructure;
+import android.app.assist.AssistStructure.ViewNode;
import android.content.Intent;
import android.content.ComponentName;
import android.os.Bundle;
import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.TextView;
public class TestStartActivity extends Activity {
static final String TAG = "TestStartActivity";
@@ -31,6 +37,7 @@
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, " in onCreate");
+ setContentView(R.layout.test_app);
}
@Override
diff --git a/tests/tests/assist/testapp/AndroidManifest.xml b/tests/tests/assist/testapp/AndroidManifest.xml
index 8d6169c..f14cf22 100644
--- a/tests/tests/assist/testapp/AndroidManifest.xml
+++ b/tests/tests/assist/testapp/AndroidManifest.xml
@@ -22,7 +22,7 @@
<uses-library android:name="android.test.runner" />
<activity android:name="TestApp"
- android:label="Assist Test App"
+ android:label="Assist Structure Test App"
android:theme="@android:style/Theme.Material.Light">
<intent-filter>
<action android:name="android.intent.action.TEST_APP_ASSIST_STRUCTURE" />
@@ -30,8 +30,16 @@
<category android:name="android.intent.category.VOICE" />
</intent-filter>
</activity>
+ <activity android:name="DisableContextActivity"
+ android:label="Disable Context Test Activity"
+ android:theme="@android:style/Theme.Material.Light">
+ <intent-filter>
+ <action android:name="android.intent.action.TEST_APP_DISABLE_CONTEXT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
<activity android:name="SecureActivity"
- android:label="Secure Test App"
+ android:label="Secure Test Activity"
android:theme="@android:style/Theme.Material.Light">
<intent-filter>
<action android:name="android.intent.action.TEST_APP_FLAG_SECURE" />
diff --git a/tests/tests/assist/testapp/res/layout/secure_app.xml b/tests/tests/assist/testapp/res/layout/secure_app.xml
index 9169a37..3b72ad6 100644
--- a/tests/tests/assist/testapp/res/layout/secure_app.xml
+++ b/tests/tests/assist/testapp/res/layout/secure_app.xml
@@ -14,12 +14,12 @@
limitations under the License.
-->
<RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
<TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/welcome" />
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/welcome" />
</RelativeLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/res/layout/test_app.xml b/tests/tests/assist/testapp/res/layout/test_app.xml
index 9169a37..3fbfd6d 100644
--- a/tests/tests/assist/testapp/res/layout/test_app.xml
+++ b/tests/tests/assist/testapp/res/layout/test_app.xml
@@ -13,13 +13,35 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
<TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/welcome" />
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/welcome" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="350dp"
+ android:orientation="vertical"
+ android:layout_gravity="bottom">
+ <FrameLayout
+ android:id="@+id/card1"
+ android:layout_width="match_parent"
+ android:layout_height="150dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginTop="16dp"
+ android:elevation="3dp">
+ </FrameLayout>
+ <View
+ android:id="@+id/card2"
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginTop="16dp"
+ android:elevation="3dp"/>
+ </LinearLayout>
</RelativeLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/res/values/strings.xml b/tests/tests/assist/testapp/res/values/strings.xml
index a245b36..ae4f16e 100644
--- a/tests/tests/assist/testapp/res/values/strings.xml
+++ b/tests/tests/assist/testapp/res/values/strings.xml
@@ -1,4 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
<resources>
<string name="welcome">Hello there!</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/tests/tests/assist/service/src/android/voiceinteraction/service/AssistStructureActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/DisableContextActivity.java
similarity index 65%
rename from tests/tests/assist/service/src/android/voiceinteraction/service/AssistStructureActivity.java
rename to tests/tests/assist/testapp/src/android/voiceinteraction/testapp/DisableContextActivity.java
index 784d63b..ae570d8 100644
--- a/tests/tests/assist/service/src/android/voiceinteraction/service/AssistStructureActivity.java
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/DisableContextActivity.java
@@ -14,29 +14,25 @@
* limitations under the License.
*/
-package android.assist.service;
+package android.assist.testapp;
import android.app.Activity;
import android.content.Intent;
-import android.content.ComponentName;
import android.os.Bundle;
import android.util.Log;
-public class AssistStructureActivity extends Activity {
- static final String TAG = "VoiceInteractionMain";
+public class DisableContextActivity extends Activity {
+ static final String TAG = "TestApp";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ Log.i(TAG, "TestApp created");
+ setContentView(R.layout.test_app);
}
@Override
- public void onStart() {
- super.onStart();
- Intent intent = new Intent();
- intent.setComponent(new ComponentName(this, MainInteractionService.class));
- Log.i(TAG, "Starting service.");
- finish();
- startService(intent);
+ public void onResume() {
+ super.onResume();
}
-}
+}
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/SecureActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/SecureActivity.java
index d9b2ff2..708061e 100644
--- a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/SecureActivity.java
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/SecureActivity.java
@@ -16,11 +16,16 @@
package android.assist.testapp;
+import android.assist.common.Utils;
+
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowManager;
public class SecureActivity extends Activity {
@@ -37,7 +42,15 @@
@Override
protected void onResume() {
super.onResume();
- Log.i(TAG, "Activity has resumed");
- sendBroadcast(new Intent("android.intent.action.flag_secure_hasResumed"));
+ Log.i(TAG, "Activity has resumed");
+ final View layout = findViewById(android.R.id.content);
+ ViewTreeObserver vto = layout.getViewTreeObserver();
+ vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ sendBroadcast(new Intent(Utils.FLAG_SECURE_HASRESUMED));
+ }
+ });
}
}
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java
index 85a9342..ff56ea8 100644
--- a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java
@@ -16,25 +16,41 @@
package android.assist.testapp;
+import android.assist.common.Utils;
+
import android.app.Activity;
+import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+
+import java.lang.Override;
public class TestApp extends Activity {
static final String TAG = "TestApp";
- Bundle mTestinfo = new Bundle();
- Bundle mTotalInfo = new Bundle();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "TestApp created");
- getLayoutInflater().inflate(R.layout.test_app, null);
+ setContentView(R.layout.test_app);
}
@Override
public void onResume() {
super.onResume();
+ Log.i(TAG, "TestApp has resumed");
+ final View layout = findViewById(android.R.id.content);
+ ViewTreeObserver vto = layout.getViewTreeObserver();
+ vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ sendBroadcast(new Intent(Utils.ASSIST_STRUCTURE_HASRESUMED));
+ }
+ });
}
}
\ No newline at end of file
diff --git a/tests/tests/content/src/android/content/res/cts/ConfigurationTest.java b/tests/tests/content/src/android/content/res/cts/ConfigurationTest.java
index 30c78a8..d4b6c63 100644
--- a/tests/tests/content/src/android/content/res/cts/ConfigurationTest.java
+++ b/tests/tests/content/src/android/content/res/cts/ConfigurationTest.java
@@ -22,6 +22,7 @@
import android.content.res.Configuration;
import android.os.Parcel;
import android.test.AndroidTestCase;
+import android.util.LocaleList;
import android.view.View;
public class ConfigurationTest extends AndroidTestCase {
@@ -40,7 +41,7 @@
mConfig = new Configuration();
mConfig.fontScale = 2;
mConfig.mcc = mConfig.mnc = 1;
- mConfig.locale = Locale.getDefault();
+ mConfig.setLocale(Locale.getDefault());
mConfig.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
mConfig.keyboard = Configuration.KEYBOARD_NOKEYS;
mConfig.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
@@ -93,6 +94,27 @@
cfg2.touchscreen = 2;
assertEquals(1, cfg1.compareTo(cfg2));
+ cfg1.setLocales(LocaleList.forLanguageTags("fr"));
+ cfg2.setLocales(LocaleList.forLanguageTags("fr,en"));
+ assertTrue(cfg1.compareTo(cfg2) < 0);
+ cfg1.setLocales(LocaleList.forLanguageTags("fr,en"));
+ cfg2.setLocales(LocaleList.forLanguageTags("fr"));
+ assertTrue(cfg1.compareTo(cfg2) > 0);
+
+ cfg1.setLocales(LocaleList.forLanguageTags("fr,en"));
+ cfg2.setLocales(LocaleList.forLanguageTags("fr,en-US"));
+ assertTrue(cfg1.compareTo(cfg2) < 0);
+ cfg1.setLocales(LocaleList.forLanguageTags("fr,en-US"));
+ cfg2.setLocales(LocaleList.forLanguageTags("fr,en"));
+ assertTrue(cfg1.compareTo(cfg2) > 0);
+
+ cfg1.locale = Locale.forLanguageTag("en");
+ cfg2.locale = Locale.forLanguageTag("en-Shaw");
+ assertTrue(cfg1.compareTo(cfg2) < 0);
+ cfg1.locale = Locale.forLanguageTag("en-Shaw");
+ cfg2.locale = Locale.forLanguageTag("en");
+ assertTrue(cfg1.compareTo(cfg2) > 0);
+
cfg1.locale = new Locale("", "", "2");
cfg2.locale = new Locale("", "", "3");
assertEquals(-1, cfg1.compareTo(cfg2));
@@ -114,6 +136,13 @@
cfg2.locale = new Locale("2", "", "");
assertEquals(1, cfg1.compareTo(cfg2));
+ cfg1.locale = new Locale("");
+ cfg2.locale = null;
+ assertTrue(cfg1.compareTo(cfg2) < 0);
+ cfg1.locale = null;
+ cfg2.locale = new Locale("");
+ assertTrue(cfg1.compareTo(cfg2) > 0);
+
cfg1.mnc = 2;
cfg2.mnc = 3;
assertEquals(-1, cfg1.compareTo(cfg2));
@@ -160,6 +189,11 @@
| ActivityInfo.CONFIG_MNC
| ActivityInfo.CONFIG_LOCALE
| ActivityInfo.CONFIG_LAYOUT_DIRECTION, mConfigDefault, config);
+ config.setLocales(LocaleList.forLanguageTags("fr,en"));
+ doConfigCompare(ActivityInfo.CONFIG_MCC
+ | ActivityInfo.CONFIG_MNC
+ | ActivityInfo.CONFIG_LOCALE
+ | ActivityInfo.CONFIG_LAYOUT_DIRECTION, mConfigDefault, config);
config.screenLayout = 1;
doConfigCompare(ActivityInfo.CONFIG_MCC
| ActivityInfo.CONFIG_MNC
@@ -282,6 +316,7 @@
assertFalse(temp.equals(mConfigDefault));
temp.setToDefaults();
assertTrue(temp.equals(mConfigDefault));
+ assertTrue(temp.getLocales().isEmpty());
}
public void testToString() {
@@ -289,47 +324,62 @@
}
public void testWriteToParcel() {
- assertWriteToParcel(createConfig(null), Parcel.obtain());
+ assertWriteToParcel(createConfig((Locale) null), Parcel.obtain());
+ assertWriteToParcel(createConfig(new Locale("")), Parcel.obtain());
assertWriteToParcel(createConfig(Locale.JAPAN), Parcel.obtain());
+ assertWriteToParcel(createConfig(Locale.forLanguageTag("en-Shaw")), Parcel.obtain());
+ assertWriteToParcel(createConfig(LocaleList.forLanguageTags("fr,en-US")), Parcel.obtain());
}
public void testSetLocale() {
Configuration config = new Configuration();
+ config.setLocale(null);
+ assertNull(config.locale);
+ assertTrue(config.getLocales().isEmpty());
+
config.setLocale(Locale.getDefault());
assertEquals(Locale.getDefault(), config.locale);
- assertEquals(View.LAYOUT_DIRECTION_LTR, config.getLayoutDirection());
+ assertEquals(new LocaleList(Locale.getDefault()), config.getLocales());
config.setLocale(Locale.ENGLISH);
assertEquals(Locale.ENGLISH, config.locale);
+ assertEquals(new LocaleList(Locale.ENGLISH), config.getLocales());
assertEquals(View.LAYOUT_DIRECTION_LTR, config.getLayoutDirection());
config.setLocale(Locale.US);
assertEquals(Locale.US, config.locale);
+ assertEquals(new LocaleList(Locale.US), config.getLocales());
assertEquals(View.LAYOUT_DIRECTION_LTR, config.getLayoutDirection());
final Locale arEGLocale = new Locale("ar", "EG");
config.setLocale(arEGLocale);
assertEquals(arEGLocale, config.locale);
+ assertEquals(new LocaleList(arEGLocale), config.getLocales());
assertEquals(View.LAYOUT_DIRECTION_RTL, config.getLayoutDirection());
- final Locale faFALocale = new Locale("fa", "FA");
- config.setLocale(faFALocale);
- assertEquals(faFALocale, config.locale);
+ final Locale faIRLocale = new Locale("fa", "IR");
+ config.setLocale(faIRLocale);
+ assertEquals(faIRLocale, config.locale);
+ assertEquals(new LocaleList(faIRLocale), config.getLocales());
assertEquals(View.LAYOUT_DIRECTION_RTL, config.getLayoutDirection());
final Locale iwILLocale = new Locale("iw", "IL");
config.setLocale(iwILLocale);
assertEquals(iwILLocale, config.locale);
+ assertEquals(new LocaleList(iwILLocale), config.getLocales());
+ assertEquals(View.LAYOUT_DIRECTION_RTL, config.getLayoutDirection());
+
+ final Locale urPKLocale = new Locale("ur", "PK");
+ config.setLocale(urPKLocale);
+ assertEquals(urPKLocale, config.locale);
+ assertEquals(new LocaleList(urPKLocale), config.getLocales());
assertEquals(View.LAYOUT_DIRECTION_RTL, config.getLayoutDirection());
}
public void testSetGetLayoutDirection() {
Configuration config = new Configuration();
- config.setLayoutDirection(Locale.getDefault());
- assertEquals(View.LAYOUT_DIRECTION_LTR, config.getLayoutDirection());
-
config.setLayoutDirection(Locale.ENGLISH);
assertEquals(View.LAYOUT_DIRECTION_LTR, config.getLayoutDirection());
@@ -340,21 +390,230 @@
config.setLayoutDirection(arEGLocale);
assertEquals(View.LAYOUT_DIRECTION_RTL, config.getLayoutDirection());
- final Locale faFALocale = new Locale("fa", "FA");
- config.setLayoutDirection(faFALocale);
+ final Locale faIRLocale = new Locale("fa", "IR");
+ config.setLayoutDirection(faIRLocale);
assertEquals(View.LAYOUT_DIRECTION_RTL, config.getLayoutDirection());
final Locale iwILLocale = new Locale("iw", "IL");
config.setLayoutDirection(iwILLocale);
assertEquals(View.LAYOUT_DIRECTION_RTL, config.getLayoutDirection());
+
+ final Locale urPKLocale = new Locale("ur", "PK");
+ config.setLayoutDirection(urPKLocale);
+ assertEquals(View.LAYOUT_DIRECTION_RTL, config.getLayoutDirection());
+ }
+
+ public void testFixUpLocaleList() {
+ Configuration config = new Configuration();
+
+ config.setLocales(LocaleList.forLanguageTags("fr"));
+ config.locale = null;
+ assertEquals(LocaleList.getEmptyLocaleList(), config.getLocales());
+
+ config.setLocales(LocaleList.forLanguageTags("fr,en"));
+ config.locale = Locale.forLanguageTag("en");
+ assertEquals(LocaleList.forLanguageTags("en"), config.getLocales());
+
+ config.setLocales(LocaleList.forLanguageTags("fr,en"));
+ config.locale = Locale.forLanguageTag("fr");
+ assertEquals(LocaleList.forLanguageTags("fr,en"), config.getLocales());
+ }
+
+ public void testSetTo_localeFixUp() {
+ Configuration config1 = new Configuration();
+ Configuration config2 = new Configuration();
+ config2.locale = Locale.FRENCH;
+
+ config1.setTo(config2);
+ assertEquals(Locale.FRENCH, config1.locale);
+ assertEquals(new LocaleList(Locale.FRENCH), config1.getLocales());
+ assertEquals(new LocaleList(Locale.FRENCH), config2.getLocales());
+ }
+
+ public void testToString_localeFixUp() {
+ Configuration config1 = new Configuration();
+ Configuration config2 = new Configuration();
+ config1.setLocales(LocaleList.forLanguageTags("fr,en"));
+ config1.locale = Locale.forLanguageTag("en");
+ config2.setLocales(LocaleList.forLanguageTags("en"));
+
+ assertEquals(config1.toString(), config2.toString());
+ }
+
+ public void testUpdateFrom_localeFixUp() {
+ Configuration config1, config2;
+ int changed;
+
+ config1 = new Configuration();
+ config2 = new Configuration();
+ config1.locale = Locale.FRENCH;
+ changed = config1.updateFrom(config2);
+ assertEquals(0, changed);
+ assertEquals(Locale.FRENCH, config1.locale);
+ assertEquals(new LocaleList(Locale.FRENCH), config1.getLocales());
+
+ config1 = new Configuration();
+ config2 = new Configuration();
+ config2.locale = Locale.FRENCH;
+ changed = config1.updateFrom(config2);
+ assertEquals(ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_LAYOUT_DIRECTION, changed);
+ assertEquals(Locale.FRENCH, config1.locale);
+ assertEquals(new LocaleList(Locale.FRENCH), config1.getLocales());
+ assertEquals(new LocaleList(Locale.FRENCH), config2.getLocales());
+
+ config1 = new Configuration();
+ config2 = new Configuration();
+ config1.setLocales(LocaleList.forLanguageTags("en,fr"));
+ config1.locale = Locale.forLanguageTag("fr");
+ config2.setLocales(LocaleList.forLanguageTags("en,de"));
+ config2.locale = Locale.forLanguageTag("fr");
+ changed = config1.updateFrom(config2);
+ assertEquals(0, changed);
+ assertEquals(Locale.forLanguageTag("fr"), config1.locale);
+ assertEquals(LocaleList.forLanguageTags("fr"), config1.getLocales());
+ assertEquals(LocaleList.forLanguageTags("fr"), config2.getLocales());
+ }
+
+ public void testUpdateFrom_layoutDirection() {
+ Configuration config1, config2;
+ int changed;
+
+ config1 = new Configuration();
+ config2 = new Configuration();
+ config1.setLocales(LocaleList.forLanguageTags("fr,en"));
+ config2.setLocales(LocaleList.forLanguageTags("de,en"));
+ changed = config1.updateFrom(config2);
+ assertTrue((changed & ActivityInfo.CONFIG_LAYOUT_DIRECTION) != 0);
+
+ config1 = new Configuration();
+ config2 = new Configuration();
+ config1.setLocales(LocaleList.forLanguageTags("fr,en"));
+ config2.setLocales(LocaleList.forLanguageTags("fr,de"));
+ changed = config1.updateFrom(config2);
+ assertEquals(0, changed & ActivityInfo.CONFIG_LAYOUT_DIRECTION);
+ }
+
+ public void testDiff_localeFixUp() {
+ Configuration config1 = new Configuration();
+ Configuration config2 = new Configuration();
+ config1.setLocales(LocaleList.forLanguageTags("en,fr"));
+ config1.locale = Locale.forLanguageTag("fr");
+ config2.setLocales(LocaleList.forLanguageTags("en,de"));
+ config2.locale = Locale.forLanguageTag("fr");
+
+ int diff = config1.diff(config2);
+ assertEquals(0, diff);
+ }
+
+ public void testCompareTo_localeFixUp() {
+ Configuration config1 = new Configuration();
+ Configuration config2 = new Configuration();
+ config1.setLocales(LocaleList.forLanguageTags("en,fr"));
+ config2.setLocales(LocaleList.forLanguageTags("en,fr"));
+ assertEquals(0, config1.compareTo(config2));
+ config1.locale = new Locale("2");
+ config2.locale = new Locale("3");
+ assertEquals(-1, config1.compareTo(config2));
+ }
+
+ public void testSetLocales_null() {
+ Configuration config = new Configuration();
+ config.setLocales(null);
+ assertNull(config.locale);
+ assertNotNull(config.getLocales());
+ assertTrue(config.getLocales().isEmpty());
+ assertEquals(View.LAYOUT_DIRECTION_LTR, config.getLayoutDirection());
+ }
+
+ public void testSetLocales_emptyList() {
+ Configuration config = new Configuration();
+ config.setLocales(LocaleList.getEmptyLocaleList());
+ assertNull(config.locale);
+ assertNotNull(config.getLocales());
+ assertTrue(config.getLocales().isEmpty());
+ assertEquals(View.LAYOUT_DIRECTION_LTR, config.getLayoutDirection());
+ }
+
+ public void testSetLocales_oneLtr() {
+ Configuration config = new Configuration();
+ Locale loc = Locale.forLanguageTag("en");
+ LocaleList ll = new LocaleList(loc);
+ config.setLocales(ll);
+ assertEquals(loc, config.locale);
+ assertEquals(ll, config.getLocales());
+ assertEquals(View.LAYOUT_DIRECTION_LTR, config.getLayoutDirection());
+ }
+
+ public void testSetLocales_oneRtl() {
+ Configuration config = new Configuration();
+ Locale loc = Locale.forLanguageTag("az-Arab");
+ LocaleList ll = new LocaleList(loc);
+ config.setLocales(ll);
+ assertEquals(loc, config.locale);
+ assertEquals(ll, config.getLocales());
+ assertEquals(View.LAYOUT_DIRECTION_RTL, config.getLayoutDirection());
+ }
+
+ public void testSetLocales_twoLocales() {
+ Configuration config = new Configuration();
+ Locale rtlLoc = Locale.forLanguageTag("az-Arab");
+ Locale ltrLoc = Locale.forLanguageTag("en");
+ LocaleList ll = LocaleList.forLanguageTags("az-Arab,en");
+ config.setLocales(ll);
+ assertEquals(rtlLoc, config.locale);
+ assertEquals(ll, config.getLocales());
+ assertEquals(View.LAYOUT_DIRECTION_RTL, config.getLayoutDirection());
+ }
+
+ public void testSetLocales_overridesLocale() {
+ Configuration config = new Configuration();
+ config.locale = Locale.forLanguageTag("en");
+ LocaleList ll = LocaleList.forLanguageTags("az-Arab,en");
+ config.setLocales(ll);
+
+ assertEquals(Locale.forLanguageTag("az-Arab"), config.locale);
+ assertEquals(ll, config.getLocales());
+ assertEquals(View.LAYOUT_DIRECTION_RTL, config.getLayoutDirection());
+ }
+
+ public void testSetLocales_overridesSetLocale() {
+ Configuration config = new Configuration();
+ config.setLocale(Locale.forLanguageTag("en"));
+ LocaleList ll = LocaleList.forLanguageTags("az-Arab,en");
+ config.setLocales(ll);
+
+ assertEquals(Locale.forLanguageTag("az-Arab"), config.locale);
+ assertEquals(ll, config.getLocales());
+ assertEquals(View.LAYOUT_DIRECTION_RTL, config.getLayoutDirection());
+ }
+
+ public void testSetLocale_overridesSetLocales() {
+ Configuration config = new Configuration();
+ config.setLocales(LocaleList.forLanguageTags("az-Arab,en"));
+ config.setLocale(Locale.ENGLISH);
+
+ assertEquals(Locale.ENGLISH, config.locale);
+ assertEquals(new LocaleList(Locale.ENGLISH), config.getLocales());
+ assertEquals(View.LAYOUT_DIRECTION_LTR, config.getLayoutDirection());
+ }
+
+ private Configuration createConfig(LocaleList list) {
+ Configuration config = createConfig();
+ config.setLocales(list);
+ return config;
}
private Configuration createConfig(Locale locale) {
+ Configuration config = createConfig();
+ config.locale = locale;
+ return config;
+ }
+
+ private Configuration createConfig() {
Configuration config = new Configuration();
config.fontScale = 13.37f;
config.mcc = 0;
config.mnc = 1;
- config.locale = locale;
config.touchscreen = Configuration.TOUCHSCREEN_STYLUS;
config.keyboard = Configuration.KEYBOARD_UNDEFINED;
config.keyboardHidden = Configuration.KEYBOARDHIDDEN_YES;
diff --git a/tests/tests/graphics/res/drawable/custom_drawable.xml b/tests/tests/graphics/res/drawable/custom_drawable.xml
new file mode 100644
index 0000000..cfb9bdb
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/custom_drawable.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<drawable xmlns:android="http://schemas.android.com/apk/res/android"
+ class="android.graphics.drawable.cts.CustomDrawableTest$CustomDrawable"
+ android:color="#ffff0000" />
diff --git a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
index 80e0253..c813bdb 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
@@ -36,6 +36,7 @@
import android.os.Build;
import android.test.AndroidTestCase;
import android.text.SpannedString;
+import android.util.LocaleList;
import android.util.Log;
import java.util.Locale;
@@ -600,30 +601,73 @@
// Check default
assertEquals(defaultLocale, p.getTextLocale());
- // Check setter / getter
+ // Check setter / getters
p.setTextLocale(Locale.US);
assertEquals(Locale.US, p.getTextLocale());
+ assertEquals(new LocaleList(Locale.US), p.getTextLocales());
p.setTextLocale(Locale.CHINESE);
assertEquals(Locale.CHINESE, p.getTextLocale());
+ assertEquals(new LocaleList(Locale.CHINESE), p.getTextLocales());
p.setTextLocale(Locale.JAPANESE);
assertEquals(Locale.JAPANESE, p.getTextLocale());
+ assertEquals(new LocaleList(Locale.JAPANESE), p.getTextLocales());
p.setTextLocale(Locale.KOREAN);
assertEquals(Locale.KOREAN, p.getTextLocale());
+ assertEquals(new LocaleList(Locale.KOREAN), p.getTextLocales());
// Check reverting back to default
p.setTextLocale(defaultLocale);
assertEquals(defaultLocale, p.getTextLocale());
+ assertEquals(new LocaleList(defaultLocale), p.getTextLocales());
// Check that we cannot pass a null locale
try {
p.setTextLocale(null);
- assertFalse(true);
+ fail("Setting the text locale to null should throw");
+ } catch (Throwable e) {
+ assertEquals(IllegalArgumentException.class, e.getClass());
}
- catch (IllegalArgumentException iae) {
- // OK !!
+ }
+
+ public void testAccessTextLocales() {
+ Paint p = new Paint();
+
+ final LocaleList defaultLocales = LocaleList.getDefault();
+
+ // Check default
+ assertEquals(defaultLocales, p.getTextLocales());
+
+ // Check setter / getters for a one-member locale list
+ p.setTextLocales(new LocaleList(Locale.CHINESE));
+ assertEquals(Locale.CHINESE, p.getTextLocale());
+ assertEquals(new LocaleList(Locale.CHINESE), p.getTextLocales());
+
+ // Check setter / getters for a two-member locale list
+ p.setTextLocales(LocaleList.forLanguageTags("fr,de"));
+ assertEquals(Locale.forLanguageTag("fr"), p.getTextLocale());
+ assertEquals(LocaleList.forLanguageTags("fr,de"), p.getTextLocales());
+
+ // Check reverting back to default
+ p.setTextLocales(defaultLocales);
+ assertEquals(defaultLocales, p.getTextLocales());
+
+ // Check that we cannot pass a null locale list
+ try {
+ p.setTextLocales(null);
+ fail("Setting the text locale list to null should throw");
+ } catch (Throwable e) {
+ assertEquals(IllegalArgumentException.class, e.getClass());
+ }
+
+ // Check that we cannot pass an empty locale list
+ try {
+ p.setTextLocales(new LocaleList());
+ fail("Setting the text locale list to an empty list should throw");
+ } catch (Throwable e) {
+ assertEquals(IllegalArgumentException.class, e.getClass());
}
}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/CustomDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/CustomDrawableTest.java
new file mode 100644
index 0000000..2d3ffd9
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/CustomDrawableTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable.cts;
+
+import com.android.cts.graphics.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.test.AndroidTestCase;
+import android.util.AttributeSet;
+
+import java.io.IOException;
+
+public class CustomDrawableTest extends AndroidTestCase {
+
+ public void testInflation() {
+ Drawable dr = getContext().getDrawable(R.drawable.custom_drawable);
+ assertTrue(dr instanceof CustomDrawable);
+ assertEquals(Color.RED, ((CustomDrawable) dr).getColor());
+ }
+
+ public static class CustomDrawable extends Drawable {
+ private static final int[] ATTRS = new int[] { android.R.attr.color };
+
+ private int mColor;
+
+ @Override
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ throws XmlPullParserException, IOException {
+ super.inflate(r, parser, attrs, theme);
+
+ final TypedArray ta;
+ if (theme != null) {
+ ta = theme.obtainStyledAttributes(attrs, ATTRS, 0, 0);
+ } else {
+ ta = r.obtainAttributes(attrs, ATTRS);
+ }
+
+ mColor = ta.getColor(0, Color.BLACK);
+ }
+
+ public int getColor() {
+ return mColor;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.OPAQUE;
+ }
+ }
+}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/LayerDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/LayerDrawableTest.java
index a2f9ddf..1d7f623 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/LayerDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/LayerDrawableTest.java
@@ -640,6 +640,21 @@
assertTrue(mockDrawable2.hasCalledOnBoundsChange());
}
+ public void testJumpToCurrentState() {
+ MockDrawable mockDrawable1 = new MockDrawable();
+ MockDrawable mockDrawable2 = new MockDrawable();
+ Drawable[] array = new Drawable[] { mockDrawable1, mockDrawable2 };
+ LayerDrawable layerDrawable = new LayerDrawable(array);
+
+ assertFalse(mockDrawable1.hasCalledJumpToCurrentState());
+ assertFalse(mockDrawable2.hasCalledJumpToCurrentState());
+
+ layerDrawable.jumpToCurrentState();
+
+ assertTrue(mockDrawable1.hasCalledJumpToCurrentState());
+ assertTrue(mockDrawable2.hasCalledJumpToCurrentState());
+ }
+
public void testSetLevel() {
MockDrawable mockDrawable1 = new MockDrawable();
MockDrawable mockDrawable2 = new MockDrawable();
@@ -1400,7 +1415,7 @@
private boolean mCalledSetState = false;
private boolean mCalledOnLevelChange = false;
private boolean mCalledOnBoundsChange = false;
-
+ private boolean mCalledJumpToCurrentState = false;
private boolean mCalledDraw = false;
@@ -1474,11 +1489,23 @@
mCalledSetState = false;
mCalledOnLevelChange = false;
mCalledOnBoundsChange = false;
+ mCalledJumpToCurrentState = false;
mCalledDraw = false;
}
@Override
+ public void jumpToCurrentState() {
+ super.jumpToCurrentState();
+
+ mCalledJumpToCurrentState = true;
+ }
+
+ public boolean hasCalledJumpToCurrentState() {
+ return mCalledJumpToCurrentState;
+ }
+
+ @Override
protected boolean onStateChange(int[] state) {
increasePadding();
return mIsStateful;
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
index 3e76fbc..0e7eb43 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -1993,12 +1993,12 @@
private long[] getExposureTimeTestValues() {
long[] testValues = new long[DEFAULT_NUM_EXPOSURE_TIME_STEPS + 1];
long maxExpTime = mStaticInfo.getExposureMaximumOrDefault(DEFAULT_EXP_TIME_NS);
- long minxExpTime = mStaticInfo.getExposureMinimumOrDefault(DEFAULT_EXP_TIME_NS);
+ long minExpTime = mStaticInfo.getExposureMinimumOrDefault(DEFAULT_EXP_TIME_NS);
- long range = maxExpTime - minxExpTime;
+ long range = maxExpTime - minExpTime;
double stepSize = range / (double)DEFAULT_NUM_EXPOSURE_TIME_STEPS;
for (int i = 0; i < testValues.length; i++) {
- testValues[i] = minxExpTime + (long)(stepSize * i);
+ testValues[i] = maxExpTime - (long)(stepSize * i);
testValues[i] = mStaticInfo.getExposureClampToRange(testValues[i]);
}
@@ -2047,7 +2047,7 @@
}
int[] testValues = new int[numSteps + 1];
for (int i = 0; i < testValues.length; i++) {
- testValues[i] = minSensitivity + stepSize * i;
+ testValues[i] = maxSensitivity - stepSize * i;
testValues[i] = mStaticInfo.getSensitivityClampToRange(testValues[i]);
}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
index a6cf613..9f85fd8 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
@@ -821,13 +821,21 @@
getAvailableMinFrameDurationsForFormatChecked(ImageFormat.JPEG);
for (int i = mOrderedStillSizes.size() - 2; i >= 0; i--) {
Size candidateSize = mOrderedStillSizes.get(i);
- Long jpegFrameDuration = minFrameDurationMap.get(candidateSize);
- assertTrue("Cannot find minimum frame duration for jpeg size " + candidateSize,
- jpegFrameDuration != null);
- if (candidateSize.getWidth() <= videoSz.getWidth() &&
- candidateSize.getHeight() <= videoSz.getHeight() &&
- jpegFrameDuration <= videoFrameDuration) {
- videoSnapshotSz = candidateSize;
+ if (mStaticInfo.isHardwareLevelLegacy()) {
+ // Legacy level doesn't report min frame duration
+ if (candidateSize.getWidth() <= videoSz.getWidth() &&
+ candidateSize.getHeight() <= videoSz.getHeight()) {
+ videoSnapshotSz = candidateSize;
+ }
+ } else {
+ Long jpegFrameDuration = minFrameDurationMap.get(candidateSize);
+ assertTrue("Cannot find minimum frame duration for jpeg size " + candidateSize,
+ jpegFrameDuration != null);
+ if (candidateSize.getWidth() <= videoSz.getWidth() &&
+ candidateSize.getHeight() <= videoSz.getHeight() &&
+ jpegFrameDuration <= videoFrameDuration) {
+ videoSnapshotSz = candidateSize;
+ }
}
}
diff --git a/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java b/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
index aa34de3..d1ca19a 100644
--- a/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
+++ b/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
@@ -16,6 +16,7 @@
package android.hardware.multiprocess.camera.cts;
+import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
@@ -112,7 +113,8 @@
super.setUp();
mCompleted = false;
- mContext = getActivity();
+ getActivity();
+ mContext = getInstrumentation().getTargetContext();
System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
mErrorServiceConnection = new ErrorLoggingService.ErrorServiceConnection(mContext);
@@ -232,6 +234,7 @@
assertTrue("Remote camera service exited early", timeoutExceptionHit);
android.os.Process.killProcess(mProcessPid);
mProcessPid = -1;
+ forceCtsActivityToTop();
}
/**
@@ -337,6 +340,19 @@
assertTrue("Remote camera service exited early", timeoutExceptionHit);
android.os.Process.killProcess(mProcessPid);
mProcessPid = -1;
+ forceCtsActivityToTop();
+ }
+
+ /**
+ * Ensure the CTS activity becomes foreground again instead of launcher.
+ */
+ private void forceCtsActivityToTop() throws InterruptedException {
+ Thread.sleep(WAIT_TIME);
+ Activity a = getActivity();
+ Intent activityIntent = new Intent(a, CameraCtsActivity.class);
+ activityIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ a.startActivity(activityIntent);
+ Thread.sleep(WAIT_TIME);
}
/**
@@ -389,15 +405,15 @@
public void startRemoteProcess(java.lang.Class<?> klass, String processName)
throws InterruptedException {
// Ensure no running activity process with same name
- String cameraActivityName = mContext.getPackageName() + ":" + processName;
+ Activity a = getActivity();
+ String cameraActivityName = a.getPackageName() + ":" + processName;
List<ActivityManager.RunningAppProcessInfo> list =
mActivityManager.getRunningAppProcesses();
assertEquals(-1, getPid(cameraActivityName, list));
// Start activity in a new top foreground process
- Intent activityIntent = new Intent(mContext, klass);
- activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(activityIntent);
+ Intent activityIntent = new Intent(a, klass);
+ a.startActivity(activityIntent);
Thread.sleep(WAIT_TIME);
// Fail if activity isn't running
diff --git a/tests/tests/media/Android.mk b/tests/tests/media/Android.mk
index 2019da3..bbf1f04 100644
--- a/tests/tests/media/Android.mk
+++ b/tests/tests/media/Android.mk
@@ -34,6 +34,8 @@
LOCAL_MODULE_TAGS := optional
# and when built explicitly put it in the data partition
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_DEX_PREOPT := false
+LOCAL_PROGUARD_ENABLED := disabled
# include both the 32 and 64 bit versions
LOCAL_MULTILIB := both
@@ -50,6 +52,8 @@
#LOCAL_SDK_VERSION := current
LOCAL_JAVA_LIBRARIES += android.test.runner org.apache.http.legacy
-include $(BUILD_CTS_PACKAGE)
+LOCAL_COMPATIBILITY_SUITE := cts_v2
+
+include $(BUILD_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/media/AndroidTest.xml b/tests/tests/media/AndroidTest.xml
new file mode 100644
index 0000000..64a048b
--- /dev/null
+++ b/tests/tests/media/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Base config for CTS package preparer">
+ <include name="common-config" />
+ <option name="apk-installer:test-file-name" value="CtsMediaTestCases.apk" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="module-name" value="CtsMediaTestCases"/>
+ <option name="version-name" value="1.0"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.InstrumentationTest" >
+ <option name="package" value="com.android.cts.media" />
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/tests/tests/media/DynamicConfig.xml b/tests/tests/media/DynamicConfig.xml
new file mode 100644
index 0000000..702157d
--- /dev/null
+++ b/tests/tests/media/DynamicConfig.xml
@@ -0,0 +1,29 @@
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<DynamicConfig>
+ <Config key="DecoderTest-VIDEO_URL">http://redirector.gvt1.com/videoplayback?id=c80658495af60617&itag=18&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000&sparams=ip,ipbits,expire,id,itag,source&signature=46A04ED550CA83B79B60060BA80C79FDA5853D26.49582D382B4A9AFAA163DED38D2AE531D85603C0&key=ik0&user=android-device-test</Config>
+ <Config key="StreamingMediaPlayerTest-testHTTP_H264Base_AAC_Video1">http://redirector.gvt1.com/videoplayback?id=271de9756065677e&itag=18&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000&sparams=ip,ipbits,expire,id,itag,source&signature=667AEEF54639926662CE62361400B8F8C1753B3F.15F46C382C68A9F121BA17BF1F56BEDEB4B06091&key=ik0&user=android-device-test</Config>
+ <Config key="StreamingMediaPlayerTest-testHTTP_H264Base_AAC_Video2">http://www.youtube.com/api/manifest/hls_variant/id/0168724d02bd9945/itag/5/source/youtube/playlist_type/DVR/ip/0.0.0.0/ipbits/0/expire/19000000000/sparams/ip,ipbits,expire,id,itag,source,playlist_type/signature/773AB8ACC68A96E5AA481996AD6A1BBCB70DCB87.95733B544ACC5F01A1223A837D2CF04DF85A3360/key/ik0/file/m3u8</Config>
+ <Config key="MediaCodecCapabilitiesTest-testAvcHigh31">http://redirector.gvt1.com/videoplayback?id=271de9756065677e&itag=22&source=youtube&user=android-device-test&sparams=ip,ipbits,expire,id,itag,source,user&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=179525311196616BD8E1381759B0E5F81A9E91B5.C4A50E44059FEBCC6BBC78E3B3A4E0E0065777&key=ik0</Config>
+ <Config key="StreamingMediaPlayerTest-testHTTP_MPEG4SP_AAC_Video2">http://redirector.gvt1.com/videoplayback?id=c80658495af60617&itag=17&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000&sparams=ip,ipbits,expire,id,itag,source&signature=70E979A621001201BC18622BDBF914FA870BDA40.6E78890B80F4A33A18835F775B1FF64F0A4D0003&key=ik0&user=android-device-test</Config>
+ <Config key="StreamingMediaPlayerTest-testHTTP_MPEG4SP_AAC_Video1">http://redirector.gvt1.com/videoplayback?id=271de9756065677e&itag=17&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000&sparams=ip,ipbits,expire,id,itag,source&signature=837198AAADF6F36BA6B2D324F690A7C5B7AFE3FF.7138CE5E36D718220726C1FC305497FF2D082249&key=ik0&user=android-device-test</Config>
+ <Config key="MediaCodecCapabilitiesTest-testAvcBaseline12">http://redirector.gvt1.com/videoplayback?id=271de9756065677e&itag=160&source=youtube&user=android-device-test&sparams=ip,ipbits,expire,id,itag,source,user&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=9EDCA0B395B8A949C511FD5E59B9F805CFF797FD.702DE9BA7AF96785FD6930AD2DD693A0486C880E&key=ik0</Config>
+ <Config key="DecoderTest-AUDIO_URL">http://redirector.gvt1.com/videoplayback?id=c80658495af60617&itag=18&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000&sparams=ip,ipbits,expire,id,itag,source&signature=46A04ED550CA83B79B60060BA80C79FDA5853D26.49582D382B4A9AFAA163DED38D2AE531D85603C0&key=ik0&user=android-device-test</Config>
+ <Config key="MediaCodecCapabilitiesTest-testAvcBaseline30">http://redirector.gvt1.com/videoplayback?id=271de9756065677e&itag=18&source=youtube&user=android-device-test&sparams=ip,ipbits,expire,id,itag,source,user&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=7DCDE3A6594D0B91A27676A3CDC3A87B149F82EA.7A83031734CB1EDCE06766B6228842F954927960&key=ik0</Config>
+ <Config key="MediaCodecCapabilitiesTest-testAvcHigh40">http://redirector.gvt1.com/videoplayback?id=271de9756065677e&itag=137&source=youtube&user=android-device-test&sparams=ip,ipbits,expire,id,itag,source,user&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=B0976085596DD42DEA3F08307F76587241CB132B.043B719C039E8B92F45391ADC0BE3665E2332930&key=ik0</Config>
+ <Config key="StreamingMediaPlayerTest-testHTTP_H263_AMR_Video2">http://redirector.gvt1.com/videoplayback?id=c80658495af60617&itag=13&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000&sparams=ip,ipbits,expire,id,itag,source&signature=508D82AB36939345BF6B8D0623CB6CABDD9C64C3.9B3336A96846DF38E5343C46AA57F6CF2956E427&key=ik0&user=android-device-test</Config>
+ <Config key="StreamingMediaPlayerTest-testHTTP_H263_AMR_Video1">http://redirector.gvt1.com/videoplayback?id=271de9756065677e&itag=13&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000&sparams=ip,ipbits,expire,id,itag,source&signature=5729247E22691EBB3E804DDD523EC42DC17DD8CE.443B81C1E8E6D64E4E1555F568BA46C206507D78&key=ik0&user=android-device-test</Config>
+</DynamicConfig>
diff --git a/tests/tests/media/res/raw/heap_oob_flac.mp3 b/tests/tests/media/res/raw/heap_oob_flac.mp3
new file mode 100644
index 0000000..ae542d0
--- /dev/null
+++ b/tests/tests/media/res/raw/heap_oob_flac.mp3
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
index 6765051..c5bdce8 100755
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -16,6 +16,7 @@
package android.media.cts;
+import com.android.compatibility.common.util.DynamicConfigDeviceSide;
import com.android.cts.media.R;
import android.content.Context;
@@ -65,20 +66,6 @@
private MediaCodecTunneledPlayer mMediaCodecPlayer;
private static final int SLEEP_TIME_MS = 1000;
private static final long PLAY_TIME_MS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES);
- private static final Uri AUDIO_URL = Uri.parse(
- "http://redirector.c.youtube.com/videoplayback?id=c80658495af60617"
- + "&itag=18&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000"
- + "&sparams=ip,ipbits,expire,id,itag,source"
- + "&signature=46A04ED550CA83B79B60060BA80C79FDA5853D26."
- + "49582D382B4A9AFAA163DED38D2AE531D85603C0"
- + "&key=ik0&user=android-device-test"); // H.264 Base + AAC
- private static final Uri VIDEO_URL = Uri.parse(
- "http://redirector.c.youtube.com/videoplayback?id=c80658495af60617"
- + "&itag=18&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000"
- + "&sparams=ip,ipbits,expire,id,itag,source"
- + "&signature=46A04ED550CA83B79B60060BA80C79FDA5853D26."
- + "49582D382B4A9AFAA163DED38D2AE531D85603C0"
- + "&key=ik0&user=android-device-test"); // H.264 Base + AAC
@Override
protected void setUp() throws Exception {
@@ -1959,8 +1946,11 @@
mMediaCodecPlayer = new MediaCodecTunneledPlayer(
getActivity().getSurfaceHolder(), true, am.generateAudioSessionId());
- mMediaCodecPlayer.setAudioDataSource(AUDIO_URL, null);
- mMediaCodecPlayer.setVideoDataSource(VIDEO_URL, null);
+ DynamicConfigDeviceSide config = new DynamicConfigDeviceSide("CtsMediaTestCases");
+ Uri audio_url = Uri.parse(config.getConfig("DecoderTest-AUDIO_URL"));
+ mMediaCodecPlayer.setAudioDataSource(audio_url, null);
+ Uri video_url = Uri.parse(config.getConfig("DecoderTest-VIDEO_URL"));
+ mMediaCodecPlayer.setVideoDataSource(video_url, null);
assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
@@ -1998,8 +1988,11 @@
mMediaCodecPlayer = new MediaCodecTunneledPlayer(
getActivity().getSurfaceHolder(), true, am.generateAudioSessionId());
- mMediaCodecPlayer.setAudioDataSource(AUDIO_URL, null);
- mMediaCodecPlayer.setVideoDataSource(VIDEO_URL, null);
+ DynamicConfigDeviceSide config = new DynamicConfigDeviceSide("CtsMediaTestCases");
+ Uri audio_url = Uri.parse(config.getConfig("DecoderTest-AUDIO_URL"));
+ mMediaCodecPlayer.setAudioDataSource(audio_url, null);
+ Uri video_url = Uri.parse(config.getConfig("DecoderTest-VIDEO_URL"));
+ mMediaCodecPlayer.setVideoDataSource(video_url, null);
assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
index 813af0f2..812f009d 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
@@ -36,6 +36,8 @@
import android.os.Build;
import android.util.Log;
+import com.android.compatibility.common.util.DynamicConfigDeviceSide;
+
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
@@ -172,39 +174,28 @@
return; // skip
}
- playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
- + "&itag=160&source=youtube&user=android-device-test"
- + "&sparams=ip,ipbits,expire,id,itag,source,user"
- + "&ip=0.0.0.0&ipbits=0&expire=19000000000"
- + "&signature=9EDCA0B395B8A949C511FD5E59B9F805CFF797FD."
- + "702DE9BA7AF96785FD6930AD2DD693A0486C880E"
- + "&key=ik0", 256, 144, PLAY_TIME_MS);
+ String url = new DynamicConfigDeviceSide("CtsMediaTestCases")
+ .getConfig("MediaCodecCapabilitiesTest-testAvcBaseline12");
+ playVideoWithRetries(url, 256, 144, PLAY_TIME_MS);
}
public void testAvcBaseline30() throws Exception {
if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel3)) {
return; // skip
}
- playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
- + "&itag=18&source=youtube&user=android-device-test"
- + "&sparams=ip,ipbits,expire,id,itag,source,user"
- + "&ip=0.0.0.0&ipbits=0&expire=19000000000"
- + "&signature=7DCDE3A6594D0B91A27676A3CDC3A87B149F82EA."
- + "7A83031734CB1EDCE06766B6228842F954927960"
- + "&key=ik0", 640, 360, PLAY_TIME_MS);
+ String url = new DynamicConfigDeviceSide("CtsMediaTestCases")
+ .getConfig("MediaCodecCapabilitiesTest-testAvcBaseline30");
+ playVideoWithRetries(url, 640, 360, PLAY_TIME_MS);
}
public void testAvcHigh31() throws Exception {
if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileHigh, AVCLevel31)) {
return; // skip
}
- playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
- + "&itag=22&source=youtube&user=android-device-test"
- + "&sparams=ip,ipbits,expire,id,itag,source,user"
- + "&ip=0.0.0.0&ipbits=0&expire=19000000000"
- + "&signature=179525311196616BD8E1381759B0E5F81A9E91B5."
- + "C4A50E44059FEBCC6BBC78E3B3A4E0E0065777"
- + "&key=ik0", 1280, 720, PLAY_TIME_MS);
+
+ String url = new DynamicConfigDeviceSide("CtsMediaTestCases")
+ .getConfig("MediaCodecCapabilitiesTest-testAvcHigh31");
+ playVideoWithRetries(url, 1280, 720, PLAY_TIME_MS);
}
public void testAvcHigh40() throws Exception {
@@ -215,13 +206,10 @@
MediaUtils.skipTest(TAG, "fragmented mp4 not supported");
return;
}
- playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
- + "&itag=137&source=youtube&user=android-device-test"
- + "&sparams=ip,ipbits,expire,id,itag,source,user"
- + "&ip=0.0.0.0&ipbits=0&expire=19000000000"
- + "&signature=B0976085596DD42DEA3F08307F76587241CB132B."
- + "043B719C039E8B92F45391ADC0BE3665E2332930"
- + "&key=ik0", 1920, 1080, PLAY_TIME_MS);
+
+ String url = new DynamicConfigDeviceSide("CtsMediaTestCases")
+ .getConfig("MediaCodecCapabilitiesTest-testAvcHigh40");
+ playVideoWithRetries(url, 1920, 1080, PLAY_TIME_MS);
}
public void testHevcMain1() throws Exception {
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index 86f0313..d5b2907 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -95,6 +95,37 @@
}
}
+ public void testFlacHeapOverflow() throws Exception {
+ testIfMediaServerDied(R.raw.heap_oob_flac);
+ }
+
+ private void testIfMediaServerDied(int res) throws Exception {
+ mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ assertTrue(mp == mMediaPlayer);
+ assertTrue("mediaserver process died", what != MediaPlayer.MEDIA_ERROR_SERVER_DIED);
+ return false;
+ }
+ });
+
+ mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ assertTrue(mp == mMediaPlayer);
+ mOnCompletionCalled.signal();
+ }
+ });
+
+ AssetFileDescriptor afd = mResources.openRawResourceFd(res);
+ mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+ afd.close();
+ mMediaPlayer.prepare();
+ mMediaPlayer.start();
+ mOnCompletionCalled.waitForSignal();
+ mMediaPlayer.release();
+ }
+
// Bug 13652927
public void testVorbisCrash() throws Exception {
MediaPlayer mp = mMediaPlayer;
diff --git a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
index 7497da2..a6c80ad 100644
--- a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
@@ -26,6 +26,8 @@
import android.util.Log;
import android.webkit.cts.CtsTestServer;
+import com.android.compatibility.common.util.DynamicConfigDeviceSide;
+
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
@@ -73,49 +75,36 @@
return; // skip
}
- playVideoTest("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
- + "&itag=13&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000"
- + "&sparams=ip,ipbits,expire,id,itag,source"
- + "&signature=5729247E22691EBB3E804DDD523EC42DC17DD8CE"
- + ".443B81C1E8E6D64E4E1555F568BA46C206507D78"
- + "&key=ik0&user=android-device-test", 176, 144);
+ String url = new DynamicConfigDeviceSide("CtsMediaTestCases")
+ .getConfig("StreamingMediaPlayerTest-testHTTP_H263_AMR_Video1");
+ playVideoTest(url, 176, 144);
}
public void testHTTP_H263_AMR_Video2() throws Exception {
if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_H263, MediaFormat.MIMETYPE_AUDIO_AMR_NB)) {
return; // skip
}
- playVideoTest("http://redirector.c.youtube.com/videoplayback?id=c80658495af60617"
- + "&itag=13&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000"
- + "&sparams=ip,ipbits,expire,id,itag,source"
- + "&signature=508D82AB36939345BF6B8D0623CB6CABDD9C64C3"
- + ".9B3336A96846DF38E5343C46AA57F6CF2956E427"
- + "&key=ik0&user=android-device-test", 176, 144);
+ String url = new DynamicConfigDeviceSide("CtsMediaTestCases")
+ .getConfig("StreamingMediaPlayerTest-testHTTP_H263_AMR_Video2");
+ playVideoTest(url, 176, 144);
}
public void testHTTP_MPEG4SP_AAC_Video1() throws Exception {
if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_MPEG4)) {
return; // skip
}
-
- playVideoTest("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
- + "&itag=17&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000"
- + "&sparams=ip,ipbits,expire,id,itag,source"
- + "&signature=837198AAADF6F36BA6B2D324F690A7C5B7AFE3FF"
- + ".7138CE5E36D718220726C1FC305497FF2D082249"
- + "&key=ik0&user=android-device-test", 176, 144);
+ String url = new DynamicConfigDeviceSide("CtsMediaTestCases")
+ .getConfig("StreamingMediaPlayerTest-testHTTP_MPEG4SP_AAC_Video1");
+ playVideoTest(url, 176, 144);
}
public void testHTTP_MPEG4SP_AAC_Video2() throws Exception {
if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_MPEG4)) {
return; // skip
}
- playVideoTest("http://redirector.c.youtube.com/videoplayback?id=c80658495af60617"
- + "&itag=17&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000"
- + "&sparams=ip,ipbits,expire,id,itag,source"
- + "&signature=70E979A621001201BC18622BDBF914FA870BDA40"
- + ".6E78890B80F4A33A18835F775B1FF64F0A4D0003"
- + "&key=ik0&user=android-device-test", 176, 144);
+ String url = new DynamicConfigDeviceSide("CtsMediaTestCases")
+ .getConfig("StreamingMediaPlayerTest-testHTTP_MPEG4SP_AAC_Video2");
+ playVideoTest(url, 176, 144);
}
public void testHTTP_H264Base_AAC_Video1() throws Exception {
@@ -123,24 +112,18 @@
return; // skip
}
- playVideoTest("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
- + "&itag=18&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000"
- + "&sparams=ip,ipbits,expire,id,itag,source"
- + "&signature=667AEEF54639926662CE62361400B8F8C1753B3F"
- + ".15F46C382C68A9F121BA17BF1F56BEDEB4B06091"
- + "&key=ik0&user=android-device-test", 640, 360);
+ String url = new DynamicConfigDeviceSide("CtsMediaTestCases")
+ .getConfig("StreamingMediaPlayerTest-testHTTP_H264Base_AAC_Video1");
+ playVideoTest(url, 640, 360);
}
public void testHTTP_H264Base_AAC_Video2() throws Exception {
if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
return; // skip
}
- playVideoTest("http://redirector.c.youtube.com/videoplayback?id=c80658495af60617"
- + "&itag=18&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000"
- + "&sparams=ip,ipbits,expire,id,itag,source"
- + "&signature=46A04ED550CA83B79B60060BA80C79FDA5853D26"
- + ".49582D382B4A9AFAA163DED38D2AE531D85603C0"
- + "&key=ik0&user=android-device-test", 640, 360);
+ String url = new DynamicConfigDeviceSide("CtsMediaTestCases")
+ .getConfig("StreamingMediaPlayerTest-testHTTP_H264Base_AAC_Video2");
+ playVideoTest(url, 640, 360);
}
// Streaming HLS video from YouTube
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index 7a01e83..2843d21 100644
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -787,7 +787,9 @@
new File("/dev/ashmem"),
new File("/dev/binder"),
new File("/dev/card0"), // b/13159510
+ new File("/dev/renderD128"),
new File("/dev/dri/card0"), // b/13159510
+ new File("/dev/dri/renderD128"),
new File("/dev/felica"), // b/11142586
new File("/dev/felica_ant"), // b/11142586
new File("/dev/felica_cen"), // b/11142586
diff --git a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
index c6f4049..f8b3cc4 100644
--- a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
@@ -975,4 +975,25 @@
assertEquals(testLabel, 6, layout.getOffsetToRightOf(7));
}
}
+
+ public void testGetOffsetForHorizontal_Multilines() {
+ // Emoticons for surrogate pairs tests.
+ String testString = "\uD83D\uDE00\uD83D\uDE01\uD83D\uDE02\uD83D\uDE03\uD83D\uDE04";
+ final float width = mDefaultPaint.measureText(testString, 0, 6);
+ StaticLayout layout = new StaticLayout(testString, mDefaultPaint, (int)width,
+ DEFAULT_ALIGN, SPACE_MULTI, SPACE_ADD, true);
+ // We expect the line break to be after the third emoticon, but we allow flexibility of the
+ // line break algorithm as long as the break is within the string. These other cases might
+ // happen if for example the font has kerning between emoticons.
+ final int lineBreakOffset = layout.getOffsetForHorizontal(1, 0.0f);
+ assertEquals(0, layout.getLineForOffset(lineBreakOffset - 1));
+
+ assertEquals(0, layout.getOffsetForHorizontal(0, 0.0f));
+ assertEquals(lineBreakOffset - 2, layout.getOffsetForHorizontal(0, width));
+ assertEquals(lineBreakOffset - 2, layout.getOffsetForHorizontal(0, width * 2));
+
+ final int lineCount = layout.getLineCount();
+ assertEquals(testString.length(), layout.getOffsetForHorizontal(lineCount - 1, width));
+ assertEquals(testString.length(), layout.getOffsetForHorizontal(lineCount - 1, width * 2));
+ }
}
diff --git a/tests/tests/util/src/android/util/cts/LocaleListTest.java b/tests/tests/util/src/android/util/cts/LocaleListTest.java
index f7abfdf9..1703591 100644
--- a/tests/tests/util/src/android/util/cts/LocaleListTest.java
+++ b/tests/tests/util/src/android/util/cts/LocaleListTest.java
@@ -31,7 +31,15 @@
assertNull(ll.get(1));
assertNull(ll.get(10));
- ll = new LocaleList(null);
+ ll = new LocaleList((Locale) null);
+ assertNotNull(ll);
+ assertTrue(ll.isEmpty());
+ assertEquals(0, ll.size());
+ assertNull(ll.getPrimary());
+ assertNull(ll.get(1));
+ assertNull(ll.get(10));
+
+ ll = new LocaleList((Locale[]) null);
assertNotNull(ll);
assertTrue(ll.isEmpty());
assertEquals(0, ll.size());
@@ -49,8 +57,7 @@
}
public void testOneMemberLocaleList() {
- final Locale[] la = {Locale.US};
- final LocaleList ll = new LocaleList(la);
+ final LocaleList ll = new LocaleList(Locale.US);
assertNotNull(ll);
assertFalse(ll.isEmpty());
assertEquals(1, ll.size());
@@ -93,4 +100,95 @@
assertEquals(IllegalArgumentException.class, e.getClass());
}
}
+
+ public void testEquals() {
+ final LocaleList empty = new LocaleList();
+ final LocaleList anotherEmpty = new LocaleList();
+ LocaleList oneMember = new LocaleList(Locale.US);
+ LocaleList sameOneMember = new LocaleList(new Locale("en", "US"));
+ LocaleList differentOneMember = new LocaleList(Locale.FRENCH);
+ Locale[] la = {Locale.US, Locale.FRENCH};
+ LocaleList twoMember = new LocaleList(la);
+
+ assertFalse(empty.equals(null));
+ assertFalse(oneMember.equals(null));
+
+ assertFalse(empty.equals(new Object()));
+
+ assertTrue(empty.equals(empty));
+ assertTrue(oneMember.equals(oneMember));
+
+ assertFalse(empty.equals(oneMember));
+ assertFalse(oneMember.equals(twoMember));
+
+ assertFalse(oneMember.equals(differentOneMember));
+
+ assertTrue(empty.equals(anotherEmpty));
+ assertTrue(oneMember.equals(sameOneMember));
+ }
+
+ public void testHashCode() {
+ final LocaleList empty = new LocaleList();
+ final LocaleList anotherEmpty = new LocaleList();
+ Locale[] la1 = {Locale.US};
+ LocaleList oneMember = new LocaleList(la1);
+ LocaleList sameOneMember = new LocaleList(la1);
+
+ assertEquals(empty.hashCode(), anotherEmpty.hashCode());
+ assertEquals(oneMember.hashCode(), sameOneMember.hashCode());
+ }
+
+ public void testToString() {
+ LocaleList ll = new LocaleList();
+ assertEquals("[]", ll.toString());
+
+ final Locale[] la1 = {Locale.US};
+ ll = new LocaleList(la1);
+ assertEquals("["+Locale.US.toString()+"]", ll.toString());
+
+ final Locale[] la2 = {Locale.US, Locale.FRENCH};
+ ll = new LocaleList(la2);
+ assertEquals("["+Locale.US.toString()+","+Locale.FRENCH.toString()+"]", ll.toString());
+ }
+
+ public void testToLanguageTags() {
+ LocaleList ll = new LocaleList();
+ assertEquals("", ll.toLanguageTags());
+
+ final Locale[] la1 = {Locale.US};
+ ll = new LocaleList(la1);
+ assertEquals(Locale.US.toLanguageTag(), ll.toLanguageTags());
+
+ final Locale[] la2 = {Locale.US, Locale.FRENCH};
+ ll = new LocaleList(la2);
+ assertEquals(Locale.US.toLanguageTag()+","+Locale.FRENCH.toLanguageTag(),
+ ll.toLanguageTags());
+ }
+
+ public void testGetEmptyLocaleList() {
+ LocaleList empty = LocaleList.getEmptyLocaleList();
+ LocaleList anotherEmpty = LocaleList.getEmptyLocaleList();
+ LocaleList constructedEmpty = new LocaleList();
+
+ assertEquals(constructedEmpty, empty);
+ assertSame(empty, anotherEmpty);
+ }
+
+ public void testForLanguageTags() {
+ assertEquals(LocaleList.getEmptyLocaleList(), LocaleList.forLanguageTags(null));
+ assertEquals(LocaleList.getEmptyLocaleList(), LocaleList.forLanguageTags(""));
+
+ assertEquals(new LocaleList(Locale.forLanguageTag("en-US")),
+ LocaleList.forLanguageTags("en-US"));
+
+ final Locale[] la = {Locale.forLanguageTag("en-PH"), Locale.forLanguageTag("en-US")};
+ assertEquals(new LocaleList(la), LocaleList.forLanguageTags("en-PH,en-US"));
+ }
+
+ public void testGetDefault() {
+ LocaleList ll = LocaleList.getDefault();
+ assertNotNull(ll);
+ assertTrue(ll.size() >= 1);
+ assertEquals(Locale.getDefault(), ll.getPrimary());
+ }
}
diff --git a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
index 6a2240e..ccbdd56 100644
--- a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
@@ -317,13 +317,8 @@
//expected
}
- try {
- assertEquals(AdapterView.INVALID_POSITION,
- mAdapterView.getPositionForView(new ImageView(mActivity)));
- fail("Should throw NullPointerException");
- } catch (NullPointerException e) {
- //expected
- }
+ assertEquals(AdapterView.INVALID_POSITION,
+ mAdapterView.getPositionForView(new ImageView(mActivity)));
}
public void testChangeFocusable() {
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 3130a26..5c75dd4 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -74,6 +74,7 @@
import android.text.style.URLSpan;
import android.text.util.Linkify;
import android.util.DisplayMetrics;
+import android.util.LocaleList;
import android.util.TypedValue;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
@@ -4040,6 +4041,41 @@
}
}
+ public void testTextLocales() {
+ TextView tv = new TextView(mActivity);
+ assertEquals(Locale.getDefault(), tv.getTextLocale());
+ assertEquals(LocaleList.getDefault(), tv.getTextLocales());
+
+ tv.setTextLocale(Locale.CHINESE);
+ assertEquals(Locale.CHINESE, tv.getTextLocale());
+ assertEquals(new LocaleList(Locale.CHINESE), tv.getTextLocales());
+
+ tv.setTextLocales(LocaleList.forLanguageTags("en,ja"));
+ assertEquals(Locale.forLanguageTag("en"), tv.getTextLocale());
+ assertEquals(LocaleList.forLanguageTags("en,ja"), tv.getTextLocales());
+
+ try {
+ tv.setTextLocale(null);
+ fail("Setting the text locale to null should throw");
+ } catch (Throwable e) {
+ assertEquals(IllegalArgumentException.class, e.getClass());
+ }
+
+ try {
+ tv.setTextLocales(null);
+ fail("Setting the text locales to null should throw");
+ } catch (Throwable e) {
+ assertEquals(IllegalArgumentException.class, e.getClass());
+ }
+
+ try {
+ tv.setTextLocales(new LocaleList());
+ fail("Setting the text locale to an empty list should throw");
+ } catch (Throwable e) {
+ assertEquals(IllegalArgumentException.class, e.getClass());
+ }
+ }
+
public void testAllCapsLocalization() {
String testString = "abcdefghijklmnopqrstuvwxyz";