summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/AppCompatTaskInfo.java29
-rw-r--r--core/java/android/hardware/display/DisplayTopology.java33
-rw-r--r--core/java/android/view/contentcapture/flags/content_capture_flags.aconfig2
-rw-r--r--core/java/android/window/WindowTokenClientController.java3
-rw-r--r--core/java/android/window/flags/large_screen_experiences_app_compat.aconfig7
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java44
-rw-r--r--core/jni/Android.bp13
-rw-r--r--core/jni/platform/darwin/libandroid_runtime_export.exp38
-rw-r--r--core/jni/platform/linux/libandroid_runtime_export.txt43
-rw-r--r--core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt86
-rw-r--r--core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java162
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java34
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java16
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt33
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java20
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java37
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupRestoreKeys.java61
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java28
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java41
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java4
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupRestoreKeysTest.java58
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/UpgradeControllerTest.java175
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java4
-rw-r--r--packages/SystemUI/res/drawable/audio_bars_idle.xml56
-rw-r--r--packages/SystemUI/res/layout/volume_dialog.xml2
-rw-r--r--packages/SystemUI/res/raw/audio_bars_in.json1
-rw-r--r--packages/SystemUI/res/raw/audio_bars_out.json1
-rw-r--r--packages/SystemUI/res/raw/audio_bars_playing.json1
-rw-r--r--packages/SystemUI/src/com/android/systemui/lottie/LottieTaskExt.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt197
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java7
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java1
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java4
-rw-r--r--services/core/java/com/android/server/display/DisplayTopologyCoordinator.java17
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java5
-rw-r--r--services/core/java/com/android/server/wm/AppCompatUtils.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt41
49 files changed, 1272 insertions, 205 deletions
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java
index 61b56877589b..599f1a8be233 100644
--- a/core/java/android/app/AppCompatTaskInfo.java
+++ b/core/java/android/app/AppCompatTaskInfo.java
@@ -27,6 +27,7 @@ import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* Stores App Compat information about a particular Task.
@@ -58,16 +59,11 @@ public class AppCompatTaskInfo implements Parcelable {
public int topActivityLetterboxHeight = PROPERTY_VALUE_UNSET;
/**
- * Contains the current app height of the letterboxed activity if available or
- * {@link TaskInfo#PROPERTY_VALUE_UNSET} otherwise.
+ * Contains the app bounds of the top activity or size compat mode
+ * bounds when in size compat mode. If null, contains bounds.
*/
- public int topActivityLetterboxAppHeight = PROPERTY_VALUE_UNSET;
-
- /**
- * Contains the current app width of the letterboxed activity if available or
- * {@link TaskInfo#PROPERTY_VALUE_UNSET} otherwise.
- */
- public int topActivityLetterboxAppWidth = PROPERTY_VALUE_UNSET;
+ @NonNull
+ public final Rect topActivityAppBounds = new Rect();
/**
* Contains the top activity bounds when the activity is letterboxed.
@@ -350,8 +346,7 @@ public class AppCompatTaskInfo implements Parcelable {
&& topActivityLetterboxVerticalPosition == that.topActivityLetterboxVerticalPosition
&& topActivityLetterboxWidth == that.topActivityLetterboxWidth
&& topActivityLetterboxHeight == that.topActivityLetterboxHeight
- && topActivityLetterboxAppWidth == that.topActivityLetterboxAppWidth
- && topActivityLetterboxAppHeight == that.topActivityLetterboxAppHeight
+ && topActivityAppBounds.equals(that.topActivityAppBounds)
&& topActivityLetterboxHorizontalPosition
== that.topActivityLetterboxHorizontalPosition
&& cameraCompatTaskInfo.equalsForTaskOrganizer(that.cameraCompatTaskInfo);
@@ -371,8 +366,7 @@ public class AppCompatTaskInfo implements Parcelable {
== that.topActivityLetterboxHorizontalPosition
&& topActivityLetterboxWidth == that.topActivityLetterboxWidth
&& topActivityLetterboxHeight == that.topActivityLetterboxHeight
- && topActivityLetterboxAppWidth == that.topActivityLetterboxAppWidth
- && topActivityLetterboxAppHeight == that.topActivityLetterboxAppHeight
+ && topActivityAppBounds.equals(that.topActivityAppBounds)
&& cameraCompatTaskInfo.equalsForCompatUi(that.cameraCompatTaskInfo);
}
@@ -385,8 +379,7 @@ public class AppCompatTaskInfo implements Parcelable {
topActivityLetterboxHorizontalPosition = source.readInt();
topActivityLetterboxWidth = source.readInt();
topActivityLetterboxHeight = source.readInt();
- topActivityLetterboxAppWidth = source.readInt();
- topActivityLetterboxAppHeight = source.readInt();
+ topActivityAppBounds.set(Objects.requireNonNull(source.readTypedObject(Rect.CREATOR)));
topActivityLetterboxBounds = source.readTypedObject(Rect.CREATOR);
cameraCompatTaskInfo = source.readTypedObject(CameraCompatTaskInfo.CREATOR);
}
@@ -401,8 +394,7 @@ public class AppCompatTaskInfo implements Parcelable {
dest.writeInt(topActivityLetterboxHorizontalPosition);
dest.writeInt(topActivityLetterboxWidth);
dest.writeInt(topActivityLetterboxHeight);
- dest.writeInt(topActivityLetterboxAppWidth);
- dest.writeInt(topActivityLetterboxAppHeight);
+ dest.writeTypedObject(topActivityAppBounds, flags);
dest.writeTypedObject(topActivityLetterboxBounds, flags);
dest.writeTypedObject(cameraCompatTaskInfo, flags);
}
@@ -421,8 +413,7 @@ public class AppCompatTaskInfo implements Parcelable {
+ topActivityLetterboxHorizontalPosition
+ " topActivityLetterboxWidth=" + topActivityLetterboxWidth
+ " topActivityLetterboxHeight=" + topActivityLetterboxHeight
- + " topActivityLetterboxAppWidth=" + topActivityLetterboxAppWidth
- + " topActivityLetterboxAppHeight=" + topActivityLetterboxAppHeight
+ + " topActivityAppBounds=" + topActivityAppBounds
+ " isUserFullscreenOverrideEnabled=" + isUserFullscreenOverrideEnabled()
+ " isSystemFullscreenOverrideEnabled=" + isSystemFullscreenOverrideEnabled()
+ " hasMinAspectRatioOverride=" + hasMinAspectRatioOverride()
diff --git a/core/java/android/hardware/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java
index 211aefffa34c..ba5dfc094afb 100644
--- a/core/java/android/hardware/display/DisplayTopology.java
+++ b/core/java/android/hardware/display/DisplayTopology.java
@@ -129,14 +129,38 @@ public final class DisplayTopology implements Parcelable {
}
/**
+ * Update the size of a display and normalize the topology.
+ * @param displayId The logical display ID
+ * @param width The new width
+ * @param height The new height
+ * @return True if the topology has changed.
+ */
+ public boolean updateDisplay(int displayId, float width, float height) {
+ TreeNode display = findDisplay(displayId, mRoot);
+ if (display == null) {
+ return false;
+ }
+ if (floatEquals(display.mWidth, width) && floatEquals(display.mHeight, height)) {
+ return false;
+ }
+ display.mWidth = width;
+ display.mHeight = height;
+ normalize();
+ Slog.i(TAG, "Display with ID " + displayId + " updated, new width: " + width
+ + ", new height: " + height);
+ return true;
+ }
+
+ /**
* Remove a display from the topology.
* The default topology is created from the remaining displays, as if they were reconnected
* one by one.
* @param displayId The logical display ID
+ * @return True if the display was present in the topology and removed.
*/
- public void removeDisplay(int displayId) {
+ public boolean removeDisplay(int displayId) {
if (findDisplay(displayId, mRoot) == null) {
- return;
+ return false;
}
Queue<TreeNode> queue = new ArrayDeque<>();
queue.add(mRoot);
@@ -159,6 +183,7 @@ public final class DisplayTopology implements Parcelable {
} else {
Slog.i(TAG, "Display with ID " + displayId + " removed");
}
+ return true;
}
/**
@@ -685,12 +710,12 @@ public final class DisplayTopology implements Parcelable {
/**
* The width of the display in density-independent pixels (dp).
*/
- private final float mWidth;
+ private float mWidth;
/**
* The height of the display in density-independent pixels (dp).
*/
- private final float mHeight;
+ private float mHeight;
/**
* The position of this display relative to its parent.
diff --git a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
index 20d193ea2351..f709ed7f57cd 100644
--- a/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
+++ b/core/java/android/view/contentcapture/flags/content_capture_flags.aconfig
@@ -12,5 +12,5 @@ flag {
name: "ccapi_baklava_enabled"
namespace: "machine_learning"
description: "Feature flag for baklava content capture API"
- bug: "309411951"
+ bug: "380381249"
}
diff --git a/core/java/android/window/WindowTokenClientController.java b/core/java/android/window/WindowTokenClientController.java
index 11019324acd8..fcd7dfbb1769 100644
--- a/core/java/android/window/WindowTokenClientController.java
+++ b/core/java/android/window/WindowTokenClientController.java
@@ -148,6 +148,9 @@ public class WindowTokenClientController {
info = wms.attachWindowContextToDisplayContent(mAppThread, client, displayId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed attachToDisplayContent", e);
+ return false;
}
if (info == null) {
return false;
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index 801698caff0e..0d04961569e6 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -58,13 +58,6 @@ flag {
}
flag {
- name: "user_min_aspect_ratio_app_default"
- namespace: "large_screen_experiences_app_compat"
- description: "Whether the API PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT is available"
- bug: "310816437"
-}
-
-flag {
name: "allow_hide_scm_button"
namespace: "large_screen_experiences_app_compat"
description: "Whether we should allow hiding the size compat restart button"
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 9bd52372e6c4..39ddea614ee4 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -25,6 +25,8 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
import static android.security.Flags.reportPrimaryAuthAttempts;
import static android.security.Flags.shouldTrustManagerListenForPrimaryAuth;
+import static com.android.internal.widget.flags.Flags.hideLastCharWithPhysicalInput;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -42,6 +44,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.hardware.input.InputManagerGlobal;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -59,6 +62,7 @@ import android.util.Log;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
+import android.view.InputDevice;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
@@ -1097,12 +1101,20 @@ public class LockPatternUtils {
return type == CREDENTIAL_TYPE_PATTERN;
}
+ private boolean hasActivePointerDeviceAttached() {
+ return !getEnabledNonTouchInputDevices(InputDevice.SOURCE_CLASS_POINTER).isEmpty();
+ }
+
/**
* @return Whether the visible pattern is enabled.
*/
@UnsupportedAppUsage
public boolean isVisiblePatternEnabled(int userId) {
- return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, true, userId);
+ boolean defaultValue = true;
+ if (hideLastCharWithPhysicalInput()) {
+ defaultValue = !hasActivePointerDeviceAttached();
+ }
+ return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, defaultValue, userId);
}
/**
@@ -1116,11 +1128,39 @@ public class LockPatternUtils {
return getString(Settings.Secure.LOCK_PATTERN_VISIBLE, userId) != null;
}
+ private List<InputDevice> getEnabledNonTouchInputDevices(int source) {
+ final InputManagerGlobal inputManager = InputManagerGlobal.getInstance();
+ final int[] inputIds = inputManager.getInputDeviceIds();
+ List<InputDevice> matchingDevices = new ArrayList<InputDevice>();
+ for (final int deviceId : inputIds) {
+ final InputDevice inputDevice = inputManager.getInputDevice(deviceId);
+ if (!inputDevice.isEnabled()) continue;
+ if (inputDevice.supportsSource(InputDevice.SOURCE_TOUCHSCREEN)) continue;
+ if (inputDevice.isVirtual()) continue;
+ if (!inputDevice.supportsSource(source)) continue;
+ matchingDevices.add(inputDevice);
+ }
+ return matchingDevices;
+ }
+
+ private boolean hasPhysicalKeyboardActive() {
+ final List<InputDevice> keyboards =
+ getEnabledNonTouchInputDevices(InputDevice.SOURCE_KEYBOARD);
+ for (final InputDevice keyboard : keyboards) {
+ if (keyboard.isFullKeyboard()) return true;
+ }
+ return false;
+ }
+
/**
* @return Whether enhanced pin privacy is enabled.
*/
public boolean isPinEnhancedPrivacyEnabled(int userId) {
- return getBoolean(LOCK_PIN_ENHANCED_PRIVACY, false, userId);
+ boolean defaultValue = false;
+ if (hideLastCharWithPhysicalInput()) {
+ defaultValue = hasPhysicalKeyboardActive();
+ }
+ return getBoolean(LOCK_PIN_ENHANCED_PRIVACY, defaultValue, userId);
}
/**
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 027113a75f6b..5acdf32a9f84 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -482,11 +482,22 @@ cc_library_shared_for_libandroid_runtime {
"libbinder",
"libhidlbase", // libhwbinder is in here
],
+ version_script: "platform/linux/libandroid_runtime_export.txt",
+ },
+ darwin: {
+ host_ldlibs: [
+ "-framework AppKit",
+ ],
+ dist: {
+ targets: ["layoutlib_jni"],
+ dir: "layoutlib_native/darwin",
+ },
+ exported_symbols_list: "platform/darwin/libandroid_runtime_export.exp",
},
linux_glibc_x86_64: {
ldflags: ["-static-libgcc"],
dist: {
- targets: ["layoutlib"],
+ targets: ["layoutlib_jni"],
dir: "layoutlib_native/linux",
tag: "stripped_all",
},
diff --git a/core/jni/platform/darwin/libandroid_runtime_export.exp b/core/jni/platform/darwin/libandroid_runtime_export.exp
new file mode 100644
index 000000000000..00a7585719ea
--- /dev/null
+++ b/core/jni/platform/darwin/libandroid_runtime_export.exp
@@ -0,0 +1,38 @@
+#
+# Copyright (C) 2024 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.
+#
+
+# symbols needed for the JNI operations
+_JNI_OnLoad
+_ANativeWindow*
+
+# symbols needed to link with layoutlib_jni
+___android_log*
+__ZNK7android7RefBase*
+__ZN7android4base9SetLogger*
+__ZN7android4base10SetAborter*
+__ZN7android4base11GetProperty*
+__ZN7android4Rect*
+__ZN7android5Fence*
+__ZN7android7RefBase*
+__ZN7android7String*
+__ZN7android10VectorImpl*
+__ZN7android11BufferQueue*
+__ZN7android14AndroidRuntime*
+__ZN7android14sp_report_raceEv*
+__ZN7android15KeyCharacterMap*
+__ZN7android15InputDeviceInfo*
+__ZN7android31android_view_InputDevice_create*
+__ZN7android53android_view_Surface_createFromIGraphicBufferProducer*
diff --git a/core/jni/platform/linux/libandroid_runtime_export.txt b/core/jni/platform/linux/libandroid_runtime_export.txt
new file mode 100644
index 000000000000..19e3478d5d38
--- /dev/null
+++ b/core/jni/platform/linux/libandroid_runtime_export.txt
@@ -0,0 +1,43 @@
+#
+# Copyright (C) 2024 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.
+#
+
+{
+ global:
+ # symbols needed for the JNI operations
+ JNI_OnLoad;
+ ANativeWindow*;
+
+ # symbols needed to link with layoutlib_jni
+ __android_log*;
+ _ZNK7android7RefBase*;
+ _ZN7android4base9SetLogger*;
+ _ZN7android4base10SetAborter*;
+ _ZN7android4base11GetProperty*;
+ _ZN7android4Rect*;
+ _ZN7android5Fence*;
+ _ZN7android7RefBase*;
+ _ZN7android7String*;
+ _ZN7android10VectorImpl*;
+ _ZN7android11BufferQueue*;
+ _ZN7android14AndroidRuntime*;
+ _ZN7android14sp_report_raceEv*;
+ _ZN7android15KeyCharacterMap*;
+ _ZN7android15InputDeviceInfo*;
+ _ZN7android31android_view_InputDevice_create*;
+ _ZN7android53android_view_Surface_createFromIGraphicBufferProducer*;
+ local:
+ *;
+};
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
index 4a227d8ff1ef..255d09b854bd 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
+++ b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
@@ -86,7 +86,7 @@ class DisplayTopologyTest {
verifyDisplay(display1, displayId1, width1, height1, noOfChildren = 1)
val display2 = display1.children[0]
- verifyDisplay(display1.children[0], displayId2, width2, height2, POSITION_TOP,
+ verifyDisplay(display2, displayId2, width2, height2, POSITION_TOP,
offset = width1 / 2 - width2 / 2, noOfChildren = 1)
var display = display2
@@ -99,6 +99,76 @@ class DisplayTopologyTest {
}
@Test
+ fun updateDisplay() {
+ val displayId = 1
+ val width = 800f
+ val height = 600f
+
+ val newWidth = 1000f
+ val newHeight = 500f
+ topology.addDisplay(displayId, width, height)
+ assertThat(topology.updateDisplay(displayId, newWidth, newHeight)).isTrue()
+
+ assertThat(topology.primaryDisplayId).isEqualTo(displayId)
+ verifyDisplay(topology.root!!, displayId, newWidth, newHeight, noOfChildren = 0)
+ }
+
+ @Test
+ fun updateDisplay_notUpdated() {
+ val displayId = 1
+ val width = 800f
+ val height = 600f
+ topology.addDisplay(displayId, width, height)
+
+ // Same size
+ assertThat(topology.updateDisplay(displayId, width, height)).isFalse()
+
+ // Display doesn't exist
+ assertThat(topology.updateDisplay(/* displayId= */ 100, width, height)).isFalse()
+
+ assertThat(topology.primaryDisplayId).isEqualTo(displayId)
+ verifyDisplay(topology.root!!, displayId, width, height, noOfChildren = 0)
+ }
+
+ @Test
+ fun updateDisplayDoesNotAffectDefaultTopology() {
+ val width1 = 700f
+ val height = 600f
+ topology.addDisplay(/* displayId= */ 1, width1, height)
+
+ val width2 = 800f
+ val noOfDisplays = 30
+ for (i in 2..noOfDisplays) {
+ topology.addDisplay(/* displayId= */ i, width2, height)
+ }
+
+ val displaysToUpdate = arrayOf(3, 7, 18)
+ val newWidth = 1000f
+ val newHeight = 1500f
+ for (i in displaysToUpdate) {
+ assertThat(topology.updateDisplay(/* displayId= */ i, newWidth, newHeight)).isTrue()
+ }
+
+ assertThat(topology.primaryDisplayId).isEqualTo(1)
+
+ val display1 = topology.root!!
+ verifyDisplay(display1, id = 1, width1, height, noOfChildren = 1)
+
+ val display2 = display1.children[0]
+ verifyDisplay(display2, id = 2, width2, height, POSITION_TOP,
+ offset = width1 / 2 - width2 / 2, noOfChildren = 1)
+
+ var display = display2
+ for (i in 3..noOfDisplays) {
+ display = display.children[0]
+ // The last display should have no children
+ verifyDisplay(display, id = i, if (i in displaysToUpdate) newWidth else width2,
+ if (i in displaysToUpdate) newHeight else height, POSITION_RIGHT, offset = 0f,
+ noOfChildren = if (i < noOfDisplays) 1 else 0)
+ }
+ }
+
+ @Test
fun removeDisplays() {
val displayId1 = 1
val width1 = 800f
@@ -117,7 +187,7 @@ class DisplayTopologyTest {
}
var removedDisplays = arrayOf(20)
- topology.removeDisplay(20)
+ assertThat(topology.removeDisplay(20)).isTrue()
assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
@@ -139,11 +209,11 @@ class DisplayTopologyTest {
noOfChildren = if (i < noOfDisplays) 1 else 0)
}
- topology.removeDisplay(22)
+ assertThat(topology.removeDisplay(22)).isTrue()
removedDisplays += 22
- topology.removeDisplay(23)
+ assertThat(topology.removeDisplay(23)).isTrue()
removedDisplays += 23
- topology.removeDisplay(25)
+ assertThat(topology.removeDisplay(25)).isTrue()
removedDisplays += 25
assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
@@ -174,7 +244,7 @@ class DisplayTopologyTest {
val height = 600f
topology.addDisplay(displayId, width, height)
- topology.removeDisplay(displayId)
+ assertThat(topology.removeDisplay(displayId)).isTrue()
assertThat(topology.primaryDisplayId).isEqualTo(Display.INVALID_DISPLAY)
assertThat(topology.root).isNull()
@@ -187,7 +257,7 @@ class DisplayTopologyTest {
val height = 600f
topology.addDisplay(displayId, width, height)
- topology.removeDisplay(3)
+ assertThat(topology.removeDisplay(3)).isFalse()
assertThat(topology.primaryDisplayId).isEqualTo(displayId)
verifyDisplay(topology.root!!, displayId, width, height, noOfChildren = 0)
@@ -203,7 +273,7 @@ class DisplayTopologyTest {
topology = DisplayTopology(/* root= */ null, displayId2)
topology.addDisplay(displayId1, width, height)
topology.addDisplay(displayId2, width, height)
- topology.removeDisplay(displayId2)
+ assertThat(topology.removeDisplay(displayId2)).isTrue()
assertThat(topology.primaryDisplayId).isEqualTo(displayId1)
verifyDisplay(topology.root!!, displayId1, width, height, noOfChildren = 0)
diff --git a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
index 00b4f464de50..d1fbc77cbd46 100644
--- a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
@@ -31,6 +31,7 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doNothing;
@@ -44,20 +45,27 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.UserInfo;
+import android.hardware.input.IInputManager;
+import android.hardware.input.InputManagerGlobal;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.platform.test.ravenwood.RavenwoodRule;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
+import android.view.InputDevice;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.widget.flags.Flags;
import com.google.android.collect.Lists;
@@ -76,6 +84,8 @@ import java.util.concurrent.CompletableFuture;
public class LockPatternUtilsTest {
@Rule
public final RavenwoodRule mRavenwood = new RavenwoodRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private ILockSettings mLockSettings;
private static final int USER_ID = 1;
@@ -395,4 +405,156 @@ public class LockPatternUtilsTest {
}
};
}
+
+ private InputManagerGlobal.TestSession configureExternalHardwareTest(InputDevice[] devices)
+ throws RemoteException {
+ final Context context = new ContextWrapper(InstrumentationRegistry.getTargetContext());
+ final ILockSettings ils = mock(ILockSettings.class);
+ when(ils.getBoolean(anyString(), anyBoolean(), anyInt())).thenThrow(RemoteException.class);
+ mLockPatternUtils = new LockPatternUtils(context, ils);
+
+ IInputManager inputManagerMock = mock(IInputManager.class);
+
+ int[] deviceIds = new int[devices.length];
+
+ for (int i = 0; i < devices.length; i++) {
+ when(inputManagerMock.getInputDevice(i)).thenReturn(devices[i]);
+ }
+
+ when(inputManagerMock.getInputDeviceIds()).thenReturn(deviceIds);
+ InputManagerGlobal.TestSession session =
+ InputManagerGlobal.createTestSession(inputManagerMock);
+
+ return session;
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isPinEnhancedPrivacyEnabled_noDevicesAttached() throws RemoteException {
+ InputManagerGlobal.TestSession session = configureExternalHardwareTest(new InputDevice[0]);
+ assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isPinEnhancedPrivacyEnabled_noEnabledDeviceAttached() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder.setEnabled(false);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isPinEnhancedPrivacyEnabled_withoutHwKeyboard() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder.setEnabled(true).setSources(InputDevice.SOURCE_TOUCHSCREEN);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isPinEnhancedPrivacyEnabled_withoutFullHwKeyboard() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder
+ .setEnabled(true)
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_NON_ALPHABETIC);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isPinEnhancedPrivacyEnabled_withHwKeyboardOldDefault() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder
+ .setEnabled(true)
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isPinEnhancedPrivacyEnabled_withHwKeyboard() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder
+ .setEnabled(true)
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertTrue(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isVisiblePatternEnabled_noDevices() throws RemoteException {
+ InputManagerGlobal.TestSession session = configureExternalHardwareTest(new InputDevice[0]);
+ assertTrue(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isVisiblePatternEnabled_noEnabledDevices() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder.setEnabled(false);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertTrue(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isVisiblePatternEnabled_noPointingDevices() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder
+ .setEnabled(true)
+ .setSources(InputDevice.SOURCE_TOUCHSCREEN);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertTrue(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isVisiblePatternEnabled_externalPointingDevice() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder
+ .setEnabled(true)
+ .setSources(InputDevice.SOURCE_CLASS_POINTER);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertFalse(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
+ session.close();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
+ public void isVisiblePatternEnabled_externalPointingDeviceOldDefault() throws RemoteException {
+ InputDevice.Builder builder = new InputDevice.Builder();
+ builder
+ .setEnabled(true)
+ .setSources(InputDevice.SOURCE_CLASS_POINTER);
+ InputManagerGlobal.TestSession session =
+ configureExternalHardwareTest(new InputDevice[]{builder.build()});
+ assertTrue(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
+ session.close();
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
index 606a729305b4..90191345147c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -82,7 +82,7 @@ fun calculateInitialBounds(
// For portrait resizeable activities, respect apps fullscreen width but
// apply ideal size height.
Size(
- taskInfo.appCompatTaskInfo.topActivityLetterboxAppWidth,
+ taskInfo.appCompatTaskInfo.topActivityAppBounds.width(),
idealSize.height,
)
} else {
@@ -104,7 +104,7 @@ fun calculateInitialBounds(
// apply custom app width.
Size(
customPortraitWidthForLandscapeApp,
- taskInfo.appCompatTaskInfo.topActivityLetterboxAppHeight,
+ taskInfo.appCompatTaskInfo.topActivityAppBounds.height(),
)
} else {
// For portrait resizeable activities, simply apply ideal size.
@@ -196,13 +196,8 @@ fun maximizeSizeGivenAspectRatio(
/** Calculates the aspect ratio of an activity from its fullscreen bounds. */
fun calculateAspectRatio(taskInfo: RunningTaskInfo): Float {
- val appLetterboxWidth = taskInfo.appCompatTaskInfo.topActivityLetterboxAppWidth
- val appLetterboxHeight = taskInfo.appCompatTaskInfo.topActivityLetterboxAppHeight
- if (taskInfo.appCompatTaskInfo.isTopActivityLetterboxed || !taskInfo.canChangeAspectRatio) {
- return maxOf(appLetterboxWidth, appLetterboxHeight) /
- minOf(appLetterboxWidth, appLetterboxHeight).toFloat()
- }
- val appBounds = taskInfo.configuration.windowConfiguration.appBounds ?: return 1f
+ if (taskInfo.appCompatTaskInfo.topActivityAppBounds.isEmpty) return 1f
+ val appBounds = taskInfo.appCompatTaskInfo.topActivityAppBounds
return maxOf(appBounds.height(), appBounds.width()) /
minOf(appBounds.height(), appBounds.width()).toFloat()
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
index 37296531ee34..fd387d1811fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
@@ -350,7 +350,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
}
cancelPhysicsAnimation();
mMenuController.hideMenu(ANIM_TYPE_DISMISS, false /* resize */);
- mPipScheduler.scheduleRemovePip();
+ mPipScheduler.removePipAfterAnimation();
}
/** Sets the movement bounds to use to constrain PIP position animations. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index 7f673d2efc68..4461a5c6a70c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -122,26 +122,34 @@ public class PipScheduler {
* Schedules exit PiP via expand transition.
*/
public void scheduleExitPipViaExpand() {
- mMainExecutor.execute(() -> {
- if (!mPipTransitionState.isInPip()) return;
- WindowContainerTransaction wct = getExitPipViaExpandTransaction();
- if (wct != null) {
+ WindowContainerTransaction wct = getExitPipViaExpandTransaction();
+ if (wct != null) {
+ mMainExecutor.execute(() -> {
mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct,
null /* destinationBounds */);
- }
- });
+ });
+ }
+ }
+
+ // TODO: Optimize this by running the animation as part of the transition
+ /** Runs remove PiP animation and schedules remove PiP transition after the animation ends. */
+ public void removePipAfterAnimation() {
+ SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+ PipAlphaAnimator animator = mPipAlphaAnimatorSupplier.get(mContext,
+ mPipTransitionState.getPinnedTaskLeash(), tx, PipAlphaAnimator.FADE_OUT);
+ animator.setAnimationEndCallback(this::scheduleRemovePipImmediately);
+ animator.start();
}
/** Schedules remove PiP transition. */
- public void scheduleRemovePip() {
- mMainExecutor.execute(() -> {
- if (!mPipTransitionState.isInPip()) return;
- WindowContainerTransaction wct = getRemovePipTransaction();
- if (wct != null) {
+ private void scheduleRemovePipImmediately() {
+ WindowContainerTransaction wct = getRemovePipTransaction();
+ if (wct != null) {
+ mMainExecutor.execute(() -> {
mPipTransitionController.startExitTransition(TRANSIT_REMOVE_PIP, wct,
null /* destinationBounds */);
- }
- });
+ });
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 2e38449d4584..acb5622b041c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -278,8 +278,7 @@ public class PipTransition extends PipTransitionController implements
}
if (isRemovePipTransition(info)) {
- mPipTransitionState.setState(PipTransitionState.EXITING_PIP);
- return startRemoveAnimation(info, startTransaction, finishTransaction, finishCallback);
+ return removePipImmediately(info, startTransaction, finishTransaction, finishCallback);
}
return false;
}
@@ -669,18 +668,13 @@ public class PipTransition extends PipTransitionController implements
return true;
}
- private boolean startRemoveAnimation(@NonNull TransitionInfo info,
+ private boolean removePipImmediately(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- TransitionInfo.Change pipChange = getChangeByToken(info,
- mPipTransitionState.getPipTaskToken());
- mFinishCallback = finishCallback;
- PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipChange.getLeash(),
- startTransaction, PipAlphaAnimator.FADE_OUT);
- finishTransaction.setAlpha(pipChange.getLeash(), 0f);
- animator.setAnimationEndCallback(this::finishTransition);
- animator.start();
+ startTransaction.apply();
+ finishCallback.onTransitionFinished(null);
+ mPipTransitionState.setState(PipTransitionState.EXITED_PIP);
return true;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 4f37180baa37..e1c2153014fa 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -4160,8 +4160,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
configuration.windowConfiguration.appBounds = bounds
}
- appCompatTaskInfo.topActivityLetterboxAppWidth = bounds.width()
- appCompatTaskInfo.topActivityLetterboxAppHeight = bounds.height()
+ appCompatTaskInfo.topActivityAppBounds.set(0, 0, bounds.width(), bounds.height())
isResizeable = false
}
@@ -4879,15 +4878,19 @@ class DesktopTasksControllerTest : ShellTestCase() {
appCompatTaskInfo.isSystemFullscreenOverrideEnabled = enableSystemFullscreenOverride
if (deviceOrientation == ORIENTATION_LANDSCAPE) {
- configuration.windowConfiguration.appBounds =
- Rect(0, 0, DISPLAY_DIMENSION_LONG, DISPLAY_DIMENSION_SHORT)
- appCompatTaskInfo.topActivityLetterboxAppWidth = DISPLAY_DIMENSION_LONG
- appCompatTaskInfo.topActivityLetterboxAppHeight = DISPLAY_DIMENSION_SHORT
+ appCompatTaskInfo.topActivityAppBounds.set(
+ 0,
+ 0,
+ DISPLAY_DIMENSION_LONG,
+ DISPLAY_DIMENSION_SHORT,
+ )
} else {
- configuration.windowConfiguration.appBounds =
- Rect(0, 0, DISPLAY_DIMENSION_SHORT, DISPLAY_DIMENSION_LONG)
- appCompatTaskInfo.topActivityLetterboxAppWidth = DISPLAY_DIMENSION_SHORT
- appCompatTaskInfo.topActivityLetterboxAppHeight = DISPLAY_DIMENSION_LONG
+ appCompatTaskInfo.topActivityAppBounds.set(
+ 0,
+ 0,
+ DISPLAY_DIMENSION_SHORT,
+ DISPLAY_DIMENSION_LONG,
+ )
}
if (shouldLetterbox) {
@@ -4897,17 +4900,15 @@ class DesktopTasksControllerTest : ShellTestCase() {
screenOrientation == SCREEN_ORIENTATION_PORTRAIT
) {
// Letterbox to portrait size
- appCompatTaskInfo.setTopActivityLetterboxed(true)
- appCompatTaskInfo.topActivityLetterboxAppWidth = 1200
- appCompatTaskInfo.topActivityLetterboxAppHeight = 1600
+ appCompatTaskInfo.isTopActivityLetterboxed = true
+ appCompatTaskInfo.topActivityAppBounds.set(0, 0, 1200, 1600)
} else if (
deviceOrientation == ORIENTATION_PORTRAIT &&
screenOrientation == SCREEN_ORIENTATION_LANDSCAPE
) {
// Letterbox to landscape size
- appCompatTaskInfo.setTopActivityLetterboxed(true)
- appCompatTaskInfo.topActivityLetterboxAppWidth = 1600
- appCompatTaskInfo.topActivityLetterboxAppHeight = 1200
+ appCompatTaskInfo.isTopActivityLetterboxed = true
+ appCompatTaskInfo.topActivityAppBounds.set(0, 0, 1600, 1200)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
index a8aa25700c7e..3fe8c109807a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
@@ -120,22 +120,15 @@ public class PipSchedulerTest {
@Test
public void scheduleExitPipViaExpand_nullTaskToken_noop() {
setNullPipTaskToken();
- when(mMockPipTransitionState.isInPip()).thenReturn(true);
mPipScheduler.scheduleExitPipViaExpand();
- verify(mMockMainExecutor, times(1)).execute(mRunnableArgumentCaptor.capture());
- assertNotNull(mRunnableArgumentCaptor.getValue());
- mRunnableArgumentCaptor.getValue().run();
-
- verify(mMockPipTransitionController, never())
- .startExitTransition(eq(TRANSIT_EXIT_PIP), any(), isNull());
+ verify(mMockMainExecutor, never()).execute(any());
}
@Test
public void scheduleExitPipViaExpand_exitTransitionCalled() {
setMockPipTaskToken();
- when(mMockPipTransitionState.isInPip()).thenReturn(true);
mPipScheduler.scheduleExitPipViaExpand();
@@ -149,13 +142,20 @@ public class PipSchedulerTest {
@Test
public void removePipAfterAnimation() {
+ //TODO: Update once this is changed to run animation as part of transition
setMockPipTaskToken();
- when(mMockPipTransitionState.isInPip()).thenReturn(true);
- mPipScheduler.scheduleRemovePip();
+ mPipScheduler.removePipAfterAnimation();
+ verify(mMockAlphaAnimator, times(1))
+ .setAnimationEndCallback(mRunnableArgumentCaptor.capture());
+ assertNotNull(mRunnableArgumentCaptor.getValue());
+ verify(mMockAlphaAnimator, times(1)).start();
+
+ mRunnableArgumentCaptor.getValue().run();
verify(mMockMainExecutor, times(1)).execute(mRunnableArgumentCaptor.capture());
assertNotNull(mRunnableArgumentCaptor.getValue());
+
mRunnableArgumentCaptor.getValue().run();
verify(mMockPipTransitionController, times(1))
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 1c4def39eaa0..e01cb84f60ae 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -16,6 +16,19 @@
package com.android.providers.settings;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_DEVICE_SPECIFIC_CONFIG;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_GLOBAL;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_LOCALE;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_LOCK_SETTINGS;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_NETWORK_POLICIES;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SECURE;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SIM_SPECIFIC_SETTINGS;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SIM_SPECIFIC_SETTINGS_2;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SOFTAP_CONFIG;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SYSTEM;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_WIFI_NEW_CONFIG;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_WIFI_SETTINGS_BACKUP_DATA;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -99,22 +112,6 @@ public class SettingsBackupAgent extends BackupAgentHelper {
private static final int NULL_SIZE = -1;
private static final float FONT_SCALE_DEF_VALUE = 1.0f;
- private static final String KEY_SYSTEM = "system";
- private static final String KEY_SECURE = "secure";
- private static final String KEY_GLOBAL = "global";
- private static final String KEY_LOCALE = "locale";
- private static final String KEY_LOCK_SETTINGS = "lock_settings";
- private static final String KEY_SOFTAP_CONFIG = "softap_config";
- private static final String KEY_NETWORK_POLICIES = "network_policies";
- private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
- private static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config";
- private static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings";
- // Restoring sim-specific data backed up from newer Android version to Android 12 was causing a
- // fatal crash. Creating a backup with a different key will prevent Android 12 versions from
- // restoring this data.
- private static final String KEY_SIM_SPECIFIC_SETTINGS_2 = "sim_specific_settings_2";
- private static final String KEY_WIFI_SETTINGS_BACKUP_DATA = "wifi_settings_backup_data";
-
// Versioning of the state file. Increment this version
// number any time the set of state items is altered.
private static final int STATE_VERSION = 9;
@@ -257,6 +254,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
if (com.android.server.backup.Flags.enableMetricsSettingsBackupAgents()) {
mBackupRestoreEventLogger = this.getBackupRestoreEventLogger();
+ mSettingsHelper.setBackupRestoreEventLogger(mBackupRestoreEventLogger);
numberOfSettingsPerKey = new HashMap<>();
areAgentMetricsEnabled = true;
}
@@ -412,9 +410,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
mSettingsHelper
.setLocaleData(
localeData,
- size,
- mBackupRestoreEventLogger,
- KEY_LOCALE);
+ size);
break;
case KEY_WIFI_CONFIG :
@@ -552,8 +548,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
if (nBytes > buffer.length) buffer = new byte[nBytes];
in.readFully(buffer, 0, nBytes);
mSettingsHelper
- .setLocaleData(
- buffer, nBytes, mBackupRestoreEventLogger, KEY_LOCALE);
+ .setLocaleData(buffer, nBytes);
// Restore older backups performing the necessary migrations.
if (version < FULL_BACKUP_ADDED_WIFI_NEW) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupRestoreKeys.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupRestoreKeys.java
new file mode 100644
index 000000000000..745c2fb5409d
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupRestoreKeys.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 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.providers.settings;
+
+import android.net.Uri;
+import android.provider.Settings;
+
+/**
+ * Class to store the keys used for backup and restore.
+ */
+final class SettingsBackupRestoreKeys {
+ static final String KEY_UNKNOWN = "unknown";
+ static final String KEY_SYSTEM = "system";
+ static final String KEY_SECURE = "secure";
+ static final String KEY_GLOBAL = "global";
+ static final String KEY_LOCALE = "locale";
+ static final String KEY_LOCK_SETTINGS = "lock_settings";
+ static final String KEY_SOFTAP_CONFIG = "softap_config";
+ static final String KEY_NETWORK_POLICIES = "network_policies";
+ static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
+ static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config";
+ static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings";
+ // Restoring sim-specific data backed up from newer Android version to Android 12 was causing a
+ // fatal crash. Creating a backup with a different key will prevent Android 12 versions from
+ // restoring this data.
+ static final String KEY_SIM_SPECIFIC_SETTINGS_2 = "sim_specific_settings_2";
+ static final String KEY_WIFI_SETTINGS_BACKUP_DATA = "wifi_settings_backup_data";
+
+ /**
+ * Returns the key corresponding to the given URI.
+ *
+ * @param uri The URI of the setting's destination.
+ * @return The key corresponding to the given URI, or KEY_UNKNOWN if the URI is not recognized.
+ */
+ static String getKeyFromUri(Uri uri) {
+ if (uri.equals(Settings.Secure.CONTENT_URI)) {
+ return KEY_SECURE;
+ } else if (uri.equals(Settings.System.CONTENT_URI)) {
+ return KEY_SYSTEM;
+ } else if (uri.equals(Settings.Global.CONTENT_URI)) {
+ return KEY_GLOBAL;
+ } else {
+ return KEY_UNKNOWN;
+ }
+ }
+
+} \ No newline at end of file
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 924c151a99a0..ab8d739feb43 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -17,6 +17,7 @@
package com.android.providers.settings;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.backup.BackupRestoreEventLogger;
@@ -84,6 +85,7 @@ public class SettingsHelper {
private Context mContext;
private AudioManager mAudioManager;
private TelephonyManager mTelephonyManager;
+ @Nullable private BackupRestoreEventLogger mBackupRestoreEventLogger;
/**
* A few settings elements are special in that a restore of those values needs to
@@ -741,11 +743,8 @@ public class SettingsHelper {
*
* @param data the comma separated BCP-47 language tags in bytes.
* @param size the size of the data in bytes.
- * @param backupRestoreEventLogger the logger to log the restore event.
- * @param dataType the data type of the setting for logging purposes.
*/
- /* package */ void setLocaleData(
- byte[] data, int size, BackupRestoreEventLogger backupRestoreEventLogger, String dataType) {
+ /* package */ void setLocaleData(byte[] data, int size) {
final Configuration conf = mContext.getResources().getConfiguration();
// Replace "_" with "-" to deal with older backups.
@@ -772,15 +771,15 @@ public class SettingsHelper {
am.updatePersistentConfigurationWithAttribution(config, mContext.getOpPackageName(),
mContext.getAttributionTag());
- if (Flags.enableMetricsSettingsBackupAgents()) {
- backupRestoreEventLogger
- .logItemsRestored(dataType, localeList.size());
+ if (Flags.enableMetricsSettingsBackupAgents() && mBackupRestoreEventLogger != null) {
+ mBackupRestoreEventLogger
+ .logItemsRestored(SettingsBackupRestoreKeys.KEY_LOCALE, localeList.size());
}
} catch (RemoteException e) {
- if (Flags.enableMetricsSettingsBackupAgents()) {
- backupRestoreEventLogger
+ if (Flags.enableMetricsSettingsBackupAgents() && mBackupRestoreEventLogger != null) {
+ mBackupRestoreEventLogger
.logItemsRestoreFailed(
- dataType,
+ SettingsBackupRestoreKeys.KEY_LOCALE,
localeList.size(),
ERROR_REMOTE_EXCEPTION_SETTING_LOCALE_DATA);
}
@@ -795,4 +794,13 @@ public class SettingsHelper {
AudioManager am = new AudioManager(mContext);
am.reloadAudioSettings();
}
+
+ /**
+ * Sets the backup restore event logger.
+ *
+ * @param backupRestoreEventLogger the logger to log B&R metrics.
+ */
+ void setBackupRestoreEventLogger(BackupRestoreEventLogger backupRestoreEventLogger) {
+ mBackupRestoreEventLogger = backupRestoreEventLogger;
+ }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 55f48e3e367f..f1f03c31f718 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -120,6 +120,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.display.RefreshRateSettingsUtils;
import com.android.internal.os.BackgroundThread;
@@ -2914,6 +2915,14 @@ public class SettingsProvider extends ContentProvider {
};
}
+ @VisibleForTesting
+ void injectServices(UserManager userManager, IPackageManager packageManager,
+ SystemConfigManager sysConfigManager) {
+ mUserManager = userManager;
+ mPackageManager = packageManager;
+ mSysConfigManager = sysConfigManager;
+ }
+
private static final class Arguments {
private static final Pattern WHERE_PATTERN_WITH_PARAM_NO_BRACKETS =
Pattern.compile("[\\s]*name[\\s]*=[\\s]*\\?[\\s]*");
@@ -3080,6 +3089,7 @@ public class SettingsProvider extends ContentProvider {
private static final String SSAID_USER_KEY = "userkey";
+ @GuardedBy("mLock")
private final SparseArray<SettingsState> mSettingsStates = new SparseArray<>();
private GenerationRegistry mGenerationRegistry;
@@ -3992,6 +4002,14 @@ public class SettingsProvider extends ContentProvider {
}
}
+ @VisibleForTesting
+ void injectSettings(SettingsState settings, int type, int userId) {
+ int key = makeKey(type, userId);
+ synchronized (mLock) {
+ mSettingsStates.put(key, settings);
+ }
+ }
+
private final class MyHandler extends Handler {
private static final int MSG_NOTIFY_URI_CHANGED = 1;
private static final int MSG_NOTIFY_DATA_CHANGED = 2;
@@ -4023,12 +4041,21 @@ public class SettingsProvider extends ContentProvider {
}
}
- private final class UpgradeController {
+ @VisibleForTesting
+ final class UpgradeController {
private static final int SETTINGS_VERSION = 226;
private final int mUserId;
+ private final Injector mInjector;
+
public UpgradeController(int userId) {
+ this(/* injector= */ null, userId);
+ }
+
+ @VisibleForTesting
+ UpgradeController(Injector injector, int userId) {
+ mInjector = injector == null ? new Injector() : injector;
mUserId = userId;
}
@@ -6136,8 +6163,8 @@ public class SettingsProvider extends ContentProvider {
systemSettings.getSettingLocked(Settings.System.PEAK_REFRESH_RATE);
final Setting minRefreshRateSetting =
systemSettings.getSettingLocked(Settings.System.MIN_REFRESH_RATE);
- float highestRefreshRate = RefreshRateSettingsUtils
- .findHighestRefreshRateForDefaultDisplay(getContext());
+ float highestRefreshRate =
+ mInjector.findHighestRefreshRateForDefaultDisplay(getContext());
if (!peakRefreshRateSetting.isNull()) {
try {
@@ -6318,6 +6345,14 @@ public class SettingsProvider extends ContentProvider {
private long getBitMask(int capability) {
return 1 << (capability - 1);
}
+
+ @VisibleForTesting
+ static class Injector {
+ float findHighestRefreshRateForDefaultDisplay(Context context) {
+ return RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(
+ context);
+ }
+ }
}
/**
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 5cd534e62ea9..bf3afeda448e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -107,7 +107,7 @@ import static com.android.aconfig_new_storage.Flags.enableAconfigStorageDaemon;
* the same lock to grab the current state to write to disk.
* </p>
*/
-final class SettingsState {
+public class SettingsState {
private static final boolean DEBUG = false;
private static final boolean DEBUG_PERSISTENCE = false;
@@ -1838,7 +1838,7 @@ final class SettingsState {
}
}
- class Setting {
+ public class Setting {
private String name;
private String value;
private String defaultValue;
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupRestoreKeysTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupRestoreKeysTest.java
new file mode 100644
index 000000000000..ef537e8c7fc0
--- /dev/null
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupRestoreKeysTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 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.providers.settings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.Uri;
+import android.provider.Settings;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link SettingsBackupRestoreKeys}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class SettingsBackupRestoreKeysTest {
+
+ @Test
+ public void getKeyFromUri_secureUri_returnsSecureKey() {
+ assertThat(SettingsBackupRestoreKeys.getKeyFromUri(Settings.Secure.CONTENT_URI))
+ .isEqualTo(SettingsBackupRestoreKeys.KEY_SECURE);
+ }
+
+ @Test
+ public void getKeyFromUri_systemUri_returnsSystemKey() {
+ assertThat(SettingsBackupRestoreKeys.getKeyFromUri(Settings.System.CONTENT_URI))
+ .isEqualTo(SettingsBackupRestoreKeys.KEY_SYSTEM);
+ }
+
+ @Test
+ public void getKeyFromUri_globalUri_returnsGlobalKey() {
+ assertThat(SettingsBackupRestoreKeys.getKeyFromUri(Settings.Global.CONTENT_URI))
+ .isEqualTo(SettingsBackupRestoreKeys.KEY_GLOBAL);
+ }
+
+ @Test
+ public void getKeyFromUri_unknownUri_returnsUnknownKey() {
+ assertThat(SettingsBackupRestoreKeys.getKeyFromUri(Uri.parse("content://unknown")))
+ .isEqualTo(SettingsBackupRestoreKeys.KEY_UNKNOWN);
+ }
+} \ No newline at end of file
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/UpgradeControllerTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/UpgradeControllerTest.java
new file mode 100644
index 000000000000..26ff376f828e
--- /dev/null
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/UpgradeControllerTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2024 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.providers.settings;
+
+import static android.provider.Settings.System.MIN_REFRESH_RATE;
+import static android.provider.Settings.System.PEAK_REFRESH_RATE;
+
+import static com.android.providers.settings.SettingsProvider.SETTINGS_TYPE_GLOBAL;
+import static com.android.providers.settings.SettingsProvider.SETTINGS_TYPE_SECURE;
+import static com.android.providers.settings.SettingsProvider.SETTINGS_TYPE_SYSTEM;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.IPackageManager;
+import android.os.Looper;
+import android.os.SystemConfigManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class UpgradeControllerTest {
+ private static final int USER_ID = UserHandle.USER_SYSTEM;
+ private static final float HIGHEST_REFRESH_RATE = 130f;
+
+ private final Context mContext =
+ spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+ private final SettingsProvider.SettingsRegistry.UpgradeController.Injector mInjector =
+ new SettingsProvider.SettingsRegistry.UpgradeController.Injector() {
+ @Override
+ float findHighestRefreshRateForDefaultDisplay(Context context) {
+ return HIGHEST_REFRESH_RATE;
+ }
+ };
+ private final SettingsProvider mSettingsProvider = new SettingsProvider() {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+ };
+ private final SettingsProvider.SettingsRegistry mSettingsRegistry =
+ mSettingsProvider.new SettingsRegistry(Looper.getMainLooper());
+ private final SettingsProvider.SettingsRegistry.UpgradeController mUpgradeController =
+ mSettingsRegistry.new UpgradeController(mInjector, USER_ID);
+
+ @Mock
+ private UserManager mUserManager;
+
+ @Mock
+ private IPackageManager mPackageManager;
+
+ @Mock
+ private SystemConfigManager mSysConfigManager;
+
+ @Mock
+ private SettingsState mSystemSettings;
+
+ @Mock
+ private SettingsState mSecureSettings;
+
+ @Mock
+ private SettingsState mGlobalSettings;
+
+ @Mock
+ private SettingsState.Setting mMockSetting;
+
+ @Mock
+ private SettingsState.Setting mPeakRefreshRateSetting;
+
+ @Mock
+ private SettingsState.Setting mMinRefreshRateSetting;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mSettingsProvider.attachInfoForTesting(mContext, /* info= */ null);
+ mSettingsProvider.injectServices(mUserManager, mPackageManager, mSysConfigManager);
+ when(mSystemSettings.getSettingLocked(any())).thenReturn(mMockSetting);
+ when(mSecureSettings.getSettingLocked(any())).thenReturn(mMockSetting);
+ when(mGlobalSettings.getSettingLocked(any())).thenReturn(mMockSetting);
+ when(mMockSetting.isNull()).thenReturn(true);
+ when(mMockSetting.getValue()).thenReturn("0");
+
+ when(mSystemSettings.getSettingLocked(PEAK_REFRESH_RATE))
+ .thenReturn(mPeakRefreshRateSetting);
+ when(mSystemSettings.getSettingLocked(MIN_REFRESH_RATE))
+ .thenReturn(mMinRefreshRateSetting);
+
+ mSettingsRegistry.injectSettings(mSystemSettings, SETTINGS_TYPE_SYSTEM, USER_ID);
+ mSettingsRegistry.injectSettings(mSecureSettings, SETTINGS_TYPE_SECURE, USER_ID);
+ mSettingsRegistry.injectSettings(mGlobalSettings, SETTINGS_TYPE_GLOBAL, USER_ID);
+
+ // Lowest version so that all upgrades are run
+ when(mSecureSettings.getVersionLocked()).thenReturn(118);
+ }
+
+ @Test
+ public void testUpgrade_refreshRateSettings_defaultValues() {
+ when(mPeakRefreshRateSetting.isNull()).thenReturn(true);
+ when(mMinRefreshRateSetting.isNull()).thenReturn(true);
+
+ mUpgradeController.upgradeIfNeededLocked();
+
+ // Should remain unchanged
+ verify(mSystemSettings, never()).insertSettingLocked(eq(PEAK_REFRESH_RATE),
+ /* value= */ any(), /* tag= */ any(), /* makeDefault= */ anyBoolean(),
+ /* packageName= */ any());
+ verify(mSystemSettings, never()).insertSettingLocked(eq(MIN_REFRESH_RATE),
+ /* value= */ any(), /* tag= */ any(), /* makeDefault= */ anyBoolean(),
+ /* packageName= */ any());
+ }
+
+ @Test
+ public void testUpgrade_refreshRateSettings_enabled() {
+ when(mPeakRefreshRateSetting.isNull()).thenReturn(false);
+ when(mMinRefreshRateSetting.isNull()).thenReturn(false);
+ when(mPeakRefreshRateSetting.getValue()).thenReturn(String.valueOf(HIGHEST_REFRESH_RATE));
+ when(mMinRefreshRateSetting.getValue()).thenReturn(String.valueOf(HIGHEST_REFRESH_RATE));
+
+ mUpgradeController.upgradeIfNeededLocked();
+
+ // Highest refresh rate gets converted to infinity
+ verify(mSystemSettings).insertSettingLocked(eq(PEAK_REFRESH_RATE),
+ eq(String.valueOf(Float.POSITIVE_INFINITY)), /* tag= */ any(),
+ /* makeDefault= */ anyBoolean(), /* packageName= */ any());
+ verify(mSystemSettings).insertSettingLocked(eq(MIN_REFRESH_RATE),
+ eq(String.valueOf(Float.POSITIVE_INFINITY)), /* tag= */ any(),
+ /* makeDefault= */ anyBoolean(), /* packageName= */ any());
+ }
+
+ @Test
+ public void testUpgrade_refreshRateSettings_disabled() {
+ when(mPeakRefreshRateSetting.isNull()).thenReturn(false);
+ when(mMinRefreshRateSetting.isNull()).thenReturn(false);
+ when(mPeakRefreshRateSetting.getValue()).thenReturn("70f");
+ when(mMinRefreshRateSetting.getValue()).thenReturn("70f");
+
+ mUpgradeController.upgradeIfNeededLocked();
+
+ // Should remain unchanged
+ verify(mSystemSettings, never()).insertSettingLocked(eq(PEAK_REFRESH_RATE),
+ /* value= */ any(), /* tag= */ any(), /* makeDefault= */ anyBoolean(),
+ /* packageName= */ any());
+ verify(mSystemSettings, never()).insertSettingLocked(eq(MIN_REFRESH_RATE),
+ /* value= */ any(), /* tag= */ any(), /* makeDefault= */ anyBoolean(),
+ /* packageName= */ any());
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index 8a5c96da5ac6..0438b811513e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -180,7 +180,6 @@ object OverlayShade {
object Dimensions {
val PanelCornerRadius = 46.dp
- val OverscrollLimit = 32.dp
}
object Shapes {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
index b88861756889..5c6657b98801 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
@@ -8,6 +8,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -231,7 +232,8 @@ public class InternetAdapterTest extends SysuiTestCase {
mViewHolder.onWifiClick(mWifiEntry, mock(View.class));
- verify(mSpyContext).startActivity(any());
+ verify(mInternetDialogController).startActivityForDialog(any());
+ verify(mSpyContext, never()).startActivity(any());
}
@Test
diff --git a/packages/SystemUI/res/drawable/audio_bars_idle.xml b/packages/SystemUI/res/drawable/audio_bars_idle.xml
new file mode 100644
index 000000000000..92a24755fece
--- /dev/null
+++ b/packages/SystemUI/res/drawable/audio_bars_idle.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2021 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="168dp"
+ android:height="168dp"
+ android:viewportWidth="168"
+ android:viewportHeight="168">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_2_G"
+ android:translateX="121.161"
+ android:translateY="83.911">
+ <path
+ android:name="_R_G_L_2_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " />
+ </group>
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="102.911"
+ android:translateY="83.911">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="139.661"
+ android:translateY="83.911">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-37.16 -5.87 C-33.94,-5.87 -31.32,-3.32 -31.2,-0.13 C-31.2,-0.06 -31.2,0.02 -31.2,0.09 C-31.2,0.16 -31.2,0.23 -31.2,0.29 C-31.31,3.49 -33.94,6.05 -37.16,6.05 C-40.39,6.05 -43.01,3.49 -43.12,0.29 C-43.12,0.23 -43.12,0.16 -43.12,0.09 C-43.12,0.01 -43.12,-0.07 -43.12,-0.15 C-42.99,-3.33 -40.37,-5.87 -37.16,-5.87c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index a3bad8f012ac..5ccedeafcb59 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -56,8 +56,8 @@
android:layout_marginTop="@dimen/volume_dialog_components_spacing"
android:background="@drawable/ripple_drawable_20dp"
android:contentDescription="@string/accessibility_volume_settings"
+ android:scaleType="centerInside"
android:soundEffectsEnabled="false"
- android:src="@drawable/horizontal_ellipsis"
android:tint="@androidprv:color/materialColorPrimary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container"
diff --git a/packages/SystemUI/res/raw/audio_bars_in.json b/packages/SystemUI/res/raw/audio_bars_in.json
new file mode 100644
index 000000000000..c90a59c47d64
--- /dev/null
+++ b/packages/SystemUI/res/raw/audio_bars_in.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":18,"w":168,"h":168,"nm":"audio_bars_in","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":5,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":5,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[102.5,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[65.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":5,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[47.25,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":17,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":5,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/audio_bars_out.json b/packages/SystemUI/res/raw/audio_bars_out.json
new file mode 100644
index 000000000000..5eab65e057ab
--- /dev/null
+++ b/packages/SystemUI/res/raw/audio_bars_out.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":31,"w":168,"h":168,"nm":"audio_bars_out","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":5,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":5,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[102.5,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[65.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":5,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[47.25,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]},{"t":30,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":5,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/audio_bars_playing.json b/packages/SystemUI/res/raw/audio_bars_playing.json
new file mode 100644
index 000000000000..6ee8e1915f36
--- /dev/null
+++ b/packages/SystemUI/res/raw/audio_bars_playing.json
@@ -0,0 +1 @@
+{"v":"5.7.13","fr":60,"ip":0,"op":121,"w":168,"h":168,"nm":"audio_bars_playing","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[120.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":38,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[-0.016,-14.1],[5.941,-8.358],[5.961,0],[5.958,8.516],[0,14.274],[-5.958,8.515],[-5.961,0],[-5.972,-8.373]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":70,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":102,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[-0.016,-14.1],[5.941,-8.358],[5.961,0],[5.958,8.516],[0,14.274],[-5.958,8.515],[-5.961,0],[-5.972,-8.373]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-22.725],[5.957,-16.983],[5.961,0],[5.958,17.391],[0,23.149],[-5.958,17.39],[-5.961,0],[-5.957,-16.998]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[102.5,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":32,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-19.1],[5.957,-13.358],[5.961,0],[5.958,13.641],[0,19.399],[-5.958,13.64],[-5.961,0],[-5.957,-13.373]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":65,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":97,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-19.1],[5.957,-13.358],[5.961,0],[5.958,13.641],[0,19.399],[-5.958,13.64],[-5.961,0],[-5.957,-13.373]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-38.225],[5.957,-32.483],[5.961,0],[5.958,32.016],[0,37.774],[-5.958,32.015],[-5.961,0],[-5.957,-32.498]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[65.75,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":29,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-15.85],[5.957,-10.108],[5.961,0],[5.958,10.516],[0,16.274],[-5.958,10.515],[-5.961,0],[-5.957,-10.123]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":59,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":91,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-15.85],[5.957,-10.108],[5.961,0],[5.958,10.516],[0,16.274],[-5.958,10.515],[-5.961,0],[-5.957,-10.123]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-25.1],[5.957,-19.358],[5.961,0],[5.958,19.516],[0,25.274],[-5.958,19.515],[-5.961,0],[-5.957,-19.373]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[84,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":24,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-9.225],[5.957,-3.483],[5.961,0],[5.958,3.766],[0,9.524],[-5.958,3.765],[-5.961,0],[-5.957,-3.498]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":54,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":86,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-9.225],[5.957,-3.483],[5.961,0],[5.958,3.766],[0,9.524],[-5.958,3.765],[-5.961,0],[-5.957,-3.498]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-18.6],[5.957,-12.858],[5.961,0],[5.958,13.141],[0,18.899],[-5.958,13.14],[-5.961,0],[-5.957,-12.873]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[47.25,84,0],"ix":2,"l":2},"a":{"a":0,"k":[-37.161,0.089,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":19,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":48,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":81,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-5.961],[5.957,-0.219],[5.961,0],[5.958,0.203],[0,5.961],[-5.958,0.203],[-5.961,0],[-5.957,-0.235]],"c":true}]},{"t":120,"s":[{"i":[[-3.214,0],[-0.115,-3.191],[0,-0.073],[0.002,-0.067],[3.224,0],[0.107,3.198],[0,0.068],[-0.003,0.078]],"o":[[3.219,0],[0.003,0.073],[0,0.068],[-0.107,3.198],[-3.224,0],[-0.002,-0.067],[0,-0.079],[0.123,-3.184]],"v":[[0,-13.475],[5.957,-7.733],[5.961,0],[5.958,6.766],[0,12.524],[-5.958,6.765],[-5.961,0],[-5.957,-7.748]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-37.161,0.089],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":121,"st":0,"bm":0}],"markers":[{"tm":60,"cm":"1","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/lottie/LottieTaskExt.kt b/packages/SystemUI/src/com/android/systemui/lottie/LottieTaskExt.kt
new file mode 100644
index 000000000000..dd2525f5ca45
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lottie/LottieTaskExt.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 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.systemui.lottie
+
+import com.airbnb.lottie.LottieComposition
+import com.airbnb.lottie.LottieListener
+import com.airbnb.lottie.LottieTask
+import kotlin.coroutines.resume
+import kotlin.coroutines.resumeWithException
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+/**
+ * Suspends until [LottieTask] is finished with a result or a failure.
+ *
+ * @return result of the [LottieTask] when it's successful
+ */
+suspend fun LottieTask<LottieComposition>.await() =
+ suspendCancellableCoroutine<LottieComposition> { continuation ->
+ val resultListener =
+ LottieListener<LottieComposition> { result ->
+ with(continuation) { if (!isCancelled && !isCompleted) resume(result) }
+ }
+ val failureListener =
+ LottieListener<Throwable> { throwable ->
+ with(continuation) {
+ if (!isCancelled && !isCompleted) resumeWithException(throwable)
+ }
+ }
+ addListener(resultListener)
+ addFailureListener(failureListener)
+ continuation.invokeOnCancellation {
+ removeListener(resultListener)
+ removeFailureListener(failureListener)
+ }
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
index 19b45d50c594..7516ca030d4b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -193,7 +193,7 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern
if (mJob == null) {
mJob = WifiUtils.checkWepAllowed(mContext, mCoroutineScope, wifiEntry.getSsid(),
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, intent -> {
- mInternetDialogController.startActivity(intent, view);
+ mInternetDialogController.startActivityForDialog(intent);
return null;
}, () -> {
wifiConnect(wifiEntry, view);
@@ -211,7 +211,7 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern
true /* connectForCaller */);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
- mContext.startActivity(intent);
+ mInternetDialogController.startActivityForDialog(intent);
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index dbe1ae90b3f6..7036ef914a1c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -781,6 +781,10 @@ public class InternetDialogController implements AccessPointController.AccessPoi
mActivityStarter.postStartActivityDismissingKeyguard(intent, 0, controller);
}
+ void startActivityForDialog(Intent intent) {
+ mActivityStarter.startActivity(intent, false /* dismissShade */);
+ }
+
void launchNetworkSetting(View view) {
startActivity(getSettingsIntent(), view);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
index 2e1f82d56fc4..70e342f3eefb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/binder/VolumeDialogSettingsButtonViewBinder.kt
@@ -17,45 +17,28 @@
package com.android.systemui.volume.dialog.settings.ui.binder
import android.view.View
-import com.android.systemui.lifecycle.WindowLifecycleState
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.lifecycle.setSnapshotBinding
-import com.android.systemui.lifecycle.viewModel
+import android.widget.ImageButton
import com.android.systemui.res.R
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.settings.ui.viewmodel.VolumeDialogSettingsButtonViewModel
import javax.inject.Inject
-import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@VolumeDialogScope
class VolumeDialogSettingsButtonViewBinder
@Inject
-constructor(private val viewModelFactory: VolumeDialogSettingsButtonViewModel.Factory) {
+constructor(private val viewModel: VolumeDialogSettingsButtonViewModel) {
- fun bind(view: View) {
- with(view) {
- val button = requireViewById<View>(R.id.volume_dialog_settings)
- repeatWhenAttached {
- viewModel(
- traceName = "VolumeDialogViewBinder",
- minWindowLifecycleState = WindowLifecycleState.ATTACHED,
- factory = { viewModelFactory.create() },
- ) { viewModel ->
- setSnapshotBinding {
- viewModel.isVisible
- .onEach { isVisible ->
- visibility = if (isVisible) View.VISIBLE else View.GONE
- }
- .launchIn(this)
+ fun CoroutineScope.bind(view: View) {
+ val button = view.requireViewById<ImageButton>(R.id.volume_dialog_settings)
+ viewModel.isVisible
+ .onEach { isVisible -> button.visibility = if (isVisible) View.VISIBLE else View.GONE }
+ .launchIn(this)
- button.setOnClickListener { viewModel.onButtonClicked() }
- }
+ viewModel.icon.onEach { button.setImageDrawable(it) }.launchIn(this)
- awaitCancellation()
- }
- }
- }
+ button.setOnClickListener { viewModel.onButtonClicked() }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
index 015d773b2c02..03442dbcde66 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/ui/viewmodel/VolumeDialogSettingsButtonViewModel.kt
@@ -14,27 +14,206 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.volume.dialog.settings.ui.viewmodel
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.ColorFilter
+import android.graphics.drawable.Drawable
+import android.media.session.PlaybackState
+import androidx.annotation.ColorInt
+import com.airbnb.lottie.LottieComposition
+import com.airbnb.lottie.LottieCompositionFactory
+import com.airbnb.lottie.LottieDrawable
+import com.airbnb.lottie.LottieProperty
+import com.airbnb.lottie.SimpleColorFilter
+import com.airbnb.lottie.model.KeyPath
+import com.airbnb.lottie.value.LottieValueCallback
+import com.android.internal.R as internalR
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.UiBackground
+import com.android.systemui.lottie.await
+import com.android.systemui.res.R
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.settings.domain.VolumeDialogSettingsButtonInteractor
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaDeviceSessionInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
+import com.android.systemui.volume.panel.shared.model.filterData
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.resume
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.FlowCollector
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.distinctUntilChangedBy
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.runningFold
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.transform
+import kotlinx.coroutines.suspendCancellableCoroutine
class VolumeDialogSettingsButtonViewModel
-@AssistedInject
-constructor(private val interactor: VolumeDialogSettingsButtonInteractor) {
+@Inject
+constructor(
+ @Application private val context: Context,
+ @UiBackground private val uiBgCoroutineContext: CoroutineContext,
+ @VolumeDialog private val coroutineScope: CoroutineScope,
+ mediaOutputInteractor: MediaOutputInteractor,
+ private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
+ private val interactor: VolumeDialogSettingsButtonInteractor,
+) {
+
+ @SuppressLint("UseCompatLoadingForDrawables")
+ private val drawables: Flow<Drawables> =
+ flow {
+ val color = context.getColor(internalR.color.materialColorPrimary)
+ emit(
+ Drawables(
+ start =
+ LottieCompositionFactory.fromRawRes(context, R.raw.audio_bars_in)
+ .await()
+ .toDrawable { setColor(color) },
+ playing =
+ LottieCompositionFactory.fromRawRes(context, R.raw.audio_bars_playing)
+ .await()
+ .toDrawable {
+ repeatCount = LottieDrawable.INFINITE
+ repeatMode = LottieDrawable.RESTART
+ setColor(color)
+ },
+ stop =
+ LottieCompositionFactory.fromRawRes(context, R.raw.audio_bars_out)
+ .await()
+ .toDrawable { setColor(color) },
+ idle = context.getDrawable(R.drawable.audio_bars_idle)!!,
+ )
+ )
+ }
+ .buffer()
+ .flowOn(uiBgCoroutineContext)
+ .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+ .filterNotNull()
val isVisible = interactor.isVisible
+ val icon: Flow<Drawable> =
+ mediaOutputInteractor.defaultActiveMediaSession
+ .filterData()
+ .flatMapLatest { session ->
+ if (session == null) {
+ flowOf(null)
+ } else {
+ mediaDeviceSessionInteractor.playbackState(session)
+ }
+ }
+ .runningFold(null) { playbackStates: PlaybackStates?, playbackState: PlaybackState? ->
+ val isCurrentActive = playbackState?.isActive ?: false
+ if (playbackStates != null && isCurrentActive == playbackState?.isActive) {
+ return@runningFold playbackStates
+ }
+ playbackStates?.copy(
+ isPreviousActive = playbackStates.isCurrentActive,
+ isCurrentActive = isCurrentActive,
+ ) ?: PlaybackStates(isPreviousActive = null, isCurrentActive = isCurrentActive)
+ }
+ .filterNotNull()
+ // only apply the most recent state if we wait for the animation.
+ .buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+ // distinct again because the changed state might've been dropped by the buffer
+ .distinctUntilChangedBy { it.isCurrentActive }
+ .transform { emitDrawables(it) }
+ .runningFold(null) { previous: Drawable?, current: Drawable ->
+ // wait for the previous animation to finish before starting the new one
+ // this also waits for the current loop of the playing animation to finish
+ (previous as? LottieDrawable)?.awaitFinish()
+ (current as? LottieDrawable)?.start()
+ current
+ }
+ .filterNotNull()
+
+ private suspend fun FlowCollector<Drawable>.emitDrawables(playbackStates: PlaybackStates) {
+ val animations = drawables.first()
+ val stateChanged =
+ playbackStates.isPreviousActive != null &&
+ playbackStates.isPreviousActive != playbackStates.isCurrentActive
+ if (playbackStates.isCurrentActive) {
+ if (stateChanged) {
+ emit(animations.start)
+ }
+ emit(animations.playing)
+ } else {
+ if (stateChanged) {
+ emit(animations.stop)
+ }
+ emit(animations.idle)
+ }
+ }
fun onButtonClicked() {
interactor.onButtonClicked()
}
- @VolumeDialogScope
- @AssistedFactory
- interface Factory {
+ private data class PlaybackStates(val isPreviousActive: Boolean?, val isCurrentActive: Boolean)
+
+ private data class Drawables(
+ val start: LottieDrawable,
+ val playing: LottieDrawable,
+ val stop: LottieDrawable,
+ val idle: Drawable,
+ )
+}
+
+private fun LottieComposition.toDrawable(setup: LottieDrawable.() -> Unit = {}): LottieDrawable =
+ LottieDrawable().also { drawable ->
+ drawable.composition = this
+ drawable.setup()
+ }
- fun create(): VolumeDialogSettingsButtonViewModel
+/** Suspends until current loop of the repeating animation is finished */
+private suspend fun LottieDrawable.awaitFinish() = suspendCancellableCoroutine { continuation ->
+ if (!isRunning) {
+ continuation.resume(Unit)
+ return@suspendCancellableCoroutine
}
+ val listener =
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationRepeat(animation: Animator) {
+ continuation.resume(Unit)
+ removeAnimatorListener(this)
+ }
+
+ override fun onAnimationEnd(animation: Animator) {
+ continuation.resume(Unit)
+ removeAnimatorListener(this)
+ }
+
+ override fun onAnimationCancel(animation: Animator) {
+ continuation.resume(Unit)
+ removeAnimatorListener(this)
+ }
+ }
+ addAnimatorListener(listener)
+ continuation.invokeOnCancellation { removeAnimatorListener(listener) }
+}
+
+/**
+ * Overrides colors of the [LottieDrawable] to a specified [color]
+ *
+ * @see com.airbnb.lottie.LottieAnimationView
+ */
+private fun LottieDrawable.setColor(@ColorInt color: Int) {
+ val callback = LottieValueCallback<ColorFilter>(SimpleColorFilter(color))
+ addValueCallback(KeyPath("**"), LottieProperty.COLOR_FILTER, callback)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index f30524638150..faf06b942cab 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -26,7 +26,7 @@ import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderStateModel
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
-import com.android.systemui.volume.dialog.ui.utils.awaitAnimation
+import com.android.systemui.volume.dialog.ui.utils.suspendAnimate
import com.google.android.material.slider.LabelFormatter
import com.google.android.material.slider.Slider
import javax.inject.Inject
@@ -84,5 +84,5 @@ private suspend fun Slider.setValueAnimated(
interpolator = DecelerateInterpolator()
addListener(jankListener)
}
- .awaitAnimation<Float> { value = it }
+ .suspendAnimate<Float> { value = it }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt
index 10cf615ce0ce..5f124806dac7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/utils/SuspendAnimators.kt
@@ -22,6 +22,7 @@ import android.animation.ValueAnimator
import android.view.ViewPropertyAnimator
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.SpringAnimation
+import com.airbnb.lottie.LottieDrawable
import kotlin.coroutines.resume
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.suspendCancellableCoroutine
@@ -66,7 +67,7 @@ suspend fun ViewPropertyAnimator.suspendAnimate(
* is cancelled.
*/
@Suppress("UNCHECKED_CAST")
-suspend fun <T> ValueAnimator.awaitAnimation(onValueChanged: (T) -> Unit) {
+suspend fun <T> ValueAnimator.suspendAnimate(onValueChanged: (T) -> Unit) {
suspendCancellableCoroutine { continuation ->
addListener(
object : AnimatorListenerAdapter() {
@@ -103,6 +104,29 @@ suspend fun SpringAnimation.suspendAnimate(
}
}
+/**
+ * Starts the animation and suspends until it's finished. Cancels the animation if the running
+ * coroutine is cancelled.
+ */
+suspend fun LottieDrawable.suspendAnimate() = suspendCancellableCoroutine { continuation ->
+ val listener =
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ continuation.resumeIfCan(Unit)
+ }
+
+ override fun onAnimationCancel(animation: Animator) {
+ continuation.resumeIfCan(Unit)
+ }
+ }
+ addAnimatorListener(listener)
+ start()
+ continuation.invokeOnCancellation {
+ removeAnimatorListener(listener)
+ stop()
+ }
+}
+
private fun <T> CancellableContinuation<T>.resumeIfCan(value: T) {
if (!isCancelled && !isCompleted) {
resume(value)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
index 6e1ebc820b08..12e624cae4d4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
@@ -19,10 +19,10 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.interacto
import android.media.session.MediaController
import android.media.session.PlaybackState
import com.android.settingslib.volume.data.repository.MediaControllerRepository
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
-import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -38,7 +38,7 @@ import kotlinx.coroutines.withContext
/** Allows to observe and change [MediaDeviceSession] state. */
@OptIn(ExperimentalCoroutinesApi::class)
-@VolumePanelScope
+@SysUISingleton
class MediaDeviceSessionInteractor
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
index b3848a6d7817..2973e11c365d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
@@ -24,12 +24,13 @@ import androidx.annotation.WorkerThread
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.volume.data.repository.LocalMediaRepository
import com.android.settingslib.volume.data.repository.MediaControllerRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.util.concurrency.Execution
import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSessions
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
-import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import com.android.systemui.volume.panel.shared.model.Result
import com.android.systemui.volume.panel.shared.model.filterData
import com.android.systemui.volume.panel.shared.model.wrapInResult
@@ -54,13 +55,13 @@ import kotlinx.coroutines.withContext
/** Provides observable models about the current media session state. */
@OptIn(ExperimentalCoroutinesApi::class)
-@VolumePanelScope
+@SysUISingleton
class MediaOutputInteractor
@Inject
constructor(
private val localMediaRepositoryFactory: LocalMediaRepositoryFactory,
private val packageManager: PackageManager,
- @VolumePanelScope private val coroutineScope: CoroutineScope,
+ @Application private val coroutineScope: CoroutineScope,
@Background private val backgroundCoroutineContext: CoroutineContext,
mediaControllerRepository: MediaControllerRepository,
private val mediaControllerInteractor: MediaControllerInteractor,
@@ -77,7 +78,7 @@ constructor(
.onStart { emit(activeSessions) }
}
.map { getMediaControllers(it) }
- .stateIn(coroutineScope, SharingStarted.Eagerly, MediaControllers(null, null))
+ .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), MediaControllers(null, null))
/** [MediaDeviceSessions] that contains currently active sessions. */
val activeMediaDeviceSessions: Flow<MediaDeviceSessions> =
@@ -89,7 +90,11 @@ constructor(
)
}
.flowOn(backgroundCoroutineContext)
- .stateIn(coroutineScope, SharingStarted.Eagerly, MediaDeviceSessions(null, null))
+ .stateIn(
+ coroutineScope,
+ SharingStarted.WhileSubscribed(),
+ MediaDeviceSessions(null, null),
+ )
/** Returns the default [MediaDeviceSession] from [activeMediaDeviceSessions] */
val defaultActiveMediaSession: StateFlow<Result<MediaDeviceSession?>> =
@@ -104,7 +109,7 @@ constructor(
}
.wrapInResult()
.flowOn(backgroundCoroutineContext)
- .stateIn(coroutineScope, SharingStarted.Eagerly, Result.Loading())
+ .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), Result.Loading())
private val localMediaRepository: Flow<LocalMediaRepository> =
defaultActiveMediaSession
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
index b26f0a6e71a3..782b24825bcf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
@@ -557,6 +557,13 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase {
}
@Test
+ public void startActivityForDialog_always_startActivityWithoutDismissShade() {
+ mInternetDialogController.startActivityForDialog(mock(Intent.class));
+
+ verify(mActivityStarter).startActivity(any(Intent.class), eq(false) /* dismissShade */);
+ }
+
+ @Test
public void launchWifiDetailsSetting_withNoWifiEntryKey_doNothing() {
mInternetDialogController.launchWifiDetailsSetting(null /* key */, mDialogLaunchView);
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 87f87c76725e..c82933c5069e 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -158,6 +158,7 @@ public class SettingsToPropertiesMapper {
"aoc",
"app_widgets",
"arc_next",
+ "art_cloud",
"art_mainline",
"art_performance",
"attack_tools",
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 2985ad330bc6..5740e16dc886 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2400,6 +2400,10 @@ public final class DisplayManagerService extends SystemService {
sendDisplayEventIfEnabledLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
applyDisplayChangedLocked(display);
+
+ if (mDisplayTopologyCoordinator != null) {
+ mDisplayTopologyCoordinator.onDisplayChanged(display.getDisplayInfoLocked());
+ }
}
private void applyDisplayChangedLocked(@NonNull LogicalDisplay display) {
diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
index 5b78726cc421..461a9f3f2a0d 100644
--- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
+++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
@@ -85,13 +85,26 @@ class DisplayTopologyCoordinator {
}
/**
+ * Update the topology with display changes.
+ * @param info The new display info
+ */
+ void onDisplayChanged(DisplayInfo info) {
+ synchronized (mSyncRoot) {
+ if (mTopology.updateDisplay(info.displayId, getWidth(info), getHeight(info))) {
+ sendTopologyUpdateLocked();
+ }
+ }
+ }
+
+ /**
* Remove a display from the topology.
* @param displayId The logical display ID
*/
void onDisplayRemoved(int displayId) {
synchronized (mSyncRoot) {
- mTopology.removeDisplay(displayId);
- sendTopologyUpdateLocked();
+ if (mTopology.removeDisplay(displayId)) {
+ sendTopologyUpdateLocked();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index bf57f56df7c2..0aff1de72cb1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -77,7 +77,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS
import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_TASK_MSG;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.wm.ActivityTaskManagerService.isPip2ExperimentEnabled;
import static com.android.server.wm.ClientLifecycleManager.shouldDispatchLaunchActivityItemIndependently;
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_ALLOWLISTED;
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE;
@@ -2526,7 +2525,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
void scheduleUpdatePictureInPictureModeIfNeeded(Task task, Rect targetRootTaskBounds) {
task.forAllActivities(r -> {
if (!r.attachedToProcess()) return;
- if (!isPip2ExperimentEnabled()) mPipModeChangedActivities.add(r);
+ mPipModeChangedActivities.add(r);
// If we are scheduling pip change, then remove this activity from multi-window
// change list as the processing of pip change will make sure multi-window changed
// message is processed in the right order relative to pip changed.
@@ -2535,7 +2534,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
mPipModeChangedTargetRootTaskBounds = targetRootTaskBounds;
- if (!isPip2ExperimentEnabled() && !mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
+ if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG);
}
}
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index 0369a0ff4c76..9f88bc952351 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -164,15 +164,13 @@ final class AppCompatUtils {
appCompatTaskInfo.setIsFromLetterboxDoubleTap(reachabilityOverrides.isFromDoubleTap());
+ appCompatTaskInfo.topActivityAppBounds.set(getAppBounds(top));
final boolean isTopActivityLetterboxed = top.areBoundsLetterboxed();
appCompatTaskInfo.setTopActivityLetterboxed(isTopActivityLetterboxed);
if (isTopActivityLetterboxed) {
final Rect bounds = top.getBounds();
- final Rect appBounds = getAppBounds(top);
appCompatTaskInfo.topActivityLetterboxWidth = bounds.width();
appCompatTaskInfo.topActivityLetterboxHeight = bounds.height();
- appCompatTaskInfo.topActivityLetterboxAppWidth = appBounds.width();
- appCompatTaskInfo.topActivityLetterboxAppHeight = appBounds.height();
// TODO(b/379824541) Remove duplicate information.
appCompatTaskInfo.topActivityLetterboxBounds = bounds;
// We need to consider if letterboxed or pillarboxed.
@@ -281,8 +279,7 @@ final class AppCompatUtils {
info.topActivityLetterboxHorizontalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
info.topActivityLetterboxWidth = TaskInfo.PROPERTY_VALUE_UNSET;
info.topActivityLetterboxHeight = TaskInfo.PROPERTY_VALUE_UNSET;
- info.topActivityLetterboxAppHeight = TaskInfo.PROPERTY_VALUE_UNSET;
- info.topActivityLetterboxAppWidth = TaskInfo.PROPERTY_VALUE_UNSET;
+ info.topActivityAppBounds.setEmpty();
info.topActivityLetterboxBounds = null;
info.cameraCompatTaskInfo.freeformCameraCompatMode =
CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_UNSPECIFIED;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9d9c53dfe0f4..db62cebf7603 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2945,7 +2945,7 @@ public class WindowManagerService extends IWindowManager.Stub
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc == null) {
if (callingPid != MY_PID) {
- throw new WindowManager.InvalidDisplayException(
+ throw new IllegalArgumentException(
"attachWindowContextToDisplayContent: trying to attach to a"
+ " non-existing display:" + displayId);
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index e45ada9438ae..fb197c566b7d 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -80,7 +80,6 @@ import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_ORG
import static com.android.server.wm.ActivityRecord.State.PAUSING;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
-import static com.android.server.wm.ActivityTaskManagerService.isPip2ExperimentEnabled;
import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
@@ -717,8 +716,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
if (forceHiddenForPip) {
wc.asTask().setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */);
- }
- if (forceHiddenForPip && !isPip2ExperimentEnabled()) {
// When removing pip, make sure that onStop is sent to the app ahead of
// onPictureInPictureModeChanged.
// See also PinnedStackTests#testStopBeforeMultiWindowCallbacksOnDismiss
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
index 5d427139a857..c65024f8f9d5 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
@@ -26,6 +26,7 @@ import org.junit.Test
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
@@ -43,7 +44,7 @@ class DisplayTopologyCoordinatorTest {
@Before
fun setUp() {
- displayInfo.displayId = 2
+ displayInfo.displayId = Display.DEFAULT_DISPLAY
displayInfo.logicalWidth = 300
displayInfo.logicalHeight = 200
displayInfo.logicalDensityDpi = 100
@@ -90,6 +91,44 @@ class DisplayTopologyCoordinatorTest {
}
@Test
+ fun updateDisplay() {
+ whenever(mockTopology.updateDisplay(eq(Display.DEFAULT_DISPLAY), anyFloat(), anyFloat()))
+ .thenReturn(true)
+
+ coordinator.onDisplayChanged(displayInfo)
+
+ verify(mockTopologyChangedCallback).invoke(mockTopologyCopy)
+ }
+
+ @Test
+ fun updateDisplay_notChanged() {
+ whenever(mockTopology.updateDisplay(eq(Display.DEFAULT_DISPLAY), anyFloat(), anyFloat()))
+ .thenReturn(false)
+
+ coordinator.onDisplayChanged(displayInfo)
+
+ verify(mockTopologyChangedCallback, never()).invoke(any())
+ }
+
+ @Test
+ fun removeDisplay() {
+ whenever(mockTopology.removeDisplay(Display.DEFAULT_DISPLAY)).thenReturn(true)
+
+ coordinator.onDisplayRemoved(Display.DEFAULT_DISPLAY)
+
+ verify(mockTopologyChangedCallback).invoke(mockTopologyCopy)
+ }
+
+ @Test
+ fun removeDisplay_notChanged() {
+ whenever(mockTopology.removeDisplay(Display.DEFAULT_DISPLAY)).thenReturn(false)
+
+ coordinator.onDisplayRemoved(Display.DEFAULT_DISPLAY)
+
+ verify(mockTopologyChangedCallback, never()).invoke(any())
+ }
+
+ @Test
fun getTopology_copy() {
assertThat(coordinator.topology).isEqualTo(mockTopologyCopy)
}