summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2023-10-05 15:40:35 -0700
committerXin Li <delphij@google.com>2023-10-05 15:40:35 -0700
commit65bef66686f9ea478626d815d565653cb8802813 (patch)
tree1e55e142ec3bec78e08830866c43f6d09d0ab7f6
parentbcc6db40f684f8507798d0ec0795c9b9c1951dbe (diff)
parent6a3b93c3539f8774653d5a0b08890ae8b2cfd72b (diff)
downloadsystemui-65bef66686f9ea478626d815d565653cb8802813.tar.gz
Merge Android 14
Bug: 298295554 Merged-In: If8dc1d32d7fd02e41c12cfb1571374c1e974e87c Change-Id: Ibab276d3fe90ca44dd682f5c5912ba958fbec336
-rw-r--r--animationlib/Android.bp59
-rw-r--r--animationlib/AndroidManifest.xml (renamed from searchuilib/AndroidManifest.xml)4
-rw-r--r--animationlib/TEST_MAPPING7
-rw-r--r--animationlib/build.gradle46
-rw-r--r--animationlib/src/com/android/app/animation/Interpolators.java211
-rw-r--r--animationlib/src/com/android/app/animation/InterpolatorsAndroidX.java218
-rw-r--r--animationlib/src/com/android/app/animation/MathUtils.java27
-rw-r--r--animationlib/tests/AndroidManifest.xml25
-rw-r--r--animationlib/tests/com/android/app/animation/InterpolatorsAndroidXTest.kt54
-rw-r--r--iconloaderlib/build.gradle6
-rw-r--r--iconloaderlib/res/drawable/ic_clone_app_badge_themed.xml43
-rw-r--r--iconloaderlib/res/drawable/ic_instant_app_badge.xml9
-rw-r--r--iconloaderlib/res/drawable/ic_instant_app_badge_themed.xml30
-rw-r--r--iconloaderlib/res/drawable/ic_work_app_badge_themed.xml39
-rw-r--r--iconloaderlib/res/values-night-v31/colors.xml24
-rw-r--r--iconloaderlib/res/values-night/colors.xml24
-rw-r--r--iconloaderlib/res/values-v31/colors.xml24
-rw-r--r--iconloaderlib/res/values/colors.xml4
-rw-r--r--iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java13
-rw-r--r--iconloaderlib/src/com/android/launcher3/icons/BubbleIconFactory.java160
-rw-r--r--iconloaderlib/src/com/android/launcher3/icons/ThemedIconDrawable.java10
-rw-r--r--motiontoollib/build.gradle18
-rw-r--r--motiontoollib/src/com/android/app/motiontool/DdmHandleMotionTool.kt4
-rw-r--r--motiontoollib/src/com/android/app/motiontool/MotionToolManager.kt59
-rw-r--r--motiontoollib/src/com/android/app/motiontool/proto/motion_tool.proto4
-rw-r--r--motiontoollib/tests/AndroidManifest.xml6
-rw-r--r--motiontoollib/tests/com/android/app/motiontool/DdmHandleMotionToolTest.kt11
-rw-r--r--motiontoollib/tests/com/android/app/motiontool/MotionToolManagerTest.kt13
-rw-r--r--searchuilib/.gitignore13
-rw-r--r--searchuilib/Android.bp30
-rw-r--r--searchuilib/build.gradle37
-rw-r--r--searchuilib/src/com/android/app/search/LayoutType.java110
-rw-r--r--searchuilib/src/com/android/app/search/QueryExtras.java42
-rw-r--r--searchuilib/src/com/android/app/search/ResultType.java92
-rw-r--r--searchuilib/src/com/android/app/search/SearchActionExtras.java40
-rw-r--r--searchuilib/src/com/android/app/search/SearchTargetConverter.java69
-rw-r--r--searchuilib/src/com/android/app/search/SearchTargetEventHelper.java106
-rw-r--r--searchuilib/src/com/android/app/search/SearchTargetExtras.java228
-rw-r--r--searchuilib/src/com/android/app/search/SearchTargetGenerator.java44
-rw-r--r--viewcapturelib/build.gradle15
-rw-r--r--viewcapturelib/src/com/android/app/viewcapture/SettingsAwareViewCapture.kt22
-rw-r--r--viewcapturelib/src/com/android/app/viewcapture/SimpleViewCapture.kt8
-rw-r--r--viewcapturelib/src/com/android/app/viewcapture/ViewCapture.java140
-rw-r--r--viewcapturelib/src/com/android/app/viewcapture/proto/view_capture.proto10
-rw-r--r--viewcapturelib/tests/AndroidManifest.xml9
-rw-r--r--viewcapturelib/tests/com/android/app/viewcapture/SettingsAwareViewCaptureTest.kt30
-rw-r--r--viewcapturelib/tests/com/android/app/viewcapture/ViewCaptureTest.kt26
47 files changed, 1227 insertions, 996 deletions
diff --git a/animationlib/Android.bp b/animationlib/Android.bp
new file mode 100644
index 0000000..d67a5de
--- /dev/null
+++ b/animationlib/Android.bp
@@ -0,0 +1,59 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+
+android_library {
+ name: "animationlib",
+ manifest: "AndroidManifest.xml",
+ sdk_version: "system_current",
+ min_sdk_version: "26",
+ static_libs: [
+ "androidx.core_core-animation",
+ "androidx.core_core-ktx",
+ "androidx.annotation_annotation",
+ ],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt"
+ ],
+ kotlincflags: ["-Xjvm-default=all"],
+ // This library is meant to access only public APIs
+ // do not flip this flag to true
+ platform_apis: false
+}
+
+android_test {
+ name: "animationlib_tests",
+ manifest: "tests/AndroidManifest.xml",
+
+ static_libs: [
+ "animationlib",
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "testables",
+ ],
+ libs: [
+ "android.test.base",
+ ],
+ srcs: [
+ "**/*.java",
+ "**/*.kt"
+ ],
+ kotlincflags: ["-Xjvm-default=all"],
+ test_suites: ["general-tests"],
+}
diff --git a/searchuilib/AndroidManifest.xml b/animationlib/AndroidManifest.xml
index 6c6c5f6..b05fb11 100644
--- a/searchuilib/AndroidManifest.xml
+++ b/animationlib/AndroidManifest.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2020 The Android Open Source Project
+ Copyright (C) 2023 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.
@@ -16,5 +16,5 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.app.search">
+ package="com.android.app.animation">
</manifest>
diff --git a/animationlib/TEST_MAPPING b/animationlib/TEST_MAPPING
new file mode 100644
index 0000000..4fd6f09
--- /dev/null
+++ b/animationlib/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "animationlib_tests"
+ }
+ ]
+}
diff --git a/animationlib/build.gradle b/animationlib/build.gradle
new file mode 100644
index 0000000..f9c4485
--- /dev/null
+++ b/animationlib/build.gradle
@@ -0,0 +1,46 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+ namespace = "com.android.app.animation"
+ testNamespace = "com.android.app.animation.tests"
+ defaultConfig {
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ sourceSets {
+ main {
+ java.srcDirs = ['src']
+ manifest.srcFile 'AndroidManifest.xml'
+ }
+ androidTest {
+ java.srcDirs = ["tests"]
+ }
+ }
+ compileSdk 33
+
+ defaultConfig {
+ minSdk 33
+ targetSdk 33
+ }
+
+ lintOptions {
+ abortOnError false
+ }
+ tasks.lint.enabled = false
+ tasks.withType(JavaCompile) {
+ options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ freeCompilerArgs = ["-Xjvm-default=all"]
+ }
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0"
+ implementation "androidx.core:core-animation:1.0.0-alpha02"
+ implementation "androidx.core:core-ktx:1.9.0"
+ androidTestImplementation "androidx.test.ext:junit:1.1.3"
+ androidTestImplementation "androidx.test:rules:1.4.0"
+}
diff --git a/animationlib/src/com/android/app/animation/Interpolators.java b/animationlib/src/com/android/app/animation/Interpolators.java
new file mode 100644
index 0000000..0f3776c
--- /dev/null
+++ b/animationlib/src/com/android/app/animation/Interpolators.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2023 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 com.android.app.animation;
+
+import android.graphics.Path;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.BounceInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.PathInterpolator;
+
+/**
+ * Utility class to receive interpolators from.
+ *
+ * Make sure that changes made to this class are also reflected in {@link InterpolatorsAndroidX}.
+ * Please consider using the androidx dependencies featuring better testability altogether.
+ */
+public class Interpolators {
+
+ /*
+ * ============================================================================================
+ * Emphasized interpolators.
+ * ============================================================================================
+ */
+
+ /**
+ * The default emphasized interpolator. Used for hero / emphasized movement of content.
+ */
+ public static final Interpolator EMPHASIZED = createEmphasizedInterpolator();
+
+ /**
+ * The accelerated emphasized interpolator. Used for hero / emphasized movement of content that
+ * is disappearing e.g. when moving off screen.
+ */
+ public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator(
+ 0.3f, 0f, 0.8f, 0.15f);
+
+ /**
+ * The decelerating emphasized interpolator. Used for hero / emphasized movement of content that
+ * is appearing e.g. when coming from off screen
+ */
+ public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
+ 0.05f, 0.7f, 0.1f, 1f);
+
+
+ /*
+ * ============================================================================================
+ * Standard interpolators.
+ * ============================================================================================
+ */
+
+ /**
+ * The standard interpolator that should be used on every normal animation
+ */
+ public static final Interpolator STANDARD = new PathInterpolator(
+ 0.2f, 0f, 0f, 1f);
+
+ /**
+ * The standard accelerating interpolator that should be used on every regular movement of
+ * content that is disappearing e.g. when moving off screen.
+ */
+ public static final Interpolator STANDARD_ACCELERATE = new PathInterpolator(
+ 0.3f, 0f, 1f, 1f);
+
+ /**
+ * The standard decelerating interpolator that should be used on every regular movement of
+ * content that is appearing e.g. when coming from off screen.
+ */
+ public static final Interpolator STANDARD_DECELERATE = new PathInterpolator(
+ 0f, 0f, 0f, 1f);
+
+ /*
+ * ============================================================================================
+ * Legacy
+ * ============================================================================================
+ */
+
+ /**
+ * The default legacy interpolator as defined in Material 1. Also known as FAST_OUT_SLOW_IN.
+ */
+ public static final Interpolator LEGACY = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+
+ /**
+ * The default legacy accelerating interpolator as defined in Material 1.
+ * Also known as FAST_OUT_LINEAR_IN.
+ */
+ public static final Interpolator LEGACY_ACCELERATE = new PathInterpolator(0.4f, 0f, 1f, 1f);
+
+ /**
+ * The default legacy decelerating interpolator as defined in Material 1.
+ * Also known as LINEAR_OUT_SLOW_IN.
+ */
+ public static final Interpolator LEGACY_DECELERATE = new PathInterpolator(0f, 0f, 0.2f, 1f);
+
+ /**
+ * Linear interpolator. Often used if the interpolator is for different properties who need
+ * different interpolations.
+ */
+ public static final Interpolator LINEAR = new LinearInterpolator();
+
+ /*
+ * ============================================================================================
+ * Custom interpolators
+ * ============================================================================================
+ */
+
+ public static final Interpolator FAST_OUT_SLOW_IN = LEGACY;
+ public static final Interpolator FAST_OUT_LINEAR_IN = LEGACY_ACCELERATE;
+ public static final Interpolator LINEAR_OUT_SLOW_IN = LEGACY_DECELERATE;
+
+ /**
+ * Like {@link #FAST_OUT_SLOW_IN}, but used in case the animation is played in reverse (i.e. t
+ * goes from 1 to 0 instead of 0 to 1).
+ */
+ public static final Interpolator FAST_OUT_SLOW_IN_REVERSE =
+ new PathInterpolator(0.8f, 0f, 0.6f, 1f);
+ public static final Interpolator SLOW_OUT_LINEAR_IN = new PathInterpolator(0.8f, 0f, 1f, 1f);
+ public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
+ public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
+ public static final Interpolator ACCELERATE = new AccelerateInterpolator();
+ public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();
+ public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f);
+ public static final Interpolator CUSTOM_40_40 = new PathInterpolator(0.4f, 0f, 0.6f, 1f);
+ public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f);
+ public static final Interpolator ICON_OVERSHOT_LESS = new PathInterpolator(0.4f, 0f, 0.2f,
+ 1.1f);
+ public static final Interpolator PANEL_CLOSE_ACCELERATED = new PathInterpolator(0.3f, 0, 0.5f,
+ 1);
+ public static final Interpolator BOUNCE = new BounceInterpolator();
+ /**
+ * For state transitions on the control panel that lives in GlobalActions.
+ */
+ public static final Interpolator CONTROL_STATE = new PathInterpolator(0.4f, 0f, 0.2f,
+ 1.0f);
+
+ /**
+ * Interpolator to be used when animating a move based on a click. Pair with enough duration.
+ */
+ public static final Interpolator TOUCH_RESPONSE =
+ new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+
+ /**
+ * Like {@link #TOUCH_RESPONSE}, but used in case the animation is played in reverse (i.e. t
+ * goes from 1 to 0 instead of 0 to 1).
+ */
+ public static final Interpolator TOUCH_RESPONSE_REVERSE =
+ new PathInterpolator(0.9f, 0f, 0.7f, 1f);
+
+ /*
+ * ============================================================================================
+ * Functions / Utilities
+ * ============================================================================================
+ */
+
+ /**
+ * Calculate the amount of overshoot using an exponential falloff function with desired
+ * properties, where the overshoot smoothly transitions at the 1.0f boundary into the
+ * overshoot, retaining its acceleration.
+ *
+ * @param progress a progress value going from 0 to 1
+ * @param overshootAmount the amount > 0 of overshoot desired. A value of 0.1 means the max
+ * value of the overall progress will be at 1.1.
+ * @param overshootStart the point in (0,1] where the result should reach 1
+ * @return the interpolated overshoot
+ */
+ public static float getOvershootInterpolation(float progress, float overshootAmount,
+ float overshootStart) {
+ if (overshootAmount == 0.0f || overshootStart == 0.0f) {
+ throw new IllegalArgumentException("Invalid values for overshoot");
+ }
+ float b = MathUtils.log((overshootAmount + 1) / (overshootAmount)) / overshootStart;
+ return MathUtils.max(0.0f,
+ (float) (1.0f - Math.exp(-b * progress)) * (overshootAmount + 1.0f));
+ }
+
+ /**
+ * Similar to {@link #getOvershootInterpolation(float, float, float)} but the overshoot
+ * starts immediately here, instead of first having a section of non-overshooting
+ *
+ * @param progress a progress value going from 0 to 1
+ */
+ public static float getOvershootInterpolation(float progress) {
+ return MathUtils.max(0.0f, (float) (1.0f - Math.exp(-4 * progress)));
+ }
+
+ // Create the default emphasized interpolator
+ private static PathInterpolator createEmphasizedInterpolator() {
+ Path path = new Path();
+ // Doing the same as fast_out_extra_slow_in
+ path.moveTo(0f, 0f);
+ path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f);
+ path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f);
+ return new PathInterpolator(path);
+ }
+} \ No newline at end of file
diff --git a/animationlib/src/com/android/app/animation/InterpolatorsAndroidX.java b/animationlib/src/com/android/app/animation/InterpolatorsAndroidX.java
new file mode 100644
index 0000000..7142f54
--- /dev/null
+++ b/animationlib/src/com/android/app/animation/InterpolatorsAndroidX.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2023 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 com.android.app.animation;
+
+import android.graphics.Path;
+
+import androidx.core.animation.AccelerateDecelerateInterpolator;
+import androidx.core.animation.AccelerateInterpolator;
+import androidx.core.animation.BounceInterpolator;
+import androidx.core.animation.DecelerateInterpolator;
+import androidx.core.animation.Interpolator;
+import androidx.core.animation.LinearInterpolator;
+import androidx.core.animation.PathInterpolator;
+
+/**
+ * Utility class to receive interpolators from. (androidx compatible version)
+ *
+ * This is the androidx compatible version of {@link Interpolators}. Make sure that changes made to
+ * this class are also reflected in {@link Interpolators}.
+ *
+ * Using the androidx versions of {@link androidx.core.animation.ValueAnimator} or
+ * {@link androidx.core.animation.ObjectAnimator} improves animation testability. This file provides
+ * the androidx compatible versions of the interpolators defined in {@link Interpolators}.
+ * AnimatorTestRule can be used in Tests to manipulate the animation under test (e.g. artificially
+ * advancing the time).
+ */
+public class InterpolatorsAndroidX {
+
+ /*
+ * ============================================================================================
+ * Emphasized interpolators.
+ * ============================================================================================
+ */
+
+ /**
+ * The default emphasized interpolator. Used for hero / emphasized movement of content.
+ */
+ public static final Interpolator EMPHASIZED = createEmphasizedInterpolator();
+
+ /**
+ * The accelerated emphasized interpolator. Used for hero / emphasized movement of content that
+ * is disappearing e.g. when moving off screen.
+ */
+ public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator(
+ 0.3f, 0f, 0.8f, 0.15f);
+
+ /**
+ * The decelerating emphasized interpolator. Used for hero / emphasized movement of content that
+ * is appearing e.g. when coming from off screen
+ */
+ public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
+ 0.05f, 0.7f, 0.1f, 1f);
+
+
+ /*
+ * ============================================================================================
+ * Standard interpolators.
+ * ============================================================================================
+ */
+
+ /**
+ * The standard interpolator that should be used on every normal animation
+ */
+ public static final Interpolator STANDARD = new PathInterpolator(
+ 0.2f, 0f, 0f, 1f);
+
+ /**
+ * The standard accelerating interpolator that should be used on every regular movement of
+ * content that is disappearing e.g. when moving off screen.
+ */
+ public static final Interpolator STANDARD_ACCELERATE = new PathInterpolator(
+ 0.3f, 0f, 1f, 1f);
+
+ /**
+ * The standard decelerating interpolator that should be used on every regular movement of
+ * content that is appearing e.g. when coming from off screen.
+ */
+ public static final Interpolator STANDARD_DECELERATE = new PathInterpolator(
+ 0f, 0f, 0f, 1f);
+
+ /*
+ * ============================================================================================
+ * Legacy
+ * ============================================================================================
+ */
+
+ /**
+ * The default legacy interpolator as defined in Material 1. Also known as FAST_OUT_SLOW_IN.
+ */
+ public static final Interpolator LEGACY = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+
+ /**
+ * The default legacy accelerating interpolator as defined in Material 1.
+ * Also known as FAST_OUT_LINEAR_IN.
+ */
+ public static final Interpolator LEGACY_ACCELERATE = new PathInterpolator(0.4f, 0f, 1f, 1f);
+
+ /**
+ * The default legacy decelerating interpolator as defined in Material 1.
+ * Also known as LINEAR_OUT_SLOW_IN.
+ */
+ public static final Interpolator LEGACY_DECELERATE = new PathInterpolator(0f, 0f, 0.2f, 1f);
+
+ /**
+ * Linear interpolator. Often used if the interpolator is for different properties who need
+ * different interpolations.
+ */
+ public static final Interpolator LINEAR = new LinearInterpolator();
+
+ /*
+ * ============================================================================================
+ * Custom interpolators
+ * ============================================================================================
+ */
+
+ public static final Interpolator FAST_OUT_SLOW_IN = LEGACY;
+ public static final Interpolator FAST_OUT_LINEAR_IN = LEGACY_ACCELERATE;
+ public static final Interpolator LINEAR_OUT_SLOW_IN = LEGACY_DECELERATE;
+
+ /**
+ * Like {@link #FAST_OUT_SLOW_IN}, but used in case the animation is played in reverse (i.e. t
+ * goes from 1 to 0 instead of 0 to 1).
+ */
+ public static final Interpolator FAST_OUT_SLOW_IN_REVERSE =
+ new PathInterpolator(0.8f, 0f, 0.6f, 1f);
+ public static final Interpolator SLOW_OUT_LINEAR_IN = new PathInterpolator(0.8f, 0f, 1f, 1f);
+ public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
+ public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
+ public static final Interpolator ACCELERATE = new AccelerateInterpolator();
+ public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();
+ public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f);
+ public static final Interpolator CUSTOM_40_40 = new PathInterpolator(0.4f, 0f, 0.6f, 1f);
+ public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f);
+ public static final Interpolator ICON_OVERSHOT_LESS = new PathInterpolator(0.4f, 0f, 0.2f,
+ 1.1f);
+ public static final Interpolator PANEL_CLOSE_ACCELERATED = new PathInterpolator(0.3f, 0, 0.5f,
+ 1);
+ public static final Interpolator BOUNCE = new BounceInterpolator();
+ /**
+ * For state transitions on the control panel that lives in GlobalActions.
+ */
+ public static final Interpolator CONTROL_STATE = new PathInterpolator(0.4f, 0f, 0.2f,
+ 1.0f);
+
+ /**
+ * Interpolator to be used when animating a move based on a click. Pair with enough duration.
+ */
+ public static final Interpolator TOUCH_RESPONSE =
+ new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+
+ /**
+ * Like {@link #TOUCH_RESPONSE}, but used in case the animation is played in reverse (i.e. t
+ * goes from 1 to 0 instead of 0 to 1).
+ */
+ public static final Interpolator TOUCH_RESPONSE_REVERSE =
+ new PathInterpolator(0.9f, 0f, 0.7f, 1f);
+
+ /*
+ * ============================================================================================
+ * Functions / Utilities
+ * ============================================================================================
+ */
+
+ /**
+ * Calculate the amount of overshoot using an exponential falloff function with desired
+ * properties, where the overshoot smoothly transitions at the 1.0f boundary into the
+ * overshoot, retaining its acceleration.
+ *
+ * @param progress a progress value going from 0 to 1
+ * @param overshootAmount the amount > 0 of overshoot desired. A value of 0.1 means the max
+ * value of the overall progress will be at 1.1.
+ * @param overshootStart the point in (0,1] where the result should reach 1
+ * @return the interpolated overshoot
+ */
+ public static float getOvershootInterpolation(float progress, float overshootAmount,
+ float overshootStart) {
+ if (overshootAmount == 0.0f || overshootStart == 0.0f) {
+ throw new IllegalArgumentException("Invalid values for overshoot");
+ }
+ float b = MathUtils.log((overshootAmount + 1) / (overshootAmount)) / overshootStart;
+ return MathUtils.max(0.0f,
+ (float) (1.0f - Math.exp(-b * progress)) * (overshootAmount + 1.0f));
+ }
+
+ /**
+ * Similar to {@link #getOvershootInterpolation(float, float, float)} but the overshoot
+ * starts immediately here, instead of first having a section of non-overshooting
+ *
+ * @param progress a progress value going from 0 to 1
+ */
+ public static float getOvershootInterpolation(float progress) {
+ return MathUtils.max(0.0f, (float) (1.0f - Math.exp(-4 * progress)));
+ }
+
+ // Create the default emphasized interpolator
+ private static PathInterpolator createEmphasizedInterpolator() {
+ Path path = new Path();
+ // Doing the same as fast_out_extra_slow_in
+ path.moveTo(0f, 0f);
+ path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f);
+ path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f);
+ return new PathInterpolator(path);
+ }
+} \ No newline at end of file
diff --git a/animationlib/src/com/android/app/animation/MathUtils.java b/animationlib/src/com/android/app/animation/MathUtils.java
new file mode 100644
index 0000000..d0a34c8
--- /dev/null
+++ b/animationlib/src/com/android/app/animation/MathUtils.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 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 com.android.app.animation;
+
+public final class MathUtils {
+ public static float log(float a) {
+ return (float) Math.log(a);
+ }
+
+ public static float max(float a, float b) {
+ return a > b ? a : b;
+ }
+}
diff --git a/animationlib/tests/AndroidManifest.xml b/animationlib/tests/AndroidManifest.xml
new file mode 100644
index 0000000..77a5990
--- /dev/null
+++ b/animationlib/tests/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 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="com.android.app.animation.tests">
+
+ <instrumentation
+ android:name="android.testing.TestableInstrumentation"
+ android:label="Tests for public Animation Lib"
+ android:targetPackage="com.android.app.animation.tests"/>
+
+</manifest>
diff --git a/animationlib/tests/com/android/app/animation/InterpolatorsAndroidXTest.kt b/animationlib/tests/com/android/app/animation/InterpolatorsAndroidXTest.kt
new file mode 100644
index 0000000..841e141
--- /dev/null
+++ b/animationlib/tests/com/android/app/animation/InterpolatorsAndroidXTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 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 com.android.app.animation
+
+import androidx.test.filters.SmallTest
+import java.lang.reflect.Modifier
+import junit.framework.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class InterpolatorsAndroidXTest {
+
+ @Test
+ fun testInterpolatorsAndInterpolatorsAndroidXPublicMethodsAreEqual() {
+ assertEquals(
+ Interpolators::class.java.getPublicMethods(),
+ InterpolatorsAndroidX::class.java.getPublicMethods()
+ )
+ }
+
+ @Test
+ fun testInterpolatorsAndInterpolatorsAndroidXPublicFieldsAreEqual() {
+ assertEquals(
+ Interpolators::class.java.getPublicFields(),
+ InterpolatorsAndroidX::class.java.getPublicFields()
+ )
+ }
+
+ private fun <T> Class<T>.getPublicMethods() =
+ declaredMethods
+ .filter { Modifier.isPublic(it.modifiers) }
+ .map { it.toString().replace(name, "") }
+ .toSet()
+
+ private fun <T> Class<T>.getPublicFields() =
+ fields.filter { Modifier.isPublic(it.modifiers) }.map { it.name }.toSet()
+}
diff --git a/iconloaderlib/build.gradle b/iconloaderlib/build.gradle
index 23c7cb0..344ac20 100644
--- a/iconloaderlib/build.gradle
+++ b/iconloaderlib/build.gradle
@@ -1,8 +1,9 @@
plugins {
- id 'sysuigradleproject.android-library-conventions'
+ id 'com.android.library'
}
android {
+ namespace = "com.android.launcher3.icons"
sourceSets {
main {
java.srcDirs = ['src', 'src_full_lib']
@@ -10,8 +11,7 @@ android {
res.srcDirs = ['res']
}
}
-
- lintOptions {
+ lint {
abortOnError false
}
diff --git a/iconloaderlib/res/drawable/ic_clone_app_badge_themed.xml b/iconloaderlib/res/drawable/ic_clone_app_badge_themed.xml
new file mode 100644
index 0000000..3a59e3d
--- /dev/null
+++ b/iconloaderlib/res/drawable/ic_clone_app_badge_themed.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/profile_badge_size"
+ android:height="@dimen/profile_badge_size"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#11000000"
+ android:pathData="M.5,12.25
+ A11.5,11.5 0 1,1 23.5,12.25
+ A11.5,11.5 0 1,1 .5,12.25" />
+
+ <path
+ android:fillColor="@color/themed_icon_background_color"
+ android:pathData="M1,12
+ A11,11 0 1,1 23,12
+ A11,11 0 1,1 1,12" />
+
+ <group android:scaleX=".6" android:scaleY=".6" android:pivotX="12" android:pivotY="12">
+ <path
+ android:pathData="M22,9.5C22,13.642 18.642,17 14.5,17C10.358,17 7,13.642 7,9.5C7,5.358 10.358,2 14.5,2C18.642,2 22,5.358 22,9.5Z"
+ android:fillColor="@color/themed_badge_icon_color"/>
+ <path
+ android:pathData="M9.5,20.333C12.722,20.333 15.333,17.722 15.333,14.5C15.333,11.278 12.722,8.667 9.5,8.667C6.278,8.667 3.667,11.278 3.667,14.5C3.667,17.722 6.278,20.333 9.5,20.333ZM9.5,22C13.642,22 17,18.642 17,14.5C17,10.358 13.642,7 9.5,7C5.358,7 2,10.358 2,14.5C2,18.642 5.358,22 9.5,22Z"
+ android:fillColor="@color/themed_badge_icon_color"
+ android:fillType="evenOdd"/>
+ </group>
+</vector>
diff --git a/iconloaderlib/res/drawable/ic_instant_app_badge.xml b/iconloaderlib/res/drawable/ic_instant_app_badge.xml
index b74317e..e6b5701 100644
--- a/iconloaderlib/res/drawable/ic_instant_app_badge.xml
+++ b/iconloaderlib/res/drawable/ic_instant_app_badge.xml
@@ -20,20 +20,11 @@
android:viewportHeight="18">
<path
- android:fillColor="@android:color/black"
- android:strokeWidth="1"
- android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
- <path
- android:fillColor="@android:color/white"
- android:strokeWidth="1"
- android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
- <path
android:fillColor="@android:color/white"
android:strokeWidth="1"
android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
<path
android:fillColor="@android:color/black"
- android:fillAlpha="0.87"
android:strokeWidth="1"
android:pathData="M 6 10.4123279 L 8.63934949 10.4123279 L 8.63934949 15.6 L 12.5577168 7.84517705 L 9.94547194 7.84517705 L 9.94547194 2 Z" />
</vector>
diff --git a/iconloaderlib/res/drawable/ic_instant_app_badge_themed.xml b/iconloaderlib/res/drawable/ic_instant_app_badge_themed.xml
new file mode 100644
index 0000000..6e19339
--- /dev/null
+++ b/iconloaderlib/res/drawable/ic_instant_app_badge_themed.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/profile_badge_size"
+ android:height="@dimen/profile_badge_size"
+ android:viewportWidth="18"
+ android:viewportHeight="18">
+
+ <path
+ android:fillColor="@color/themed_badge_icon_background_color"
+ android:strokeWidth="1"
+ android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
+ <path
+ android:fillColor="@color/themed_badge_icon_color"
+ android:strokeWidth="1"
+ android:pathData="M 6 10.4123279 L 8.63934949 10.4123279 L 8.63934949 15.6 L 12.5577168 7.84517705 L 9.94547194 7.84517705 L 9.94547194 2 Z" />
+</vector>
diff --git a/iconloaderlib/res/drawable/ic_work_app_badge_themed.xml b/iconloaderlib/res/drawable/ic_work_app_badge_themed.xml
new file mode 100644
index 0000000..6866d2f
--- /dev/null
+++ b/iconloaderlib/res/drawable/ic_work_app_badge_themed.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/profile_badge_size"
+ android:height="@dimen/profile_badge_size"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#11000000"
+ android:pathData="M.5,12.25
+ A11.5,11.5 0 1,1 23.5,12.25
+ A11.5,11.5 0 1,1 .5,12.25" />
+
+ <path
+ android:fillColor="@color/themed_badge_icon_background_color"
+ android:pathData="M1,12
+ A11,11 0 1,1 23,12
+ A11,11 0 1,1 1,12" />
+
+ <group android:scaleX=".6" android:scaleY=".6" android:pivotX="12" android:pivotY="12">
+ <path
+ android:fillColor="@color/themed_badge_icon_color"
+ android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM14,6h-4L10,4h4v2z" />
+ </group>
+</vector>
diff --git a/iconloaderlib/res/values-night-v31/colors.xml b/iconloaderlib/res/values-night-v31/colors.xml
new file mode 100644
index 0000000..e5ebda6
--- /dev/null
+++ b/iconloaderlib/res/values-night-v31/colors.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2023, 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>
+ <color name="themed_icon_color">@android:color/system_accent1_200</color>
+ <color name="themed_icon_background_color">@android:color/system_accent2_800</color>
+ <color name="themed_badge_icon_color">@android:color/system_accent2_800</color>
+ <color name="themed_badge_icon_background_color">@android:color/system_accent1_200</color>
+</resources>
diff --git a/iconloaderlib/res/values-night/colors.xml b/iconloaderlib/res/values-night/colors.xml
new file mode 100644
index 0000000..9de7074
--- /dev/null
+++ b/iconloaderlib/res/values-night/colors.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2023, 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>
+ <color name="themed_icon_color">#A8C7FA</color>
+ <color name="themed_icon_background_color">#003355</color>
+ <color name="themed_badge_icon_color">#003355</color>
+ <color name="themed_badge_icon_background_color">#A8C7FA</color>
+</resources>
diff --git a/iconloaderlib/res/values-v31/colors.xml b/iconloaderlib/res/values-v31/colors.xml
new file mode 100644
index 0000000..1405ad0
--- /dev/null
+++ b/iconloaderlib/res/values-v31/colors.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2023, 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>
+ <color name="themed_icon_color">@android:color/system_accent1_700</color>
+ <color name="themed_icon_background_color">@android:color/system_accent1_100</color>
+ <color name="themed_badge_icon_color">@android:color/system_accent1_700</color>
+ <color name="themed_badge_icon_background_color">@android:color/system_accent1_100</color>
+</resources>
diff --git a/iconloaderlib/res/values/colors.xml b/iconloaderlib/res/values/colors.xml
index 70582c2..8eeafb4 100644
--- a/iconloaderlib/res/values/colors.xml
+++ b/iconloaderlib/res/values/colors.xml
@@ -17,6 +17,10 @@
*/
-->
<resources>
+ <color name="themed_icon_color">#0842A0</color>
+ <color name="themed_icon_background_color">#D3E3FD</color>
+ <color name="themed_badge_icon_color">#0842A0</color>
+ <color name="themed_badge_icon_background_color">#D3E3FD</color>
<color name="legacy_icon_background">#FFFFFF</color>
<!-- Yellow 600, used for highlighting "important" conversations in settings & notifications -->
diff --git a/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java b/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java
index 37ad04d..d1ef6f7 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java
@@ -16,6 +16,7 @@
package com.android.launcher3.icons;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
@@ -154,11 +155,17 @@ public class BitmapInfo {
if (badgeInfo != null) {
drawable.setBadge(badgeInfo.newIcon(context, creationFlags));
} else if ((flags & FLAG_INSTANT) != 0) {
- drawable.setBadge(context.getDrawable(R.drawable.ic_instant_app_badge));
+ drawable.setBadge(context.getDrawable(drawable.isThemed()
+ ? R.drawable.ic_instant_app_badge_themed
+ : R.drawable.ic_instant_app_badge));
} else if ((flags & FLAG_WORK) != 0) {
- drawable.setBadge(context.getDrawable(R.drawable.ic_work_app_badge));
+ drawable.setBadge(context.getDrawable(drawable.isThemed()
+ ? R.drawable.ic_work_app_badge_themed
+ : R.drawable.ic_work_app_badge));
} else if ((flags & FLAG_CLONE) != 0) {
- drawable.setBadge(context.getDrawable(R.drawable.ic_clone_app_badge));
+ drawable.setBadge(context.getDrawable(drawable.isThemed()
+ ? R.drawable.ic_clone_app_badge_themed
+ : R.drawable.ic_clone_app_badge));
}
}
}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/BubbleIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BubbleIconFactory.java
new file mode 100644
index 0000000..a4ac812
--- /dev/null
+++ b/iconloaderlib/src/com/android/launcher3/icons/BubbleIconFactory.java
@@ -0,0 +1,160 @@
+package com.android.launcher3.icons;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Factory for creating normalized bubble icons and app badges.
+ */
+public class BubbleIconFactory extends BaseIconFactory {
+
+ private final int mRingColor;
+ private final int mRingWidth;
+
+ private final BaseIconFactory mBadgeFactory;
+
+ /**
+ * Creates a bubble icon factory.
+ *
+ * @param context the context for the factory.
+ * @param iconSize the size of the bubble icon (i.e. the large icon for the bubble).
+ * @param badgeSize the size of the badge (i.e. smaller icon shown on top of the large icon).
+ * @param ringColor the color of the ring optionally shown around the badge.
+ * @param ringWidth the width of the ring optionally shown around the badge.
+ */
+ public BubbleIconFactory(Context context, int iconSize, int badgeSize, int ringColor,
+ int ringWidth) {
+ super(context, context.getResources().getConfiguration().densityDpi, iconSize);
+ mRingColor = ringColor;
+ mRingWidth = ringWidth;
+
+ mBadgeFactory = new BaseIconFactory(context,
+ context.getResources().getConfiguration().densityDpi,
+ badgeSize);
+ }
+
+ /**
+ * Returns the drawable that the developer has provided to display in the bubble.
+ */
+ public Drawable getBubbleDrawable(@NonNull final Context context,
+ @Nullable final ShortcutInfo shortcutInfo, @Nullable final Icon ic) {
+ if (shortcutInfo != null) {
+ LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
+ int density = context.getResources().getConfiguration().densityDpi;
+ return launcherApps.getShortcutIconDrawable(shortcutInfo, density);
+ } else {
+ if (ic != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ if (ic.getType() == Icon.TYPE_URI
+ || ic.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+ context.grantUriPermission(context.getPackageName(),
+ ic.getUri(),
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ return ic.loadDrawable(context);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Creates the bitmap for the provided drawable and returns the scale used for
+ * drawing the actual drawable. This is used for the larger icon shown for the bubble.
+ */
+ public Bitmap getBubbleBitmap(@NonNull Drawable icon, float[] outScale) {
+ if (outScale == null) {
+ outScale = new float[1];
+ }
+ icon = normalizeAndWrapToAdaptiveIcon(icon,
+ true /* shrinkNonAdaptiveIcons */,
+ null /* outscale */,
+ outScale);
+ return createIconBitmap(icon, outScale[0], MODE_WITH_SHADOW);
+ }
+
+ /**
+ * Returns a {@link BitmapInfo} for the app-badge that is shown on top of each bubble. This
+ * will include the workprofile indicator on the badge if appropriate.
+ */
+ public BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation) {
+ if (userBadgedAppIcon instanceof AdaptiveIconDrawable) {
+ AdaptiveIconDrawable ad = (AdaptiveIconDrawable) userBadgedAppIcon;
+ userBadgedAppIcon = new CircularAdaptiveIcon(ad.getBackground(),
+ ad.getForeground());
+ }
+ if (isImportantConversation) {
+ userBadgedAppIcon = new CircularRingDrawable(userBadgedAppIcon);
+ }
+ Bitmap userBadgedBitmap = mBadgeFactory.createIconBitmap(
+ userBadgedAppIcon, 1, MODE_WITH_SHADOW);
+ return mBadgeFactory.createIconBitmap(userBadgedBitmap);
+ }
+
+ private class CircularRingDrawable extends CircularAdaptiveIcon {
+ final Rect mInnerBounds = new Rect();
+
+ final Drawable mDr;
+
+ CircularRingDrawable(Drawable dr) {
+ super(null, null);
+ mDr = dr;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ int save = canvas.save();
+ canvas.clipPath(getIconMask());
+ canvas.drawColor(mRingColor);
+ mInnerBounds.set(getBounds());
+ mInnerBounds.inset(mRingWidth, mRingWidth);
+ canvas.translate(mInnerBounds.left, mInnerBounds.top);
+ mDr.setBounds(0, 0, mInnerBounds.width(), mInnerBounds.height());
+ mDr.draw(canvas);
+ canvas.restoreToCount(save);
+ }
+ }
+
+ private static class CircularAdaptiveIcon extends AdaptiveIconDrawable {
+
+ final Path mPath = new Path();
+
+ CircularAdaptiveIcon(Drawable bg, Drawable fg) {
+ super(bg, fg);
+ }
+
+ @Override
+ public Path getIconMask() {
+ mPath.reset();
+ Rect bounds = getBounds();
+ mPath.addOval(bounds.left, bounds.top, bounds.right, bounds.bottom, Path.Direction.CW);
+ return mPath;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ int save = canvas.save();
+ canvas.clipPath(getIconMask());
+
+ Drawable d;
+ if ((d = getBackground()) != null) {
+ d.draw(canvas);
+ }
+ if ((d = getForeground()) != null) {
+ d.draw(canvas);
+ }
+ canvas.restoreToCount(save);
+ }
+ }
+}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/ThemedIconDrawable.java b/iconloaderlib/src/com/android/launcher3/icons/ThemedIconDrawable.java
index 8442eed..6724d6b 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/ThemedIconDrawable.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/ThemedIconDrawable.java
@@ -24,6 +24,7 @@ import android.graphics.Bitmap;
import android.graphics.BlendMode;
import android.graphics.BlendModeColorFilter;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -128,13 +129,8 @@ public class ThemedIconDrawable extends FastBitmapDrawable {
public static int[] getColors(Context context) {
Resources res = context.getResources();
int[] colors = new int[2];
- if ((res.getConfiguration().uiMode & UI_MODE_NIGHT_MASK) == UI_MODE_NIGHT_YES) {
- colors[0] = res.getColor(android.R.color.system_neutral1_800);
- colors[1] = res.getColor(android.R.color.system_accent1_100);
- } else {
- colors[0] = res.getColor(android.R.color.system_accent1_100);
- colors[1] = res.getColor(android.R.color.system_neutral2_700);
- }
+ colors[0] = res.getColor(R.color.themed_icon_background_color);
+ colors[1] = res.getColor(R.color.themed_icon_color);
return colors;
}
diff --git a/motiontoollib/build.gradle b/motiontoollib/build.gradle
index a1f39b9..e3750ec 100644
--- a/motiontoollib/build.gradle
+++ b/motiontoollib/build.gradle
@@ -1,5 +1,5 @@
plugins {
- id 'sysuigradleproject.android-library-conventions'
+ id 'com.android.library'
id 'org.jetbrains.kotlin.android'
id 'com.google.protobuf'
}
@@ -7,18 +7,12 @@ plugins {
final String PROTOS_DIR = "${ANDROID_TOP}/frameworks/libs/systemui/motiontoollib/src/com/android/app/motiontool/proto"
android {
- compileSdk TARGET_SDK.toInteger()
- buildToolsVersion = BUILD_TOOLS_VERSION
-
+ namespace = "com.android.app.motiontool"
+ testNamespace = "com.android.app.motiontool.tests"
defaultConfig {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
- defaultConfig {
- minSdkVersion TARGET_SDK.toInteger()
- targetSdkVersion TARGET_SDK.toInteger()
- }
-
sourceSets {
main {
java.srcDirs = ['src']
@@ -30,15 +24,15 @@ android {
manifest.srcFile "tests/AndroidManifest.xml"
}
}
-
- lintOptions {
+ lint {
abortOnError false
}
+
}
dependencies {
implementation "androidx.core:core:1.9.0"
- implementation "com.google.protobuf:protobuf-lite:${protobuf_version}"
+ implementation "com.google.protobuf:protobuf-lite:${protobuf_lite_version}"
api project(":ViewCaptureLib")
androidTestImplementation project(':SharedTestLib')
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
diff --git a/motiontoollib/src/com/android/app/motiontool/DdmHandleMotionTool.kt b/motiontoollib/src/com/android/app/motiontool/DdmHandleMotionTool.kt
index 63a6fee..c7a6b0d 100644
--- a/motiontoollib/src/com/android/app/motiontool/DdmHandleMotionTool.kt
+++ b/motiontoollib/src/com/android/app/motiontool/DdmHandleMotionTool.kt
@@ -107,7 +107,7 @@ class DdmHandleMotionTool private constructor(
MotionToolsResponse.newBuilder().apply {
tryCatchingMotionToolManagerExceptions {
setPollTrace(PollTraceResponse.newBuilder()
- .setExportedData(motionToolManager.pollTrace(pollTraceRequest.traceId)))
+ .setData(motionToolManager.pollTrace(pollTraceRequest.traceId)))
}
}.build()
@@ -115,7 +115,7 @@ class DdmHandleMotionTool private constructor(
MotionToolsResponse.newBuilder().apply {
tryCatchingMotionToolManagerExceptions {
setEndTrace(EndTraceResponse.newBuilder()
- .setExportedData(motionToolManager.endTrace(endTraceRequest.traceId)))
+ .setData(motionToolManager.endTrace(endTraceRequest.traceId)))
}
}.build()
diff --git a/motiontoollib/src/com/android/app/motiontool/MotionToolManager.kt b/motiontoollib/src/com/android/app/motiontool/MotionToolManager.kt
index 66b00f7..a98a588 100644
--- a/motiontoollib/src/com/android/app/motiontool/MotionToolManager.kt
+++ b/motiontoollib/src/com/android/app/motiontool/MotionToolManager.kt
@@ -22,8 +22,9 @@ import android.view.Choreographer
import android.view.View
import android.view.WindowManagerGlobal
import androidx.annotation.VisibleForTesting
+import com.android.app.viewcapture.SimpleViewCapture
import com.android.app.viewcapture.ViewCapture
-import com.android.app.viewcapture.data.ExportedData
+import com.android.app.viewcapture.data.MotionWindowData
/**
* Singleton to manage motion tracing sessions.
@@ -43,7 +44,7 @@ import com.android.app.viewcapture.data.ExportedData
* @see [DdmHandleMotionTool]
*/
class MotionToolManager private constructor(private val windowManagerGlobal: WindowManagerGlobal) {
- private val viewCapture: ViewCapture = SimpleViewCapture()
+ private val viewCapture: ViewCapture = SimpleViewCapture("MTViewCapture")
companion object {
private const val TAG = "MotionToolManager"
@@ -77,29 +78,29 @@ class MotionToolManager private constructor(private val windowManagerGlobal: Win
}
/**
- * Ends [ViewCapture] and returns the captured [ExportedData] since the [beginTrace] call or the
- * last [pollTrace] call.
+ * Ends [ViewCapture] and returns the captured [MotionWindowData] since the [beginTrace] call or
+ * the last [pollTrace] call.
*/
@Synchronized
- fun endTrace(traceId: Int): ExportedData {
+ fun endTrace(traceId: Int): MotionWindowData {
Log.d(TAG, "End Trace for id: $traceId")
val traceMetadata = traces.getOrElse(traceId) { throw UnknownTraceIdException(traceId) }
- val exportedData = pollTrace(traceId)
+ val data = pollTrace(traceId)
traceMetadata.stopTrace()
traces.remove(traceId)
- return exportedData
+ return data
}
/**
- * Returns the [ExportedData] captured since the [beginTrace] call or the last [pollTrace] call.
+ * Returns the [MotionWindowData] captured since the [beginTrace] call or last [pollTrace] call.
* This function can only be used after [beginTrace] is called and before [endTrace] is called.
*/
@Synchronized
- fun pollTrace(traceId: Int): ExportedData {
+ fun pollTrace(traceId: Int): MotionWindowData {
val traceMetadata = traces.getOrElse(traceId) { throw UnknownTraceIdException(traceId) }
- val exportedData = getExportedDataFromViewCapture(traceMetadata)
- traceMetadata.updateLastPolledTime(exportedData)
- return exportedData
+ val data = getDataFromViewCapture(traceMetadata)
+ traceMetadata.updateLastPolledTime(data)
+ return data
}
/**
@@ -115,32 +116,26 @@ class MotionToolManager private constructor(private val windowManagerGlobal: Win
traceIdCounter = 0
}
- private fun getExportedDataFromViewCapture(traceMetadata: TraceMetadata): ExportedData {
+ private fun getDataFromViewCapture(traceMetadata: TraceMetadata): MotionWindowData {
val rootView =
getRootView(traceMetadata.windowId)
?: throw WindowNotFoundException(traceMetadata.windowId)
- val exportedData = viewCapture
- .getDumpTask(rootView)
- ?.orElse(null)
- ?.get() ?: return ExportedData.newBuilder().build()
-
- val filteredFrameData = exportedData.frameDataList
- ?.filter { it.timestamp > traceMetadata.lastPolledTime }
-
- return exportedData.toBuilder()
- .clearFrameData()
- .addAllFrameData(filteredFrameData)
- .build()
+ val data: MotionWindowData = viewCapture
+ .getDumpTask(rootView).get()
+ ?.orElse(null) ?: return MotionWindowData.newBuilder().build()
+ val filteredFrameData = data.frameDataList.filter {
+ it.timestamp > traceMetadata.lastPolledTime
+ }
+ return data.toBuilder()
+ .clearFrameData()
+ .addAllFrameData(filteredFrameData)
+ .build()
}
private fun getRootView(windowId: String): View? {
return windowManagerGlobal.getRootView(windowId)
}
-
- class SimpleViewCapture : ViewCapture(DEFAULT_MEMORY_SIZE, DEFAULT_INIT_POOL_SIZE,
- MAIN_EXECUTOR.submit { Choreographer.getInstance() }.get(),
- createAndStartNewLooperExecutor("MTViewCapture", Process.THREAD_PRIORITY_FOREGROUND))
}
private data class TraceMetadata(
@@ -148,9 +143,9 @@ private data class TraceMetadata(
var lastPolledTime: Long,
var stopTrace: () -> Unit
) {
- fun updateLastPolledTime(exportedData: ExportedData?) {
- exportedData?.frameDataList?.maxOfOrNull { it.timestamp }?.let { maxFrameTimestamp ->
- lastPolledTime = maxFrameTimestamp
+ fun updateLastPolledTime(data: MotionWindowData?) {
+ data?.frameDataList?.maxOfOrNull { it.timestamp }?.let {
+ lastPolledTime = it
}
}
}
diff --git a/motiontoollib/src/com/android/app/motiontool/proto/motion_tool.proto b/motiontoollib/src/com/android/app/motiontool/proto/motion_tool.proto
index cd5ad2f..04ee020 100644
--- a/motiontoollib/src/com/android/app/motiontool/proto/motion_tool.proto
+++ b/motiontoollib/src/com/android/app/motiontool/proto/motion_tool.proto
@@ -98,7 +98,7 @@ message EndTraceRequest {
}
message EndTraceResponse {
- optional com.android.app.viewcapture.data.ExportedData exported_data = 1;
+ optional com.android.app.viewcapture.data.MotionWindowData data = 1;
}
// Polls collected motion trace data collected since the last PollTraceRequest (or the
@@ -108,6 +108,6 @@ message PollTraceRequest {
}
message PollTraceResponse {
- optional com.android.app.viewcapture.data.ExportedData exported_data = 1;
+ optional com.android.app.viewcapture.data.MotionWindowData data = 1;
}
diff --git a/motiontoollib/tests/AndroidManifest.xml b/motiontoollib/tests/AndroidManifest.xml
index 3db8d2f..c16e25f 100644
--- a/motiontoollib/tests/AndroidManifest.xml
+++ b/motiontoollib/tests/AndroidManifest.xml
@@ -15,14 +15,14 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.app.motiontool">
+ package="com.android.app.motiontool.tests">
<application
android:debuggable="true"
android:theme="@android:style/Theme.NoTitleBar">
<activity
- android:name=".util.TestActivity"
+ android:name="com.android.app.motiontool.util.TestActivity"
android:exported="false" />
<uses-library android:name="android.test.runner" />
@@ -32,6 +32,6 @@
<instrumentation
android:name="android.testing.TestableInstrumentation"
android:label="Tests for MotionTool Lib"
- android:targetPackage="com.android.app.motiontool"/>
+ android:targetPackage="com.android.app.motiontool.tests"/>
</manifest>
diff --git a/motiontoollib/tests/com/android/app/motiontool/DdmHandleMotionToolTest.kt b/motiontoollib/tests/com/android/app/motiontool/DdmHandleMotionToolTest.kt
index 7d78237..f112d0b 100644
--- a/motiontoollib/tests/com/android/app/motiontool/DdmHandleMotionToolTest.kt
+++ b/motiontoollib/tests/com/android/app/motiontool/DdmHandleMotionToolTest.kt
@@ -119,7 +119,7 @@ class DdmHandleMotionToolTest {
activityScenarioRule.scenario.onActivity {
val beginTraceResponse = performBeginTraceRequest(getActivityViewRootId())
val endTraceResponse = performEndTraceRequest(beginTraceResponse.beginTrace.traceId)
- Assert.assertTrue(endTraceResponse.endTrace.exportedData.frameDataList.isEmpty())
+ Assert.assertTrue(endTraceResponse.endTrace.data.frameDataList.isEmpty())
}
}
@@ -130,14 +130,17 @@ class DdmHandleMotionToolTest {
val traceId = beginTraceResponse.beginTrace.traceId
Choreographer.getInstance().postFrameCallback {
- activity.findViewById<View>(android.R.id.content).viewTreeObserver.dispatchOnDraw()
+ activity
+ .requireViewById<View>(android.R.id.content)
+ .viewTreeObserver
+ .dispatchOnDraw()
val pollTraceResponse = performPollTraceRequest(traceId)
- assertEquals(1, pollTraceResponse.pollTrace.exportedData.frameDataList.size)
+ assertEquals(1, pollTraceResponse.pollTrace.data.frameDataList.size)
// Verify that frameData is only included once and is not returned again
val endTraceResponse = performEndTraceRequest(traceId)
- assertEquals(0, endTraceResponse.endTrace.exportedData.frameDataList.size)
+ assertEquals(0, endTraceResponse.endTrace.data.frameDataList.size)
}
}
}
diff --git a/motiontoollib/tests/com/android/app/motiontool/MotionToolManagerTest.kt b/motiontoollib/tests/com/android/app/motiontool/MotionToolManagerTest.kt
index 560f798..05400a2 100644
--- a/motiontoollib/tests/com/android/app/motiontool/MotionToolManagerTest.kt
+++ b/motiontoollib/tests/com/android/app/motiontool/MotionToolManagerTest.kt
@@ -85,14 +85,17 @@ class MotionToolManagerTest {
activityScenarioRule.scenario.onActivity { activity ->
val traceId = motionToolManager.beginTrace(getActivityViewRootId())
Choreographer.getInstance().postFrameCallback {
- activity.findViewById<View>(android.R.id.content).viewTreeObserver.dispatchOnDraw()
+ activity
+ .requireViewById<View>(android.R.id.content)
+ .viewTreeObserver
+ .dispatchOnDraw()
- val polledExportedData = motionToolManager.pollTrace(traceId)
- assertEquals(1, polledExportedData.frameDataList.size)
+ val polledData = motionToolManager.pollTrace(traceId)
+ assertEquals(1, polledData.frameDataList.size)
// Verify that frameData is only included once and is not returned again
- val endExportedData = motionToolManager.endTrace(traceId)
- assertEquals(0, endExportedData.frameDataList.size)
+ val endData = motionToolManager.endTrace(traceId)
+ assertEquals(0, endData.frameDataList.size)
}
}
}
diff --git a/searchuilib/.gitignore b/searchuilib/.gitignore
deleted file mode 100644
index 6213826..0000000
--- a/searchuilib/.gitignore
+++ /dev/null
@@ -1,13 +0,0 @@
-*.iml
-.project
-.classpath
-.project.properties
-gen/
-bin/
-.idea/
-.gradle/
-local.properties
-gradle/
-build/
-gradlew*
-.DS_Store
diff --git a/searchuilib/Android.bp b/searchuilib/Android.bp
deleted file mode 100644
index f7b0b83..0000000
--- a/searchuilib/Android.bp
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2020 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 {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-android_library {
- name: "search_ui",
-
- sdk_version: "system_current",
-
- static_libs: [
- "androidx.annotation_annotation",
- ],
- srcs: [
- "src/**/*.java",
- ],
-}
diff --git a/searchuilib/build.gradle b/searchuilib/build.gradle
deleted file mode 100644
index 6b5027b..0000000
--- a/searchuilib/build.gradle
+++ /dev/null
@@ -1,37 +0,0 @@
-plugins {
- id 'com.android.library'
-}
-
-android {
- compileSdk TARGET_SDK.toInteger()
- buildToolsVersion = BUILD_TOOLS_VERSION
-
- defaultConfig {
- minSdkVersion TARGET_SDK.toInteger()
- targetSdkVersion TARGET_SDK.toInteger()
- }
-
- sourceSets {
- main {
- java.srcDirs = ['src']
- manifest.srcFile 'AndroidManifest.xml'
- }
- }
-
- lintOptions {
- abortOnError false
- }
-
- tasks.withType(JavaCompile) {
- options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
- }
-
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
-}
-
-dependencies {
- implementation "androidx.core:core:+"
-}
diff --git a/searchuilib/src/com/android/app/search/LayoutType.java b/searchuilib/src/com/android/app/search/LayoutType.java
deleted file mode 100644
index 53c663d..0000000
--- a/searchuilib/src/com/android/app/search/LayoutType.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.app.search;
-
-/**
- * Constants to be used with {@link SearchTarget}.
- */
-public class LayoutType {
-
- // ------
- // | icon |
- // ------
- // text
- public static final String ICON_SINGLE_VERTICAL_TEXT = "icon";
-
- // Below three layouts (to be deprecated) and two layouts render
- // {@link SearchTarget}s in following layout.
- // ------ ------ ------
- // | | title |(opt)| |(opt)|
- // | icon | subtitle (optional) | icon| | icon|
- // ------ ------ ------
- @Deprecated
- public static final String ICON_SINGLE_HORIZONTAL_TEXT = "icon_text_row";
- @Deprecated
- public static final String ICON_DOUBLE_HORIZONTAL_TEXT = "icon_texts_row";
- @Deprecated
- public static final String ICON_DOUBLE_HORIZONTAL_TEXT_BUTTON = "icon_texts_button";
-
- // will replace ICON_DOUBLE_* ICON_SINGLE_* layouts
- public static final String ICON_HORIZONTAL_TEXT = "icon_row";
- public static final String HORIZONTAL_MEDIUM_TEXT = "icon_row_medium";
- public static final String EXTRA_TALL_ICON_ROW = "extra_tall_icon_row";
- public static final String SMALL_ICON_HORIZONTAL_TEXT = "short_icon_row";
- public static final String SMALL_ICON_HORIZONTAL_TEXT_THUMBNAIL = "short_icon_row_thumbnail";
-
- // This layout contains a series of icon results (currently up to 4 per row).
- // The container does not support stretching for its children, and can only contain
- // {@link #ICON_SINGLE_VERTICAL_TEXT} layout types.
- public static final String ICON_CONTAINER = "icon_container";
-
- // This layout contains a series of thumbnails (currently up to 3 per row).
- // The container supports stretching for its children, and can only contain {@link #THUMBNAIL}
- // layout types.
- public static final String THUMBNAIL_CONTAINER = "thumbnail_container";
-
- // This layout creates a container for people grouping
- // Only available above version code 2
- public static final String BIG_ICON_MEDIUM_HEIGHT_ROW = "big_icon_medium_row";
-
- // This layout creates square thumbnail image (currently 3 column)
- public static final String THUMBNAIL = "thumbnail";
-
- // This layout contains an icon and slice
- public static final String ICON_SLICE = "slice";
-
- // Widget bitmap preview
- public static final String WIDGET_PREVIEW = "widget_preview";
-
- // Live widget search result
- public static final String WIDGET_LIVE = "widget_live";
-
- // Layout type used to display people tiles using shortcut info
- public static final String PEOPLE_TILE = "people_tile";
-
- // Deprecated
- // text based header to group various layouts in low confidence section of the results.
- public static final String TEXT_HEADER = "header";
-
- // horizontal bar to be inserted between fallback search results and low confidence section
- public static final String EMPTY_DIVIDER = "empty_divider";
-
- // layout representing quick calculations
- public static final String CALCULATOR = "calculator";
-
- // From version code 4, if TEXT_HEADER_ROW is used, no need to insert this on-device
- // section header.
- public static final String SECTION_HEADER = "section_header";
-
- // layout for a tall card with header and image, and no icon.
- public static final String TALL_CARD_WITH_IMAGE_NO_ICON = "tall_card_with_image_no_icon";
-
- // Layout for a text header
- // Available for SearchUiManager proxy service to use above version code 3
- public static final String TEXT_HEADER_ROW = "text_header_row";
-
- // Layout for a quick settings tile
- public static final String QS_TILE = "qs_tile";
-
- // Placeholder for web suggest.
- public static final String PLACEHOLDER = "placeholder";
-
- // Placeholder for rich answer cards.
- // Only available on or above version code 3.
- public static final String RICHANSWER_PLACEHOLDER = "richanswer_placeholder";
-
-}
diff --git a/searchuilib/src/com/android/app/search/QueryExtras.java b/searchuilib/src/com/android/app/search/QueryExtras.java
deleted file mode 100644
index a6dbba1..0000000
--- a/searchuilib/src/com/android/app/search/QueryExtras.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2023 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 com.android.app.search;
-
-import android.app.search.Query;
-
-/**
- * Utility class used to define implicit contract between aiai and launcher regarding
- * what constant string key should be used to pass sub session information inside
- * the {@link Query} object.
- *
- * This decorated query object is passed to aiai using two method calls:
- * <ul>
- * <ol>android.app.search.SearchSession.query()</ol>
- * <ol>android.app.search.SearchSession.notifyEvent()</ol>
- * </ul>
- */
-public class QueryExtras {
-
- // Can be either 1 (ALLAPPS) or 2 (QSB)
- public static final String EXTRAS_KEY_ENTRY = "entry";
-
- // This value overrides the timeout that is defined inside {@link SearchContext#getTimeout}
- public static final String EXTRAS_KEY_TIMEOUT_OVERRIDE = "timeout";
-
- // Used to know which target is deleted.
- public static final String EXTRAS_BUNDLE_DELETED_TARGET_ID = "deleted_target_id";
-}
diff --git a/searchuilib/src/com/android/app/search/ResultType.java b/searchuilib/src/com/android/app/search/ResultType.java
deleted file mode 100644
index 9dd9dad..0000000
--- a/searchuilib/src/com/android/app/search/ResultType.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.app.search;
-
-/**
- * Constants to be used with {@link android.app.search.SearchContext} and
- * {@link android.app.search.SearchTarget}.
- *
- * Note, a result type could be a of two types.
- * For example, unpublished settings result type could be in slices:
- * <code> resultType = SETTING | SLICE </code>
- */
-public class ResultType {
-
- // published corpus by 3rd party app, supported by SystemService
- public static final int APPLICATION = 1 << 0;
- public static final int SHORTCUT = 1 << 1;
- public static final int SLICE = 1 << 6;
- public static final int WIDGETS = 1 << 7;
-
- // Not extracted from any of the SystemService
- public static final int PEOPLE = 1 << 2;
- public static final int ACTION = 1 << 3;
- public static final int SETTING = 1 << 4;
- public static final int IMAGE = 1 << 5;
- public static final int PLAY = 1 << 8;
- public static final int SUGGEST = 1 << 9;
- public static final int ASSISTANT = 1 << 10;
- public static final int CHROMETAB = 1 << 11;
- public static final int NAVVYSITE = 1 << 12;
- public static final int TIPS = 1 << 13;
- public static final int PEOPLE_TILE = 1 << 14;
- public static final int LEGACY_SHORTCUT = 1 << 15;
- public static final int MEMORY = 1 << 16;
- public static final int WEB_SUGGEST = 1 << 17;
- public static final int NO_FULFILLMENT = 1 << 18;
- public static final int EDUCARD = 1 << 19;
- public static final int SYSTEM_POINTER = 1 << 20;
- public static final int VIDEO = 1 << 21;
-
- public static final int PUBLIC_DATA_TYPE = APPLICATION | SETTING | PLAY | WEB_SUGGEST;
- public static final int PRIMITIVE_TYPE = APPLICATION | SLICE | SHORTCUT | WIDGETS | ACTION |
- LEGACY_SHORTCUT;
- public static final int CORPUS_TYPE =
- PEOPLE | SETTING | IMAGE | PLAY | SUGGEST | ASSISTANT | CHROMETAB | NAVVYSITE | TIPS
- | PEOPLE_TILE | MEMORY | WEB_SUGGEST | VIDEO;
- public static final int RANK_TYPE = SYSTEM_POINTER;
- public static final int UI_TYPE = EDUCARD | NO_FULFILLMENT;
-
- public static boolean isSlice(int resultType) {
- return (resultType & SLICE) != 0;
- }
-
- public static boolean isSystemPointer(int resultType) {
- return (resultType & SYSTEM_POINTER) != 0;
- }
-
- /**
- * Returns result type integer where only {@code #CORPUS_TYPE} bit will turned on.
- */
- public static int getCorpusType(int resultType) {
- return (resultType & CORPUS_TYPE);
- }
-
- /**
- * Returns result type integer where only {@code #PRIMITIVE_TYPE} bit will be turned on.
- */
- public static int getPrimitiveType(int resultType) {
- return (resultType & PRIMITIVE_TYPE);
- }
-
- /**
- * Returns whether the given result type is privacy safe or not.
- */
- public static boolean isPrivacySafe(int resultType) {
- return (resultType & PUBLIC_DATA_TYPE) != 0;
- }
-}
diff --git a/searchuilib/src/com/android/app/search/SearchActionExtras.java b/searchuilib/src/com/android/app/search/SearchActionExtras.java
deleted file mode 100644
index 2f33d5d..0000000
--- a/searchuilib/src/com/android/app/search/SearchActionExtras.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2023 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 com.android.app.search;
-
-import android.app.search.SearchAction;
-
-/**
- * Helper class that defines key string value for {@link SearchAction#getExtras()}
- */
-public class SearchActionExtras {
- public static final String BUNDLE_EXTRA_HIDE_SUBTITLE = "hide_subtitle";
- public static final String BUNDLE_EXTRA_HIDE_ICON = "hide_icon";
- public static final String BUNDLE_EXTRA_ALLOW_PINNING = "allow_pinning";
- public static final String BUNDLE_EXTRA_BADGE_WITH_PACKAGE = "badge_with_package";
- public static final String BUNDLE_EXTRA_PRIMARY_ICON_FROM_TITLE = "primary_icon_from_title";
- public static final String BUNDLE_EXTRA_IS_SEARCH_IN_APP = "is_search_in_app";
- public static final String BUNDLE_EXTRA_BADGE_WITH_COMPONENT_NAME = "badge_with_component_name";
- public static final String BUNDLE_EXTRA_ICON_CACHE_KEY = "icon_cache_key";
- public static final String BUNDLE_EXTRA_ICON_TOKEN_INTEGER = "icon_integer";
- public static final String BUNDLE_EXTRA_SHOULD_START = "should_start";
- public static final String BUNDLE_EXTRA_SHOULD_START_FOR_RESULT = "should_start_for_result";
- public static final String BUNDLE_EXTRA_SUGGESTION_ACTION_TEXT = "suggestion_action_text";
- public static final String BUNDLE_EXTRA_SUGGESTION_ACTION_RPC = "suggestion_action_rpc";
- public static final String BUNDLE_EXTRA_SKIP_LOGGING_IN_TARGET_HANDLER =
- "skip_logging_in_target_handler";
-}
diff --git a/searchuilib/src/com/android/app/search/SearchTargetConverter.java b/searchuilib/src/com/android/app/search/SearchTargetConverter.java
deleted file mode 100644
index 2080966..0000000
--- a/searchuilib/src/com/android/app/search/SearchTargetConverter.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2023 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 com.android.app.search;
-
-import static com.android.app.search.LayoutType.SMALL_ICON_HORIZONTAL_TEXT;
-import static com.android.app.search.SearchActionExtras.BUNDLE_EXTRA_HIDE_ICON;
-import static com.android.app.search.SearchActionExtras.BUNDLE_EXTRA_HIDE_SUBTITLE;
-import static com.android.app.search.SearchTargetExtras.BUNDLE_EXTRA_CLASS;
-import static com.android.app.search.SearchTargetExtras.BUNDLE_EXTRA_SUBTITLE_OVERRIDE;
-import static com.android.app.search.SearchTargetExtras.BUNDLE_EXTRA_SUPPORT_QUERY_BUILDER;
-import static com.android.app.search.SearchTargetExtras.EXTRAS_RECENT_BLOCK_TARGET;
-
-import android.app.search.SearchAction;
-import android.app.search.SearchTarget;
-import android.content.pm.ShortcutInfo;
-import android.os.Bundle;
-
-public class SearchTargetConverter {
- /**
- * Generate a searchTarget that uses {@link LayoutType#SMALL_ICON_HORIZONTAL_TEXT} from a
- * searchTarget where original layout type may not have been SMALL_ICON_HORIZONTAL_TEXT. Only
- * possible if the given SearchTarget contains a searchAction or shortcutInfo, otherwise the
- * original searchTarget will be returned.
- */
- public static SearchTarget convertLayoutTypeToSmallIconHorizontalText(
- SearchTarget searchTarget) {
- SearchAction searchTargetAction = searchTarget.getSearchAction();
- ShortcutInfo shortcutInfo = searchTarget.getShortcutInfo();
- int resultType = searchTarget.getResultType();
- String subtitle = "";
-
- Bundle searchTargetBundle = searchTarget.getExtras();
- searchTargetBundle.putString(BUNDLE_EXTRA_CLASS,
- searchTargetBundle.getString(BUNDLE_EXTRA_CLASS));
- searchTargetBundle.putBoolean(BUNDLE_EXTRA_SUPPORT_QUERY_BUILDER, true);
- searchTargetBundle.putBoolean(BUNDLE_EXTRA_HIDE_SUBTITLE, false);
- searchTargetBundle.putString(BUNDLE_EXTRA_SUBTITLE_OVERRIDE, subtitle);
- searchTargetBundle.putBoolean(BUNDLE_EXTRA_HIDE_ICON, false);
- searchTargetBundle.putBoolean(EXTRAS_RECENT_BLOCK_TARGET, true);
-
- SearchTarget.Builder builder = new SearchTarget.Builder(resultType,
- SMALL_ICON_HORIZONTAL_TEXT, searchTarget.getId())
- .setPackageName(searchTarget.getPackageName())
- .setExtras(searchTargetBundle)
- .setUserHandle(searchTarget.getUserHandle());
- if (searchTargetAction != null) {
- builder.setSearchAction(searchTargetAction);
- } else if (shortcutInfo != null) {
- builder.setShortcutInfo(shortcutInfo);
- } else {
- return searchTarget;
- }
- return builder.build();
- }
-}
diff --git a/searchuilib/src/com/android/app/search/SearchTargetEventHelper.java b/searchuilib/src/com/android/app/search/SearchTargetEventHelper.java
deleted file mode 100644
index a323625..0000000
--- a/searchuilib/src/com/android/app/search/SearchTargetEventHelper.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2023 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 com.android.app.search;
-
-import static com.android.app.search.SearchTargetExtras.isRichAnswer;
-
-import android.app.search.SearchTarget;
-import android.content.ComponentName;
-import android.os.Process;
-
-import androidx.annotation.Nullable;
-
-/**
- * Helper class that defines helper methods for {@link android.app.search.SearchTargetEvent} to
- * define the contract between Launcher and AiAi for notifyEvent.
- */
-
-public class SearchTargetEventHelper {
-
- public static final String PKG_NAME_AGSA = "com.google.android.googlequicksearchbox";
-
- /**
- * Generate web target id similar to AiAi targetId for logging search button tap and Launcher
- * sends raw query to AGA.
- * AiAi target id is of format "resultType:userId:packageName:extraInfo"
- *
- * @return string webTargetId
- * Example webTargetId for
- * web suggestion - WEB_SUGGEST:0:com.google.android.googlequicksearchbox:SUGGESTION
- */
- public static String generateWebTargetIdForRawQuery() {
- // For raw query, there is no search target, so we pass null.
- return generateWebTargetIdForLogging(null);
- }
-
- /**
- * Generate web target id similar to AiAi targetId for logging both 0-state and n-state.
- * AiAi target id is of format "resultType:userId:packageName:extraInfo"
- *
- * @return string webTargetId
- * Example webTargetId for
- * web suggestion - WEB_SUGGEST:0:com.google.android.googlequicksearchbox:SUGGESTION
- * rich answer - WEB_SUGGEST:0:com.google.android.googlequicksearchbox:RICH_ANSWER
- */
- public static String generateWebTargetIdForLogging(@Nullable SearchTarget webTarget) {
- StringBuilder webTargetId = new StringBuilder(
- "WEB_SUGGEST" + ":" + Process.myUserHandle().getIdentifier() + ":");
- if (webTarget == null) {
- webTargetId.append(PKG_NAME_AGSA + ":SUGGESTION");
- return webTargetId.toString();
- }
- webTargetId.append(webTarget.getPackageName());
- if (isRichAnswer(webTarget)) {
- webTargetId.append(":RICH_ANSWER");
- } else {
- webTargetId.append(":SUGGESTION");
- }
- return webTargetId.toString();
- }
-
- /**
- * Generate application target id similar to AiAi targetId for logging only 0-state.
- * For n-state, AiAi already populates the target id in right format.
- * AiAi target id is of format "resultType:userId:packageName:extraInfo"
- *
- * When the apps from AiAi's AppPredictionService are converted to {@link SearchTarget}, we need
- * to construct the targetId using componentName.
- *
- * @return string appTargetId
- * Example appTargetId for
- * maps - APPLICATION:0:com.google.android.apps.maps:com.google.android.maps.MapsActivity
- * clock - APPLICATION:0:com.google.android.deskclock:com.android.deskclock.DeskClock
- */
- public static String generateAppTargetIdForLogging(@Nullable ComponentName appComponentName) {
- StringBuilder appTargetId = new StringBuilder(
- "APPLICATION" + ":" + Process.myUserHandle().getIdentifier() + ":");
- if (appComponentName == null) return appTargetId.append(" : ").toString();
- return appTargetId + appComponentName.getPackageName() + ":"
- + appComponentName.getClassName();
- }
-
- /**
- * Generate gms play target id similar to AiAi targetId for logging only n-state.
- * AiAi target id is of format "resultType:userId:packageName:extraInfo"
- *
- * @return string playTargetId
- * Example playTargetId for Candy Crush
- * PLAY:0:com.king.candycrushsaga:Gms
- */
- public static String generatePlayTargetIdForLogging(String appPackage) {
- return "PLAY" + ":" + Process.myUserHandle().getIdentifier() + ":" + appPackage + ":Gms";
- }
-}
diff --git a/searchuilib/src/com/android/app/search/SearchTargetExtras.java b/searchuilib/src/com/android/app/search/SearchTargetExtras.java
deleted file mode 100644
index 887c457..0000000
--- a/searchuilib/src/com/android/app/search/SearchTargetExtras.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2023 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 com.android.app.search;
-
-import static com.android.app.search.LayoutType.TALL_CARD_WITH_IMAGE_NO_ICON;
-
-import android.app.blob.BlobHandle;
-import android.app.search.SearchAction;
-import android.app.search.SearchTarget;
-import android.os.Bundle;
-import android.text.TextUtils;
-
-import androidx.annotation.Nullable;
-
-/**
- * Helper class that defines key string value for {@link SearchTarget#getExtras()}
- * and also defines helper methods
- */
-public class SearchTargetExtras {
-
- /** on device data related extras and helper methods */
- // Used to extra component name.
- public static final String BUNDLE_EXTRA_CLASS = "class";
-
- // Used for UI treatment. Labels whether search target should support quick launch.
- public static final String BUNDLE_EXTRA_QUICK_LAUNCH = "quick_launch";
- // Used for UI treatment. Targets grouped with same group id are decorated together.
- public static final String BUNDLE_EXTRA_GROUP_ID = "group_id";
- public static final String BUNDLE_EXTRA_GROUP_DECORATE_TOGETHER = "decorate_together";
- // Used if slice title should be rendered else where outside of slice (e.g., edit text).
- public static final String BUNDLE_EXTRA_SLICE_TITLE = "slice_title";
- // Used if slice view should be rendered using full height mode.
- public static final String BUNDLE_EXTRA_USE_FULL_HEIGHT = "use_full_height";
- public static final String BUNDLE_EXTRA_IS_NON_TAPPABLE = "is_non_tappable";
- public static final String BUNDLE_EXTRA_TITLE_OVERWRITE = "title_overwrite";
- // Used if subtitle view should be overridden to string that is not natively defined by the
- // search target.
- public static final String BUNDLE_EXTRA_SUBTITLE_OVERRIDE = "subtitle_override";
-
- // Used for logging. Returns whether spelling correction was applied.
- public static final String BUNDLE_EXTRA_IS_QUERY_CORRECTED = "is_query_corrected";
- // Used for logging. Returns whether the result matched block title or the inline item.
- public static final String BUNDLE_EXTRA_RESULT_MATCH_USER_TYPED = "result_match_user_typed";
- // Used for logging. Returns the timestamp when system service received the data.
- public static final String BUNDLE_EXTRA_START_TIMESTAMP = "start_timestamp";
- // Indicates the search result app location column.
- public static final String BUNDLE_EXTRA_RESULT_APP_GRIDX = "app_gridx";
-
- // Used for thumbnail loading. Contains handle to retrieve Blobstore asset.
- public static final String BUNDLE_EXTRA_BLOBSTORE_HANDLE = "blobstore_handle_key";
-
- // Used to denote this searchTarget is for recent block in 0-state.
- public static final String EXTRAS_RECENT_BLOCK_TARGET = "recent_block_target";
-
- public static final int GROUPING = 1 << 1;
-
- @Nullable
- public static String getDecoratorId(@Nullable SearchTarget target) {
- return isTargetOrExtrasNull(target) ? null :
- target.getExtras().getString(BUNDLE_EXTRA_GROUP_ID);
- }
-
- public static int getDecoratorType(@Nullable SearchTarget target) {
- int type = 0;
- if (isTargetOrExtrasNull(target)) {
- return type;
- }
- if (!TextUtils.isEmpty(target.getExtras().getString(BUNDLE_EXTRA_GROUP_ID))) {
- type |= GROUPING;
- }
- return type;
- }
-
- /** Whether or not the SearchTarget's Extras contains a blobstore image. */
- public static boolean isSearchTargetBlobstoreAsset(@Nullable SearchTarget target) {
- if (isTargetOrExtrasNull(target)) {
- return false;
- }
- return target.getExtras().getParcelable(
- BUNDLE_EXTRA_BLOBSTORE_HANDLE) instanceof BlobHandle;
- }
-
- /** Check if SearchTarget contains information to tell if this target is from recent block. */
- public static boolean isSearchTargetRecentItem(@Nullable SearchTarget target) {
- if (isTargetOrExtrasNull(target)) {
- return false;
- }
- return target.getExtras().getBoolean(EXTRAS_RECENT_BLOCK_TARGET, false);
- }
-
- private static boolean isTargetOrExtrasNull(@Nullable SearchTarget target) {
- return target == null || target.getExtras() == null;
- }
-
- /** Web data related extras and helper methods */
- public static final String BUNDLE_EXTRA_PROXY_WEB_ITEM = "proxy_web_item";
- public static final String BUNDLE_EXTRA_ENTITY = "is_entity";
- public static final String BUNDLE_EXTRA_ANSWER = "is_answer";
- public static final String BUNDLE_EXTRA_RESPONSE_ID = "response_id";
- public static final String BUNDLE_EXTRA_LEARN_MORE_URL = "learn_more_url";
- public static final String BUNDLE_EXTRA_PERSONAL = "is_personal";
- public static final String BUNDLE_EXTRA_SUGGESTION_TYPE = "suggestion_type";
- public static final String BUNDLE_EXTRA_SUGGEST_RENDER_TEXT = "suggest_render_text";
- public static final String BUNDLE_EXTRA_ZERO_STATE_CACHE = "zero_state_cache";
- public static final String BUNDLE_EXTRA_TALL_CARD_HEADER = "tall_card_header";
- public static final String BUNDLE_EXTRA_TALL_CARD_IMAGE_DESCRIPTION =
- "tall_card_image_description";
- public static final String BUNDLE_EXTRA_BITMAP_URL = "bitmap_url";
-
- // Used for web suggestions count for both AA+ and QSB entry point.
- // Returns the number of web suggestions to be shown.
- public static final String WEB_SUG_COUNT = "web_sug_count";
-
- /**
- * Replaced with thumbnail crop type
- *
- * Flag to control whether thumbnail(s) should fill the thumbnail container's width or not.
- * When this flag is true, when there are less than the maximum number of thumbnails in the
- * container, the thumbnails will stretch to fill the container's width.
- * When this flag is false, thumbnails will always be cropped to a square ratio even if
- * there aren't enough thumbnails to fill the container.
- *
- * Only relevant in {@link LayoutType#THUMBNAIL_CONTAINER} and {@link LayoutType#THUMBNAIL}.
- */
- @Deprecated
- public static final String BUNDLE_EXTRA_SHOULD_FILL_CONTAINER_WIDTH =
- "should_fill_container_width";
-
- /**
- * Flag to control thumbnail container's crop mode, controlling the layout
- *
- * <ul>
- * <li>SQUARE: Thumbnail(s) will be cropped to a square aspect ratio around the center.</li>
- * <li>FILL_WIDTH: Thumbnail(s) should collectively fill the thumbnail container's width.
- * When there are less than the maximum number of thumbnails in the container, the
- * layouts' width will stretch to fit the container, the images will fill the width
- * and then the top/bottom cropped to fit.</li>
- * <li>FILL_HEIGHT: Thumbnail(s) should fill height and be cropped to fit in the width
- * based on {@link BUNDLE_EXTRA_THUMBNAIL_MAX_COUNT} as the column count. When the image
- * width is larger than the width / column, both sides will be cropped while maintaining
- * the center.
- * When there are less thumbnails than the max count, the layout will be constrained to
- * equally divide the width of the container. If there are more thumbnails than the max
- * count, the excessive thumbnails will be ignored.</li>
- * </ul>
- *
- * Only relevant in {@link LayoutType#THUMBNAIL_CONTAINER} and {@link LayoutType#THUMBNAIL}.
- */
- public static final String BUNDLE_EXTRA_THUMBNAIL_CROP_TYPE = "thumbnail_crop_type";
- public enum ThumbnailCropType {
- DEFAULT(0), // defaults to SQUARE behavior by {@link LayoutType#THUMBNAIL_CONTAINER}.
- SQUARE(1),
- FILL_WIDTH(2),
- FILL_HEIGHT(3);
-
- private final int mTypeId;
-
- ThumbnailCropType(int typeId) {
- mTypeId = typeId;
- }
-
- public int toTypeId() {
- return mTypeId;
- }
- };
-
- /**
- * How many grid spaces for the thumbnail container should be reserved.
- * Only relevant for {@link ThumbnailCropType#FILL_HEIGHT} crop type.
- */
- public static final String BUNDLE_EXTRA_THUMBNAIL_MAX_COUNT = "thumbnail_max_count";
-
- /**
- * Flag to control whether the SearchTarget's label should be hidden.
- * When this flag is true, label will be hidden.
- * When this flag is false (or omitted), {@link SearchAction#mTitle} will be shown.
- */
- public static final String BUNDLE_EXTRA_HIDE_LABEL =
- "hide_label";
- public static final String BUNDLE_EXTRA_SUGGESTION_ACTION_TEXT = "suggestion_action_text";
- public static final String BUNDLE_EXTRA_SUGGESTION_ACTION_RPC = "suggestion_action_rpc";
- public static final String BUNDLE_EXTRA_SUPPORT_QUERY_BUILDER = "support_query_builder";
- public static final String BUNDLE_EXTRA_SUGGEST_RAW_TEXT = "suggest_raw_text";
- public static final String BUNDLE_EXTRA_SUGGEST_TRUNCATE_START = "suggest_truncate_start";
-
- /** Web data related helper methods */
- public static boolean isEntity(@Nullable SearchTarget target) {
- return target != null && target.getExtras() != null
- && target.getExtras().getBoolean(BUNDLE_EXTRA_ENTITY);
- }
-
- public static boolean isAnswer(@Nullable SearchTarget target) {
- return target != null && target.getExtras() != null
- && target.getExtras().getBoolean(BUNDLE_EXTRA_ANSWER);
- }
-
- /** Whether the search target is a rich answer web result. */
- public static boolean isRichAnswer(@Nullable SearchTarget target) {
- return target != null && isAnswer(target)
- && target.getLayoutType().equals(TALL_CARD_WITH_IMAGE_NO_ICON);
- }
-
- /** Get the crop type thumbnails should use. Returns DEFAULT if not specified. */
- public static ThumbnailCropType getThumbnailCropType(@Nullable SearchTarget target)
- throws ArrayIndexOutOfBoundsException {
- Bundle extras = target == null ? Bundle.EMPTY : target.getExtras();
- if (extras.isEmpty()) {
- return ThumbnailCropType.DEFAULT;
- }
- ThumbnailCropType cropType = ThumbnailCropType.values()[extras.getInt(
- BUNDLE_EXTRA_THUMBNAIL_CROP_TYPE)];
- return cropType != null ? cropType : ThumbnailCropType.DEFAULT;
- }
-}
diff --git a/searchuilib/src/com/android/app/search/SearchTargetGenerator.java b/searchuilib/src/com/android/app/search/SearchTargetGenerator.java
deleted file mode 100644
index 22e5a86..0000000
--- a/searchuilib/src/com/android/app/search/SearchTargetGenerator.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2023 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 com.android.app.search;
-
-import static com.android.app.search.LayoutType.EMPTY_DIVIDER;
-import static com.android.app.search.LayoutType.SECTION_HEADER;
-import static com.android.app.search.ResultType.NO_FULFILLMENT;
-
-import android.app.search.SearchTarget;
-import android.os.Bundle;
-import android.os.Process;
-import android.os.UserHandle;
-
-public class SearchTargetGenerator {
- private static final UserHandle USERHANDLE = Process.myUserHandle();
-
- public static SearchTarget EMPTY_DIVIDER_TARGET =
- new SearchTarget.Builder(NO_FULFILLMENT, EMPTY_DIVIDER, "divider")
- .setPackageName("") /* required but not used*/
- .setUserHandle(USERHANDLE) /* required */
- .setExtras(new Bundle())
- .build();
-
- public static SearchTarget SECTION_HEADER_TARGET =
- new SearchTarget.Builder(NO_FULFILLMENT, SECTION_HEADER, "section_header")
- .setPackageName("") /* required but not used*/
- .setUserHandle(USERHANDLE) /* required */
- .setExtras(new Bundle())
- .build();
-}
diff --git a/viewcapturelib/build.gradle b/viewcapturelib/build.gradle
index 3f40ad6..e5442cc 100644
--- a/viewcapturelib/build.gradle
+++ b/viewcapturelib/build.gradle
@@ -1,5 +1,5 @@
plugins {
- id 'sysuigradleproject.android-library-conventions'
+ id 'com.android.library'
id 'org.jetbrains.kotlin.android'
id 'com.google.protobuf'
}
@@ -7,12 +7,9 @@ plugins {
final String PROTOS_DIR = "${ANDROID_TOP}/frameworks/libs/systemui/viewcapturelib/src/com/android/app/viewcapture/proto"
android {
- compileSdk TARGET_SDK.toInteger()
- buildToolsVersion = BUILD_TOOLS_VERSION
-
+ namespace = "com.android.app.viewcapture"
+ testNamespace = "com.android.app.viewcapture.test"
defaultConfig {
- minSdkVersion TARGET_SDK.toInteger()
- targetSdkVersion TARGET_SDK.toInteger()
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@@ -27,15 +24,15 @@ android {
manifest.srcFile "tests/AndroidManifest.xml"
}
}
-
- lintOptions {
+ lint {
abortOnError false
}
+
}
dependencies {
implementation "androidx.core:core:1.9.0"
- implementation "com.google.protobuf:protobuf-lite:${protobuf_version}"
+ implementation "com.google.protobuf:protobuf-lite:${protobuf_lite_version}"
androidTestImplementation project(':SharedTestLib')
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation "androidx.test:rules:1.4.0"
diff --git a/viewcapturelib/src/com/android/app/viewcapture/SettingsAwareViewCapture.kt b/viewcapturelib/src/com/android/app/viewcapture/SettingsAwareViewCapture.kt
index c84d4d5..8a3cf1c 100644
--- a/viewcapturelib/src/com/android/app/viewcapture/SettingsAwareViewCapture.kt
+++ b/viewcapturelib/src/com/android/app/viewcapture/SettingsAwareViewCapture.kt
@@ -17,16 +17,22 @@
package com.android.app.viewcapture
import android.content.Context
+import android.content.pm.LauncherApps
import android.database.ContentObserver
import android.os.Handler
import android.os.Looper
+import android.os.ParcelFileDescriptor
import android.os.Process
import android.provider.Settings
+import android.util.Log
import android.view.Choreographer
+import android.window.IDumpCallback
import androidx.annotation.AnyThread
import androidx.annotation.VisibleForTesting
import java.util.concurrent.Executor
+private val TAG = SettingsAwareViewCapture::class.java.simpleName
+
/**
* ViewCapture that listens to system updates and enables / disables attached ViewCapture
* WindowListeners accordingly. The Settings toggle is currently controlled by the Winscope
@@ -36,6 +42,16 @@ class SettingsAwareViewCapture
@VisibleForTesting
internal constructor(private val context: Context, choreographer: Choreographer, executor: Executor)
: ViewCapture(DEFAULT_MEMORY_SIZE, DEFAULT_INIT_POOL_SIZE, choreographer, executor) {
+ /** Dumps all the active view captures to the wm trace directory via LauncherAppService */
+ private val mDumpCallback: IDumpCallback.Stub = object : IDumpCallback.Stub() {
+ override fun onDump(out: ParcelFileDescriptor) {
+ try {
+ ParcelFileDescriptor.AutoCloseOutputStream(out).use { os -> dumpTo(os, context) }
+ } catch (e: Exception) {
+ Log.e(TAG, "failed to dump data to wm trace", e)
+ }
+ }
+ }
init {
enableOrDisableWindowListeners()
@@ -57,6 +73,12 @@ internal constructor(private val context: Context, choreographer: Choreographer,
MAIN_EXECUTOR.execute {
enableOrDisableWindowListeners(isEnabled)
}
+ val launcherApps = context.getSystemService(LauncherApps::class.java)
+ if (isEnabled) {
+ launcherApps?.registerDumpCallback(mDumpCallback)
+ } else {
+ launcherApps?.unRegisterDumpCallback(mDumpCallback)
+ }
}
}
diff --git a/viewcapturelib/src/com/android/app/viewcapture/SimpleViewCapture.kt b/viewcapturelib/src/com/android/app/viewcapture/SimpleViewCapture.kt
new file mode 100644
index 0000000..2773f6b
--- /dev/null
+++ b/viewcapturelib/src/com/android/app/viewcapture/SimpleViewCapture.kt
@@ -0,0 +1,8 @@
+package com.android.app.viewcapture
+
+import android.os.Process
+import android.view.Choreographer
+
+open class SimpleViewCapture(threadName: String) : ViewCapture(DEFAULT_MEMORY_SIZE, DEFAULT_INIT_POOL_SIZE,
+ MAIN_EXECUTOR.submit { Choreographer.getInstance() }.get(),
+ createAndStartNewLooperExecutor(threadName, Process.THREAD_PRIORITY_FOREGROUND)) \ No newline at end of file
diff --git a/viewcapturelib/src/com/android/app/viewcapture/ViewCapture.java b/viewcapturelib/src/com/android/app/viewcapture/ViewCapture.java
index fcd7ad8..fb5abd6 100644
--- a/viewcapturelib/src/com/android/app/viewcapture/ViewCapture.java
+++ b/viewcapturelib/src/com/android/app/viewcapture/ViewCapture.java
@@ -16,8 +16,6 @@
package com.android.app.viewcapture;
-import static java.util.stream.Collectors.toList;
-
import android.content.Context;
import android.content.res.Resources;
import android.media.permission.SafeCloseable;
@@ -25,10 +23,6 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.Trace;
import android.text.TextUtils;
-import android.util.Base64;
-import android.util.Base64OutputStream;
-import android.util.Log;
-import android.util.Pair;
import android.util.SparseArray;
import android.view.Choreographer;
import android.view.View;
@@ -36,25 +30,29 @@ import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
+import androidx.annotation.AnyThread;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import com.android.app.viewcapture.data.ExportedData;
import com.android.app.viewcapture.data.FrameData;
+import com.android.app.viewcapture.data.MotionWindowData;
import com.android.app.viewcapture.data.ViewNode;
+import com.android.app.viewcapture.data.WindowData;
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.OutputStream;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
-import java.util.concurrent.FutureTask;
import java.util.function.Consumer;
-import java.util.zip.GZIPOutputStream;
+import java.util.function.Predicate;
/**
* Utility class for capturing view data every frame
@@ -122,6 +120,7 @@ public abstract class ViewCapture {
/**
* Attaches the ViewCapture to the provided window and returns a handle to detach the listener
*/
+ @NonNull
public SafeCloseable startCapture(Window window) {
String title = window.getAttributes().getTitle().toString();
String name = TextUtils.isEmpty(title) ? window.toString() : title;
@@ -132,6 +131,7 @@ public abstract class ViewCapture {
* Attaches the ViewCapture to the provided window and returns a handle to detach the listener.
* Verifies that ViewCapture is enabled before actually attaching an onDrawListener.
*/
+ @NonNull
public SafeCloseable startCapture(View view, String name) {
WindowListener listener = new WindowListener(view, name);
if (mIsEnabled) MAIN_EXECUTOR.execute(listener::attachToRoot);
@@ -142,6 +142,25 @@ public abstract class ViewCapture {
};
}
+ /**
+ * Launcher checks for leaks in many spots during its instrumented tests. The WindowListeners
+ * appear to have leaks because they store mRoot views. In reality, attached views close their
+ * respective window listeners when they are destroyed.
+ * <p>
+ * This method deletes detaches and deletes mRoot views from windowListeners. This makes the
+ * WindowListeners unusable for anything except dumping previously captured information. They
+ * are still technically enabled to allow for dumping.
+ */
+ @VisibleForTesting
+ public void stopCapture(@NonNull View rootView) {
+ mListeners.forEach(it -> {
+ if (rootView == it.mRoot) {
+ it.mRoot.getViewTreeObserver().removeOnDrawListener(it);
+ it.mRoot = null;
+ }
+ });
+ }
+
@UiThread
protected void enableOrDisableWindowListeners(boolean isEnabled) {
mIsEnabled = isEnabled;
@@ -149,62 +168,46 @@ public abstract class ViewCapture {
if (mIsEnabled) mListeners.forEach(WindowListener::attachToRoot);
}
-
- /**
- * Dumps all the active view captures
- */
- public void dump(PrintWriter writer, FileDescriptor out, Context context) {
+ @AnyThread
+ public void dumpTo(OutputStream os, Context context)
+ throws InterruptedException, ExecutionException, IOException {
if (!mIsEnabled) {
return;
}
- ViewIdProvider idProvider = new ViewIdProvider(context.getResources());
+ ArrayList<Class> classList = new ArrayList<>();
+ ExportedData.newBuilder()
+ .setPackage(context.getPackageName())
+ .addAllWindowData(getWindowData(context, classList, l -> l.mIsActive).get())
+ .addAllClassname(toStringList(classList))
+ .build()
+ .writeTo(os);
+ }
- // Collect all the tasks first so that all the tasks are posted on the executor
- List<Pair<String, FutureTask<ExportedData>>> tasks = mListeners.stream()
- .map(l -> {
- FutureTask<ExportedData> task =
- new FutureTask<ExportedData>(() -> l.dumpToProto(idProvider));
- mBgExecutor.execute(task);
- return Pair.create(l.name, task);
- })
- .collect(toList());
- tasks.forEach(pair -> {
- writer.println();
- writer.println(" ContinuousViewCapture:");
- writer.println(" window " + pair.first + ":");
- writer.println(" pkg:" + context.getPackageName());
- writer.print(" data:");
- writer.flush();
- try (OutputStream os = new FileOutputStream(out)) {
- ExportedData data = pair.second.get();
- OutputStream encodedOS = new GZIPOutputStream(new Base64OutputStream(os,
- Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP));
- data.writeTo(encodedOS);
- encodedOS.close();
- os.flush();
- } catch (Exception e) {
- Log.e(TAG, "Error capturing proto", e);
- }
- writer.println();
- writer.println("--end--");
- });
+ private static List<String> toStringList(List<Class> classList) {
+ return classList.stream().map(Class::getName).toList();
}
- public Optional<FutureTask<ExportedData>> getDumpTask(View view) {
- Context context = view.getContext().getApplicationContext();
- ViewIdProvider idProvider = new ViewIdProvider(context.getResources());
+ public CompletableFuture<Optional<MotionWindowData>> getDumpTask(View view) {
+ ArrayList<Class> classList = new ArrayList<>();
+ return getWindowData(view.getContext().getApplicationContext(), classList,
+ l -> l.mRoot.equals(view)).thenApply(list -> list.stream().findFirst().map(w ->
+ MotionWindowData.newBuilder()
+ .addAllFrameData(w.getFrameDataList())
+ .addAllClassname(toStringList(classList))
+ .build()));
+ }
- return mListeners.stream()
- .filter(l -> l.mRoot.equals(view))
- .map(l -> {
- FutureTask<ExportedData> task =
- new FutureTask<ExportedData>(() -> l.dumpToProto(idProvider));
- mBgExecutor.execute(task);
- return task;
- })
- .findFirst();
+ @AnyThread
+ private CompletableFuture<List<WindowData>> getWindowData(Context context,
+ ArrayList<Class> outClassList, Predicate<WindowListener> filter) {
+ ViewIdProvider idProvider = new ViewIdProvider(context.getResources());
+ return CompletableFuture.supplyAsync(() ->
+ mListeners.stream().filter(filter).toList(), MAIN_EXECUTOR).thenApplyAsync(it ->
+ it.stream().map(l -> l.dumpToProto(idProvider, outClassList)).toList(),
+ mBgExecutor);
}
+
/**
* Once this window listener is attached to a window's root view, it traverses the entire
* view tree on the main thread every time onDraw is called. It then saves the state of the view
@@ -245,7 +248,8 @@ public abstract class ViewCapture {
*/
private class WindowListener implements ViewTreeObserver.OnDrawListener {
- public final View mRoot;
+ @Nullable // Nullable in tests only
+ public View mRoot;
public final String name;
private final ViewRef mViewRef = new ViewRef();
@@ -391,7 +395,9 @@ public abstract class ViewCapture {
void detachFromRoot() {
mIsActive = false;
- mRoot.getViewTreeObserver().removeOnDrawListener(this);
+ if (mRoot != null) {
+ mRoot.getViewTreeObserver().removeOnDrawListener(this);
+ }
}
private void safelyEnableOnDrawListener() {
@@ -400,11 +406,9 @@ public abstract class ViewCapture {
}
@WorkerThread
- private ExportedData dumpToProto(ViewIdProvider idProvider) {
+ private WindowData dumpToProto(ViewIdProvider idProvider, ArrayList<Class> classList) {
+ WindowData.Builder builder = WindowData.newBuilder().setTitle(name);
int size = (mNodesBg[mMemorySize - 1] == null) ? mFrameIndexBg + 1 : mMemorySize;
- ExportedData.Builder exportedDataBuilder = ExportedData.newBuilder();
- ArrayList<Class> classList = new ArrayList<>();
-
for (int i = size - 1; i >= 0; i--) {
int index = (mMemorySize + mFrameIndexBg - i) % mMemorySize;
ViewNode.Builder nodeBuilder = ViewNode.newBuilder();
@@ -412,11 +416,9 @@ public abstract class ViewCapture {
FrameData.Builder frameDataBuilder = FrameData.newBuilder()
.setNode(nodeBuilder)
.setTimestamp(mFrameTimesNanosBg[index]);
- exportedDataBuilder.addFrameData(frameDataBuilder);
+ builder.addFrameData(frameDataBuilder);
}
- return exportedDataBuilder
- .addAllClassname(classList.stream().map(Class::getName).collect(toList()))
- .build();
+ return builder.build();
}
private ViewRef captureViewTree(View view, ViewRef start) {
@@ -517,6 +519,8 @@ public abstract class ViewCapture {
.setHeight(bottom - top)
.setTranslationX(translateX)
.setTranslationY(translateY)
+ .setScrollX(scrollX)
+ .setScrollY(scrollY)
.setScaleX(scaleX)
.setScaleY(scaleY)
.setAlpha(alpha)
diff --git a/viewcapturelib/src/com/android/app/viewcapture/proto/view_capture.proto b/viewcapturelib/src/com/android/app/viewcapture/proto/view_capture.proto
index 899f678..d4df2ae 100644
--- a/viewcapturelib/src/com/android/app/viewcapture/proto/view_capture.proto
+++ b/viewcapturelib/src/com/android/app/viewcapture/proto/view_capture.proto
@@ -21,7 +21,17 @@ package com.android.app.viewcapture.data;
option java_multiple_files = true;
message ExportedData {
+ repeated WindowData windowData = 1;
+ optional string package = 2;
+ repeated string classname = 3;
+}
+
+message WindowData {
+ repeated FrameData frameData = 1;
+ optional string title = 2;
+}
+message MotionWindowData {
repeated FrameData frameData = 1;
repeated string classname = 2;
}
diff --git a/viewcapturelib/tests/AndroidManifest.xml b/viewcapturelib/tests/AndroidManifest.xml
index fb47dd8..8d31c0e 100644
--- a/viewcapturelib/tests/AndroidManifest.xml
+++ b/viewcapturelib/tests/AndroidManifest.xml
@@ -15,14 +15,13 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.app.viewcapture">
-
+ package="com.android.app.viewcapture.test">
<application
android:debuggable="true"
android:theme="@android:style/Theme.NoTitleBar">
<activity
- android:name=".TestActivity"
+ android:name="com.android.app.viewcapture.TestActivity"
android:exported="false" />
<uses-library android:name="android.test.runner" />
@@ -31,7 +30,7 @@
<instrumentation
android:name="android.testing.TestableInstrumentation"
- android:label="Tests for ViewCapture Lib"
- android:targetPackage="com.android.app.viewcapture" />
+ android:label="Tests for MotionTool Lib"
+ android:targetPackage="com.android.app.viewcapture.test"/>
</manifest>
diff --git a/viewcapturelib/tests/com/android/app/viewcapture/SettingsAwareViewCaptureTest.kt b/viewcapturelib/tests/com/android/app/viewcapture/SettingsAwareViewCaptureTest.kt
index e08b549..49d50bf 100644
--- a/viewcapturelib/tests/com/android/app/viewcapture/SettingsAwareViewCaptureTest.kt
+++ b/viewcapturelib/tests/com/android/app/viewcapture/SettingsAwareViewCaptureTest.kt
@@ -16,6 +16,7 @@
package com.android.app.viewcapture
+import android.Manifest
import android.content.Context
import android.content.Intent
import android.media.permission.SafeCloseable
@@ -26,6 +27,7 @@ import android.view.View
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.rule.GrantPermissionRule
import com.android.app.viewcapture.SettingsAwareViewCapture.Companion.VIEW_CAPTURE_ENABLED
import com.android.app.viewcapture.ViewCapture.MAIN_EXECUTOR
import junit.framework.Assert.assertEquals
@@ -40,6 +42,8 @@ class SettingsAwareViewCaptureTest {
private val activityIntent = Intent(context, TestActivity::class.java)
@get:Rule val activityScenarioRule = ActivityScenarioRule<TestActivity>(activityIntent)
+ @get:Rule val grantPermissionRule =
+ GrantPermissionRule.grant(Manifest.permission.WRITE_SECURE_SETTINGS)
@Test
fun do_not_capture_view_hierarchies_if_setting_is_disabled() {
@@ -48,14 +52,21 @@ class SettingsAwareViewCaptureTest {
activityScenarioRule.scenario.onActivity { activity ->
val viewCapture: ViewCapture =
SettingsAwareViewCapture(context, Choreographer.getInstance(), MAIN_EXECUTOR)
- val rootView: View = activity.findViewById(android.R.id.content)
+ val rootView: View = activity.requireViewById(android.R.id.content)
val closeable: SafeCloseable = viewCapture.startCapture(rootView, "rootViewId")
Choreographer.getInstance().postFrameCallback {
rootView.viewTreeObserver.dispatchOnDraw()
- assertEquals(0, viewCapture.getDumpTask(
- activity.findViewById(android.R.id.content)).get().get().frameDataList.size)
+ assertEquals(
+ 0,
+ viewCapture
+ .getDumpTask(activity.requireViewById(android.R.id.content))
+ .get()
+ .get()
+ .frameDataList
+ .size
+ )
closeable.close()
}
}
@@ -68,14 +79,21 @@ class SettingsAwareViewCaptureTest {
activityScenarioRule.scenario.onActivity { activity ->
val viewCapture: ViewCapture =
SettingsAwareViewCapture(context, Choreographer.getInstance(), MAIN_EXECUTOR)
- val rootView: View = activity.findViewById(android.R.id.content)
+ val rootView: View = activity.requireViewById(android.R.id.content)
val closeable: SafeCloseable = viewCapture.startCapture(rootView, "rootViewId")
Choreographer.getInstance().postFrameCallback {
rootView.viewTreeObserver.dispatchOnDraw()
- assertEquals(1, viewCapture.getDumpTask(activity.findViewById(
- android.R.id.content)).get().get().frameDataList.size)
+ assertEquals(
+ 1,
+ viewCapture
+ .getDumpTask(activity.requireViewById(android.R.id.content))
+ .get()
+ .get()
+ .frameDataList
+ .size
+ )
closeable.close()
}
diff --git a/viewcapturelib/tests/com/android/app/viewcapture/ViewCaptureTest.kt b/viewcapturelib/tests/com/android/app/viewcapture/ViewCaptureTest.kt
index b0fcca1..c1873a6 100644
--- a/viewcapturelib/tests/com/android/app/viewcapture/ViewCaptureTest.kt
+++ b/viewcapturelib/tests/com/android/app/viewcapture/ViewCaptureTest.kt
@@ -27,7 +27,7 @@ import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.app.viewcapture.TestActivity.Companion.TEXT_VIEW_COUNT
-import com.android.app.viewcapture.data.ExportedData
+import com.android.app.viewcapture.data.MotionWindowData
import junit.framework.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
@@ -50,40 +50,40 @@ class ViewCaptureTest {
@get:Rule val activityScenarioRule = ActivityScenarioRule<TestActivity>(activityIntent)
@Test
- fun testViewCaptureDumpsOneFrameAfterInvalidate() {
+ fun testWindowListenerDumpsOneFrameAfterInvalidate() {
activityScenarioRule.scenario.onActivity { activity ->
Choreographer.getInstance().postFrameCallback {
val closeable = startViewCaptureAndInvalidateNTimes(1, activity)
- val rootView = activity.findViewById<View>(android.R.id.content)
- val exportedData = viewCapture.getDumpTask(rootView).get().get()
+ val rootView = activity.requireViewById<View>(android.R.id.content)
+ val data = viewCapture.getDumpTask(rootView).get().get()
- assertEquals(1, exportedData.frameDataList.size)
- verifyTestActivityViewHierarchy(exportedData)
+ assertEquals(1, data.frameDataList.size)
+ verifyTestActivityViewHierarchy(data)
closeable.close()
}
}
}
@Test
- fun testViewCaptureDumpsCorrectlyAfterRecyclingStarted() {
+ fun testWindowListenerDumpsCorrectlyAfterRecyclingStarted() {
activityScenarioRule.scenario.onActivity { activity ->
Choreographer.getInstance().postFrameCallback {
val closeable = startViewCaptureAndInvalidateNTimes(memorySize + 5, activity)
- val rootView = activity.findViewById<View>(android.R.id.content)
- val exportedData = viewCapture.getDumpTask(rootView).get().get()
+ val rootView = activity.requireViewById<View>(android.R.id.content)
+ val data = viewCapture.getDumpTask(rootView).get().get()
// since ViewCapture MEMORY_SIZE is [viewCaptureMemorySize], only
// [viewCaptureMemorySize] frames are exported, although the view is invalidated
// [viewCaptureMemorySize + 5] times
- assertEquals(memorySize, exportedData.frameDataList.size)
- verifyTestActivityViewHierarchy(exportedData)
+ assertEquals(memorySize, data.frameDataList.size)
+ verifyTestActivityViewHierarchy(data)
closeable.close()
}
}
}
private fun startViewCaptureAndInvalidateNTimes(n: Int, activity: TestActivity): SafeCloseable {
- val rootView: View = activity.findViewById(android.R.id.content)
+ val rootView: View = activity.requireViewById(android.R.id.content)
val closeable: SafeCloseable = viewCapture.startCapture(rootView, "rootViewId")
dispatchOnDraw(rootView, times = n)
return closeable
@@ -96,7 +96,7 @@ class ViewCaptureTest {
}
}
- private fun verifyTestActivityViewHierarchy(exportedData: ExportedData) {
+ private fun verifyTestActivityViewHierarchy(exportedData: MotionWindowData) {
for (frame in exportedData.frameDataList) {
val testActivityRoot =
frame.node // FrameLayout (android.R.id.content)