upload android base code part4

This commit is contained in:
August 2018-08-08 17:00:29 +08:00
parent b9e30e05b1
commit 78ea2404cd
23455 changed files with 5250148 additions and 0 deletions

View file

@ -0,0 +1,45 @@
# 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.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under, src)
# Special handling for pre-release builds where the SDK version has not been
# updated, in which case we'll use the version codename (ex. "O").
ifeq (REL,$(PLATFORM_VERSION_CODENAME))
LOCAL_JAVA_RESOURCE_DIRS := assets/$(PLATFORM_SDK_VERSION)/
else
LOCAL_JAVA_RESOURCE_DIRS := assets/$(PLATFORM_VERSION_CODENAME)/
endif
LOCAL_MODULE_TAGS := optional
# Must match the package name in CtsTestCaseList.mk
LOCAL_MODULE := CtsThemeHostTestCases
LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed compatibility-host-util
LOCAL_CTS_TEST_PACKAGE := android.host.theme
LOCAL_SDK_VERSION := current
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
include $(BUILD_CTS_HOST_JAVA_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))

View file

@ -0,0 +1,26 @@
<?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="Config for CTS Theme host test cases">
<option name="config-descriptor:metadata" key="component" value="uitoolkit" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsThemeDeviceApp.apk" />
</target_preparer>
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="CtsThemeHostTestCases.jar" />
<option name="runtime-hint" value="7m30s" />
</test>
</configuration>

View file

@ -0,0 +1,119 @@
* 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 from an emulator, do the following:
1. Ensure the Android SDK is installed locally. Either a public or internal
distribution is fine. From the console, set the ANDROID_SDK_ROOT env var:
export ANDROID_SDK_ROOT = /path/to/sdk
2. Obtain an x86_64 emulator image from the build server by using the script
available internally at go/emu-dev. This script will install the image in
your SDK.
3. Use the SDK's AVD Manager tool to create a single virtual device using the
emulator image from step 2. The exact configuration doesn't really matter;
you can use Nexus 5 as a template. Name the emulator "theme_emulator".
4. From the console, set up your build environment for x86_64 and build CTS:
lunch sdk_x86_64-eng && make cts -j32
5. Use the reference image script to generate the reference images. The script
will automatically start the emulator in the required configurations and
install the resulting reference images in assets/<platform>/<dpi>.zip,
overwriting any existing images.
./cts/hostsidetests/theme/generate_images.py theme_emulator
You can also generate reference images using a real device. To generate a new set
of reference images from a real device, do the following:
1. Connect the device. Verify the device is connected:
adb devices
2. Use the reference image script to generate the reference images:
./cts/hostsidetests/theme/generate_images.py
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.
For a list of devices and their DPI buckets, see Device Metrics:
https://design.google.com/devices/
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
To obtain reliable results, theme tests should be run against the device's
native density; however, many of the tests will also work in a scaled density.
If you don't possess a device for a given density and would still like to run
tests for that density, you can manually force scaling via:
adb shell wm density <dpi>
As of API 24, the results will NOT be 100% reliable due to scaling artifacts.
To reset the device to its native density, run:
adb shell wm density reset
Once the device is in the desired state, do the following:
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 singleCommand cts-dev --module CtsThemeHostTestCases \
--test android.theme.cts.ThemeHostTest
3. Wait for the tests to complete. This should take less than five minutes.
If any test failures are encountered, diff PNGs will be generated and collected
in the "diffs" directory of the CTS results indicated by "Test Result:" in the
test output.

View file

@ -0,0 +1,135 @@
#!/usr/bin/python3
#
# 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 subprocess
import sys
import threading
import time
from subprocess import PIPE
# class for running android device from python
# it will fork the device processor
class AndroidDevice(object):
def __init__(self, serial):
self._serial = serial
def run_adb_command(self, cmd, timeout=None):
adb_cmd = "adb -s %s %s" % (self._serial, cmd)
print(adb_cmd)
adb_process = subprocess.Popen(args=adb_cmd.split(), bufsize=-1, stderr=PIPE, stdout=PIPE)
(out, err) = adb_process.communicate(timeout=timeout)
return out.decode('utf-8').strip(), err.decode('utf-8').strip()
def run_shell_command(self, cmd):
return self.run_adb_command("shell %s" % cmd)
def wait_for_device(self, timeout=30):
return self.run_adb_command('wait-for-device', timeout)
def wait_for_prop(self, key, value, timeout=30):
boot_complete = False
attempts = 0
wait_period = 1
while not boot_complete and (attempts*wait_period) < timeout:
(out, err) = self.run_shell_command("getprop %s" % key)
if out == value:
boot_complete = True
else:
time.sleep(wait_period)
attempts += 1
if not boot_complete:
print("%s not set to %s within timeout!" % (key, value))
return boot_complete
def wait_for_service(self, name, timeout=30):
service_found = False
attempts = 0
wait_period = 1
while not service_found and (attempts*wait_period) < timeout:
(output, err) = self.run_shell_command("service check %s" % name)
if 'not found' not in output:
service_found = True
else:
time.sleep(wait_period)
attempts += 1
if not service_found:
print("Service '%s' not found within timeout!" % name)
return service_found
def wait_for_boot_complete(self, timeout=60):
return self.wait_for_prop('dev.bootcomplete', '1', timeout)
def install_apk(self, apk_path):
self.wait_for_service('package')
(out, err) = self.run_adb_command("install -r -d -g %s" % apk_path)
result = out.split()
return out, err, "Success" in result
def uninstall_package(self, package):
self.wait_for_service('package')
(out, err) = self.run_adb_command("uninstall %s" % package)
result = out.split()
return "Success" in result
def run_instrumentation_test(self, option):
self.wait_for_service('activity')
return self.run_shell_command("am instrument -w --no-window-animation %s" % option)
def is_process_alive(self, process_name):
(out, err) = self.run_shell_command("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 process_name in names
def get_version_sdk(self):
return int(self.run_shell_command("getprop ro.build.version.sdk")[0])
def get_version_codename(self):
return self.run_shell_command("getprop ro.build.version.codename")[0].strip()
def get_density(self):
if "emulator" in self._serial:
return int(self.run_shell_command("getprop qemu.sf.lcd_density")[0])
else:
return int(self.run_shell_command("getprop ro.sf.lcd_density")[0])
def get_orientation(self):
return int(self.run_shell_command("dumpsys | grep SurfaceOrientation")[0].split()[1])
def enumerate_android_devices(require_prefix=''):
devices = subprocess.check_output(["adb", "devices"])
if not devices:
return []
devices = devices.decode('UTF-8').split('\n')[1:]
device_list = []
for device in devices:
if device is not "" and device.startswith(require_prefix):
info = device.split('\t')
if info[1] == "device":
device_list.append(info[0])
return device_list

View file

@ -0,0 +1,43 @@
# 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.
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
# Don't include this package in any target.
LOCAL_MODULE_TAGS := optional
# When built, explicitly put it in the data partition.
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
# and because it is in data, do not strip classes.dex
LOCAL_DEX_PREOPT := false
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
#Flag to tell the Android Asset Packaging Tool not to strip for some densities
LOCAL_AAPT_INCLUDE_ALL_RESOURCES := true
LOCAL_PACKAGE_NAME := CtsThemeDeviceApp
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
LOCAL_SDK_VERSION := 23
include $(BUILD_CTS_SUPPORT_PACKAGE)

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
* 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.theme.app">
<uses-sdk android:minSdkVersion="17" />
<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=".ThemeDeviceActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".GenerateImagesActivity"
android:screenOrientation="portrait"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
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>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button"
/>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<CalendarView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
android:text="@string/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<Chronometer xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/textview"
android:textColor="@android:color/holo_blue_bright"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/textview"
android:textColor="@android:color/holo_blue_dark"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/textview"
android:textColor="@android:color/holo_blue_light"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/textview"
android:textColor="@android:color/holo_green_dark"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/textview"
android:textColor="@android:color/holo_green_light"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/textview"
android:textColor="@android:color/holo_orange_dark"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/textview"
android:textColor="@android:color/holo_orange_light"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/textview"
android:textColor="@android:color/holo_purple"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/textview"
android:textColor="@android:color/holo_red_dark"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/textview"
android:textColor="@android:color/holo_red_light"
/>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<DatePicker xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dip"
android:layout_height="0dip"
/>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progress="0"
android:max="100"
style="?android:attr/progressBarStyleHorizontal"
/>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progress="100"
android:max="100"
style="?android:attr/progressBarStyleHorizontal"
/>

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/progressBar1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progress="50"
android:max="100"
style="?android:attr/progressBarStyleHorizontal"
/>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleLarge"
/>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleSmall"
/>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<RadioButton xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/radiobutton"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<RadioButton xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/radiobutton_checked"
/>

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<RadioButton android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/radiobutton_1"
/>
<RadioButton android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/radiobutton_2"
/>
</RadioGroup>

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<RadioButton android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/radiobutton_1"
/>
<RadioButton android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/radiobutton_2"
/>
</RadioGroup>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<RatingBar xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:rating="0"
android:numStars="5"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<RatingBar xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:rating="2.5"
android:numStars="5"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<RatingBar xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:rating="5"
android:numStars="5"
/>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<SearchView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<SeekBar xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progress="0"
android:max="100"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<SeekBar xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progress="100"
android:max="100"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<SeekBar xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:progress="50"
android:max="100"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<Spinner xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:prompt="@string/spinner_prompt"
android:entries="@array/spinner_entries"
/>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<Switch xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/switch_button"
/>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<Switch xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/switch_button"
/>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/textview"
/>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:focusable="true"
android:keepScreenOn="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.theme.app.ReferenceViewGroup
android:id="@+id/reference_view_group"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<TimePicker xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<ToggleButton xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<ToggleButton xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
/>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<ZoomControls xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<resources>
<string-array name="spinner_entries">
<item>Spinner 1</item>
<item>Spinner 2</item>
<item>Spinner 3</item>
</string-array>
</resources>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<resources>
<dimen name="reference_width">320dp</dimen>
<dimen name="reference_height">480dp</dimen>
</resources>

View file

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<resources>
<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>
<string name="button">Button</string>
<string name="checkbox">CheckBox</string>
<string name="chronometer">Chronometer</string>
<string name="datepicker">DatePicker</string>
<string name="edittext">EditText</string>
<string name="progressbar">ProgressBar</string>
<string name="progressbar_small">ProgressBar Small</string>
<string name="progressbar_large">ProgressBar Large</string>
<string name="progressbar_horizontal_0">ProgressBar Horizontal 0%</string>
<string name="progressbar_horizontal_50">ProgressBar Horizontal 50%</string>
<string name="progressbar_horizontal_100">ProgressBar Horizontal 100%</string>
<string name="radiobutton">RadioButton</string>
<string name="radiobutton_checked">RadioButton Checked</string>
<string name="radiogroup_horizontal">RadioGroup Horizontal</string>
<string name="radiogroup_vertical">RadioGroup Vertical</string>
<string name="ratingbar_0">RatingBar 0 Stars</string>
<string name="ratingbar_2point5">RatingBar 2.5 Stars</string>
<string name="ratingbar_5">RatingBar 5 Stars</string>
<string name="ratingbar_0_pressed">RatingBar 0 Stars Pressed</string>
<string name="ratingbar_2point5_pressed">RatingBar 2.5 Stars Pressed</string>
<string name="ratingbar_5_pressed">RatingBar 5 Stars Pressed</string>
<string name="searchview">SearchView</string>
<string name="searchview_query">SearchView Query</string>
<string name="searchview_query_hint">SearchView Query Hint</string>
<string name="seekbar_0">SeekBar 0%</string>
<string name="seekbar_50">SeekBar 50%</string>
<string name="seekbar_100">SeekBar 100%</string>
<string name="spinner">Spinner</string>
<string name="switch_button">Switch</string>
<string name="switch_button_checked">Switch Checked</string>
<string name="tabhost">TabHost</string>
<string name="textview">TextView</string>
<string name="timepicker">TimePicker</string>
<string name="togglebutton">ToggleButton</string>
<string name="togglebutton_checked">ToggleButton Checked</string>
<string name="zoomcontrols">ZoomControls</string>
<string name="alertdialog_onebutton">AlertDialog One Button</string>
<string name="alertdialog_twobuttons">AlertDialog Two Buttons</string>
<string name="alertdialog_threebuttons">AlertDialog Three Buttons</string>
<string name="alertdialog_list">AlertDialog List</string>
<string name="alertdialog_singlechoice">AlertDialog Single Choice</string>
<string name="alertdialog_multichoice">AlertDialog Multiple Choice</string>
<string name="progressdialog_spinner">ProgressDialog Spinner</string>
<string name="progressdialog_horizontal">ProgressDialog Horizontal</string>
<string name="color_blue_bright">Bright Blue Color</string>
<string name="color_blue_dark">Dark Blue Color</string>
<string name="color_blue_light">Light Blue Color</string>
<string name="color_green_dark">Dark Green Color</string>
<string name="color_green_light">Light Green Color</string>
<string name="color_orange_dark">Dark Orange Color</string>
<string name="color_orange_light">Light Orange Color</string>
<string name="color_purple">Purple Color</string>
<string name="color_red_dark">Dark Red Color</string>
<string name="color_red_light">Light Red Color</string>
<string name="spinner_prompt">Spinner Prompt</string>
<string name="radiobutton_1">Radio Button 1</string>
<string name="radiobutton_2">Radio Button 2</string>
<string name="loading">Loading...</string>
</resources>

View file

@ -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;
}
}

View file

@ -0,0 +1,225 @@
/*
* 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.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* 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;
public static final String EXTRA_REASON = "reason";
private final CountDownLatch mLatch = new CountDownLatch(1);
private File mOutputDir;
private File mOutputZip;
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();
}
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();
}
/**
* 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) {
finish("Failed to generate images for theme " + mCurrentTheme + " ("
+ data.getStringExtra(EXTRA_REASON) + ")", false);
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) {
compressOutput();
finish("Image generation complete!", true);
}
}
private void compressOutput() {
mOutputZip = new File(mOutputDir.getParentFile(), mOutputDir.getName() + ".zip");
if (mOutputZip.exists()) {
// Remove any old test results.
mOutputZip.delete();
}
try {
ThemeTestUtils.compressDirectory(mOutputDir, mOutputZip);
ThemeTestUtils.deleteDirectory(mOutputDir);
} catch (IOException e) {
e.printStackTrace();
}
}
private void finish(String reason, boolean success) {
mFinishSuccess = success;
mFinishReason = reason;
finish();
}
@Override
public void finish() {
mLatch.countDown();
super.finish();
}
public File getOutputZip() {
return mOutputZip;
}
public boolean waitForCompletion(long timeoutMillis) throws InterruptedException {
return mLatch.await(timeoutMillis, TimeUnit.MILLISECONDS);
}
}

View file

@ -0,0 +1,34 @@
/*
* 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.view.View;
/**
* Interface used to do further setup on a view after it has been inflated.
*/
public interface LayoutModifier {
/** Actions to take before inflating the view. */
void prepare();
/**
* @param view inflated by the test activity
* @return the same view or another view that will be snapshotted by the test
*/
View modifyView(View view);
}

View file

@ -0,0 +1,47 @@
/*
* 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> {
/** Overall test timeout is 30 minutes. Should only take about 5. */
private static final int TEST_RESULT_TIMEOUT = 30 * 60 * 1000;
public ReferenceImagesTest() {
super(GenerateImagesActivity.class);
}
public void testGenerateReferenceImages() throws Exception {
setActivityInitialTouchMode(true);
final GenerateImagesActivity activity = getActivity();
assertTrue("Activity failed to complete within " + TEST_RESULT_TIMEOUT + " ms",
activity.waitForCompletion(TEST_RESULT_TIMEOUT));
assertTrue(activity.getFinishReason(), activity.isFinishSuccess());
final File outputZip = activity.getOutputZip();
assertTrue("Failed to generate reference image ZIP",
outputZip != null && outputZip.exists());
}
}

View file

@ -0,0 +1,81 @@
/*
* 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.theme.app.R;
import android.content.Context;
import android.content.res.Resources;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/**
* {@link ViewGroup} that inflates to a reference width and height.
*/
public class ReferenceViewGroup extends ViewGroup {
private final int mWidthDp;
private final int mHeightDp;
public ReferenceViewGroup(Context context) {
this(context, null);
}
public ReferenceViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
Resources resources = context.getResources();
mWidthDp = resources.getDimensionPixelSize(R.dimen.reference_width);
mHeightDp = resources.getDimensionPixelSize(R.dimen.reference_height);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
widthMeasureSpec = getMeasureSpec(LayoutParams.MATCH_PARENT, mWidthDp);
heightMeasureSpec = getMeasureSpec(LayoutParams.MATCH_PARENT, mHeightDp);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
LayoutParams params = child.getLayoutParams();
int width = getMeasureSpec(params.width, mWidthDp);
int height = getMeasureSpec(params.height, mHeightDp);
child.measure(width, height);
}
}
private int getMeasureSpec(int value, int size) {
if (value == LayoutParams.MATCH_PARENT) {
return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
} else if (value == LayoutParams.WRAP_CONTENT) {
return MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
} else {
return value;
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
}
}
}

View file

@ -0,0 +1,420 @@
/*
* 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";
/**
* Delay that allows the Holo-style CalendarView to settle to its final
* position.
*/
private static final long HOLO_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) {
final Intent data = new Intent();
data.putExtra(GenerateImagesActivity.EXTRA_REASON, "Only rendered "
+ mLayoutIndex + "/" + LAYOUTS.length + " layouts");
setResult(RESULT_CANCELED, data);
}
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 && mTheme.spec == Theme.HOLO) {
// The Holo-styled DatePicker uses a CalendarView that has a
// non-configurable adjustment duration of 540ms.
view.postDelayed(generateBitmapRunnable, HOLO_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 static final int HOLO = 0;
public static final int MATERIAL = 1;
public final int spec;
public final int id;
public final int apiLevel;
public final String name;
private Theme(int spec, int id, int apiLevel, String name) {
this.spec = spec;
this.id = id;
this.apiLevel = apiLevel;
this.name = name;
}
}
// List of themes to verify.
static final Theme[] THEMES = {
// Holo
new Theme(Theme.HOLO, android.R.style.Theme_Holo,
Build.VERSION_CODES.HONEYCOMB, "holo"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Dialog,
Build.VERSION_CODES.HONEYCOMB, "holo_dialog"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Dialog_MinWidth,
Build.VERSION_CODES.HONEYCOMB, "holo_dialog_minwidth"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Dialog_NoActionBar,
Build.VERSION_CODES.HONEYCOMB, "holo_dialog_noactionbar"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Dialog_NoActionBar_MinWidth,
Build.VERSION_CODES.HONEYCOMB, "holo_dialog_noactionbar_minwidth"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_DialogWhenLarge,
Build.VERSION_CODES.HONEYCOMB, "holo_dialogwhenlarge"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_DialogWhenLarge_NoActionBar,
Build.VERSION_CODES.HONEYCOMB, "holo_dialogwhenlarge_noactionbar"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_InputMethod,
Build.VERSION_CODES.HONEYCOMB, "holo_inputmethod"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_NoActionBar,
Build.VERSION_CODES.HONEYCOMB, "holo_noactionbar"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_NoActionBar_Fullscreen,
Build.VERSION_CODES.HONEYCOMB, "holo_noactionbar_fullscreen"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_NoActionBar_Overscan,
Build.VERSION_CODES.JELLY_BEAN_MR2, "holo_noactionbar_overscan"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_NoActionBar_TranslucentDecor,
Build.VERSION_CODES.KITKAT, "holo_noactionbar_translucentdecor"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Panel,
Build.VERSION_CODES.HONEYCOMB, "holo_panel"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Wallpaper,
Build.VERSION_CODES.HONEYCOMB, "holo_wallpaper"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Wallpaper_NoTitleBar,
Build.VERSION_CODES.HONEYCOMB, "holo_wallpaper_notitlebar"),
// Holo Light
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light,
Build.VERSION_CODES.HONEYCOMB, "holo_light"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_DarkActionBar,
Build.VERSION_CODES.ICE_CREAM_SANDWICH, "holo_light_darkactionbar"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_Dialog,
Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_Dialog_MinWidth,
Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_minwidth"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_Dialog_NoActionBar,
Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_noactionbar"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_Dialog_NoActionBar_MinWidth,
Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_noactionbar_minwidth"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_DialogWhenLarge,
Build.VERSION_CODES.HONEYCOMB, "holo_light_dialogwhenlarge"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_DialogWhenLarge_NoActionBar,
Build.VERSION_CODES.HONEYCOMB, "holo_light_dialogwhenlarge_noactionbar"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_NoActionBar,
Build.VERSION_CODES.HONEYCOMB_MR2, "holo_light_noactionbar"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_NoActionBar_Fullscreen,
Build.VERSION_CODES.HONEYCOMB_MR2, "holo_light_noactionbar_fullscreen"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_NoActionBar_Overscan,
Build.VERSION_CODES.JELLY_BEAN_MR2, "holo_light_noactionbar_overscan"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_NoActionBar_TranslucentDecor,
Build.VERSION_CODES.KITKAT, "holo_light_noactionbar_translucentdecor"),
new Theme(Theme.HOLO, android.R.style.Theme_Holo_Light_Panel,
Build.VERSION_CODES.HONEYCOMB, "holo_light_panel"),
// Material
new Theme(Theme.MATERIAL, android.R.style.Theme_Material,
Build.VERSION_CODES.LOLLIPOP, "material"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Dialog,
Build.VERSION_CODES.LOLLIPOP, "material_dialog"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Dialog_Alert,
Build.VERSION_CODES.LOLLIPOP, "material_dialog_alert"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Dialog_MinWidth,
Build.VERSION_CODES.LOLLIPOP, "material_dialog_minwidth"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Dialog_NoActionBar,
Build.VERSION_CODES.LOLLIPOP, "material_dialog_noactionbar"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Dialog_NoActionBar_MinWidth,
Build.VERSION_CODES.LOLLIPOP, "material_dialog_noactionbar_minwidth"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Dialog_Presentation,
Build.VERSION_CODES.LOLLIPOP, "material_dialog_presentation"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_DialogWhenLarge,
Build.VERSION_CODES.LOLLIPOP, "material_dialogwhenlarge"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_DialogWhenLarge_NoActionBar,
Build.VERSION_CODES.LOLLIPOP, "material_dialogwhenlarge_noactionbar"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_InputMethod,
Build.VERSION_CODES.LOLLIPOP, "material_inputmethod"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_NoActionBar,
Build.VERSION_CODES.LOLLIPOP, "material_noactionbar"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_NoActionBar_Fullscreen,
Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_fullscreen"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_NoActionBar_Overscan,
Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_overscan"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_NoActionBar_TranslucentDecor,
Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_translucentdecor"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Panel,
Build.VERSION_CODES.LOLLIPOP, "material_panel"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Settings,
Build.VERSION_CODES.LOLLIPOP, "material_settings"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Voice,
Build.VERSION_CODES.LOLLIPOP, "material_voice"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Wallpaper,
Build.VERSION_CODES.LOLLIPOP, "material_wallpaper"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Wallpaper_NoTitleBar,
Build.VERSION_CODES.LOLLIPOP, "material_wallpaper_notitlebar"),
// Material Light
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light,
Build.VERSION_CODES.LOLLIPOP, "material_light"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_DarkActionBar,
Build.VERSION_CODES.LOLLIPOP, "material_light_darkactionbar"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Dialog,
Build.VERSION_CODES.LOLLIPOP, "material_light_dialog"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Dialog_Alert,
Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_alert"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Dialog_MinWidth,
Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_minwidth"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Dialog_NoActionBar,
Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_noactionbar"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Dialog_NoActionBar_MinWidth,
Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_noactionbar_minwidth"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Dialog_Presentation,
Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_presentation"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_DialogWhenLarge,
Build.VERSION_CODES.LOLLIPOP, "material_light_dialogwhenlarge"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_DialogWhenLarge_NoActionBar,
Build.VERSION_CODES.LOLLIPOP, "material_light_dialogwhenlarge_noactionbar"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_LightStatusBar,
Build.VERSION_CODES.M, "material_light_lightstatusbar"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_NoActionBar,
Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_NoActionBar_Fullscreen,
Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_fullscreen"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_NoActionBar_Overscan,
Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_overscan"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_NoActionBar_TranslucentDecor,
Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_translucentdecor"),
new Theme(Theme.MATERIAL, android.R.style.Theme_Material_Light_Panel,
Build.VERSION_CODES.LOLLIPOP, "material_light_panel"),
new Theme(Theme.MATERIAL, 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.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"),
};
}

View file

@ -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[8192];
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();
}
}

View file

@ -0,0 +1,29 @@
/*
* 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.modifiers;
import android.theme.app.LayoutModifier;
/**
* {@link LayoutModifier} that does nothing in {@link #prepare()}.
*/
abstract class AbstractLayoutModifier implements LayoutModifier {
@Override
public void prepare() {
}
}

View file

@ -0,0 +1,34 @@
/*
* 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.modifiers;
import android.theme.app.LayoutModifier;
import android.view.View;
import android.widget.DatePicker;
/**
* {@link LayoutModifier} that sets a precise date on a {@link DatePicker}.
*/
public class DatePickerModifier extends AbstractLayoutModifier {
@Override
public View modifyView(View view) {
DatePicker tp = (DatePicker) view;
tp.updateDate(2011, 4, 20);
return view;
}
}

View file

@ -0,0 +1,38 @@
/*
* 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.modifiers;
import android.view.View;
import android.view.animation.Interpolator;
import android.widget.ProgressBar;
public class ProgressBarModifier extends AbstractLayoutModifier {
@Override
public View modifyView(View view) {
ProgressBar pb = (ProgressBar) view;
pb.setInterpolator(new ZeroInterpolator());
return pb;
}
private static class ZeroInterpolator implements Interpolator {
@Override
public float getInterpolation(float input) {
return 0;
}
}
}

View file

@ -0,0 +1,57 @@
/*
* 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.modifiers;
import android.theme.app.R;
import android.content.Context;
import android.view.View;
import android.widget.SearchView;
public class SearchViewModifier extends AbstractLayoutModifier {
public static final int QUERY_HINT = 0;
public static final int QUERY = 1;
private int mSearchViewType;
public SearchViewModifier(int searchViewType) {
mSearchViewType = searchViewType;
}
@Override
public View modifyView(View view) {
SearchView searchView = (SearchView) view;
Context context = view.getContext();
switch (mSearchViewType) {
case QUERY_HINT:
searchView.setQueryHint(context.getString(R.string.searchview_query_hint));
break;
case QUERY:
searchView.setQuery(context.getString(R.string.searchview_query), false);
break;
default:
throw new IllegalArgumentException("Bad search view type: " + mSearchViewType);
}
searchView.setIconifiedByDefault(false);
return searchView;
}
}

View file

@ -0,0 +1,32 @@
/*
* 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.modifiers;
import android.view.View;
import android.widget.TimePicker;
public class TimePickerModifier extends AbstractLayoutModifier {
@Override
public View modifyView(View view) {
TimePicker timePicker = (TimePicker) view;
timePicker.setIs24HourView(true);
timePicker.setCurrentHour(13);
timePicker.setCurrentMinute(37);
return view;
}
}

View file

@ -0,0 +1,29 @@
/*
* 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.modifiers;
import android.view.View;
import android.widget.CheckBox;
public class ViewCheckedModifier extends AbstractLayoutModifier {
@Override
public View modifyView(View view) {
((CheckBox) view).setChecked(true);
return view;
}
}

View file

@ -0,0 +1,28 @@
/*
* 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.modifiers;
import android.view.View;
public class ViewPressedModifier extends AbstractLayoutModifier {
@Override
public View modifyView(View view) {
view.setPressed(true);
return view;
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,81 @@
#!/usr/bin/python3
#
# Copyright (C) 2017 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 functools
import math
import socket
import subprocess
import sys
import tempfile
from android_device import *
def find_free_port():
s = socket.socket()
s.bind(('', 0))
return int(s.getsockname()[1])
class AVD(object):
def __init__(self, name, emu_path):
self._name = name
self._emu_path = emu_path
self._opts = ''
self._adb_name = None
self._emu_proc = None
def start(self):
if self._emu_proc:
raise Exception('Emulator already running')
port_adb = find_free_port()
port_tty = find_free_port()
# -no-window might be useful here
emu_cmd = "%s -avd %s %s-ports %d,%d" \
% (self._emu_path, self._name, self._opts, port_adb, port_tty)
print(emu_cmd)
emu_proc = subprocess.Popen(emu_cmd.split(" "), bufsize=-1, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
# The emulator ought to be starting now.
self._adb_name = "emulator-%d" % (port_tty - 1)
self._emu_proc = emu_proc
def stop(self):
if not self._emu_proc:
raise Exception('Emulator not currently running')
self._emu_proc.kill()
(out, err) = self._emu_proc.communicate()
self._emu_proc = None
return out, err
def get_serial(self):
if not self._emu_proc:
raise Exception('Emulator not currently running')
return self._adb_name
def get_device(self):
if not self._emu_proc:
raise Exception('Emulator not currently running')
return AndroidDevice(self._adb_name)
def configure_screen(self, density, width_dp, height_dp):
width_px = int(math.ceil(width_dp * density / 1600) * 10)
height_px = int(math.ceil(height_dp * density / 1600) * 10)
self._opts = "-prop qemu.sf.lcd_density=%d -skin %dx%d " % (density, width_px, height_px)

View file

@ -0,0 +1,244 @@
#!/usr/bin/python3
#
# 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 tempfile
import threading
import time
import traceback
from android_device import *
from avd import *
from queue import Queue, Empty
# This dict should contain one entry for every density listed in CDD 7.1.1.3.
CTS_THEME_dict = {
120: "ldpi",
160: "mdpi",
213: "tvdpi",
240: "hdpi",
260: "260dpi",
280: "280dpi",
300: "300dpi",
320: "xhdpi",
340: "340dpi",
360: "360dpi",
400: "400dpi",
420: "420dpi",
480: "xxhdpi",
560: "560dpi",
640: "xxxhdpi",
}
OUT_FILE = "/sdcard/cts-theme-assets.zip"
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])
traceback.print_exc()
q.task_done()
except KeyboardInterrupt:
raise
except Empty:
pass
def get_result(self):
return self._result
# pass a function with number of instances to be executed in parallel
# each thread continues until config q is empty.
def execute_parallel(tasks, setup, q, num_threads):
result = 0
threads = []
for i in range(num_threads):
t = ParallelExecutor(tasks, q)
t.start()
threads.append(t)
for t in threads:
t.join()
result += t.get_result()
return result
def print_adb_result(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 do_capture(setup, device_serial):
(themeApkPath, out_path) = setup
device = AndroidDevice(device_serial)
version = device.get_version_codename()
if version == "REL":
version = str(device.get_version_sdk())
density = device.get_density()
if CTS_THEME_dict[density]:
density_bucket = CTS_THEME_dict[density]
else:
density_bucket = str(density) + "dpi"
out_file = os.path.join(out_path, os.path.join(version, "%s.zip" % density_bucket))
device.uninstall_package('android.theme.app')
(out, err, success) = device.install_apk(themeApkPath)
if not success:
print("Failed to install APK on " + device_serial)
print_adb_result(device_serial, out, err)
return False
print("Generating images on " + device_serial + "...")
try:
(out, err) = device.run_instrumentation_test(
"android.theme.app/android.support.test.runner.AndroidJUnitRunner")
except KeyboardInterrupt:
raise
except:
(out, err) = device.run_instrumentation_test(
"android.theme.app/android.test.InstrumentationTestRunner")
# Detect test failure and abort.
if "FAILURES!!!" in out.split():
print_adb_result(device_serial, out, err)
return False
# Make sure that the run is complete by checking the process itself
print("Waiting for " + device_serial + "...")
wait_time = 0
while device.is_process_alive("android.theme.app"):
time.sleep(1)
wait_time = wait_time + 1
if wait_time > 180:
print("Timed out")
break
time.sleep(10)
print("Pulling images from " + device_serial + " to " + out_file)
device.run_adb_command("pull " + OUT_FILE + " " + out_file)
device.run_adb_command("shell rm -rf " + OUT_FILE)
return True
def get_emulator_path():
if 'ANDROID_SDK_ROOT' not in os.environ:
print('Environment variable ANDROID_SDK_ROOT must point to your Android SDK root.')
sys.exit(1)
sdk_path = os.environ['ANDROID_SDK_ROOT']
if not os.path.isdir(sdk_path):
print("Failed to find Android SDK at ANDROID_SDK_ROOT: %s" % sdk_path)
sys.exit(1)
emu_path = os.path.join(os.path.join(sdk_path, 'tools'), 'emulator')
if not os.path.isfile(emu_path):
print("Failed to find emulator within ANDROID_SDK_ROOT: %s" % sdk_path)
sys.exit(1)
return emu_path
def start_emulator(name, density):
emu_path = get_emulator_path()
# Start emulator for 560dpi, normal screen size.
test_avd = AVD(name, emu_path)
test_avd.configure_screen(density, 360, 640)
test_avd.start()
try:
test_avd_device = test_avd.get_device()
test_avd_device.wait_for_device()
test_avd_device.wait_for_boot_complete()
return test_avd
except:
test_avd.stop()
return None
def main(argv):
if 'ANDROID_BUILD_TOP' not in os.environ or 'ANDROID_HOST_OUT' not in os.environ:
print('Missing environment variables. Did you run build/envsetup.sh and lunch?')
sys.exit(1)
theme_apk = os.path.join(os.environ['ANDROID_HOST_OUT'],
'cts/android-cts/testcases/CtsThemeDeviceApp.apk')
if not os.path.isfile(theme_apk):
print('Couldn\'t find test APK. Did you run make cts?')
sys.exit(1)
out_path = os.path.join(os.environ['ANDROID_BUILD_TOP'],
'cts/hostsidetests/theme/assets')
os.system("mkdir -p %s" % out_path)
if len(argv) is 2:
for density in CTS_THEME_dict.keys():
emulator = start_emulator(argv[1], density)
result = do_capture(setup=(theme_apk, out_path), device_serial=emulator.get_serial())
emulator.stop()
if result:
print("Generated reference images for %ddpi" % density)
else:
print("Failed to generate reference images for %ddpi" % density)
break
else:
tasks = [do_capture]
setup = (theme_apk, out_path)
devices = enumerate_android_devices('emulator')
device_queue = Queue()
for device in devices:
device_queue.put(device)
result = execute_parallel(tasks, setup, device_queue, len(devices))
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)

View file

@ -0,0 +1,185 @@
/*
* 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.cts;
import com.android.ddmlib.Log;
import com.android.ddmlib.Log.LogLevel;
import com.android.tradefed.util.Pair;
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;
import javax.imageio.ImageIO;
/**
* Compares the images generated by the device with the reference images.
*/
public class ComparisonTask implements Callable<Pair<String, File>> {
private static final String TAG = "ComparisonTask";
private static final int IMAGE_THRESHOLD = 8;
/** Neutral gray for blending colors. */
private static final int GRAY = 0xFF808080;
/** Maximum allowable number of consecutive failed pixels. */
private static final int MAX_CONSECUTIVE_FAILURES = 1;
private final String mName;
private final File mExpected;
private final File mActual;
public ComparisonTask(String name, File expected, File actual) {
mName = name;
mExpected = expected;
mActual = actual;
}
public Pair<String, File> call() {
try {
final BufferedImage expected = ImageIO.read(mExpected);
final BufferedImage actual = ImageIO.read(mActual);
if (!compare(expected, actual, IMAGE_THRESHOLD)) {
final File diff = File.createTempFile("diff_" + mExpected.getName(), ".png");
createDiff(expected, actual, diff);
return new Pair<>(mName, diff);
}
} catch (IOException e) {
Log.logAndDisplay(LogLevel.ERROR, TAG, e.toString());
e.printStackTrace();
}
return null;
}
private static int getAlphaScaledBlue(final int color) {
return (color & 0x000000FF) * getAlpha(color) / 255;
}
private static int getAlphaScaledGreen(final int color) {
return ((color & 0x0000FF00) >> 8) * getAlpha(color) / 255;
}
private static int getAlphaScaledRed(final int color) {
return ((color & 0x00FF0000) >> 16) * getAlpha(color) / 255;
}
private static int getAlpha(final int color) {
// use logical shift for keeping an unsigned value
return (color & 0xFF000000) >>> 24;
}
/**
* Verifies that the pixels of reference and generated images are similar
* within a specified threshold.
*
* @param reference expected image
* @param generated actual image
* @param threshold maximum difference per channel
* @return {@code true} if the images are similar, false otherwise
*/
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()) {
return false;
}
double maxDist = 0;
for (int i = 0; i < w; i++) {
int consecutive = 0;
for (int j = 0; j < h; j++) {
final int p1 = reference.getRGB(i, j);
final int p2 = generated.getRGB(i, j);
final int dr = getAlphaScaledRed(p1) - getAlphaScaledRed(p2);
final int dg = getAlphaScaledGreen(p1) - getAlphaScaledGreen(p2);
final int db = getAlphaScaledBlue(p1) - getAlphaScaledBlue(p2);
if (Math.abs(db) > threshold ||
Math.abs(dg) > threshold ||
Math.abs(dr) > threshold) {
System.err.println("fail dr=" + dr+ " dg=" + dg+ " db=" + db);
consecutive++;
if (consecutive > MAX_CONSECUTIVE_FAILURES) {
System.err.println("consecutive fail");
return false;
}
} else {
consecutive = 0;
}
}
}
return true;
}
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);
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 colorExpected = Color.WHITE.getRGB();
int colorActual = Color.WHITE.getRGB();
int colorDiff;
if (inBounds1 && inBounds2) {
colorExpected = expected.getRGB(i, j);
colorActual = actual.getRGB(i, j);
colorDiff = colorExpected == colorActual ? colorExpected : Color.RED.getRGB();
} else if (inBounds1 && !inBounds2) {
colorExpected = expected.getRGB(i, j);
colorDiff = Color.BLUE.getRGB();
} else if (!inBounds1 && inBounds2) {
colorActual = actual.getRGB(i, j);
colorDiff = Color.GREEN.getRGB();
} else {
colorDiff = Color.MAGENTA.getRGB();
}
int x = i;
diff.setRGB(x, j, colorExpected);
x += width;
diff.setRGB(x, j, colorActual);
x += width;
diff.setRGB(x, j, colorDiff);
}
}
ImageIO.write(diff, "png", out);
}
}

View file

@ -0,0 +1,296 @@
/*
* 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.cts;
import com.android.ddmlib.Log;
import com.android.ddmlib.Log.LogLevel;
import com.android.tradefed.device.CollectingOutputReceiver;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.result.FileInputStreamSource;
import com.android.tradefed.result.InputStreamSource;
import com.android.tradefed.result.LogDataType;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.util.Pair;
import com.android.tradefed.util.StreamUtil;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* Test to check non-modifiable themes have not been changed.
*/
public class ThemeHostTest extends DeviceTestCase {
private static final String LOG_TAG = "ThemeHostTest";
private static final String APP_PACKAGE_NAME = "android.theme.app";
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 TEST_CLASS = "android.support.test.runner.AndroidJUnitRunner";
/** The command to launch the main instrumentation test. */
private static final String START_CMD = String.format(
"am instrument -w --no-window-animation %s/%s", APP_PACKAGE_NAME, TEST_CLASS);
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";
/** Shell command used to obtain current device density. */
private static final String WM_DENSITY = "wm density";
/** Overall test timeout is 30 minutes. Should only take about 5. */
private static final int TEST_RESULT_TIMEOUT = 30 * 60 * 1000;
/** Map of reference image names and files. */
private Map<String, File> mReferences;
/** A reference to the device under test. */
private ITestDevice mDevice;
private ExecutorService mExecutionService;
private ExecutorCompletionService<Pair<String, File>> mCompletionService;
/** the string identifying the hardware type. */
private String mHardwareType;
@Override
protected void setUp() throws Exception {
super.setUp();
mDevice = getDevice();
mHardwareType = mDevice.executeShellCommand(HARDWARE_TYPE_CMD).trim();
final String density = getDensityBucketForDevice(mDevice, mHardwareType);
final String referenceZipAssetPath = String.format("/%s.zip", density);
mReferences = extractReferenceImages(referenceZipAssetPath);
final int numCores = Runtime.getRuntime().availableProcessors();
mExecutionService = Executors.newFixedThreadPool(numCores * 2);
mCompletionService = new ExecutorCompletionService<>(mExecutionService);
}
private Map<String, File> extractReferenceImages(String zipFile) throws Exception {
final Map<String, File> references = new HashMap<>();
final InputStream zipStream = ThemeHostTest.class.getResourceAsStream(zipFile);
if (zipStream != null) {
try (ZipInputStream in = new ZipInputStream(zipStream)) {
final byte[] buffer = new byte[1024];
for (ZipEntry ze; (ze = in.getNextEntry()) != null; ) {
final String name = ze.getName();
final File tmp = File.createTempFile("ref_" + name, ".png");
tmp.deleteOnExit();
try (FileOutputStream out = new FileOutputStream(tmp)) {
for (int count; (count = in.read(buffer)) != -1; ) {
out.write(buffer, 0, count);
}
}
references.put(name, tmp);
}
} catch (IOException e) {
fail("Failed to unzip assets: " + zipFile);
}
} else {
if (checkHardwareTypeSkipTest(mHardwareType)) {
Log.logAndDisplay(LogLevel.WARN, LOG_TAG,
"Could not obtain resources for skipped themes test: " + zipFile);
} else {
fail("Failed to get resource: " + zipFile);
}
}
return references;
}
@Override
protected void tearDown() throws Exception {
mExecutionService.shutdown();
// Remove generated images.
mDevice.executeShellCommand(CLEAR_GENERATED_CMD);
super.tearDown();
}
public void testThemes() throws Exception {
if (checkHardwareTypeSkipTest(mHardwareType)) {
Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Skipped themes test for watch / TV");
return;
}
if (mReferences.isEmpty()) {
Log.logAndDisplay(LogLevel.INFO, LOG_TAG,
"Skipped themes test due to missing reference images");
return;
}
assertTrue("Aborted image generation", generateDeviceImages());
// Pull ZIP file from remote device.
final File localZip = File.createTempFile("generated", ".zip");
assertTrue("Failed to pull generated assets from device",
mDevice.pullFile(GENERATED_ASSETS_ZIP, localZip));
final int numTasks = extractGeneratedImages(localZip, mReferences);
int failureCount = 0;
for (int i = numTasks; i > 0; i--) {
final Pair<String, File> comparison = mCompletionService.take().get();
if (comparison != null) {
InputStreamSource inputStream = new FileInputStreamSource(comparison.second);
try{
// Log the diff file
addTestLog(comparison.first, LogDataType.PNG, inputStream);
} finally {
StreamUtil.cancel(inputStream);
}
failureCount++;
}
}
assertTrue(failureCount + " failures in theme test", failureCount == 0);
}
private int extractGeneratedImages(File localZip, Map<String, File> references)
throws IOException {
int numTasks = 0;
// Extract generated images to temporary files.
final byte[] data = new byte[8192];
try (ZipInputStream zipInput = new ZipInputStream(new FileInputStream(localZip))) {
for (ZipEntry entry; (entry = zipInput.getNextEntry()) != null; ) {
final String name = entry.getName();
final File expected = references.get(name);
if (expected != null && expected.exists()) {
final File actual = File.createTempFile("actual_" + name, ".png");
actual.deleteOnExit();
try (FileOutputStream pngOutput = new FileOutputStream(actual)) {
for (int count; (count = zipInput.read(data, 0, data.length)) != -1; ) {
pngOutput.write(data, 0, count);
}
}
final String shortName = name.substring(0, name.indexOf('.'));
mCompletionService.submit(new ComparisonTask(shortName, expected, actual));
numTasks++;
} else {
Log.logAndDisplay(LogLevel.INFO, LOG_TAG,
"Missing reference image for " + name);
}
zipInput.closeEntry();
}
}
return numTasks;
}
private boolean generateDeviceImages() throws Exception {
// Stop any existing instances.
mDevice.executeShellCommand(STOP_CMD);
// Start instrumentation test.
final CollectingOutputReceiver receiver = new CollectingOutputReceiver();
mDevice.executeShellCommand(START_CMD, receiver, TEST_RESULT_TIMEOUT,
TimeUnit.MILLISECONDS, 0);
return receiver.getOutput().contains("OK ");
}
private static String getDensityBucketForDevice(ITestDevice device, String hardwareType) {
if (hardwareType.contains("android.hardware.type.television")) {
// references images for tv are under bucket "tvdpi".
return "tvdpi";
}
final int density;
try {
density = getDensityForDevice(device);
} catch (DeviceNotAvailableException e) {
throw new RuntimeException("Failed to detect device density", e);
}
final String bucket;
switch (density) {
case 120:
bucket = "ldpi";
break;
case 160:
bucket = "mdpi";
break;
case 240:
bucket = "hdpi";
break;
case 320:
bucket = "xhdpi";
break;
case 480:
bucket = "xxhdpi";
break;
case 640:
bucket = "xxxhdpi";
break;
default:
bucket = density + "dpi";
break;
}
Log.logAndDisplay(LogLevel.INFO, LOG_TAG,
"Device density detected as " + density + " (" + bucket + ")");
return bucket;
}
private static int getDensityForDevice(ITestDevice device) throws DeviceNotAvailableException {
final String output = device.executeShellCommand(WM_DENSITY);
final Pattern p = Pattern.compile("Override density: (\\d+)");
final Matcher m = p.matcher(output);
if (m.find()) {
return Integer.parseInt(m.group(1));
}
final String densityProp;
if (device.getSerialNumber().startsWith("emulator-")) {
densityProp = DENSITY_PROP_EMULATOR;
} else {
densityProp = DENSITY_PROP_DEVICE;
}
return Integer.parseInt(device.getProperty(densityProp));
}
private static boolean checkHardwareTypeSkipTest(String hardwareTypeString) {
return hardwareTypeString.contains("android.hardware.type.watch")
|| hardwareTypeString.contains("android.hardware.type.television");
}
}