Merge "Updating CTS to account for behavior change in LauncherApps" into pi-dev
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
index e4dee54..7296513 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
@@ -66,16 +66,6 @@
private static final String TAG = AtomTests.class.getSimpleName();
@Test
- public void testAppStart() throws Exception {
- Context context = InstrumentationRegistry.getContext();
- Intent intent = new Intent(context, StatsdCtsForegroundActivity.class);
- intent.putExtra(StatsdCtsForegroundActivity.KEY_ACTION,
- StatsdCtsForegroundActivity.ACTION_SLEEP_WHILE_TOP);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(intent);
- }
-
- @Test
public void testAudioState() {
// TODO: This should surely be getTargetContext(), here and everywhere, but test first.
Context context = InstrumentationRegistry.getContext();
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
index 14f3dba..615bbc0 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
@@ -38,6 +38,7 @@
public static final String ACTION_END_IMMEDIATELY = "action.end_immediately";
public static final String ACTION_SLEEP_WHILE_TOP = "action.sleep_top";
public static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
+ public static final String ACTION_CRASH = "action.crash";
public static final int SLEEP_OF_ACTION_SLEEP_WHILE_TOP = 2_000;
public static final int SLEEP_OF_ACTION_SHOW_APPLICATION_OVERLAY = 2_000;
@@ -65,6 +66,9 @@
case ACTION_SHOW_APPLICATION_OVERLAY:
doShowApplicationOverlay();
break;
+ case ACTION_CRASH:
+ doCrash();
+ break;
default:
Log.e(TAG, "Intent had invalid action " + action);
finish();
@@ -116,4 +120,8 @@
AtomTests.sleep(SLEEP_OF_ACTION_SHOW_APPLICATION_OVERLAY);
finish();
}
+
+ private void doCrash() {
+ Log.e(TAG, "About to crash the app with 1/0 " + (long)1/0);
+ }
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index a9d5163..b5677ce 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -16,6 +16,8 @@
package android.cts.statsd.atom;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
import android.os.WakeLockLevelEnum;
@@ -30,6 +32,7 @@
import com.android.os.AtomsProto.CameraStateChanged;
import com.android.os.AtomsProto.CpuTimePerUid;
import com.android.os.AtomsProto.CpuTimePerUidFreq;
+import com.android.os.AtomsProto.DropboxErrorChanged;
import com.android.os.AtomsProto.FlashlightStateChanged;
import com.android.os.AtomsProto.ForegroundServiceStateChanged;
import com.android.os.AtomsProto.GpsScanStateChanged;
@@ -73,19 +76,17 @@
public void testAppStartChanged() throws Exception {
final int atomTag = Atom.APP_START_CHANGED_FIELD_NUMBER;
- final String name = "testAppStart";
createAndUploadConfig(atomTag, false);
Thread.sleep(WAIT_TIME_SHORT);
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
+ runActivity("StatsdCtsForegroundActivity", "action", "action.sleep_top");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
AppStartChanged atom = data.get(0).getAtom().getAppStartChanged();
assertEquals("com.android.server.cts.device.statsd", atom.getPkgName());
- assertEquals(AppStartChanged.TransitionType.WARM, atom.getType());
assertEquals("com.android.server.cts.device.statsd.StatsdCtsForegroundActivity",
atom.getActivityName());
assertFalse(atom.getIsInstantApp());
@@ -601,4 +602,20 @@
assertStatesOccurred(stateSet, data, 1_000,
atom -> atom.getOverlayStateChanged().getState().getNumber());
}
+
+ public void testDropboxErrorChanged() throws Exception {
+ final int atomTag = Atom.DROPBOX_ERROR_CHANGED_FIELD_NUMBER;
+ createAndUploadConfig(atomTag, false);
+ Thread.sleep(WAIT_TIME_SHORT);
+
+ runActivity("StatsdCtsForegroundActivity", "action", "action.crash");
+
+ Thread.sleep(WAIT_TIME_SHORT);
+ // Sorted list of events in order in which they occurred.
+ List<EventMetricData> data = getEventMetricDataList();
+
+ DropboxErrorChanged atom = data.get(0).getAtom().getDropboxErrorChanged();
+ assertTrue(atom.getIsInstantApp() == 0);
+ assertEquals("data_app_crash", atom.getTag());
+ }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
index 16626a4..7e2947a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
@@ -456,7 +456,6 @@
@Test
public void testSave_submitButtonClicked() throws Throwable {
- if (mCompatMode) return; // TODO(b/73649008): implement it
saveTest(CommitType.SUBMIT_BUTTON_CLICKED);
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
index 8b677b4..a5a991b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
@@ -269,15 +269,12 @@
void clickLogin() {
Log.d(TAG, "clickLogin()");
if (mCompatMode) {
- // TODO(b/73649008): implement it
- throw new IllegalArgumentException("clickLogin() on compat mode not implemented yet");
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED, LOGIN_BUTTON_VIRTUAL_ID);
} else {
mAfm.notifyViewClicked(this, LOGIN_BUTTON_VIRTUAL_ID);
}
}
-
-
private Item getItem(int id) {
final Item item = mItems.get(id);
assertWithMessage("No item for id %s", id).that(item).isNotNull();
@@ -303,6 +300,15 @@
return node;
}
+ private AccessibilityNodeInfo onProvideAutofillCompatModeAccessibilityNodeInfoForLoginButton() {
+ final AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain();
+ node.setSource(this, LOGIN_BUTTON_VIRTUAL_ID);
+ node.setPackageName(getContext().getPackageName());
+ // TODO(b/72811561): ideally this button should be visible / drawn in the canvas and contain
+ // more properties like boundaries, class name, text etc...
+ return node;
+ }
+
static void assertHtmlInfo(ViewNode node) {
final String name = node.getText().toString();
final HtmlInfo info = node.getHtmlInfo();
@@ -341,12 +347,16 @@
@Override
public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
Log.d(TAG, "createAccessibilityNodeInfo(): id=" + virtualViewId);
- if (virtualViewId == AccessibilityNodeProvider.HOST_VIEW_ID) {
- return onProvideAutofillCompatModeAccessibilityNodeInfo();
+ switch (virtualViewId) {
+ case AccessibilityNodeProvider.HOST_VIEW_ID:
+ return onProvideAutofillCompatModeAccessibilityNodeInfo();
+ case LOGIN_BUTTON_VIRTUAL_ID:
+ return onProvideAutofillCompatModeAccessibilityNodeInfoForLoginButton();
+ default:
+ final Item item = getItem(virtualViewId);
+ return item.provideAccessibilityNodeInfo(VirtualContainerView.this,
+ getContext());
}
- final Item item = getItem(virtualViewId);
- return item.provideAccessibilityNodeInfo(VirtualContainerView.this,
- getContext());
}
@Override
@@ -377,6 +387,16 @@
mOverrideDispatchProvideAutofillStructure = flag;
}
+ private void sendAccessibilityEvent(int eventType, int virtualId) {
+ final AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setEventType(eventType);
+ event.setSource(VirtualContainerView.this, virtualId);
+ event.setEnabled(true);
+ event.setPackageName(getContext().getPackageName());
+ // TODO(b/72811561): recycle event?
+ getContext().getSystemService(AccessibilityManager.class).sendAccessibilityEvent(event);
+ }
+
private static int nextId;
final class Line {
@@ -405,14 +425,7 @@
}
if (mCompatMode) {
- final AccessibilityEvent event = AccessibilityEvent.obtain();
- event.setEventType(AccessibilityEvent.TYPE_VIEW_FOCUSED);
- event.setSource(VirtualContainerView.this, text.id);
- event.setEnabled(true);
- event.setPackageName(getContext().getPackageName());
- // TODO(b/72811561): recycle event?
- getContext().getSystemService(AccessibilityManager.class)
- .sendAccessibilityEvent(event);
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED, text.id);
return;
}
diff --git a/tests/tests/display/AndroidManifest.xml b/tests/tests/display/AndroidManifest.xml
index 7b1e371..5651f47 100644
--- a/tests/tests/display/AndroidManifest.xml
+++ b/tests/tests/display/AndroidManifest.xml
@@ -23,6 +23,8 @@
<!-- For testing brightness slider tracking. -->
<uses-permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
+ <!-- For testing pushing brightness curves. -->
+ <uses-permission android:name="android.permission.CONFIGURE_DISPLAY_BRIGHTNESS" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/tests/tests/display/src/android/display/cts/BrightnessTest.java b/tests/tests/display/src/android/display/cts/BrightnessTest.java
index 62617d6..65fcb6e 100644
--- a/tests/tests/display/src/android/display/cts/BrightnessTest.java
+++ b/tests/tests/display/src/android/display/cts/BrightnessTest.java
@@ -21,6 +21,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.hardware.display.BrightnessChangeEvent;
+import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.DisplayManager;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
@@ -49,8 +50,16 @@
InstrumentationRegistry.getContext().getSystemService(DisplayManager.class);
PowerManager pm =
InstrumentationRegistry.getContext().getSystemService(PowerManager.class);
+ // Fail early if screen isn't on as wakelock won't wake it up.
+ assertTrue(pm.isInteractive());
+
mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "BrightnessTest");
mWakeLock.acquire();
+
+ runShellCommand("pm revoke " + InstrumentationRegistry.getContext().getPackageName()
+ + " android.permission.CONFIGURE_DISPLAY_BRIGHTNESS");
+ runShellCommand("pm revoke " + InstrumentationRegistry.getContext().getPackageName()
+ + " android.permission.BRIGHTNESS_SLIDER_USAGE");
}
@Override
@@ -111,6 +120,151 @@
}
}
+ public void testNoTrackingForManualBrightness() throws IOException, InterruptedException {
+ if (!systemAppWithPermission("android.permission.BRIGHTNESS_SLIDER_USAGE",
+ InstrumentationRegistry.getContext())) {
+ // Don't run as there is no app that has permission to access slider usage.
+ return;
+ }
+ int previousBrightness = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS);
+ int previousBrightnessMode =
+ getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
+ try {
+ setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+ int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
+ assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, mode);
+
+ runShellCommand("pm grant " + InstrumentationRegistry.getContext().getPackageName()
+ + " android.permission.BRIGHTNESS_SLIDER_USAGE");
+
+ // Setup and remember some initial state.
+ recordSliderEvents();
+ setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 20);
+ assertTrue(getNewEvents().isEmpty());
+
+ // Then change the brightness
+ setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 80);
+ Thread.sleep(200);
+ // There shouldn't be any events.
+ assertTrue(getNewEvents().isEmpty());
+ } finally {
+ setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, previousBrightness);
+ setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, previousBrightnessMode);
+ }
+ }
+
+ public void testSliderUsagePermission() throws IOException, InterruptedException {
+ runShellCommand("pm revoke " + InstrumentationRegistry.getContext().getPackageName()
+ + " android.permission.BRIGHTNESS_SLIDER_USAGE");
+
+ try {
+ mDisplayManager.getBrightnessEvents();
+ } catch (SecurityException e) {
+ // Expected
+ return;
+ }
+ fail();
+ }
+
+ public void testConfigureBrightnessPermission() throws IOException, InterruptedException {
+ runShellCommand("pm revoke " + InstrumentationRegistry.getContext().getPackageName()
+ + " android.permission.CONFIGURE_DISPLAY_BRIGHTNESS");
+
+ BrightnessConfiguration config =
+ new BrightnessConfiguration.Builder(
+ new float[]{0.0f, 1000.0f},new float[]{20.0f, 500.0f})
+ .setDescription("some test").build();
+
+ try {
+ mDisplayManager.setBrightnessConfiguration(config);
+ } catch (SecurityException e) {
+ // Expected
+ return;
+ }
+ fail();
+ }
+
+ public void testPushSimpleCurves() throws IOException, InterruptedException {
+ if (!systemAppWithPermission("android.permission.CONFIGURE_DISPLAY_BRIGHTNESS",
+ InstrumentationRegistry.getContext())) {
+ // Don't run as there is no app that has permission to push curves.
+ return;
+ }
+ runShellCommand("pm grant " + InstrumentationRegistry.getContext().getPackageName()
+ + " android.permission.CONFIGURE_DISPLAY_BRIGHTNESS");
+
+ BrightnessConfiguration config =
+ new BrightnessConfiguration.Builder(
+ new float[]{0.0f, 1000.0f},new float[]{20.0f, 500.0f})
+ .setDescription("some test").build();
+ mDisplayManager.setBrightnessConfiguration(config);
+ mDisplayManager.setBrightnessConfiguration(null);
+ }
+
+ public void testSliderEventsReflectCurves() throws IOException, InterruptedException {
+ if (!systemAppWithPermission("android.permission.BRIGHTNESS_SLIDER_USAGE",
+ InstrumentationRegistry.getContext())) {
+ // Don't run as there is no app that has permission to access slider usage.
+ return;
+ }
+ if (!systemAppWithPermission("android.permission.CONFIGURE_DISPLAY_BRIGHTNESS",
+ InstrumentationRegistry.getContext())) {
+ // Don't run as there is no app that has permission to push curves.
+ return;
+ }
+
+ BrightnessConfiguration config =
+ new BrightnessConfiguration.Builder(
+ new float[]{0.0f, 10000.0f},new float[]{15.0f, 400.0f})
+ .setDescription("model:8").build();
+
+ int previousBrightness = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS);
+ int previousBrightnessMode =
+ getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
+ try {
+ setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
+ assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC, mode);
+
+ runShellCommand("pm grant " + InstrumentationRegistry.getContext().getPackageName()
+ + " android.permission.BRIGHTNESS_SLIDER_USAGE");
+ runShellCommand("pm grant " + InstrumentationRegistry.getContext().getPackageName()
+ + " android.permission.CONFIGURE_DISPLAY_BRIGHTNESS");
+
+ // Setup and remember some initial state.
+ recordSliderEvents();
+ setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 20);
+ getNewEvents(1);
+
+ // Update brightness while we have a custom curve.
+ mDisplayManager.setBrightnessConfiguration(config);
+ setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 60);
+
+ // Check we got a slider event for the change.
+ List<BrightnessChangeEvent> newEvents = getNewEvents(1);
+ assertEquals(1, newEvents.size());
+ BrightnessChangeEvent firstEvent = newEvents.get(0);
+ assertValidLuxData(firstEvent);
+ assertFalse(firstEvent.isDefaultBrightnessConfig);
+
+ // Update brightness again now with default curve.
+ mDisplayManager.setBrightnessConfiguration(null);
+ setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 200);
+
+ // Check we get a second slider event.
+ newEvents = getNewEvents(1);
+ assertEquals(1, newEvents.size());
+ BrightnessChangeEvent secondEvent = newEvents.get(0);
+ assertValidLuxData(secondEvent);
+ assertTrue(secondEvent.isDefaultBrightnessConfig);
+ } finally {
+ setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, previousBrightness);
+ setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, previousBrightnessMode);
+ }
+ }
+
private void assertValidLuxData(BrightnessChangeEvent event) {
assertNotNull(event.luxTimestamps);
assertNotNull(event.luxValues);
@@ -142,16 +296,22 @@
if (i != 0) {
Thread.sleep(100);
}
- List<BrightnessChangeEvent> events = mDisplayManager.getBrightnessEvents();
- for (BrightnessChangeEvent event : events) {
- if (!mLastReadEvents.containsKey(event.timeStamp)) {
- newEvents.add(event);
- }
+ newEvents.addAll(getNewEvents());
+ }
+ return newEvents;
+ }
+
+ private List<BrightnessChangeEvent> getNewEvents() {
+ List<BrightnessChangeEvent> newEvents = new ArrayList<>();
+ List<BrightnessChangeEvent> events = mDisplayManager.getBrightnessEvents();
+ for (BrightnessChangeEvent event : events) {
+ if (!mLastReadEvents.containsKey(event.timeStamp)) {
+ newEvents.add(event);
}
- mLastReadEvents = new HashMap<>();
- for (BrightnessChangeEvent event : events) {
- mLastReadEvents.put(event.timeStamp, event);
- }
+ }
+ mLastReadEvents = new HashMap<>();
+ for (BrightnessChangeEvent event : events) {
+ mLastReadEvents.put(event.timeStamp, event);
}
return newEvents;
}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
index 0d2fd02..fe52159 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
@@ -42,6 +42,7 @@
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
import android.widget.ImageView;
import com.android.compatibility.common.util.BitmapUtils;
@@ -567,6 +568,51 @@
assertTrue(drawable.isAutoMirrored());
}
+ private void drawAndCompare(Bitmap expected, Drawable drawable) {
+ Bitmap test = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+ drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(test);
+ drawable.draw(canvas);
+ BitmapUtils.compareBitmaps(expected, test);
+ }
+
+ @Test
+ public void testAutoMirroredDrawing() {
+ AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID);
+ assertFalse(drawable.isAutoMirrored());
+
+ final int width = drawable.getIntrinsicWidth();
+ final int height = drawable.getIntrinsicHeight();
+ Bitmap normal = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ {
+ Canvas canvas = new Canvas(normal);
+ drawable.draw(canvas);
+ }
+
+ Bitmap flipped = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ {
+ Canvas canvas = new Canvas(flipped);
+ canvas.translate(width, 0);
+ canvas.scale(-1, 1);
+ drawable.draw(canvas);
+ }
+
+ for (int i = 0; i < width; ++i) {
+ for (int j = 0; j < height; ++j) {
+ assertEquals(normal.getPixel(i, j), flipped.getPixel(width - 1 - i, j));
+ }
+ }
+
+ drawable.setAutoMirrored(true);
+ drawAndCompare(normal, drawable);
+
+ drawable.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
+ drawAndCompare(flipped, drawable);
+
+ drawable.setAutoMirrored(false);
+ drawAndCompare(normal, drawable);
+ }
+
@Test
public void testRepeatCountFromXml() throws XmlPullParserException, IOException {
XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable_loop_count);
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
index 7f588bd..94f6772 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
@@ -115,6 +115,40 @@
}
@Test
+ public void testLayerPaintAlphaChanged() {
+ final CountDownLatch fence = new CountDownLatch(1);
+ createTest()
+ .addLayout(R.layout.frame_layout, view -> {
+ FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout);
+ View child = new View(view.getContext());
+ child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ child.setAlpha(0.0f);
+ // add rendering content
+ child.setBackgroundColor(Color.RED);
+ root.addView(child, new FrameLayout.LayoutParams(TEST_WIDTH, TEST_HEIGHT,
+ Gravity.TOP | Gravity.LEFT));
+
+ // Post non-zero alpha a few frames in, so that the initial layer draw completes.
+ root.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ int mDrawCount = 0;
+ @Override
+ public boolean onPreDraw() {
+ if (mDrawCount++ == 5) {
+ root.getChildAt(0).setAlpha(1.00f);
+ root.getViewTreeObserver().removeOnPreDrawListener(this);
+ root.post(fence::countDown);
+ } else {
+ root.postInvalidate();
+ }
+ return true;
+ }
+ });
+ }, true, fence)
+ .runWithVerifier(new ColorVerifier(Color.RED));
+ }
+
+ @Test
public void testLayerPaintColorFilter() {
// Red, fully desaturated. Note that it's not 255/3 in each channel.
// See ColorMatrix#setSaturation()
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewPrecomputedTextTest.java b/tests/tests/widget/src/android/widget/cts/TextViewPrecomputedTextTest.java
new file mode 100644
index 0000000..282ee5f
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/TextViewPrecomputedTextTest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2018 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.widget.cts;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.text.Layout;
+import android.text.PrecomputedText;
+import android.text.PrecomputedText.Params;
+import android.text.PrecomputedText.Params.Builder;
+import android.text.TextDirectionHeuristic;
+import android.text.TextDirectionHeuristics;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.util.Pair;
+import android.widget.TextView;
+
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Locale;
+
+/**
+ * Tests for TextView with precomputed text.
+ */
+@RunWith(Parameterized.class)
+public class TextViewPrecomputedTextTest {
+ private static final String TEXT = "Hello, World!";
+
+ @Parameterized.Parameter(0)
+ public boolean differentTextSize;
+ @Parameterized.Parameter(1)
+ public boolean differentScaleX;
+ @Parameterized.Parameter(2)
+ public boolean differentSkewX;
+ @Parameterized.Parameter(3)
+ public boolean differentLetterSpacing;
+ @Parameterized.Parameter(4)
+ public boolean differentTextLocale;
+ @Parameterized.Parameter(5)
+ public boolean differentTypeface;
+ @Parameterized.Parameter(6)
+ public boolean differentFontVariationSettings;
+ @Parameterized.Parameter(7)
+ public boolean differentElegantTextHeight;
+ @Parameterized.Parameter(8)
+ public boolean differentBreakStrategy;
+ @Parameterized.Parameter(9)
+ public boolean differentHyphenationFrequency;
+ @Parameterized.Parameter(10)
+ public boolean differentTextDir;
+
+ // text size from the default value.
+ private Pair<Params, String[]> makeDifferentParams(Params params) {
+ final TextPaint paint = new TextPaint(params.getTextPaint());
+ ArrayList<String> differenceList = new ArrayList();
+
+ if (differentTextSize) {
+ paint.setTextSize(paint.getTextSize() * 2.0f + 1.0f);
+ differenceList.add("Text Size");
+ }
+ if (differentScaleX) {
+ paint.setTextScaleX(paint.getTextScaleX() * 2.0f + 1.0f);
+ differenceList.add("Text Scale X");
+ }
+ if (differentSkewX) {
+ paint.setTextSkewX(paint.getTextSkewX() * 2.0f + 1.0f);
+ differenceList.add("Text Skew X");
+ }
+ if (differentLetterSpacing) {
+ paint.setLetterSpacing(paint.getLetterSpacing() * 2.0f + 1.0f);
+ differenceList.add("Letter Spacing");
+ }
+ if (differentTextLocale) {
+ paint.setTextLocale(Locale.US.equals(paint.getTextLocale()) ? Locale.JAPAN : Locale.US);
+ }
+ if (differentTypeface) {
+ final Typeface tf = paint.getTypeface();
+ if (tf == null || tf == Typeface.DEFAULT) {
+ paint.setTypeface(Typeface.SERIF);
+ } else {
+ paint.setTypeface(Typeface.DEFAULT);
+ }
+ differenceList.add("Typeface");
+ }
+ if (differentFontVariationSettings) {
+ final String wght = "'wght' 700";
+ final String wdth = "'wdth' 100";
+
+ final String varSettings = paint.getFontVariationSettings();
+ if (varSettings == null || varSettings.equals(wght)) {
+ paint.setFontVariationSettings(wdth);
+ } else {
+ paint.setFontVariationSettings(wght);
+ }
+ differenceList.add("Font variation settings");
+ }
+ if (differentElegantTextHeight) {
+ paint.setElegantTextHeight(!paint.isElegantTextHeight());
+ differenceList.add("Elegant Text Height");
+ }
+
+ int strategy = params.getBreakStrategy();
+ if (differentBreakStrategy) {
+ strategy = strategy == Layout.BREAK_STRATEGY_SIMPLE
+ ? Layout.BREAK_STRATEGY_HIGH_QUALITY : Layout.BREAK_STRATEGY_SIMPLE;
+ differenceList.add("Break strategy");
+ }
+
+ int hyFreq = params.getHyphenationFrequency();
+ if (differentHyphenationFrequency) {
+ hyFreq = hyFreq == Layout.HYPHENATION_FREQUENCY_NONE
+ ? Layout.HYPHENATION_FREQUENCY_FULL : Layout.HYPHENATION_FREQUENCY_NONE;
+ differenceList.add("Hyphenation Frequency");
+ }
+
+ TextDirectionHeuristic dir = params.getTextDirection();
+ if (differentTextDir) {
+ dir = dir == TextDirectionHeuristics.LTR
+ ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR;
+ differenceList.add("Text Direction");
+ }
+
+ final Params outParams = new Builder(paint).setBreakStrategy(strategy)
+ .setHyphenationFrequency(hyFreq).setTextDirection(dir).build();
+ return new Pair(outParams, differenceList.toArray(new String[differenceList.size()]));
+ }
+
+ private static Context getContext() {
+ return InstrumentationRegistry.getInstrumentation().getTargetContext();
+ }
+
+ @Parameterized.Parameters
+ public static Collection<Object[]> getParameters() {
+ ArrayList<Object[]> allParams = new ArrayList<>();
+
+ // Compute the powerset except for all false case.
+ int allParameterCount = 11;
+ for (int bits = 1; bits < (1 << allParameterCount); ++bits) {
+ Object[] param = new Object[allParameterCount];
+ for (int j = 0; j < allParameterCount; ++j) {
+ param[j] = new Boolean((bits & (1 << j)) != 0);
+ }
+ allParams.add(param);
+ }
+ return allParams;
+ }
+
+ @SmallTest
+ @Test
+ public void setText() {
+ final TextView tv = new TextView(getContext());
+ final Params tvParams = tv.getTextMetricsParams();
+ final Pair<Params, String[]> testConfig = makeDifferentParams(tvParams);
+ final Params pctParams = testConfig.first;
+
+ final PrecomputedText pct = PrecomputedText.create(TEXT, pctParams);
+ try {
+ tv.setText(pct);
+ fail("Test Case: {" + TextUtils.join(",", testConfig.second) + "}, "
+ + tvParams.toString() + " vs " + pctParams.toString());
+ } catch (IllegalArgumentException e) {
+ // pass
+ }
+
+ tv.setTextMetricsParams(pctParams);
+ tv.setText(pct);
+ }
+}