summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt2
-rw-r--r--core/java/android/app/ActivityManager.java2
-rw-r--r--core/java/android/app/ActivityThread.java2
-rw-r--r--core/java/android/app/PictureInPictureParams.java2
-rw-r--r--core/java/android/app/activity_manager.aconfig7
-rw-r--r--core/java/android/app/admin/StringSetIntersection.java80
-rw-r--r--core/java/android/app/admin/flags/flags.aconfig10
-rw-r--r--core/java/android/os/Parcel.java8
-rw-r--r--core/java/android/os/flags.aconfig9
-rw-r--r--core/tests/coretests/src/android/os/ParcelTest.java61
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt93
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml2
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml2
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipTransitionUtils.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java3
-rw-r--r--packages/Shell/AndroidManifest.xml4
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig18
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt41
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt14
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt12
-rw-r--r--packages/SystemUI/res/drawable/hearing_device_ambient_expand_icon_background.xml21
-rw-r--r--packages/SystemUI/res/drawable/hearing_device_ambient_icon_background.xml27
-rw-r--r--packages/SystemUI/res/layout/hearing_device_ambient_volume_layout.xml6
-rw-r--r--packages/SystemUI/res/values/colors.xml3
-rw-r--r--packages/SystemUI/res/values/strings.xml5
-rw-r--r--packages/SystemUI/shared/res/values/bools.xml3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModel.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt143
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/ui/slider/Slider.kt24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProviderKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt2
-rw-r--r--services/core/java/com/android/server/adb/AdbDebuggingManager.java44
-rw-r--r--services/core/java/com/android/server/adb/AdbService.java57
-rw-r--r--services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java6
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java85
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java62
-rw-r--r--services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java2
-rw-r--r--services/core/java/com/android/server/wm/Dimmer.java5
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java5
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/StringSetIntersection.java59
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java103
-rw-r--r--tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java10
93 files changed, 1264 insertions, 514 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index fbc8eefdd945..216bbab882a9 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -34264,6 +34264,7 @@ package android.os {
method public boolean hasFileDescriptors();
method public boolean hasFileDescriptors(int, int);
method public byte[] marshall();
+ method @FlaggedApi("android.os.parcel_marshall_bytebuffer") public void marshall(@NonNull java.nio.ByteBuffer);
method @NonNull public static android.os.Parcel obtain();
method @NonNull public static android.os.Parcel obtain(@NonNull android.os.IBinder);
method @Deprecated @Nullable public Object[] readArray(@Nullable ClassLoader);
@@ -34333,6 +34334,7 @@ package android.os {
method public void setDataSize(int);
method public void setPropagateAllowBlocking();
method public void unmarshall(@NonNull byte[], int, int);
+ method @FlaggedApi("android.os.parcel_marshall_bytebuffer") public void unmarshall(@NonNull java.nio.ByteBuffer);
method public void writeArray(@Nullable Object[]);
method public void writeBinderArray(@Nullable android.os.IBinder[]);
method public void writeBinderList(@Nullable java.util.List<android.os.IBinder>);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 62816a2fa0f5..9cc7b8ff47c2 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1188,7 +1188,7 @@ public class ActivityManager {
/** @hide Should this process state be considered jank perceptible? */
public static final boolean isProcStateJankPerceptible(int procState) {
- if (Flags.jankPerceptibleNarrow()) {
+ if (Flags.jankPerceptibleNarrow() && !Flags.jankPerceptibleNarrowHoldback()) {
return procState == PROCESS_STATE_PERSISTENT_UI
|| procState == PROCESS_STATE_TOP
|| procState == PROCESS_STATE_IMPORTANT_FOREGROUND
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 96b50960f0a2..f44c2305591d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4014,7 +4014,7 @@ public final class ActivityThread extends ClientTransactionHandler
return VM_PROCESS_STATE_JANK_PERCEPTIBLE;
}
- if (Flags.jankPerceptibleNarrow()) {
+ if (Flags.jankPerceptibleNarrow() && !Flags.jankPerceptibleNarrowHoldback()) {
// Unlike other persistent processes, system server is often on
// the critical path for application startup. Mark it explicitly
// as jank perceptible regardless of processState.
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index afe915eece26..dd87d288566e 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -594,7 +594,7 @@ public final class PictureInPictureParams implements Parcelable {
* @see PictureInPictureParams.Builder#setSeamlessResizeEnabled(boolean)
*/
public boolean isSeamlessResizeEnabled() {
- return mSeamlessResizeEnabled == null ? true : mSeamlessResizeEnabled;
+ return mSeamlessResizeEnabled == null ? false : mSeamlessResizeEnabled;
}
/**
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
index 720e045dc944..e431426d5d09 100644
--- a/core/java/android/app/activity_manager.aconfig
+++ b/core/java/android/app/activity_manager.aconfig
@@ -163,3 +163,10 @@ flag {
description: "Narrow the scope of Jank Perceptible"
bug: "304837972"
}
+
+flag {
+ name: "jank_perceptible_narrow_holdback"
+ namespace: "system_performance"
+ description: "Holdback study for jank_perceptible_narrow"
+ bug: "304837972"
+}
diff --git a/core/java/android/app/admin/StringSetIntersection.java b/core/java/android/app/admin/StringSetIntersection.java
new file mode 100644
index 000000000000..5f2031e93ad9
--- /dev/null
+++ b/core/java/android/app/admin/StringSetIntersection.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Set;
+
+/**
+ * Class to identify a intersection resolution mechanism for {@code Set<String>} policies, it's
+ * used to resolve the enforced policy when being set by multiple admins (see {@link
+ * PolicyState#getResolutionMechanism()}).
+ *
+ * @hide
+ */
+public final class StringSetIntersection extends ResolutionMechanism<Set<String>> {
+
+ /**
+ * Intersection resolution for policies represented {@code Set<String>} which resolves as the
+ * intersection of all sets.
+ */
+ @NonNull
+ public static final StringSetIntersection STRING_SET_INTERSECTION = new StringSetIntersection();
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ return o != null && getClass() == o.getClass();
+ }
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "StringSetIntersection {}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {}
+
+ @NonNull
+ public static final Parcelable.Creator<StringSetIntersection> CREATOR =
+ new Parcelable.Creator<StringSetIntersection>() {
+ @Override
+ public StringSetIntersection createFromParcel(Parcel source) {
+ return new StringSetIntersection();
+ }
+
+ @Override
+ public StringSetIntersection[] newArray(int size) {
+ return new StringSetIntersection[size];
+ }
+ };
+}
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 572bffe6c6a4..b87ef7061c39 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -412,3 +412,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "use_policy_intersection_for_permitted_input_methods"
+ namespace: "enterprise"
+ description: "When deciding on permitted input methods, use policy intersection instead of last recorded policy."
+ bug: "340914586"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 6cb49b3ea166..4a9928532b93 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -20,6 +20,7 @@ import static com.android.internal.util.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,6 +28,7 @@ import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.AppOpsManager;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Flags;
import android.ravenwood.annotation.RavenwoodClassLoadHook;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.ravenwood.annotation.RavenwoodReplace;
@@ -837,9 +839,8 @@ public final class Parcel {
* @param buffer The ByteBuffer to write the data to.
* @throws ReadOnlyBufferException if the buffer is read-only.
* @throws BufferOverflowException if the buffer is too small.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_PARCEL_MARSHALL_BYTEBUFFER)
public final void marshall(@NonNull ByteBuffer buffer) {
if (buffer == null) {
throw new NullPointerException();
@@ -875,9 +876,8 @@ public final class Parcel {
* Fills the raw bytes of this Parcel with data from the supplied buffer.
*
* @param buffer will read buffer.remaining() bytes from the buffer.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_PARCEL_MARSHALL_BYTEBUFFER)
public final void unmarshall(@NonNull ByteBuffer buffer) {
if (buffer == null) {
throw new NullPointerException();
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 86acb2b21cfa..0150d171d51c 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -354,6 +354,15 @@ flag {
flag {
namespace: "system_performance"
+ name: "parcel_marshall_bytebuffer"
+ is_exported: true
+ description: "Parcel marshal/unmarshall APIs that use ByteBuffer."
+ is_fixed_read_only: true
+ bug: "401362825"
+}
+
+flag {
+ namespace: "system_performance"
name: "perfetto_sdk_tracing"
description: "Tracing using Perfetto SDK."
bug: "303199244"
diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java
index bb059108d4b6..3e6520106ab0 100644
--- a/core/tests/coretests/src/android/os/ParcelTest.java
+++ b/core/tests/coretests/src/android/os/ParcelTest.java
@@ -29,8 +29,6 @@ import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import java.nio.BufferOverflowException;
-import java.nio.ByteBuffer;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -418,63 +416,4 @@ public class ParcelTest {
int binderEndPos = pA.dataPosition();
assertTrue(pA.hasBinders(binderStartPos, binderEndPos - binderStartPos));
}
-
- private static final byte[] TEST_DATA = new byte[] {4, 8, 15, 16, 23, 42};
-
- // Allow for some Parcel overhead
- private static final int TEST_DATA_LENGTH = TEST_DATA.length + 100;
-
- @Test
- public void testMarshall_ByteBuffer_wrapped() {
- ByteBuffer bb = ByteBuffer.allocate(TEST_DATA_LENGTH);
- testMarshall_ByteBuffer(bb);
- }
-
- @Test
- public void testMarshall_DirectByteBuffer() {
- ByteBuffer bb = ByteBuffer.allocateDirect(TEST_DATA_LENGTH);
- testMarshall_ByteBuffer(bb);
- }
-
- private void testMarshall_ByteBuffer(ByteBuffer bb) {
- // Ensure that Parcel respects the starting offset by not starting at 0
- bb.position(1);
- bb.mark();
-
- // Parcel test data, then marshall into the ByteBuffer
- Parcel p1 = Parcel.obtain();
- p1.writeByteArray(TEST_DATA);
- p1.marshall(bb);
- p1.recycle();
-
- assertTrue(bb.position() > 1);
- bb.reset();
-
- // Unmarshall test data into a new Parcel
- Parcel p2 = Parcel.obtain();
- bb.reset();
- p2.unmarshall(bb);
- assertTrue(bb.position() > 1);
- p2.setDataPosition(0);
- byte[] marshalled = p2.marshall();
-
- bb.reset();
- for (int i = 0; i < TEST_DATA.length; i++) {
- assertEquals(bb.get(), marshalled[i]);
- }
-
- byte[] testDataCopy = new byte[TEST_DATA.length];
- p2.setDataPosition(0);
- p2.readByteArray(testDataCopy);
- for (int i = 0; i < TEST_DATA.length; i++) {
- assertEquals(TEST_DATA[i], testDataCopy[i]);
- }
-
- // Test that overflowing the buffer throws an exception
- bb.reset();
- // Leave certainly not enough room for the test data
- bb.limit(bb.position() + TEST_DATA.length - 1);
- assertThrows(BufferOverflowException.class, () -> p2.marshall(bb));
- p2.recycle();
- }
}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
index 14c15210252a..90011f4018f6 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
@@ -24,6 +24,9 @@ import android.graphics.PointF
import android.graphics.Rect
import android.os.Handler
import android.os.UserManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.view.IWindowManager
import android.view.MotionEvent
import android.view.View
@@ -36,6 +39,7 @@ import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.protolog.ProtoLog
import com.android.internal.statusbar.IStatusBarService
+import com.android.wm.shell.Flags
import com.android.wm.shell.R
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.bubbles.Bubble
@@ -64,6 +68,10 @@ import com.android.wm.shell.shared.TransactionPool
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.bubbles.DeviceConfig
+import com.android.wm.shell.shared.bubbles.DragZone
+import com.android.wm.shell.shared.bubbles.DragZoneFactory
+import com.android.wm.shell.shared.bubbles.DragZoneFactory.SplitScreenModeChecker.SplitScreenMode
+import com.android.wm.shell.shared.bubbles.DraggedObject
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
@@ -88,6 +96,8 @@ class BubbleBarLayerViewTest {
const val SCREEN_HEIGHT = 1000
}
+ @get:Rule val setFlagsRule = SetFlagsRule()
+
@get:Rule val animatorTestRule: AnimatorTestRule = AnimatorTestRule(this)
private val context = ApplicationProvider.getApplicationContext<Context>()
@@ -101,6 +111,7 @@ class BubbleBarLayerViewTest {
private lateinit var bgExecutor: TestShellExecutor
private lateinit var bubbleLogger: BubbleLogger
private lateinit var testBubblesList: MutableList<Bubble>
+ private lateinit var dragZoneFactory: DragZoneFactory
@Before
fun setUp() {
@@ -134,6 +145,10 @@ class BubbleBarLayerViewTest {
whenever(bubbleData.bubbles).thenReturn(testBubblesList)
whenever(bubbleData.hasBubbles()).thenReturn(!testBubblesList.isEmpty())
+ dragZoneFactory = DragZoneFactory(context, deviceConfig,
+ { SplitScreenMode.UNSUPPORTED },
+ { false })
+
bubbleController =
createBubbleController(
bubbleData,
@@ -280,6 +295,7 @@ class BubbleBarLayerViewTest {
assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
}
+ @DisableFlags(Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_BUBBLE_ANYTHING)
@Test
fun testEventLogging_dragExpandedViewLeft() {
val bubble = createBubble("first")
@@ -287,7 +303,7 @@ class BubbleBarLayerViewTest {
getInstrumentation().runOnMainSync {
bubbleBarLayerView.showExpandedView(bubble)
- bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true)
+ bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true /* visible */)
}
waitForExpandedViewAnimation()
@@ -305,6 +321,7 @@ class BubbleBarLayerViewTest {
assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
}
+ @DisableFlags(Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_BUBBLE_ANYTHING)
@Test
fun testEventLogging_dragExpandedViewRight() {
val bubble = createBubble("first")
@@ -312,7 +329,7 @@ class BubbleBarLayerViewTest {
getInstrumentation().runOnMainSync {
bubbleBarLayerView.showExpandedView(bubble)
- bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true)
+ bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true /* visible */)
}
waitForExpandedViewAnimation()
@@ -330,6 +347,76 @@ class BubbleBarLayerViewTest {
assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
}
+ @EnableFlags(Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE)
+ @Test
+ fun testEventLogging_dragExpandedViewLeft_bubbleAnything() {
+ val bubble = createBubble("first")
+ bubblePositioner.bubbleBarLocation = BubbleBarLocation.RIGHT
+
+ getInstrumentation().runOnMainSync {
+ bubbleBarLayerView.showExpandedView(bubble)
+ bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true /* visible */)
+ }
+ waitForExpandedViewAnimation()
+
+ val handleView = bubbleBarLayerView.findViewById<View>(R.id.bubble_bar_handle_view)
+ assertThat(handleView).isNotNull()
+
+ val dragZones = dragZoneFactory.createSortedDragZones(
+ DraggedObject.ExpandedView(BubbleBarLocation.RIGHT))
+ val rightDragZone = dragZones.filterIsInstance<DragZone.Bubble.Right>().first()
+ val rightPoint = PointF(rightDragZone.bounds.centerX().toFloat(),
+ rightDragZone.bounds.centerY().toFloat())
+ val leftDragZone = dragZones.filterIsInstance<DragZone.Bubble.Left>().first()
+ val leftPoint = PointF(leftDragZone.bounds.centerX().toFloat(),
+ leftDragZone.bounds.centerY().toFloat())
+
+ // Drag from right to left
+ handleView.dispatchTouchEvent(0L, MotionEvent.ACTION_DOWN, rightPoint)
+ handleView.dispatchTouchEvent(10L, MotionEvent.ACTION_MOVE, leftPoint)
+ handleView.dispatchTouchEvent(20L, MotionEvent.ACTION_UP, leftPoint)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.logs[0].eventId)
+ .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_EXP_VIEW.id)
+ assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
+ }
+
+ @EnableFlags(Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE)
+ @Test
+ fun testEventLogging_dragExpandedViewRight_bubbleAnything() {
+ val bubble = createBubble("first")
+ bubblePositioner.bubbleBarLocation = BubbleBarLocation.LEFT
+
+ getInstrumentation().runOnMainSync {
+ bubbleBarLayerView.showExpandedView(bubble)
+ bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true /* visible */)
+ }
+ waitForExpandedViewAnimation()
+
+ val handleView = bubbleBarLayerView.findViewById<View>(R.id.bubble_bar_handle_view)
+ assertThat(handleView).isNotNull()
+
+ val dragZones = dragZoneFactory.createSortedDragZones(
+ DraggedObject.ExpandedView(BubbleBarLocation.LEFT))
+ val rightDragZone = dragZones.filterIsInstance<DragZone.Bubble.Right>().first()
+ val rightPoint = PointF(rightDragZone.bounds.centerX().toFloat(),
+ rightDragZone.bounds.centerY().toFloat())
+ val leftDragZone = dragZones.filterIsInstance<DragZone.Bubble.Left>().first()
+ val leftPoint = PointF(leftDragZone.bounds.centerX().toFloat(),
+ leftDragZone.bounds.centerY().toFloat())
+
+ // Drag from left to right
+ handleView.dispatchTouchEvent(0L, MotionEvent.ACTION_DOWN, leftPoint)
+ handleView.dispatchTouchEvent(10L, MotionEvent.ACTION_MOVE, rightPoint)
+ handleView.dispatchTouchEvent(20L, MotionEvent.ACTION_UP, rightPoint)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.logs[0].eventId)
+ .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_EXP_VIEW.id)
+ assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
+ }
+
@Test
fun testUpdateExpandedView_updateLocation() {
bubblePositioner.bubbleBarLocation = BubbleBarLocation.RIGHT
@@ -385,7 +472,7 @@ class BubbleBarLayerViewTest {
bubbleLogger,
)
// Mark visible so we don't wait for task view before animations can start
- bubbleBarExpandedView.onContentVisibilityChanged(true)
+ bubbleBarExpandedView.onContentVisibilityChanged(true /* visible */)
val viewInfo = FakeBubbleFactory.createViewInfo(bubbleBarExpandedView)
return FakeBubbleFactory.createChatBubble(context, key, viewInfo).also {
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
index 9bb51a87c08f..ef30d8965452 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
@@ -34,6 +34,7 @@
android:src="@drawable/bubble_ic_settings"/>
<TextView
+ android:id="@+id/education_manage_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/bubble_popup_text_margin"
@@ -45,6 +46,7 @@
android:text="@string/bubble_bar_education_manage_title"/>
<TextView
+ android:id="@+id/education_manage_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/bubble_popup_text_margin"
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
index 1616707954f5..9076d6a87678 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
@@ -34,6 +34,7 @@
android:src="@drawable/ic_floating_landscape"/>
<TextView
+ android:id="@+id/education_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/bubble_popup_text_margin"
@@ -45,6 +46,7 @@
android:text="@string/bubble_bar_education_stack_title"/>
<TextView
+ android:id="@+id/education_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/bubble_popup_text_margin"
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
index 225303b2d942..17ebac95e1dd 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
@@ -40,6 +40,7 @@
android:tint="@color/bubbles_icon_tint"/>
<TextView
+ android:id="@+id/manage_dismiss"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
@@ -67,6 +68,7 @@
android:tint="@color/bubbles_icon_tint"/>
<TextView
+ android:id="@+id/manage_dont_bubble"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 426c3ee5b853..290ef1633819 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -27,6 +27,7 @@ import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
+import static com.android.wm.shell.shared.TypefaceUtils.setTypeface;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
@@ -71,6 +72,7 @@ import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.common.AlphaOptimizedButton;
import com.android.wm.shell.shared.TriangleShape;
+import com.android.wm.shell.shared.TypefaceUtils;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.taskview.TaskView;
@@ -551,6 +553,7 @@ public class BubbleExpandedView extends LinearLayout {
mManageButton = (AlphaOptimizedButton) LayoutInflater.from(ctw).inflate(
R.layout.bubble_manage_button, this /* parent */, false /* attach */);
addView(mManageButton);
+ setTypeface(mManageButton, TypefaceUtils.FontFamily.GSF_LABEL_LARGE);
mManageButton.setVisibility(visibility);
setManageClickListener();
post(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
index da6948d947d8..92007a4df71e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
@@ -50,6 +50,7 @@ import androidx.annotation.Nullable;
import com.android.wm.shell.R;
import com.android.wm.shell.shared.TriangleShape;
+import com.android.wm.shell.shared.TypefaceUtils;
/**
* Flyout view that appears as a 'chat bubble' alongside the bubble stack. The flyout can visually
@@ -165,8 +166,10 @@ public class BubbleFlyoutView extends FrameLayout {
LayoutInflater.from(context).inflate(R.layout.bubble_flyout, this, true);
mFlyoutTextContainer = findViewById(R.id.bubble_flyout_text_container);
mSenderText = findViewById(R.id.bubble_flyout_name);
+ TypefaceUtils.setTypeface(mSenderText, TypefaceUtils.FontFamily.GSF_LABEL_LARGE);
mSenderAvatar = findViewById(R.id.bubble_flyout_avatar);
mMessageText = mFlyoutTextContainer.findViewById(R.id.bubble_flyout_text);
+ TypefaceUtils.setTypeface(mMessageText, TypefaceUtils.FontFamily.GSF_BODY_MEDIUM);
final Resources res = getResources();
mFlyoutPadding = res.getDimensionPixelSize(R.dimen.bubble_flyout_padding_x);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
index 64f54b8ab5be..e901e0c07fe4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
@@ -46,6 +46,7 @@ import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.ContrastColorUtil;
import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
+import com.android.wm.shell.shared.TypefaceUtils;
import java.util.ArrayList;
import java.util.List;
@@ -234,6 +235,10 @@ public class BubbleOverflowContainerView extends LinearLayout {
setBackgroundColor(bgColor);
mEmptyStateTitle.setTextColor(textColor);
mEmptyStateSubtitle.setTextColor(textColor);
+ TypefaceUtils.setTypeface(mEmptyStateTitle,
+ TypefaceUtils.FontFamily.GSF_BODY_MEDIUM_EMPHASIZED);
+ TypefaceUtils.setTypeface(mEmptyStateSubtitle, TypefaceUtils.FontFamily.GSF_BODY_MEDIUM);
+
}
public void updateFontSize() {
@@ -322,6 +327,7 @@ class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.V
TextView viewName = overflowView.findViewById(R.id.bubble_view_name);
viewName.setTextColor(textColor);
+ TypefaceUtils.setTypeface(viewName, TypefaceUtils.FontFamily.GSF_LABEL_LARGE);
return new ViewHolder(overflowView, mPositioner);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index dd5a23aae7f9..3dce45690cf2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -89,6 +89,8 @@ import com.android.wm.shell.bubbles.animation.PhysicsAnimationLayout;
import com.android.wm.shell.bubbles.animation.StackAnimationController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
import com.android.wm.shell.shared.animation.Interpolators;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
@@ -1397,6 +1399,14 @@ public class BubbleStackView extends FrameLayout
// The menu itself should respect locale direction so the icons are on the correct side.
mManageMenu.setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
addView(mManageMenu);
+
+ // Doesn't seem to work unless view is added; so set font after.
+ TypefaceUtils.setTypeface(findViewById(R.id.manage_dismiss), FontFamily.GSF_LABEL_LARGE);
+ TypefaceUtils.setTypeface(findViewById(R.id.manage_dont_bubble),
+ FontFamily.GSF_LABEL_LARGE);
+ TypefaceUtils.setTypeface(mManageSettingsText, FontFamily.GSF_LABEL_LARGE);
+ TypefaceUtils.setTypeface(findViewById(R.id.bubble_manage_menu_fullscreen_title),
+ FontFamily.GSF_LABEL_LARGE);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
index 39a2a7b868a0..d2ad70886fa1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
@@ -27,6 +27,7 @@ import android.widget.Button
import android.widget.LinearLayout
import com.android.internal.R.color.system_neutral1_900
import com.android.wm.shell.R
+import com.android.wm.shell.shared.TypefaceUtils
import com.android.wm.shell.shared.animation.Interpolators
/**
@@ -53,6 +54,12 @@ class ManageEducationView(
init {
LayoutInflater.from(context).inflate(R.layout.bubbles_manage_button_education, this)
+ TypefaceUtils.setTypeface(findViewById(R.id.user_education_title),
+ TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+ TypefaceUtils.setTypeface(findViewById(R.id.user_education_description),
+ TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
+ TypefaceUtils.setTypeface(manageButton, TypefaceUtils.FontFamily.GSF_LABEL_LARGE_EMPHASIZED)
+ TypefaceUtils.setTypeface(gotItButton, TypefaceUtils.FontFamily.GSF_LABEL_LARGE_EMPHASIZED)
visibility = View.GONE
elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
index 16606198b240..9ac059890dd7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
@@ -26,6 +26,7 @@ import android.widget.LinearLayout
import android.widget.TextView
import com.android.internal.util.ContrastColorUtil
import com.android.wm.shell.R
+import com.android.wm.shell.shared.TypefaceUtils
import com.android.wm.shell.shared.animation.Interpolators
/**
@@ -59,6 +60,9 @@ class StackEducationView(
init {
LayoutInflater.from(context).inflate(R.layout.bubble_stack_user_education, this)
+ TypefaceUtils.setTypeface(titleTextView,
+ TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+ TypefaceUtils.setTypeface(descTextView, TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
visibility = View.GONE
elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java
index 6c14d83dfafa..bccc6dcd91db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java
@@ -25,6 +25,7 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.wm.shell.R;
+import com.android.wm.shell.shared.TypefaceUtils;
/**
* Bubble bar expanded view menu item view to display menu action details
@@ -55,6 +56,7 @@ public class BubbleBarMenuItemView extends LinearLayout {
super.onFinishInflate();
mImageView = findViewById(R.id.bubble_bar_menu_item_icon);
mTextView = findViewById(R.id.bubble_bar_menu_item_title);
+ TypefaceUtils.setTypeface(mTextView, TypefaceUtils.FontFamily.GSF_TITLE_MEDIUM);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
index dfbf655bb6fc..7c0f8e138aae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
@@ -33,6 +33,7 @@ import androidx.core.widget.ImageViewCompat;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubble;
+import com.android.wm.shell.shared.TypefaceUtils;
import java.util.ArrayList;
@@ -75,6 +76,7 @@ public class BubbleBarMenuView extends LinearLayout {
mActionsSectionView = findViewById(R.id.bubble_bar_manage_menu_actions_section);
mBubbleIconView = findViewById(R.id.bubble_bar_manage_menu_bubble_icon);
mBubbleTitleView = findViewById(R.id.bubble_bar_manage_menu_bubble_title);
+ TypefaceUtils.setTypeface(mBubbleTitleView, TypefaceUtils.FontFamily.GSF_TITLE_MEDIUM);
mBubbleDismissIconView = findViewById(R.id.bubble_bar_manage_menu_dismiss_icon);
updateThemeColors();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
index 7adec39c9d66..0bd3a54ceeaa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
@@ -35,6 +35,7 @@ import com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME
import com.android.wm.shell.bubbles.BubbleEducationController
import com.android.wm.shell.bubbles.BubbleViewProvider
import com.android.wm.shell.bubbles.setup
+import com.android.wm.shell.shared.TypefaceUtils
import com.android.wm.shell.shared.animation.PhysicsAnimator
import com.android.wm.shell.shared.bubbles.BubblePopupDrawable
import com.android.wm.shell.shared.bubbles.BubblePopupView
@@ -108,6 +109,10 @@ class BubbleEducationViewController(private val context: Context, private val li
root.getBoundsOnScreen(rootBounds)
educationView =
createEducationView(R.layout.bubble_bar_stack_education, root).apply {
+ TypefaceUtils.setTypeface(findViewById(R.id.education_title),
+ TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+ TypefaceUtils.setTypeface(findViewById(R.id.education_text),
+ TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
setArrowDirection(BubblePopupDrawable.ArrowDirection.DOWN)
updateEducationPosition(view = this, position, rootBounds)
val arrowToEdgeOffset = popupDrawable?.config?.cornerRadius ?: 0f
@@ -153,6 +158,10 @@ class BubbleEducationViewController(private val context: Context, private val li
educationView =
createEducationView(R.layout.bubble_bar_manage_education, root).apply {
+ TypefaceUtils.setTypeface(findViewById(R.id.education_manage_title),
+ TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+ TypefaceUtils.setTypeface(findViewById(R.id.education_manage_text),
+ TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
pivotY = 0f
doOnLayout { it.pivotX = it.width / 2f }
setOnClickListener { hideEducation(animated = true) }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index cef18f55b86d..c58bb6e3bd31 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -40,7 +40,6 @@ import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
-import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
@@ -341,23 +340,6 @@ public abstract class PipTransitionController implements Transitions.TransitionH
return false;
}
- /**
- * @return a change representing a config-at-end activity for a given parent.
- */
- @Nullable
- public TransitionInfo.Change getDeferConfigActivityChange(TransitionInfo info,
- @android.annotation.NonNull WindowContainerToken parent) {
- for (TransitionInfo.Change change : info.getChanges()) {
- if (change.getTaskInfo() == null
- && change.hasFlags(TransitionInfo.FLAG_CONFIG_AT_END)
- && change.getParent() != null && change.getParent().equals(parent)) {
- return change;
- }
- }
- return null;
- }
-
-
/** Whether a particular package is same as current pip package. */
public boolean isPackageActiveInPip(@Nullable String packageName) {
// No-op, to be handled differently in PIP1 and PIP2
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 cfcd56393bc2..5d8d8b685a23 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
@@ -76,6 +76,7 @@ import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
import com.android.wm.shell.pip2.animation.PipEnterAnimator;
import com.android.wm.shell.pip2.phone.transition.PipExpandHandler;
+import com.android.wm.shell.pip2.phone.transition.PipTransitionUtils;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellInit;
@@ -387,8 +388,8 @@ public class PipTransition extends PipTransitionController implements
mFinishCallback = finishCallback;
// We expect the PiP activity as a separate change in a config-at-end transition;
// only flings are not using config-at-end for resize bounds changes
- TransitionInfo.Change pipActivityChange = getDeferConfigActivityChange(info,
- pipChange.getTaskInfo().getToken());
+ TransitionInfo.Change pipActivityChange = PipTransitionUtils.getDeferConfigActivityChange(
+ info, pipChange.getTaskInfo().getToken());
if (pipActivityChange != null) {
// Transform calculations use PiP params by default, so make sure they are null to
// default to using bounds for scaling calculations instead.
@@ -427,8 +428,8 @@ public class PipTransition extends PipTransitionController implements
}
// We expect the PiP activity as a separate change in a config-at-end transition.
- TransitionInfo.Change pipActivityChange = getDeferConfigActivityChange(info,
- pipChange.getTaskInfo().getToken());
+ TransitionInfo.Change pipActivityChange = PipTransitionUtils.getDeferConfigActivityChange(
+ info, pipChange.getTaskInfo().getToken());
if (pipActivityChange == null) {
return false;
}
@@ -497,8 +498,8 @@ public class PipTransition extends PipTransitionController implements
}
// We expect the PiP activity as a separate change in a config-at-end transition.
- TransitionInfo.Change pipActivityChange = getDeferConfigActivityChange(info,
- pipChange.getTaskInfo().getToken());
+ TransitionInfo.Change pipActivityChange = PipTransitionUtils.getDeferConfigActivityChange(
+ info, pipChange.getTaskInfo().getToken());
if (pipActivityChange == null) {
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipTransitionUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipTransitionUtils.java
index 01cda6c91108..e562f3340217 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipTransitionUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipTransitionUtils.java
@@ -67,6 +67,36 @@ public class PipTransitionUtils {
}
/**
+ * @return a change representing a config-at-end activity for ancestor.
+ */
+ @Nullable
+ public static TransitionInfo.Change getDeferConfigActivityChange(TransitionInfo info,
+ @NonNull WindowContainerToken ancestor) {
+ final TransitionInfo.Change ancestorChange =
+ PipTransitionUtils.getChangeByToken(info, ancestor);
+ if (ancestorChange == null) return null;
+
+ // Iterate through changes bottom-to-top, going up the parent chain starting with ancestor.
+ TransitionInfo.Change lastPipChildChange = ancestorChange;
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (change == ancestorChange) continue;
+
+ if (change.getParent() != null
+ && change.getParent().equals(lastPipChildChange.getContainer())) {
+ // Found a child of the last cached child along the ancestral chain.
+ lastPipChildChange = change;
+ if (change.getTaskInfo() == null
+ && change.hasFlags(TransitionInfo.FLAG_CONFIG_AT_END)) {
+ // If this is a config-at-end activity change, then we found the chain leaf.
+ return change;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
* @return the leash to interact with the container this change represents.
* @throws NullPointerException if the leash is null.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
index 1853ffa96dfc..320a63a95302 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
@@ -34,6 +34,7 @@ import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip2.phone.transition.PipTransitionUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.StageCoordinator;
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
@@ -132,7 +133,7 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition {
TransitionInfo.Change pipActivityChange = null;
if (pipChange != null) {
- pipActivityChange = mPipHandler.getDeferConfigActivityChange(
+ pipActivityChange = PipTransitionUtils.getDeferConfigActivityChange(
info, pipChange.getContainer());
everythingElse.getChanges().remove(pipActivityChange);
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 55f7317f25e4..758ad797f761 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -1015,6 +1015,10 @@
<uses-permission android:name="android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE" />
<uses-permission android:name="android.permission.READ_COLOR_ZONES" />
+ <!-- Permissions required for CTS test - CtsModernMediaProviderTests -->
+ <uses-permission android:name="com.android.providers.media.permission.ACCESS_OEM_METADATA" />
+ <uses-permission android:name="com.android.providers.media.permission.UPDATE_OEM_METADATA" />
+
<!-- Permission required for trade-in mode testing -->
<uses-permission android:name="android.permission.ENTER_TRADE_IN_MODE" />
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 4693377654f8..436e92bc0efa 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -398,13 +398,6 @@ flag {
}
flag {
- name: "light_reveal_migration"
- namespace: "systemui"
- description: "Move LightRevealScrim to recommended architecture"
- bug: "281655028"
-}
-
-flag {
name: "theme_overlay_controller_wakefulness_deprecation"
namespace: "systemui"
description: "Replacing WakefulnessLifecycle by KeyguardTransitionInteractor in "
@@ -2136,3 +2129,14 @@ flag {
description: "Enables return animations for status bar chips"
bug: "202516970"
}
+
+flag {
+ name: "media_projection_grey_error_text"
+ namespace: "systemui"
+ description: "Set the error text color to grey when app sharing is hidden by the requesting app"
+ bug: "390624334"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index 82e5f5bb6dc8..cd9fcefcdda3 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -127,6 +127,8 @@ import kotlin.math.min
*
* @sample com.android.systemui.compose.gallery.ActivityLaunchScreen
* @sample com.android.systemui.compose.gallery.DialogLaunchScreen
+ * @param defaultMinSize true if a default minimum size should be enforced even if this Expandable
+ * isn't currently clickable and false otherwise.
*/
@Composable
fun Expandable(
@@ -140,6 +142,7 @@ fun Expandable(
// TODO(b/285250939): Default this to true then remove once the Compose QS expandables have
// proven that the new implementation is robust.
useModifierBasedImplementation: Boolean = false,
+ defaultMinSize: Boolean = true,
transitionControllerFactory: ComposableControllerFactory? = null,
content: @Composable (Expandable) -> Unit,
) {
@@ -155,6 +158,7 @@ fun Expandable(
onClick,
interactionSource,
useModifierBasedImplementation,
+ defaultMinSize,
content,
)
}
@@ -182,6 +186,8 @@ fun Expandable(
*
* @sample com.android.systemui.compose.gallery.ActivityLaunchScreen
* @sample com.android.systemui.compose.gallery.DialogLaunchScreen
+ * @param defaultMinSize true if a default minimum size should be enforced even if this Expandable
+ * isn't currently clickable and false otherwise.
*/
@Composable
fun Expandable(
@@ -192,6 +198,7 @@ fun Expandable(
// TODO(b/285250939): Default this to true then remove once the Compose QS expandables have
// proven that the new implementation is robust.
useModifierBasedImplementation: Boolean = false,
+ defaultMinSize: Boolean = true,
content: @Composable (Expandable) -> Unit,
) {
val controller = controller as ExpandableControllerImpl
@@ -209,7 +216,12 @@ fun Expandable(
if (useModifierBasedImplementation) {
Box(modifier.expandable(controller, onClick, interactionSource)) {
- WrappedContent(controller.expandable, controller.contentColor, content)
+ WrappedContent(
+ controller.expandable,
+ controller.contentColor,
+ defaultMinSize = defaultMinSize,
+ content,
+ )
}
return
}
@@ -221,7 +233,7 @@ fun Expandable(
val wrappedContent =
remember(content) {
movableContentOf { expandable: Expandable ->
- WrappedContent(expandable, contentColor, content)
+ WrappedContent(expandable, contentColor, defaultMinSize = defaultMinSize, content)
}
}
@@ -306,21 +318,24 @@ fun Expandable(
private fun WrappedContent(
expandable: Expandable,
contentColor: Color,
+ defaultMinSize: Boolean,
content: @Composable (Expandable) -> Unit,
) {
val minSizeContent =
@Composable {
- // We make sure that the content itself (wrapped by the background) is at least 40.dp,
- // which is the same as the M3 buttons. This applies even if onClick is null, to make it
- // easier to write expandables that are sometimes clickable and sometimes not. There
- // shouldn't be any Expandable smaller than 40dp because if the expandable is not
- // clickable directly, then something in its content should be (and with a size >=
- // 40dp).
- val minSize = 40.dp
- Box(
- Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
- contentAlignment = Alignment.Center,
- ) {
+ if (defaultMinSize) {
+ // We make sure that the content itself (wrapped by the background) is at
+ // least 40.dp, which is the same as the M3 buttons. This applies even if
+ // onClick is null, to make it easier to write expandables that are
+ // sometimes clickable and sometimes not.
+ val minSize = 40.dp
+ Box(
+ modifier = Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
+ contentAlignment = Alignment.Center,
+ ) {
+ content(expandable)
+ }
+ } else {
content(expandable)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 7782705d4c61..336f9e1ad6e3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -38,6 +38,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
@@ -81,6 +82,7 @@ import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsButtonViewModel
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsForegroundServicesButtonViewModel
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsSecurityButtonViewModel
@@ -388,6 +390,7 @@ private fun NewChangesDot(modifier: Modifier = Modifier) {
}
/** A larger button with an icon, some text and an optional dot (to indicate new changes). */
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun TextButton(
icon: Icon,
@@ -422,10 +425,13 @@ private fun TextButton(
Text(
text,
Modifier.weight(1f),
- style = MaterialTheme.typography.bodyMedium,
- // TODO(b/242040009): Remove this letter spacing. We should only use the M3 text
- // styles without modifying them.
- letterSpacing = 0.01.em,
+ style =
+ if (QsInCompose.isEnabled) {
+ MaterialTheme.typography.labelLarge
+ } else {
+ MaterialTheme.typography.bodyMedium
+ },
+ letterSpacing = if (QsInCompose.isEnabled) 0.em else 0.01.em,
color = colorAttr(R.attr.onShadeInactiveVariant),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
index 4b3ebc2bd53d..da54cb8e4679 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -58,6 +58,7 @@ import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.customActions
import androidx.compose.ui.semantics.disabled
import androidx.compose.ui.semantics.progressBarRangeInfo
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.setProgress
import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.unit.dp
@@ -106,7 +107,10 @@ fun VolumeSlider(
return
}
- Column(modifier = modifier.animateContentSize(), verticalArrangement = Arrangement.Top) {
+ Column(
+ modifier = modifier.animateContentSize().semantics(true) {},
+ verticalArrangement = Arrangement.Top,
+ ) {
Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
modifier = Modifier.fillMaxWidth().height(40.dp),
@@ -123,7 +127,7 @@ fun VolumeSlider(
text = state.label,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurface,
- modifier = Modifier.weight(1f),
+ modifier = Modifier.weight(1f).clearAndSetSemantics {},
)
button?.invoke()
}
@@ -134,12 +138,11 @@ fun VolumeSlider(
onValueChanged = onValueChange,
onValueChangeFinished = { onValueChangeFinished?.invoke() },
isEnabled = state.isEnabled,
- stepDistance = state.a11yStep,
+ stepDistance = state.step,
accessibilityParams =
AccessibilityParams(
- label = state.label,
- disabledMessage = state.disabledMessage,
- currentStateDescription = state.a11yStateDescription,
+ contentDescription = state.a11yContentDescription,
+ stateDescription = state.a11yStateDescription,
),
haptics =
hapticsViewModelFactory?.let {
@@ -169,7 +172,7 @@ fun VolumeSlider(
text = disabledMessage,
color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.labelSmall,
- modifier = Modifier.basicMarquee(),
+ modifier = Modifier.basicMarquee().clearAndSetSemantics {},
)
}
}
@@ -229,7 +232,7 @@ private fun LegacyVolumeSlider(
}
val newValue =
- (value + targetDirection * state.a11yStep).coerceIn(
+ (value + targetDirection * state.step).coerceIn(
state.valueRange.start,
state.valueRange.endInclusive,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
index 893c17998a17..4c099b305332 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
@@ -455,6 +455,7 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
assertThat(content).isNotNull()
assertThat(content?.style).isEqualTo(Style.Call)
+ assertThat(content?.title).isEqualTo(TEST_PERSON_NAME)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index d306a5bea433..0d453352e882 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -51,6 +51,7 @@ import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController.BUBBLES_SETTING_URI
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
@@ -318,7 +319,7 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() {
val notification = Notification.Builder(mContext).build()
val sbn =
SbnBuilder().setNotification(notification).setUser(UserHandle.of(USER_ALL)).build()
- whenever(view.entry)
+ whenever(view.entryLegacy)
.thenReturn(
NotificationEntryBuilder().setSbn(sbn).setUser(UserHandle.of(USER_ALL)).build()
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 41cca19346f0..6ec1f919fb47 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -129,7 +129,7 @@ class StackScrollAlgorithmTest(flags: FlagsParameterization) : SysuiTestCase() {
whenever(notificationShelf.viewState).thenReturn(ExpandableViewState())
whenever(notificationRow.key).thenReturn("key")
whenever(notificationRow.viewState).thenReturn(ExpandableViewState())
- whenever(notificationRow.entry).thenReturn(notificationEntry)
+ whenever(notificationRow.entryLegacy).thenReturn(notificationEntry)
whenever(notificationRow.entryAdapter).thenReturn(notificationEntryAdapter)
whenever(notificationRow.roundableState)
.thenReturn(RoundableState(notificationRow, notificationRow, 0f))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index c23e0e733b41..1cc291199531 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -221,6 +221,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.getEntryLegacy()).thenReturn(enrEntry);
when(enr.getEntryAdapter()).thenReturn(enrEntryAdapter);
when(enr.isChildInGroup()).thenReturn(true);
when(enr.areChildrenExpanded()).thenReturn(false);
@@ -251,6 +252,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.getEntryLegacy()).thenReturn(enrEntry);
when(enr.isChildInGroup()).thenReturn(true);
when(enr.areChildrenExpanded()).thenReturn(true);
@@ -277,6 +279,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.getEntryLegacy()).thenReturn(enrEntry);
when(enr.isChildInGroup()).thenReturn(false);
when(enr.isPinned()).thenReturn(false);
when(enr.isExpanded()).thenReturn(false);
@@ -305,6 +308,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.getEntryLegacy()).thenReturn(enrEntry);
when(enr.isChildInGroup()).thenReturn(false);
when(enr.isPinned()).thenReturn(false);
when(enr.isExpanded()).thenReturn(true);
@@ -333,6 +337,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.getEntryLegacy()).thenReturn(enrEntry);
when(enr.isChildInGroup()).thenReturn(false);
when(enr.isPinned()).thenReturn(true);
when(enr.isPinnedAndExpanded()).thenReturn(false);
@@ -361,6 +366,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.getEntryLegacy()).thenReturn(enrEntry);
when(enr.isChildInGroup()).thenReturn(false);
when(enr.isPinned()).thenReturn(true);
when(enr.isPinnedAndExpanded()).thenReturn(true);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelTest.kt
index 04ab98889755..b1a3caf98f09 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
import android.bluetooth.BluetoothDevice
+import android.graphics.drawable.TestStubDrawable
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.CachedBluetoothDevice
@@ -63,6 +64,12 @@ class AudioSharingStreamSliderViewModelTest : SysuiTestCase() {
assertThat(audioSharingSlider!!.label).isEqualTo("my headset 2")
assertThat(audioSharingSlider!!.icon)
- .isEqualTo(Icon.Resource(R.drawable.ic_volume_media_bt, null))
+ .isEqualTo(
+ Icon.Loaded(
+ drawable = TestStubDrawable(),
+ res = R.drawable.ic_volume_media_bt,
+ contentDescription = null,
+ )
+ )
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
index 9e8cde3bc936..ffe8e923815f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
import android.app.Flags
import android.app.NotificationManager.INTERRUPTION_FILTER_NONE
+import android.graphics.drawable.TestStubDrawable
import android.media.AudioManager
import android.platform.test.annotations.EnableFlags
import android.service.notification.ZenPolicy
@@ -28,7 +29,6 @@ import com.android.settingslib.notification.modes.TestModeBuilder
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.collectLastValue
@@ -39,8 +39,6 @@ import com.android.systemui.statusbar.policy.data.repository.fakeZenModeReposito
import com.android.systemui.testKosmos
import com.android.systemui.volume.data.repository.audioSharingRepository
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
@@ -173,6 +171,12 @@ class AudioStreamSliderViewModelTest : SysuiTestCase() {
assertThat(mediaSlider!!.label).isEqualTo("my headset 1")
assertThat(mediaSlider!!.icon)
- .isEqualTo(Icon.Resource(R.drawable.ic_volume_media_bt, null))
+ .isEqualTo(
+ Icon.Loaded(
+ drawable = TestStubDrawable(),
+ res = R.drawable.ic_volume_media_bt,
+ contentDescription = null,
+ )
+ )
}
}
diff --git a/packages/SystemUI/res/drawable/hearing_device_ambient_expand_icon_background.xml b/packages/SystemUI/res/drawable/hearing_device_ambient_expand_icon_background.xml
new file mode 100644
index 000000000000..2173641376fa
--- /dev/null
+++ b/packages/SystemUI/res/drawable/hearing_device_ambient_expand_icon_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 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.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/hearing_device_ambient_icon_background"
+ android:insetTop="10dp"
+ android:insetBottom="10dp"/>
diff --git a/packages/SystemUI/res/drawable/hearing_device_ambient_icon_background.xml b/packages/SystemUI/res/drawable/hearing_device_ambient_icon_background.xml
new file mode 100644
index 000000000000..81ef3d7c11b7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/hearing_device_ambient_icon_background.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerHighest" />
+ <corners
+ android:bottomLeftRadius="?android:attr/dialogCornerRadius"
+ android:topLeftRadius="?android:attr/dialogCornerRadius"
+ android:bottomRightRadius="?android:attr/dialogCornerRadius"
+ android:topRightRadius="?android:attr/dialogCornerRadius" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/hearing_device_ambient_volume_layout.xml b/packages/SystemUI/res/layout/hearing_device_ambient_volume_layout.xml
index fd409a5a8bb1..d3e9db15dfdd 100644
--- a/packages/SystemUI/res/layout/hearing_device_ambient_volume_layout.xml
+++ b/packages/SystemUI/res/layout/hearing_device_ambient_volume_layout.xml
@@ -36,7 +36,8 @@
android:padding="12dp"
android:contentDescription="@string/hearing_devices_ambient_unmute"
android:src="@drawable/ic_ambient_volume"
- android:tint="@androidprv:color/materialColorOnSurface" />
+ android:tint="@androidprv:color/materialColorOnSurface"
+ android:background="@drawable/hearing_device_ambient_icon_background"/>
<TextView
android:id="@+id/ambient_title"
android:layout_width="0dp"
@@ -56,7 +57,8 @@
android:padding="10dp"
android:contentDescription="@string/hearing_devices_ambient_expand_controls"
android:src="@drawable/ic_hearing_device_expand"
- android:tint="@androidprv:color/materialColorOnSurface" />
+ android:tint="@androidprv:color/materialColorOnSurface"
+ android:background="@drawable/hearing_device_ambient_expand_icon_background"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ambient_control_container"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 15519ff37d0c..7c6a1b1bf63d 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -146,7 +146,8 @@
<color name="smart_reply_button_stroke">@*android:color/accent_device_default</color>
<!-- Magic Action colors -->
- <color name="magic_action_button_text_color">@androidprv:color/materialColorOnSurfaceVariant</color>
+ <color name="magic_action_button_text_color">@androidprv:color/materialColorOnSurface</color>
+ <color name="magic_action_button_stroke_color">@androidprv:color/materialColorOnSurface</color>
<!-- Biometric dialog colors -->
<color name="biometric_dialog_gray">#ff757575</color>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3fdb98b4e00b..b627bdf22a6c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1483,6 +1483,8 @@
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen">Share screen</string>
<!-- 1P/3P apps disabled the single app projection option. [CHAR LIMIT=NONE] -->
<string name="media_projection_entry_app_permission_dialog_single_app_disabled"><xliff:g id="app_name" example="Meet">%1$s</xliff:g> has disabled this option</string>
+ <!-- Explanation that the app requesting the projection does not support single app sharing[CHAR LIMIT=70] -->
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported">Not supported by the app</string>
<!-- Title of the activity that allows users to select an app to share to a 1P/3P app [CHAR LIMIT=70] -->
<string name="media_projection_entry_share_app_selector_title">Choose app to share</string>
@@ -2558,6 +2560,9 @@
<!-- Button to edit the tile ordering of quick settings [CHAR LIMIT=60] -->
<string name="qs_edit">Edit</string>
+ <!-- Title for QS Edit mode screen [CHAR LIMIT=30] -->
+ <string name="qs_edit_tiles">Edit tiles</string>
+
<!-- SysUI Tuner: Options for how clock is displayed [CHAR LIMIT=NONE] -->
<string name="tuner_time">Time</string>
diff --git a/packages/SystemUI/shared/res/values/bools.xml b/packages/SystemUI/shared/res/values/bools.xml
index 98e5cea0e78f..a7ad48d17abb 100644
--- a/packages/SystemUI/shared/res/values/bools.xml
+++ b/packages/SystemUI/shared/res/values/bools.xml
@@ -22,7 +22,4 @@
<resources>
<!-- Whether to add padding at the bottom of the complication clock -->
<bool name="dream_overlay_complication_clock_bottom_padding">false</bool>
-
- <!-- Whether to mark tasks that are present in the UI as perceptible tasks. -->
- <bool name="config_usePerceptibleTasks">false</bool>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 487d1ce2514e..b981f9837787 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -46,8 +46,6 @@ import android.view.Display;
import android.window.TaskSnapshot;
import com.android.internal.app.IVoiceInteractionManagerService;
-import com.android.server.am.Flags;
-import com.android.systemui.shared.R;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -324,14 +322,6 @@ public class ActivityManagerWrapper {
}
/**
- * Returns true if tasks with a presence in the UI should be marked as perceptible tasks.
- */
- public static boolean usePerceptibleTasks(Context context) {
- return Flags.perceptibleTasks()
- && context.getResources().getBoolean(R.bool.config_usePerceptibleTasks);
- }
-
- /**
* Returns true if the running task represents the home task
*/
public static boolean isHomeTask(RunningTaskInfo info) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
index bd3dfe049587..8025d4b0c2ea 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
@@ -317,14 +317,14 @@ class MenuViewAppearance {
public static float avoidVerticalDisplayCutout(
float y, float menuHeight, Rect bounds, Rect cutout) {
if (cutout.top > y + menuHeight || cutout.bottom < y) {
- return y;
+ return clampVerticalPosition(y, menuHeight, bounds.top, bounds.bottom);
}
boolean topAvailable = cutout.top - bounds.top >= menuHeight;
boolean bottomAvailable = bounds.bottom - cutout.bottom >= menuHeight;
boolean topOrBottom;
if (!topAvailable && !bottomAvailable) {
- return y;
+ return clampVerticalPosition(y, menuHeight, bounds.top, bounds.bottom);
} else if (topAvailable && !bottomAvailable) {
topOrBottom = true;
} else if (!topAvailable && bottomAvailable) {
@@ -332,7 +332,16 @@ class MenuViewAppearance {
} else {
topOrBottom = y + menuHeight * 0.5f < cutout.centerY();
}
- return (topOrBottom) ? cutout.top - menuHeight : cutout.bottom;
+
+ float finalPosition = (topOrBottom) ? cutout.top - menuHeight : cutout.bottom;
+ return clampVerticalPosition(finalPosition, menuHeight, bounds.top, bounds.bottom);
+ }
+
+ private static float clampVerticalPosition(
+ float position, float height, float min, float max) {
+ position = Float.max(min + height / 2, position);
+ position = Float.min(max - height / 2, position);
+ return position;
}
boolean isMenuOnLeftSide() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 22d2aaf2a8e7..87e9784ddbc9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -32,7 +32,6 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.keyguard.logging.KeyguardLogger
import com.android.settingslib.Utils
import com.android.systemui.CoreStartable
-import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.biometrics.data.repository.FacePropertyRepository
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.dagger.SysUISingleton
@@ -43,6 +42,7 @@ import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
+import com.android.systemui.shared.Flags.ambientAod
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LiftReveal
import com.android.systemui.statusbar.LightRevealEffect
@@ -196,7 +196,7 @@ constructor(
// This code path is not used if the KeyguardTransitionRepository is managing the light
// reveal scrim.
- if (!lightRevealMigration()) {
+ if (!ambientAod()) {
if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
circleReveal?.let {
lightRevealScrim.revealAmount = 0f
@@ -213,7 +213,7 @@ constructor(
}
override fun onKeyguardFadingAwayChanged() {
- if (lightRevealMigration()) {
+ if (ambientAod()) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
index 2adaec21867f..6792f3188986 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
@@ -19,6 +19,7 @@ package com.android.systemui.common.shared.model
import android.annotation.DrawableRes
import android.graphics.drawable.Drawable
import androidx.compose.runtime.Stable
+import com.android.systemui.common.shared.model.Icon.Loaded
/**
* Models an icon, that can either be already [loaded][Icon.Loaded] or be a [reference]
@@ -33,8 +34,37 @@ sealed class Icon {
constructor(
val drawable: Drawable,
override val contentDescription: ContentDescription?,
+ /**
+ * Serves as an id to compare two instances. When provided this is used alongside
+ * [contentDescription] to determine equality. This is useful when comparing icons
+ * representing the same UI, but with different [drawable] instances.
+ */
@DrawableRes val res: Int? = null,
- ) : Icon()
+ ) : Icon() {
+
+ override fun equals(other: Any?): Boolean {
+ val that = other as? Loaded ?: return false
+
+ if (this.res != null && that.res != null) {
+ return this.res == that.res && this.contentDescription == that.contentDescription
+ }
+
+ return this.res == that.res &&
+ this.drawable == that.drawable &&
+ this.contentDescription == that.contentDescription
+ }
+
+ override fun hashCode(): Int {
+ var result = contentDescription?.hashCode() ?: 0
+ result =
+ if (res != null) {
+ 31 * result + res.hashCode()
+ } else {
+ 31 * result + drawable.hashCode()
+ }
+ return result
+ }
+ }
data class Resource(
@DrawableRes val res: Int,
@@ -49,4 +79,4 @@ sealed class Icon {
fun Drawable.asIcon(
contentDescription: ContentDescription? = null,
@DrawableRes res: Int? = null,
-): Icon.Loaded = Icon.Loaded(this, contentDescription, res)
+): Loaded = Loaded(this, contentDescription, res)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 756edb3d048d..5a4b0b0e2d24 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -229,7 +229,7 @@ constructor(
MutableStateFlow(false)
}
- val blurRadiusPx: Float = blurConfig.maxBlurRadiusPx / 2.0f
+ val blurRadiusPx: Float = blurConfig.maxBlurRadiusPx
init {
// Initialize our media host for the UMO. This only needs to happen once and must be done
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index c61530c3dbcc..74cf7e4f7359 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -20,7 +20,6 @@ package com.android.systemui.keyguard
import android.content.Context
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.CoreStartable
-import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.biometrics.ui.binder.DeviceEntryUnlockTrackerViewBinder
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
@@ -46,6 +45,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shared.Flags.ambientAod
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.VibratorHelper
@@ -105,7 +105,7 @@ constructor(
bindJankViewModel()
initializeViews()
- if (lightRevealMigration()) {
+ if (ambientAod()) {
LightRevealScrimViewBinder.bind(
lightRevealScrim,
lightRevealScrimViewModel,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt
index 45f8f10595e4..3cf05062325d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.util.MathUtils
-import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.communal.ui.compose.TransitionDuration
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.dagger.GlanceableHubBlurComponent
@@ -28,6 +27,7 @@ import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.keyguard.ui.transitions.GlanceableHubTransition
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shared.Flags.ambientAod
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -56,14 +56,14 @@ constructor(
return transitionAnimation.sharedFlow(
duration = 250.milliseconds,
startTime =
- if (lightRevealMigration()) {
+ if (ambientAod()) {
100.milliseconds // Wait for the light reveal to "hit" the LS elements.
} else {
0.milliseconds
},
onStart = {
currentAlpha =
- if (lightRevealMigration()) {
+ if (ambientAod()) {
viewState.alpha()
} else {
0f
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
index d981eeb0989b..ba6bda8a2a04 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.util.MathUtils
-import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -25,6 +24,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.shared.Flags.ambientAod
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -52,13 +52,13 @@ constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTra
return transitionAnimation.sharedFlow(
duration = 250.milliseconds,
startTime =
- if (lightRevealMigration()) {
+ if (ambientAod()) {
100.milliseconds // Wait for the light reveal to "hit" the LS elements.
} else {
0.milliseconds
},
onStart = {
- if (lightRevealMigration()) {
+ if (ambientAod()) {
currentAlpha = viewState.alpha()
} else {
currentAlpha = 0f
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
index b15cacf077a4..2eb5bf9328e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.util.MathUtils
-import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -25,6 +24,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.shared.Flags.ambientAod
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -56,7 +56,7 @@ constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTra
duration = 250.milliseconds,
startTime = 0.milliseconds,
onStart = {
- if (lightRevealMigration()) {
+ if (ambientAod()) {
currentAlpha = viewState.alpha()
} else {
currentAlpha = 0f
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
index c6e4db7af2d9..324a3efc2989 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
@@ -150,6 +150,13 @@ private class OptionsAdapter(context: Context, private val options: List<ScreenS
titleTextView.isEnabled = true
} else {
errorTextView.visibility = View.VISIBLE
+ if (com.android.systemui.Flags.mediaProjectionGreyErrorText()) {
+ errorTextView.isEnabled = false
+ errorTextView.setTextColor(context.getColorStateList(R.color.menu_item_text))
+ errorTextView.setText(
+ R.string.media_projection_entry_app_permission_dialog_single_app_not_supported
+ )
+ }
titleTextView.isEnabled = false
}
return view
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt
index d40ecc9565ae..1176095fbb1c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.panels.ui.compose
+import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -23,11 +24,13 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
+import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -63,6 +66,7 @@ fun TileDetails(modifier: Modifier = Modifier, detailsViewModel: DetailsViewMode
val title = tileDetailedViewModel.title
val subTitle = tileDetailedViewModel.subTitle
+ val colors = MaterialTheme.colorScheme
Column(
modifier =
@@ -70,20 +74,33 @@ fun TileDetails(modifier: Modifier = Modifier, detailsViewModel: DetailsViewMode
.fillMaxWidth()
// The height of the details view is TBD.
.fillMaxHeight()
+ .background(color = colors.onPrimary)
) {
CompositionLocalProvider(
value = LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant
) {
Row(
- modifier = Modifier.fillMaxWidth(),
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(
+ start = TileDetailsDefaults.TitleRowStart,
+ top = TileDetailsDefaults.TitleRowTop,
+ end = TileDetailsDefaults.TitleRowEnd,
+ bottom = TileDetailsDefaults.TitleRowBottom
+ ),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
IconButton(
onClick = { detailsViewModel.closeDetailedView() },
+ colors = IconButtonDefaults.iconButtonColors(
+ contentColor = colors.onSurface
+ ),
modifier =
- Modifier.align(Alignment.CenterVertically)
+ Modifier
+ .align(Alignment.CenterVertically)
.height(TileDetailsDefaults.IconHeight)
+ .width(TileDetailsDefaults.IconWidth)
.padding(start = TileDetailsDefaults.IconPadding),
) {
Icon(
@@ -96,13 +113,19 @@ fun TileDetails(modifier: Modifier = Modifier, detailsViewModel: DetailsViewMode
text = title,
modifier = Modifier.align(Alignment.CenterVertically),
textAlign = TextAlign.Center,
- style = MaterialTheme.typography.titleLarge,
+ style = MaterialTheme.typography.titleMedium,
+ color = colors.onSurface,
)
IconButton(
onClick = { tileDetailedViewModel.clickOnSettingsButton() },
+ colors = IconButtonDefaults.iconButtonColors(
+ contentColor = colors.onSurface
+ ),
modifier =
- Modifier.align(Alignment.CenterVertically)
+ Modifier
+ .align(Alignment.CenterVertically)
.height(TileDetailsDefaults.IconHeight)
+ .width(TileDetailsDefaults.IconWidth)
.padding(end = TileDetailsDefaults.IconPadding),
) {
Icon(
@@ -116,7 +139,8 @@ fun TileDetails(modifier: Modifier = Modifier, detailsViewModel: DetailsViewMode
text = subTitle,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
- style = MaterialTheme.typography.titleSmall,
+ style = MaterialTheme.typography.bodySmall,
+ color = colors.onSurfaceVariant,
)
}
MapTileDetailsContent(tileDetailedViewModel)
@@ -135,6 +159,11 @@ private fun MapTileDetailsContent(tileDetailsViewModel: TileDetailsViewModel) {
}
private object TileDetailsDefaults {
- val IconHeight = 48.dp
+ val IconHeight = 24.dp
+ val IconWidth = 24.dp
val IconPadding = 4.dp
+ val TitleRowStart = 14.dp
+ val TitleRowTop = 22.dp
+ val TitleRowEnd = 20.dp
+ val TitleRowBottom = 8.dp
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
index b3b6cfdcc306..699778f3b6f9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
@@ -39,6 +39,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
@@ -173,6 +174,7 @@ fun LargeTileContent(
}
}
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun LargeTileLabels(
label: String,
@@ -188,7 +190,7 @@ fun LargeTileLabels(
Column(verticalArrangement = Arrangement.Center, modifier = modifier.fillMaxHeight()) {
TileLabel(
text = label,
- style = MaterialTheme.typography.labelLarge,
+ style = MaterialTheme.typography.titleSmallEmphasized,
color = { animatedLabelColor },
isVisible = isVisible,
)
@@ -196,7 +198,7 @@ fun LargeTileLabels(
TileLabel(
secondaryLabel ?: "",
color = { animatedSecondaryLabelColor },
- style = MaterialTheme.typography.bodyMedium,
+ style = MaterialTheme.typography.labelMedium,
isVisible = isVisible,
modifier =
Modifier.thenIf(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index ccbd8fdbe00c..46f05d0ac895 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -65,6 +65,7 @@ import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalContentColor
@@ -109,11 +110,11 @@ import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.customActions
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
+import androidx.compose.ui.text.style.Hyphens
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
import androidx.compose.ui.util.fastMap
import com.android.compose.gesture.effect.rememberOffsetOverscrollEffectFactory
import com.android.compose.modifiers.height
@@ -165,7 +166,7 @@ import kotlinx.coroutines.launch
object TileType
-@OptIn(ExperimentalMaterial3Api::class)
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
val primaryContainerColor = MaterialTheme.colorScheme.primaryContainer
@@ -177,7 +178,8 @@ private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
),
title = {
Text(
- text = stringResource(id = R.string.qs_edit),
+ text = stringResource(id = R.string.qs_edit_tiles),
+ style = MaterialTheme.typography.titleLargeEmphasized,
modifier = Modifier.padding(start = 24.dp),
)
},
@@ -204,7 +206,10 @@ private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
contentColor = MaterialTheme.colorScheme.onPrimary,
),
) {
- Text(stringResource(id = com.android.internal.R.string.reset))
+ Text(
+ text = stringResource(id = com.android.internal.R.string.reset),
+ style = MaterialTheme.typography.labelLarge,
+ )
}
}
},
@@ -212,6 +217,7 @@ private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
)
}
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun DefaultEditTileGrid(
listState: EditTileListState,
@@ -283,7 +289,9 @@ fun DefaultEditTileGrid(
}
}
} else {
- Text(text = stringResource(id = R.string.drag_to_rearrange_tiles))
+ EditGridCenteredText(
+ text = stringResource(id = R.string.drag_to_rearrange_tiles)
+ )
}
}
}
@@ -401,6 +409,11 @@ private fun EditGridHeader(
}
@Composable
+private fun EditGridCenteredText(text: String, modifier: Modifier = Modifier) {
+ Text(text = text, style = MaterialTheme.typography.titleSmall, modifier = modifier)
+}
+
+@Composable
private fun RemoveTileTarget(onClick: () -> Unit) {
Row(
verticalAlignment = Alignment.CenterVertically,
@@ -486,6 +499,7 @@ private fun CurrentTilesGrid(
}
}
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun AvailableTileGrid(
tiles: List<AvailableTileGridCell>,
@@ -524,7 +538,7 @@ private fun AvailableTileGrid(
) {
Text(
text = category.label.load() ?: "",
- fontSize = 20.sp,
+ style = MaterialTheme.typography.titleMediumEmphasized,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.fillMaxWidth().padding(start = 8.dp, bottom = 16.dp),
)
@@ -737,6 +751,7 @@ private fun TileGridCell(
}
}
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun AvailableTileGridCell(
cell: AvailableTileGridCell,
@@ -803,6 +818,7 @@ private fun AvailableTileGridCell(
color = colors.label,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.labelMedium.copy(hyphens = Hyphens.Auto),
modifier = Modifier.align(Alignment.TopCenter),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
index 74b3f302cdc8..b94e7b249233 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
@@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -36,8 +37,10 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.Expandable
+import com.android.compose.modifiers.thenIf
import com.android.systemui.animation.Expandable
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.common.ui.compose.load
@@ -79,6 +82,17 @@ fun OngoingActivityChip(
}
is OngoingActivityChipModel.ClickBehavior.None -> null
}
+ val isClickable = onClick != null
+
+ val chipSidePadding = dimensionResource(id = R.dimen.ongoing_activity_chip_side_padding)
+ val minWidth =
+ if (isClickable) {
+ dimensionResource(id = R.dimen.min_clickable_item_size)
+ } else if (model.icon != null) {
+ dimensionResource(id = R.dimen.ongoing_activity_chip_icon_size) + chipSidePadding
+ } else {
+ dimensionResource(id = R.dimen.ongoing_activity_chip_min_text_width) + chipSidePadding
+ }
Expandable(
color = Color(model.colors.background(LocalContext.current).defaultColor),
@@ -92,6 +106,15 @@ fun OngoingActivityChip(
this.contentDescription = contentDescription
}
}
+ .thenIf(isClickable) { Modifier.widthIn(min = minWidth) }
+ .layout { measurable, constraints ->
+ val placeable = measurable.measure(constraints)
+ layout(placeable.width, placeable.height) {
+ if (constraints.maxWidth >= minWidth.roundToPx()) {
+ placeable.place(0, 0)
+ }
+ }
+ }
.graphicsLayer(
alpha =
if (model.transitionManager?.hideChipForTransition == true) {
@@ -103,9 +126,12 @@ fun OngoingActivityChip(
borderStroke = borderStroke,
onClick = onClick,
useModifierBasedImplementation = StatusBarChipsReturnAnimations.isEnabled,
+ // Some chips like the 3-2-1 countdown chip should be very small, smaller than a
+ // reasonable minimum size.
+ defaultMinSize = false,
transitionControllerFactory = model.transitionManager?.controllerFactory,
) {
- ChipBody(model, iconViewStore, isClickable = onClick != null)
+ ChipBody(model, iconViewStore, isClickable = isClickable, minWidth = minWidth)
}
}
@@ -114,36 +140,22 @@ private fun ChipBody(
model: OngoingActivityChipModel.Active,
iconViewStore: NotificationIconContainerViewBinder.IconViewStore?,
isClickable: Boolean,
+ minWidth: Dp,
modifier: Modifier = Modifier,
) {
val hasEmbeddedIcon =
model.icon is OngoingActivityChipModel.ChipIcon.StatusBarView ||
model.icon is OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon
- val chipSidePadding = dimensionResource(id = R.dimen.ongoing_activity_chip_side_padding)
- val minWidth =
- if (isClickable) {
- dimensionResource(id = R.dimen.min_clickable_item_size)
- } else if (model.icon != null) {
- dimensionResource(id = R.dimen.ongoing_activity_chip_icon_size) + chipSidePadding
- } else {
- dimensionResource(id = R.dimen.ongoing_activity_chip_min_text_width) + chipSidePadding
- }
-
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier =
modifier
.fillMaxHeight()
- .layout { measurable, constraints ->
- val placeable = measurable.measure(constraints)
- layout(placeable.width, placeable.height) {
- if (constraints.maxWidth >= minWidth.roundToPx()) {
- placeable.place(0, 0)
- }
- }
- }
+ // Set the minWidth here as well as on the Expandable so that the content within
+ // this row is still centered correctly horizontally
+ .thenIf(isClickable) { Modifier.widthIn(min = minWidth) }
.padding(
horizontal =
if (hasEmbeddedIcon) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
index d35c3b617246..7e19ff115a92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
@@ -21,6 +21,7 @@ import android.app.Notification.BigPictureStyle
import android.app.Notification.BigTextStyle
import android.app.Notification.CallStyle
import android.app.Notification.EXTRA_BIG_TEXT
+import android.app.Notification.EXTRA_CALL_PERSON
import android.app.Notification.EXTRA_CHRONOMETER_COUNT_DOWN
import android.app.Notification.EXTRA_PROGRESS
import android.app.Notification.EXTRA_PROGRESS_INDETERMINATE
@@ -33,6 +34,7 @@ import android.app.Notification.EXTRA_VERIFICATION_ICON
import android.app.Notification.EXTRA_VERIFICATION_TEXT
import android.app.Notification.InboxStyle
import android.app.Notification.ProgressStyle
+import android.app.Person
import android.content.Context
import android.graphics.drawable.Icon
import com.android.systemui.Flags
@@ -108,12 +110,12 @@ constructor(
contentBuilder.shortCriticalText = notification.shortCriticalText()
contentBuilder.lastAudiblyAlertedMs = entry.lastAudiblyAlertedMs
contentBuilder.profileBadgeResId = null // TODO
- contentBuilder.title = notification.resolveTitle(recoveredBuilder.style)
- contentBuilder.text = notification.resolveText(recoveredBuilder.style)
+ contentBuilder.title = notification.title(recoveredBuilder.style)
+ contentBuilder.text = notification.text(recoveredBuilder.style)
contentBuilder.skeletonLargeIcon = notification.skeletonLargeIcon(imageModelProvider)
contentBuilder.oldProgress = notification.oldProgress()
- val colorsFromNotif = recoveredBuilder.getColors(/* header= */ false)
+ val colorsFromNotif = recoveredBuilder.getColors(/* isHeader= */ false)
contentBuilder.colors =
PromotedNotificationContentModel.Colors(
backgroundColor = colorsFromNotif.backgroundColor,
@@ -132,20 +134,16 @@ constructor(
private fun Notification.bigTitle(): CharSequence? = extras?.getCharSequence(EXTRA_TITLE_BIG)
- private fun Notification.Style.bigTitleOverridesTitle(): Boolean {
- return when (this) {
+ private fun Notification.callPerson(): Person? =
+ extras?.getParcelable(EXTRA_CALL_PERSON, Person::class.java)
+
+ private fun Notification.title(style: Notification.Style?): CharSequence? {
+ return when (style) {
is BigTextStyle,
is BigPictureStyle,
- is InboxStyle -> true
- else -> false
- }
- }
-
- private fun Notification.resolveTitle(style: Notification.Style?): CharSequence? {
- return if (style?.bigTitleOverridesTitle() == true) {
- bigTitle()
- } else {
- null
+ is InboxStyle -> bigTitle()
+ is CallStyle -> callPerson()?.name
+ else -> null
} ?: title()
}
@@ -153,13 +151,10 @@ constructor(
private fun Notification.bigText(): CharSequence? = extras?.getCharSequence(EXTRA_BIG_TEXT)
- private fun Notification.Style.bigTextOverridesText(): Boolean = this is BigTextStyle
-
- private fun Notification.resolveText(style: Notification.Style?): CharSequence? {
- return if (style?.bigTextOverridesText() == true) {
- bigText()
- } else {
- null
+ private fun Notification.text(style: Notification.Style?): CharSequence? {
+ return when (style) {
+ is BigTextStyle -> bigText()
+ else -> null
} ?: text()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
index 468934791525..5f9678a06b59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
@@ -63,7 +63,7 @@ constructor(@PromotedNotificationLog private val buffer: LogBuffer) {
INFO,
{
str1 = entry.logKey
- str2 = content.toString()
+ str2 = content.toRedactedString()
},
{ "extraction succeeded: $str2 for $str1" },
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
index 0c2859fa1766..57b07204fc6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
@@ -25,6 +25,8 @@ import androidx.annotation.ColorInt
import com.android.internal.widget.NotificationProgressModel
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
+import com.android.systemui.statusbar.notification.row.ImageResult
+import com.android.systemui.statusbar.notification.row.LazyImage
import com.android.systemui.statusbar.notification.row.shared.ImageModel
/**
@@ -152,6 +154,54 @@ data class PromotedNotificationContentModel(
Ineligible,
}
+ fun toRedactedString(): String {
+ return ("PromotedNotificationContentModel(" +
+ "identity=$identity, " +
+ "wasPromotedAutomatically=$wasPromotedAutomatically, " +
+ "smallIcon=${smallIcon?.toRedactedString()}, " +
+ "appName=$appName, " +
+ "subText=${subText?.toRedactedString()}, " +
+ "shortCriticalText=$shortCriticalText, " +
+ "time=$time, " +
+ "lastAudiblyAlertedMs=$lastAudiblyAlertedMs, " +
+ "profileBadgeResId=$profileBadgeResId, " +
+ "title=${title?.toRedactedString()}, " +
+ "text=${text?.toRedactedString()}, " +
+ "skeletonLargeIcon=${skeletonLargeIcon?.toRedactedString()}, " +
+ "oldProgress=$oldProgress, " +
+ "colors=$colors, " +
+ "style=$style, " +
+ "personIcon=${personIcon?.toRedactedString()}, " +
+ "personName=${personName?.toRedactedString()}, " +
+ "verificationIcon=$verificationIcon, " +
+ "verificationText=$verificationText, " +
+ "newProgress=$newProgress)")
+ }
+
+ private fun CharSequence.toRedactedString(): String = "[$length]"
+
+ private fun ImageModel.toRedactedString(): String {
+ return when (this) {
+ is LazyImage -> this.toRedactedString()
+ else -> this.toString()
+ }
+ }
+
+ private fun LazyImage.toRedactedString(): String {
+ return ("LazyImage(" +
+ "icon=[${icon.javaClass.simpleName}], " +
+ "sizeClass=$sizeClass, " +
+ "transform=$transform, " +
+ "result=${result?.toRedactedString()})")
+ }
+
+ private fun ImageResult.toRedactedString(): String {
+ return when (this) {
+ is ImageResult.Empty -> this.toString()
+ is ImageResult.Image -> "Image(drawable=[${drawable.javaClass.simpleName}])"
+ }
+ }
+
companion object {
@JvmStatic
fun featureFlagEnabled(): Boolean =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 256d549dc880..2a3b266c8d10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -4011,7 +4011,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
} else if (isChildInGroup()) {
final int childColor = getShowingLayout().getBackgroundColorForExpansionState();
- if (Flags.notificationRowTransparency() && childColor == Color.TRANSPARENT) {
+ if ((Flags.notificationRowTransparency() || notificationsRedesignTemplates())
+ && childColor == Color.TRANSPARENT) {
// If child is not customizing its background color, switch from the parent to
// the child background when the expansion finishes.
mShowNoBackground = !mNotificationParent.mShowNoBackground;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
index fe3a856e711e..a9ca6359b570 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
@@ -62,6 +62,7 @@ class BaseBackgroundDrawable(
private val buttonShape = Path()
// Color and style
+ private val outlineStaticColor = context.getColor(R.color.magic_action_button_stroke_color)
private val bgPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
val bgColor =
context.getColor(
@@ -70,15 +71,17 @@ class BaseBackgroundDrawable(
color = bgColor
style = Paint.Style.FILL
}
- private val outlinePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- val outlineColor =
- context.getColor(
- com.android.internal.R.color.materialColorOutlineVariant
- )
- color = outlineColor
+ private val outlineGradientPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
+ color = outlineStaticColor
+ style = Paint.Style.STROKE
+ strokeWidth = outlineStrokeWidth
+ }
+ private val outlineSolidPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
+ color = outlineStaticColor
style = Paint.Style.STROKE
strokeWidth = outlineStrokeWidth
}
+
private val outlineStartColor =
context.getColor(
com.android.internal.R.color.materialColorTertiaryContainer
@@ -91,21 +94,35 @@ class BaseBackgroundDrawable(
context.getColor(
com.android.internal.R.color.materialColorPrimary
)
+
// Animation
private var gradientAnimator: ValueAnimator
private var rotationAngle = 20f // Start rotation at 20 degrees
+ private var fadeAnimator: ValueAnimator? = null
+ private var gradientAlpha = 255 // Fading out gradient
+ private var solidAlpha = 0 // Fading in solid color
init {
gradientAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
- duration = 5000 // 5 seconds
+ duration = 1500
interpolator = Interpolators.LINEAR
- repeatCount = 1
+ repeatCount = 0
addUpdateListener { animator ->
val animatedValue = animator.animatedValue as Float
rotationAngle = 20f + animatedValue * 360f // Rotate in a spiral
invalidateSelf()
}
- // TODO: Reset the outline color when animation ends.
+ start()
+ }
+ fadeAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
+ duration = 500
+ startDelay = 1000
+ addUpdateListener { animator ->
+ val progress = animator.animatedValue as Float
+ gradientAlpha = ((1 - progress) * 255).toInt() // Fade out gradient
+ solidAlpha = (progress * 255).toInt() // Fade in color
+ invalidateSelf()
+ }
start()
}
}
@@ -120,14 +137,9 @@ class BaseBackgroundDrawable(
// Draw background
canvas.clipPath(buttonShape)
canvas.drawPath(buttonShape, bgPaint)
- // Apply gradient to outline
- canvas.drawPath(buttonShape, outlinePaint)
- updateGradient(boundsF)
- canvas.restore()
- }
- private fun updateGradient(boundsF: RectF) {
- val gradient = LinearGradient(
+ // Set up outline gradient
+ val gradientShader = LinearGradient(
boundsF.left, boundsF.top,
boundsF.right, boundsF.bottom,
intArrayOf(outlineStartColor, outlineMiddleColor, outlineEndColor),
@@ -137,9 +149,17 @@ class BaseBackgroundDrawable(
// Create a rotation matrix for the spiral effect
val matrix = Matrix()
matrix.setRotate(rotationAngle, boundsF.centerX(), boundsF.centerY())
- gradient.setLocalMatrix(matrix)
+ gradientShader.setLocalMatrix(matrix)
+
+ // Apply gradient to outline
+ outlineGradientPaint.shader = gradientShader
+ outlineGradientPaint.alpha = gradientAlpha
+ canvas.drawPath(buttonShape, outlineGradientPaint)
+ // Apply solid color to outline
+ outlineSolidPaint.alpha = solidAlpha
+ canvas.drawPath(buttonShape, outlineSolidPaint)
- outlinePaint.shader = gradient
+ canvas.restore()
}
override fun onBoundsChange(bounds: Rect) {
@@ -149,13 +169,15 @@ class BaseBackgroundDrawable(
override fun setAlpha(alpha: Int) {
bgPaint.alpha = alpha
- outlinePaint.alpha = alpha
+ outlineGradientPaint.alpha = alpha
+ outlineSolidPaint.alpha = alpha
invalidateSelf()
}
override fun setColorFilter(colorFilter: ColorFilter?) {
bgPaint.colorFilter = colorFilter
- outlinePaint.colorFilter = colorFilter
+ outlineGradientPaint.colorFilter = colorFilter
+ outlineSolidPaint.colorFilter = colorFilter
invalidateSelf()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index a4ee4ad6f6ec..9d9f01b571a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -31,11 +31,11 @@ import static androidx.lifecycle.Lifecycle.State.RESUMED;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import static com.android.systemui.Flags.keyboardShortcutHelperRewrite;
-import static com.android.systemui.Flags.lightRevealMigration;
import static com.android.systemui.Flags.relockWithPowerButtonImmediately;
import static com.android.systemui.Flags.statusBarSignalPolicyRefactor;
import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
import static com.android.systemui.flags.Flags.SHORTCUT_LIST_SEARCH_LAYOUT;
+import static com.android.systemui.shared.Flags.ambientAod;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import android.annotation.Nullable;
@@ -980,7 +980,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public void onKeyguardGoingAwayChanged() {
- if (lightRevealMigration()) {
+ if (ambientAod()) {
// This code path is not used if the KeyguardTransitionRepository is managing
// the lightreveal scrim.
return;
@@ -2446,7 +2446,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
return;
}
- if (lightRevealMigration()) {
+ if (ambientAod()) {
return;
}
@@ -3103,7 +3103,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public void onDozeAmountChanged(float linear, float eased) {
- if (!lightRevealMigration()
+ if (!ambientAod()
&& !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
// If wakeAndUnlocking, this is handled in AuthRippleInteractor
if (!mBiometricUnlockController.isWakeAndUnlock()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 0d43789e95a8..8890db3cd943 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -18,7 +18,6 @@ import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
import com.android.systemui.DejankUtils
-import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.KeyguardViewMediator
@@ -26,6 +25,7 @@ import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor
+import com.android.systemui.shared.Flags.ambientAod
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -100,7 +100,7 @@ constructor(
duration = LIGHT_REVEAL_ANIMATION_DURATION
interpolator = Interpolators.LINEAR
addUpdateListener {
- if (lightRevealMigration()) return@addUpdateListener
+ if (ambientAod()) return@addUpdateListener
if (lightRevealScrim.revealEffect !is CircleReveal) {
lightRevealScrim.revealAmount = it.animatedValue as Float
}
@@ -116,7 +116,7 @@ constructor(
addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationCancel(animation: Animator) {
- if (lightRevealMigration()) return
+ if (ambientAod()) return
if (lightRevealScrim.revealEffect !is CircleReveal) {
lightRevealScrim.revealAmount = 1f
}
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 43d1ef478ae1..32f784f17bb7 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
@@ -16,7 +16,6 @@
package com.android.systemui.volume.dialog.sliders.ui
-import android.graphics.drawable.Drawable
import android.view.View
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
@@ -29,7 +28,6 @@ import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SliderDefaults
import androidx.compose.runtime.Composable
@@ -43,7 +41,8 @@ import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.theme.PlatformTheme
-import com.android.compose.ui.graphics.painter.DrawablePainter
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.res.R
@@ -155,7 +154,7 @@ private fun VolumeDialogSlider(
},
)
},
- accessibilityParams = AccessibilityParams(label = sliderStateModel.label),
+ accessibilityParams = AccessibilityParams(contentDescription = sliderStateModel.label),
modifier =
modifier.pointerInput(Unit) {
coroutineScope {
@@ -172,7 +171,7 @@ private fun VolumeDialogSlider(
@Composable
private fun BoxScope.VolumeIcon(
- drawable: Drawable,
+ icon: Icon.Loaded,
isVisible: Boolean,
modifier: Modifier = Modifier,
) {
@@ -182,6 +181,6 @@ private fun BoxScope.VolumeIcon(
exit = fadeOut(animationSpec = tween(durationMillis = 50)),
modifier = modifier.align(Alignment.Center).size(40.dp).padding(10.dp),
) {
- Icon(painter = DrawablePainter(drawable), contentDescription = null)
+ Icon(icon)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
index ef147c741bec..3712276488ff 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
@@ -18,32 +18,36 @@ package com.android.systemui.volume.dialog.sliders.ui.viewmodel
import android.annotation.SuppressLint
import android.content.Context
-import android.graphics.drawable.Drawable
import android.media.AudioManager
import androidx.annotation.DrawableRes
import com.android.settingslib.R as SettingsR
import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.RingerMode
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes
import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.withContext
@SuppressLint("UseCompatLoadingForDrawables")
class VolumeDialogSliderIconProvider
@Inject
constructor(
private val context: Context,
+ @UiBackground private val uiBackgroundContext: CoroutineContext,
private val zenModeInteractor: ZenModeInteractor,
private val audioVolumeInteractor: AudioVolumeInteractor,
) {
- fun getAudioSharingIcon(isMuted: Boolean): Flow<Drawable> {
+ fun getAudioSharingIcon(isMuted: Boolean): Flow<Icon.Loaded> {
return flow {
val iconRes =
if (isMuted) {
@@ -51,11 +55,12 @@ constructor(
} else {
R.drawable.ic_volume_media_bt
}
- emit(context.getDrawable(iconRes)!!)
+ val drawable = withContext(uiBackgroundContext) { context.getDrawable(iconRes)!! }
+ emit(Icon.Loaded(drawable = drawable, contentDescription = null, res = iconRes))
}
}
- fun getCastIcon(isMuted: Boolean): Flow<Drawable> {
+ fun getCastIcon(isMuted: Boolean): Flow<Icon.Loaded> {
return flow {
val iconRes =
if (isMuted) {
@@ -63,7 +68,8 @@ constructor(
} else {
SettingsR.drawable.ic_volume_remote
}
- emit(context.getDrawable(iconRes)!!)
+ val drawable = withContext(uiBackgroundContext) { context.getDrawable(iconRes)!! }
+ emit(Icon.Loaded(drawable = drawable, contentDescription = null, res = iconRes))
}
}
@@ -74,15 +80,18 @@ constructor(
levelMax: Int,
isMuted: Boolean,
isRoutedToBluetooth: Boolean,
- ): Flow<Drawable> {
+ ): Flow<Icon.Loaded> {
return combine(
zenModeInteractor.activeModesBlockingStream(stream),
ringerModeForStream(stream),
) { activeModesBlockingStream, ringerMode ->
if (activeModesBlockingStream?.mainMode?.icon != null) {
- return@combine activeModesBlockingStream.mainMode.icon.drawable
+ Icon.Loaded(
+ drawable = activeModesBlockingStream.mainMode.icon.drawable,
+ contentDescription = null,
+ )
} else {
- context.getDrawable(
+ val iconRes =
getIconRes(
stream,
level,
@@ -92,7 +101,8 @@ constructor(
isRoutedToBluetooth,
ringerMode,
)
- )!!
+ val drawable = withContext(uiBackgroundContext) { context.getDrawable(iconRes)!! }
+ Icon.Loaded(drawable = drawable, contentDescription = null, res = iconRes)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
index 88a061f3813c..ed59598d97d0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
@@ -17,7 +17,7 @@
package com.android.systemui.volume.dialog.sliders.ui.viewmodel
import android.content.Context
-import android.graphics.drawable.Drawable
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import com.android.systemui.volume.dialog.shared.model.streamLabel
@@ -25,14 +25,14 @@ data class VolumeDialogSliderStateModel(
val value: Float,
val isDisabled: Boolean,
val valueRange: ClosedFloatingPointRange<Float>,
- val icon: Drawable,
+ val icon: Icon.Loaded,
val label: String,
)
fun VolumeDialogStreamModel.toStateModel(
context: Context,
isDisabled: Boolean,
- icon: Drawable,
+ icon: Icon.Loaded,
): VolumeDialogSliderStateModel {
return VolumeDialogSliderStateModel(
value = level.toFloat(),
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
index f6aa189eb571..faf0abd4cabd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
@@ -108,11 +108,9 @@ constructor(
isMuted = isMuted,
isRoutedToBluetooth = routedToBluetooth,
)
-
is VolumeDialogSliderType.RemoteMediaStream -> {
volumeDialogSliderIconProvider.getCastIcon(isMuted)
}
-
is VolumeDialogSliderType.AudioSharingStream -> {
volumeDialogSliderIconProvider.getAudioSharingIcon(isMuted)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModel.kt
index 3d98ebacc7ca..f6452679cbf4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModel.kt
@@ -16,9 +16,11 @@
package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
+import android.content.Context
import com.android.internal.logging.UiEventLogger
import com.android.systemui.Flags
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.res.R
@@ -28,6 +30,7 @@ import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlin.coroutines.CoroutineContext
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
@@ -39,11 +42,14 @@ import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
class AudioSharingStreamSliderViewModel
@AssistedInject
constructor(
+ private val context: Context,
@Assisted private val coroutineScope: CoroutineScope,
+ @UiBackground private val uiBackgroundContext: CoroutineContext,
private val audioSharingInteractor: AudioSharingInteractor,
private val uiEventLogger: UiEventLogger,
private val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
@@ -51,6 +57,12 @@ constructor(
) : SliderViewModel {
private val volumeChanges = MutableStateFlow<Int?>(null)
+ private val audioSharingIcon =
+ Icon.Loaded(
+ drawable = context.getDrawable(R.drawable.ic_volume_media_bt)!!,
+ contentDescription = null,
+ res = R.drawable.ic_volume_media_bt,
+ )
override val slider: StateFlow<SliderState> =
combine(
audioSharingInteractor.volume.distinctUntilChanged().onEach {
@@ -62,16 +74,17 @@ constructor(
if (volume == null) {
SliderState.Empty
} else {
-
- State(
- value = volume.toFloat(),
- valueRange =
- audioSharingInteractor.volumeMin.toFloat()..audioSharingInteractor
- .volumeMax
- .toFloat(),
- icon = Icon.Resource(R.drawable.ic_volume_media_bt, null),
- label = deviceName,
- )
+ withContext(uiBackgroundContext) {
+ State(
+ value = volume.toFloat(),
+ valueRange =
+ audioSharingInteractor.volumeMin.toFloat()..audioSharingInteractor
+ .volumeMax
+ .toFloat(),
+ icon = audioSharingIcon,
+ label = deviceName,
+ )
+ }
}
}
.stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty)
@@ -107,7 +120,7 @@ constructor(
private data class State(
override val value: Float,
override val valueRange: ClosedFloatingPointRange<Float>,
- override val icon: Icon,
+ override val icon: Icon.Loaded?,
override val label: String,
) : SliderState {
override val hapticFilter: SliderHapticFeedbackFilter
@@ -116,7 +129,7 @@ constructor(
override val isEnabled: Boolean
get() = true
- override val a11yStep: Float
+ override val step: Float
get() = 1f
override val disabledMessage: String?
@@ -125,6 +138,9 @@ constructor(
override val isMutable: Boolean
get() = false
+ override val a11yContentDescription: String
+ get() = label
+
override val a11yClickDescription: String?
get() = null
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
index 9d32285fecb3..9fe0ad42cdba 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
@@ -19,6 +19,7 @@ package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
import android.content.Context
import android.media.AudioManager
import android.util.Log
+import androidx.annotation.DrawableRes
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.bluetooth.CachedBluetoothDevice
@@ -28,6 +29,7 @@ import com.android.settingslib.volume.shared.model.AudioStreamModel
import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.Flags
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.modes.shared.ModesUiIcons
@@ -40,18 +42,21 @@ import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlin.coroutines.CoroutineContext
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
/** Models a particular slider state. */
class AudioStreamSliderViewModel
@@ -59,10 +64,11 @@ class AudioStreamSliderViewModel
constructor(
@Assisted private val audioStreamWrapper: FactoryAudioStreamWrapper,
@Assisted private val coroutineScope: CoroutineScope,
+ @UiBackground private val uiBackgroundContext: CoroutineContext,
private val context: Context,
private val audioVolumeInteractor: AudioVolumeInteractor,
private val zenModeInteractor: ZenModeInteractor,
- private val audioSharingInteractor: AudioSharingInteractor,
+ audioSharingInteractor: AudioSharingInteractor,
private val uiEventLogger: UiEventLogger,
private val volumePanelLogger: VolumePanelLogger,
private val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
@@ -148,57 +154,69 @@ constructor(
null
}
- private fun AudioStreamModel.toState(
+ private suspend fun AudioStreamModel.toState(
isEnabled: Boolean,
ringerMode: RingerMode,
disabledMessage: String?,
inAudioSharing: Boolean,
primaryDevice: CachedBluetoothDevice?,
- ): State {
- val label = getLabel(inAudioSharing, primaryDevice)
- val icon = getIcon(ringerMode, inAudioSharing)
- return State(
- value = volume.toFloat(),
- valueRange = volumeRange.first.toFloat()..volumeRange.last.toFloat(),
- hapticFilter = createHapticFilter(ringerMode),
- icon = icon,
- label = label,
- disabledMessage = disabledMessage,
- isEnabled = isEnabled,
- a11yStep = volumeRange.step.toFloat(),
- a11yClickDescription =
- if (isAffectedByMute) {
- context.getString(
- if (isMuted) {
- R.string.volume_panel_hint_unmute
- } else {
- R.string.volume_panel_hint_mute
- },
- label,
- )
- } else {
- null
- },
- a11yStateDescription =
- if (isMuted) {
- context.getString(
- if (isAffectedByRingerMode) {
- if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) {
- R.string.volume_panel_hint_vibrate
+ ): State =
+ withContext(uiBackgroundContext) {
+ val label = getLabel(inAudioSharing, primaryDevice)
+ State(
+ value = volume.toFloat(),
+ valueRange = volumeRange.first.toFloat()..volumeRange.last.toFloat(),
+ hapticFilter = createHapticFilter(ringerMode),
+ icon = getIcon(ringerMode, inAudioSharing),
+ label = label,
+ disabledMessage = disabledMessage,
+ isEnabled = isEnabled,
+ step = volumeRange.step.toFloat(),
+ a11yContentDescription =
+ if (isEnabled) {
+ label
+ } else {
+ disabledMessage?.let {
+ context.getString(
+ R.string.volume_slider_disabled_message_template,
+ label,
+ disabledMessage,
+ )
+ } ?: label
+ },
+ a11yClickDescription =
+ if (isAffectedByMute) {
+ context.getString(
+ if (isMuted) {
+ R.string.volume_panel_hint_unmute
+ } else {
+ R.string.volume_panel_hint_mute
+ },
+ label,
+ )
+ } else {
+ null
+ },
+ a11yStateDescription =
+ if (isMuted) {
+ context.getString(
+ if (isAffectedByRingerMode) {
+ if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) {
+ R.string.volume_panel_hint_vibrate
+ } else {
+ R.string.volume_panel_hint_muted
+ }
} else {
R.string.volume_panel_hint_muted
}
- } else {
- R.string.volume_panel_hint_muted
- }
- )
- } else {
- null
- },
- audioStreamModel = this,
- isMutable = isAffectedByMute,
- )
- }
+ )
+ } else {
+ null
+ },
+ audioStreamModel = this@toState,
+ isMutable = isAffectedByMute,
+ )
+ }
private fun AudioStreamModel.createHapticFilter(
ringerMode: RingerMode
@@ -220,12 +238,14 @@ constructor(
flowOf(context.getString(R.string.stream_notification_unavailable))
} else {
if (zenModeInteractor.canBeBlockedByZenMode(audioStream)) {
- zenModeInteractor.activeModesBlockingStream(audioStream).map { blockingZenModes
- ->
- blockingZenModes.mainMode?.name?.let {
- context.getString(R.string.stream_unavailable_by_modes, it)
- } ?: context.getString(R.string.stream_unavailable_by_unknown)
- }
+ zenModeInteractor
+ .activeModesBlockingStream(audioStream)
+ .map { blockingZenModes ->
+ blockingZenModes.mainMode?.name?.let {
+ context.getString(R.string.stream_unavailable_by_modes, it)
+ } ?: context.getString(R.string.stream_unavailable_by_unknown)
+ }
+ .distinctUntilChanged()
} else {
flowOf(context.getString(R.string.stream_unavailable_by_unknown))
}
@@ -256,8 +276,11 @@ constructor(
?: error("No label for the stream: $audioStream")
}
- private fun AudioStreamModel.getIcon(ringerMode: RingerMode, inAudioSharing: Boolean): Icon {
- val iconRes =
+ private fun AudioStreamModel.getIcon(
+ ringerMode: RingerMode,
+ inAudioSharing: Boolean,
+ ): Icon.Loaded {
+ val iconResource: Int =
if (isMuted) {
if (isAffectedByRingerMode) {
if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) {
@@ -272,14 +295,21 @@ constructor(
inAudioSharing
) {
R.drawable.ic_volume_media_bt_mute
- } else R.drawable.ic_volume_off
+ } else {
+ R.drawable.ic_volume_off
+ }
}
} else {
getIconByStream(audioStream, inAudioSharing)
}
- return Icon.Resource(iconRes, null)
+ return Icon.Loaded(
+ drawable = context.getDrawable(iconResource)!!,
+ contentDescription = null,
+ res = iconResource,
+ )
}
+ @DrawableRes
private fun getIconByStream(audioStream: AudioStream, inAudioSharing: Boolean): Int =
when (audioStream.value) {
AudioManager.STREAM_MUSIC ->
@@ -302,14 +332,15 @@ constructor(
private data class State(
override val value: Float,
override val valueRange: ClosedFloatingPointRange<Float>,
+ override val step: Float,
override val hapticFilter: SliderHapticFeedbackFilter,
- override val icon: Icon,
+ override val icon: Icon.Loaded?,
override val label: String,
override val disabledMessage: String?,
override val isEnabled: Boolean,
- override val a11yStep: Float,
override val a11yClickDescription: String?,
override val a11yStateDescription: String?,
+ override val a11yContentDescription: String,
override val isMutable: Boolean,
val audioStreamModel: AudioStreamModel,
) : SliderState
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
index a6c809186ca5..01810f9aafc3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
@@ -21,6 +21,7 @@ import android.media.session.MediaController.PlaybackInfo
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.res.R
@@ -30,30 +31,40 @@ import com.android.systemui.volume.panel.shared.VolumePanelLogger
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlin.coroutines.CoroutineContext
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
class CastVolumeSliderViewModel
@AssistedInject
constructor(
@Assisted private val session: MediaDeviceSession,
@Assisted private val coroutineScope: CoroutineScope,
+ @UiBackground private val uiBackgroundContext: CoroutineContext,
private val context: Context,
private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
private val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
private val volumePanelLogger: VolumePanelLogger,
) : SliderViewModel {
+ private val castLabel = context.getString(R.string.media_device_cast)
+ private val castIcon =
+ Icon.Loaded(
+ drawable = context.getDrawable(R.drawable.ic_cast)!!,
+ contentDescription = null,
+ res = R.drawable.ic_cast,
+ )
override val slider: StateFlow<SliderState> =
mediaDeviceSessionInteractor
.playbackInfo(session)
.mapNotNull {
volumePanelLogger.onVolumeUpdateReceived(session.sessionToken, it.currentVolume)
- it.getCurrentState()
+ withContext(uiBackgroundContext) { it.getCurrentState() }
}
.stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty)
@@ -83,20 +94,20 @@ constructor(
return State(
value = currentVolume.toFloat(),
valueRange = volumeRange.first.toFloat()..volumeRange.last.toFloat(),
- icon = Icon.Resource(R.drawable.ic_cast, null),
- label = context.getString(R.string.media_device_cast),
+ icon = castIcon,
+ label = castLabel,
isEnabled = true,
- a11yStep = 1f,
+ step = 1f,
)
}
private data class State(
override val value: Float,
override val valueRange: ClosedFloatingPointRange<Float>,
- override val icon: Icon,
+ override val icon: Icon.Loaded?,
override val label: String,
override val isEnabled: Boolean,
- override val a11yStep: Float,
+ override val step: Float,
) : SliderState {
override val hapticFilter: SliderHapticFeedbackFilter
get() = SliderHapticFeedbackFilter()
@@ -107,6 +118,9 @@ constructor(
override val isMutable: Boolean
get() = false
+ override val a11yContentDescription: String
+ get() = label
+
override val a11yClickDescription: String?
get() = null
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt
index 4bc237bd36f5..b1d183404a9f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt
@@ -27,18 +27,17 @@ import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
sealed interface SliderState {
val value: Float
val valueRange: ClosedFloatingPointRange<Float>
+ val step: Float
val hapticFilter: SliderHapticFeedbackFilter
- val icon: Icon?
+ // Force preloaded icon
+ val icon: Icon.Loaded?
val isEnabled: Boolean
val label: String
- /**
- * A11y slider controls works by adjusting one step up or down. The default slider step isn't
- * enough to trigger rounding to the correct value.
- */
- val a11yStep: Float
+
val a11yClickDescription: String?
val a11yStateDescription: String?
+ val a11yContentDescription: String
val disabledMessage: String?
val isMutable: Boolean
@@ -46,12 +45,13 @@ sealed interface SliderState {
override val value: Float = 0f
override val valueRange: ClosedFloatingPointRange<Float> = 0f..1f
override val hapticFilter = SliderHapticFeedbackFilter()
- override val icon: Icon? = null
+ override val icon: Icon.Loaded? = null
override val label: String = ""
override val disabledMessage: String? = null
- override val a11yStep: Float = 0f
+ override val step: Float = 0f
override val a11yClickDescription: String? = null
override val a11yStateDescription: String? = null
+ override val a11yContentDescription: String = label
override val isEnabled: Boolean = true
override val isMutable: Boolean = false
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/slider/Slider.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/slider/Slider.kt
index f6582a005035..502b311f7b40 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ui/slider/Slider.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/ui/slider/Slider.kt
@@ -40,7 +40,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.ProgressBarRangeInfo
import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.semantics.clearAndSetSemantics
@@ -52,7 +51,6 @@ import androidx.compose.ui.semantics.stateDescription
import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.lifecycle.rememberViewModel
-import com.android.systemui.res.R
import com.android.systemui.volume.haptics.ui.VolumeHapticsConfigsProvider
import kotlin.math.round
import kotlinx.coroutines.Job
@@ -108,7 +106,8 @@ fun Slider(
}
}
val semantics =
- accessibilityParams.createSemantics(
+ createSemantics(
+ accessibilityParams,
animatable.targetValue,
valueRange,
valueChange,
@@ -167,24 +166,18 @@ private fun snapValue(
return Math.round(coercedValue / stepDistance) * stepDistance
}
-@Composable
-private fun AccessibilityParams.createSemantics(
+private fun createSemantics(
+ params: AccessibilityParams,
value: Float,
valueRange: ClosedFloatingPointRange<Float>,
onValueChanged: (Float) -> Unit,
isEnabled: Boolean,
stepDistance: Float,
): SemanticsPropertyReceiver.() -> Unit {
- val semanticsContentDescription =
- disabledMessage
- ?.takeIf { !isEnabled }
- ?.let { message ->
- stringResource(R.string.volume_slider_disabled_message_template, label, message)
- } ?: label
return {
- contentDescription = semanticsContentDescription
+ contentDescription = params.contentDescription
if (isEnabled) {
- currentStateDescription?.let { stateDescription = it }
+ params.stateDescription?.let { stateDescription = it }
progressBarRangeInfo = ProgressBarRangeInfo(value, valueRange)
} else {
disabled()
@@ -253,9 +246,8 @@ private fun Haptics.createViewModel(
}
data class AccessibilityParams(
- val label: String,
- val currentStateDescription: String? = null,
- val disabledMessage: String? = null,
+ val contentDescription: String,
+ val stateDescription: String? = null,
)
sealed interface Haptics {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java
index 146488b523ad..108f3ae86f9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java
@@ -69,4 +69,25 @@ public class MenuViewAppearanceTest extends SysuiTestCase {
assertThat(end_y).isEqualTo(end_y);
}
+
+ @Test
+ public void avoidVerticalDisplayCutout_doesNotExceedTopBounds() {
+ final int y = DRAGGABLE_BOUNDS.top - 100;
+
+ final float end_y = MenuViewAppearance.avoidVerticalDisplayCutout(
+ y, MENU_HEIGHT, DRAGGABLE_BOUNDS, new Rect(0, 5, 0, 6));
+
+ assertThat(end_y).isGreaterThan(DRAGGABLE_BOUNDS.top);
+ }
+
+
+ @Test
+ public void avoidVerticalDisplayCutout_doesNotExceedBottomBounds() {
+ final int y = DRAGGABLE_BOUNDS.bottom + 100;
+
+ final float end_y = MenuViewAppearance.avoidVerticalDisplayCutout(
+ y, MENU_HEIGHT, DRAGGABLE_BOUNDS, new Rect(0, 5, 0, 6));
+
+ assertThat(end_y).isLessThan(DRAGGABLE_BOUNDS.bottom);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 8fb2a245921a..6a9a485151de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -734,6 +734,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
NotificationEntry entry = mock(NotificationEntry.class);
when(row.getEntry()).thenReturn(entry);
+ when(row.getEntryLegacy()).thenReturn(entry);
when(entry.isAmbient()).thenReturn(false);
EntryAdapter entryAdapter = mock(EntryAdapter.class);
when(entryAdapter.isAmbient()).thenReturn(false);
@@ -753,6 +754,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
NotificationEntry entry = mock(NotificationEntry.class);
when(row.getEntry()).thenReturn(entry);
+ when(row.getEntryLegacy()).thenReturn(entry);
when(entry.isAmbient()).thenReturn(true);
EntryAdapter entryAdapter = mock(EntryAdapter.class);
when(entryAdapter.isAmbient()).thenReturn(true);
@@ -772,6 +774,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
NotificationEntry entry = mock(NotificationEntry.class);
when(row.getEntry()).thenReturn(entry);
+ when(row.getEntryLegacy()).thenReturn(entry);
when(entry.isAmbient()).thenReturn(false);
EntryAdapter entryAdapter = mock(EntryAdapter.class);
when(entryAdapter.isAmbient()).thenReturn(false);
@@ -1384,6 +1387,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.isSeenInShade()).thenReturn(true);
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ when(row.getEntryLegacy()).thenReturn(entry);
when(row.getEntry()).thenReturn(entry);
// WHEN we generate an add event
@@ -1440,6 +1444,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
NotificationEntry entry = mock(NotificationEntry.class);
when(row.canViewBeCleared()).thenReturn(true);
when(row.getEntry()).thenReturn(entry);
+ when(row.getEntryLegacy()).thenReturn(entry);
when(entry.isClearable()).thenReturn(true);
EntryAdapter entryAdapter = mock(EntryAdapter.class);
when(entryAdapter.isClearable()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index a3616d20e11f..a7f3fdcb517e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -28,8 +28,8 @@ import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED;
import static android.provider.Settings.Global.HEADS_UP_ON;
import static com.android.systemui.Flags.FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE;
-import static com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION;
import static com.android.systemui.flags.Flags.SHORTCUT_LIST_SEARCH_LAYOUT;
+import static com.android.systemui.shared.Flags.FLAG_AMBIENT_AOD;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.phone.CentralSurfaces.MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU;
@@ -242,7 +242,7 @@ import javax.inject.Provider;
@SmallTest
@RunWith(AndroidJUnit4.class)
@RunWithLooper(setAsMainLooper = true)
-@EnableFlags(FLAG_LIGHT_REVEAL_MIGRATION)
+@EnableFlags(FLAG_AMBIENT_AOD)
public class CentralSurfacesImplTest extends SysuiTestCase {
private static final DeviceState FOLD_STATE_FOLDED = new DeviceState(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProviderKosmos.kt
index 09f9f1c6362e..44d7a22b6258 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProviderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProviderKosmos.kt
@@ -18,6 +18,7 @@ package com.android.systemui.volume.dialog.sliders.ui.viewmodel
import android.content.applicationContext
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.volume.domain.interactor.audioVolumeInteractor
@@ -25,6 +26,7 @@ val Kosmos.volumeDialogSliderIconProvider by
Kosmos.Fixture {
VolumeDialogSliderIconProvider(
context = applicationContext,
+ uiBackgroundContext = backgroundCoroutineContext,
audioVolumeInteractor = audioVolumeInteractor,
zenModeInteractor = zenModeInteractor,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelKosmos.kt
index 8c8d0240f572..6e43d79295ff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelKosmos.kt
@@ -16,9 +16,11 @@
package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
+import android.content.applicationContext
import com.android.internal.logging.uiEventLogger
import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.volume.domain.interactor.audioSharingInteractor
import com.android.systemui.volume.shared.volumePanelLogger
import kotlinx.coroutines.CoroutineScope
@@ -28,7 +30,9 @@ val Kosmos.audioSharingStreamSliderViewModelFactory by
object : AudioSharingStreamSliderViewModel.Factory {
override fun create(coroutineScope: CoroutineScope): AudioSharingStreamSliderViewModel {
return AudioSharingStreamSliderViewModel(
+ applicationContext,
coroutineScope,
+ backgroundCoroutineContext,
audioSharingInteractor,
uiEventLogger,
sliderHapticsViewModelFactory,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt
index 88c716e0ab10..47016e535ea4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt
@@ -20,6 +20,7 @@ import android.content.applicationContext
import com.android.internal.logging.uiEventLogger
import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.volume.domain.interactor.audioSharingInteractor
import com.android.systemui.volume.domain.interactor.audioVolumeInteractor
@@ -37,6 +38,7 @@ val Kosmos.audioStreamSliderViewModelFactory by
return AudioStreamSliderViewModel(
audioStream,
coroutineScope,
+ backgroundCoroutineContext,
applicationContext,
audioVolumeInteractor,
zenModeInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt
index 6875619d45fc..ed51e054e50c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt
@@ -19,6 +19,7 @@ package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
import android.content.applicationContext
import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.volume.mediaDeviceSessionInteractor
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
import com.android.systemui.volume.shared.volumePanelLogger
@@ -34,6 +35,7 @@ val Kosmos.castVolumeSliderViewModelFactory by
return CastVolumeSliderViewModel(
session,
coroutineScope,
+ backgroundCoroutineContext,
applicationContext,
mediaDeviceSessionInteractor,
sliderHapticsViewModelFactory,
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 658ea4c27e4c..a4cbf420b93b 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -168,7 +168,6 @@ public class AdbDebuggingManager {
private AdbConnectionInfo mAdbConnectionInfo = new AdbConnectionInfo();
// Polls for a tls port property when adb wifi is enabled
private AdbConnectionPortPoller mConnectionPortPoller;
- private final PortListenerImpl mPortListener = new PortListenerImpl();
private final Ticker mTicker;
public AdbDebuggingManager(Context context) {
@@ -323,10 +322,6 @@ public class AdbDebuggingManager {
}
}
- interface AdbConnectionPortListener {
- void onPortReceived(int port);
- }
-
/**
* This class will poll for a period of time for adbd to write the port
* it connected to.
@@ -336,16 +331,11 @@ public class AdbDebuggingManager {
* port through different means. A better fix would be to always start AdbDebuggingManager, but
* it needs to adjust accordingly on whether ro.adb.secure is set.
*/
- static class AdbConnectionPortPoller extends Thread {
+ private class AdbConnectionPortPoller extends Thread {
private final String mAdbPortProp = "service.adb.tls.port";
- private AdbConnectionPortListener mListener;
private final int mDurationSecs = 10;
private AtomicBoolean mCanceled = new AtomicBoolean(false);
- AdbConnectionPortPoller(AdbConnectionPortListener listener) {
- mListener = listener;
- }
-
@Override
public void run() {
Slog.d(TAG, "Starting adb port property poller");
@@ -362,13 +352,22 @@ public class AdbDebuggingManager {
// to start the server. Otherwise we should have a valid port.
int port = SystemProperties.getInt(mAdbPortProp, Integer.MAX_VALUE);
if (port == -1 || (port > 0 && port <= 65535)) {
- mListener.onPortReceived(port);
+ onPortReceived(port);
return;
}
SystemClock.sleep(1000);
}
Slog.w(TAG, "Failed to receive adb connection port");
- mListener.onPortReceived(-1);
+ onPortReceived(-1);
+ }
+
+ private void onPortReceived(int port) {
+ Slog.d(TAG, "Received tls port=" + port);
+ Message msg = mHandler.obtainMessage(port > 0
+ ? AdbDebuggingHandler.MSG_SERVER_CONNECTED
+ : AdbDebuggingHandler.MSG_SERVER_DISCONNECTED);
+ msg.obj = port;
+ mHandler.sendMessage(msg);
}
public void cancelAndWait() {
@@ -382,17 +381,6 @@ public class AdbDebuggingManager {
}
}
- class PortListenerImpl implements AdbConnectionPortListener {
- public void onPortReceived(int port) {
- Slog.d(TAG, "Received tls port=" + port);
- Message msg = mHandler.obtainMessage(port > 0
- ? AdbDebuggingHandler.MSG_SERVER_CONNECTED
- : AdbDebuggingHandler.MSG_SERVER_DISCONNECTED);
- msg.obj = port;
- mHandler.sendMessage(msg);
- }
- }
-
@VisibleForTesting
static class AdbDebuggingThread extends Thread {
private boolean mStopped;
@@ -1088,8 +1076,7 @@ public class AdbDebuggingManager {
mContext.registerReceiver(mBroadcastReceiver, intentFilter);
SystemProperties.set(AdbService.WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
- mConnectionPortPoller =
- new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+ mConnectionPortPoller = new AdbDebuggingManager.AdbConnectionPortPoller();
mConnectionPortPoller.start();
startAdbDebuggingThread();
@@ -1138,8 +1125,7 @@ public class AdbDebuggingManager {
mContext.registerReceiver(mBroadcastReceiver, intentFilter);
SystemProperties.set(AdbService.WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
- mConnectionPortPoller =
- new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+ mConnectionPortPoller = new AdbDebuggingManager.AdbConnectionPortPoller();
mConnectionPortPoller.start();
startAdbDebuggingThread();
@@ -1257,7 +1243,7 @@ public class AdbDebuggingManager {
if (mAdbWifiEnabled) {
// In scenarios where adbd is restarted, the tls port may change.
mConnectionPortPoller =
- new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+ new AdbDebuggingManager.AdbConnectionPortPoller();
mConnectionPortPoller.start();
}
break;
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index 40f7c873eae8..d12a0a2a1e00 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -21,10 +21,8 @@ import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
-import android.debug.AdbManager;
import android.debug.AdbManagerInternal;
import android.debug.AdbTransportType;
import android.debug.FingerprintAndPairDevice;
@@ -40,10 +38,8 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.os.UserHandle;
import android.provider.Settings;
import android.service.adb.AdbServiceDumpProto;
-import android.sysprop.AdbProperties;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -63,7 +59,6 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* The Android Debug Bridge (ADB) service. This controls the availability of ADB and authorization
@@ -85,12 +80,6 @@ public class AdbService extends IAdbManager.Stub {
*/
static final String CTL_STOP = "ctl.stop";
- // The tcp port adb is currently using
- AtomicInteger mConnectionPort = new AtomicInteger(-1);
-
- private final AdbConnectionPortListener mPortListener = new AdbConnectionPortListener();
- private AdbDebuggingManager.AdbConnectionPortPoller mConnectionPortPoller;
-
private final RemoteCallbackList<IAdbCallback> mCallbacks = new RemoteCallbackList<>();
/**
* Manages the service lifecycle for {@code AdbService} in {@code SystemServer}.
@@ -404,39 +393,6 @@ public class AdbService extends IAdbManager.Stub {
Slog.d(TAG, "Unregistering callback " + callback);
mCallbacks.unregister(callback);
}
- /**
- * This listener is only used when ro.adb.secure=0. Otherwise, AdbDebuggingManager will
- * do this.
- */
- class AdbConnectionPortListener implements AdbDebuggingManager.AdbConnectionPortListener {
- public void onPortReceived(int port) {
- if (port > 0 && port <= 65535) {
- mConnectionPort.set(port);
- } else {
- mConnectionPort.set(-1);
- // Turn off wifi debugging, since the server did not start.
- try {
- Settings.Global.putInt(mContentResolver,
- Settings.Global.ADB_WIFI_ENABLED, 0);
- } catch (SecurityException e) {
- // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't
- // be changed.
- Slog.d(TAG, "ADB_ENABLED is restricted.");
- }
- }
- broadcastPortInfo(mConnectionPort.get());
- }
- }
-
- private void broadcastPortInfo(int port) {
- Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
- intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, (port >= 0)
- ? AdbManager.WIRELESS_STATUS_CONNECTED
- : AdbManager.WIRELESS_STATUS_DISCONNECTED);
- intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
- AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
- Slog.i(TAG, "sent port broadcast port=" + port);
- }
private void startAdbd() {
SystemProperties.set(CTL_START, ADBD);
@@ -470,20 +426,11 @@ public class AdbService extends IAdbManager.Stub {
} else if (transportType == AdbTransportType.WIFI && enable != mIsAdbWifiEnabled) {
mIsAdbWifiEnabled = enable;
if (mIsAdbWifiEnabled) {
- if (!AdbProperties.secure().orElse(false)) {
- // Start adbd. If this is secure adb, then we defer enabling adb over WiFi.
- SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
- mConnectionPortPoller =
- new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
- mConnectionPortPoller.start();
- }
+ // Start adb over WiFi.
+ SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
} else {
// Stop adb over WiFi.
SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "0");
- if (mConnectionPortPoller != null) {
- mConnectionPortPoller.cancelAndWait();
- mConnectionPortPoller = null;
- }
}
} else {
// No change
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java b/services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java
index 9118c46e3b22..574e484edd16 100644
--- a/services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java
@@ -167,7 +167,11 @@ final class DeviceSelectActionFromTv extends HdmiCecFeatureAction {
private boolean handleReportPowerStatus(int powerStatus) {
switch (powerStatus) {
case HdmiControlManager.POWER_STATUS_ON:
- selectDevice();
+ if (tv().getActiveSource().physicalAddress == mTarget.getPhysicalAddress()) {
+ finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+ } else {
+ selectDevice();
+ }
return true;
case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY:
if (mPowerStatusCounter < 4) {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
index 6db62c8397f3..ccb9e3ea5cbe 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -301,7 +301,8 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
throw new IllegalArgumentException(
"Unknown session ID in closeSession: id=" + sessionId);
}
- halCloseEndpointSession(sessionId, ContextHubServiceUtil.toHalReason(reason));
+ mEndpointManager.halCloseEndpointSession(
+ sessionId, ContextHubServiceUtil.toHalReason(reason));
}
@Override
@@ -312,7 +313,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
// Iterate in reverse since cleanupSessionResources will remove the entry
for (int i = mSessionMap.size() - 1; i >= 0; i--) {
int id = mSessionMap.keyAt(i);
- halCloseEndpointSessionNoThrow(id, Reason.ENDPOINT_GONE);
+ mEndpointManager.halCloseEndpointSessionNoThrow(id, Reason.ENDPOINT_GONE);
cleanupSessionResources(id);
}
}
@@ -444,7 +445,8 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
int id = mSessionMap.keyAt(i);
HubEndpointInfo target = mSessionMap.get(id).getRemoteEndpointInfo();
if (!hasEndpointPermissions(target)) {
- halCloseEndpointSessionNoThrow(id, Reason.PERMISSION_DENIED);
+ mEndpointManager.halCloseEndpointSessionNoThrow(
+ id, Reason.PERMISSION_DENIED);
onCloseEndpointSession(id, Reason.PERMISSION_DENIED);
// Resource cleanup is done in onCloseEndpointSession
}
@@ -503,17 +505,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
mContextHubEndpointCallback.asBinder().linkToDeath(this, 0 /* flags */);
}
- /* package */ void onEndpointSessionOpenRequest(
- int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
- Optional<Byte> error =
- onEndpointSessionOpenRequestInternal(sessionId, initiator, serviceDescriptor);
- if (error.isPresent()) {
- halCloseEndpointSessionNoThrow(sessionId, error.get());
- onCloseEndpointSession(sessionId, error.get());
- // Resource cleanup is done in onCloseEndpointSession
- }
- }
-
+ /** Handle close endpoint callback to the client side */
/* package */ void onCloseEndpointSession(int sessionId, byte reason) {
if (!cleanupSessionResources(sessionId)) {
Log.w(TAG, "Unknown session ID in onCloseEndpointSession: id=" + sessionId);
@@ -585,7 +577,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
}
}
- private Optional<Byte> onEndpointSessionOpenRequestInternal(
+ /* package */ Optional<Byte> onEndpointSessionOpenRequest(
int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
if (!hasEndpointPermissions(initiator)) {
Log.e(
@@ -594,15 +586,41 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
+ initiator
+ " doesn't have permission for "
+ mEndpointInfo);
- return Optional.of(Reason.PERMISSION_DENIED);
+ byte reason = Reason.PERMISSION_DENIED;
+ onCloseEndpointSession(sessionId, reason);
+ return Optional.of(reason);
}
+ // Check & handle error cases for duplicated session id.
+ final boolean existingSession;
+ final boolean existingSessionActive;
synchronized (mOpenSessionLock) {
if (hasSessionId(sessionId)) {
- Log.e(TAG, "Existing session in onEndpointSessionOpenRequest: id=" + sessionId);
- return Optional.of(Reason.UNSPECIFIED);
+ existingSession = true;
+ existingSessionActive = mSessionMap.get(sessionId).isActive();
+ Log.w(
+ TAG,
+ "onEndpointSessionOpenRequest: "
+ + "Existing session ID: "
+ + sessionId
+ + ", isActive: "
+ + existingSessionActive);
+ } else {
+ existingSession = false;
+ existingSessionActive = false;
+ mSessionMap.put(sessionId, new Session(initiator, true));
+ }
+ }
+
+ if (existingSession) {
+ if (existingSessionActive) {
+ // Existing session is already active, call onSessionOpenComplete.
+ openSessionRequestComplete(sessionId);
+ return Optional.empty();
}
- mSessionMap.put(sessionId, new Session(initiator, true));
+ // Reject the session open request for now. Consider invalidating previous pending
+ // session open request based on timeout.
+ return Optional.of(Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
}
boolean success =
@@ -610,7 +628,11 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
(consumer) ->
consumer.onSessionOpenRequest(
sessionId, initiator, serviceDescriptor));
- return success ? Optional.empty() : Optional.of(Reason.UNSPECIFIED);
+ byte reason = Reason.UNSPECIFIED;
+ if (!success) {
+ onCloseEndpointSession(sessionId, reason);
+ }
+ return success ? Optional.empty() : Optional.of(reason);
}
private byte onMessageReceivedInternal(int sessionId, HubMessage message) {
@@ -657,29 +679,6 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
}
/**
- * Calls the HAL closeEndpointSession API.
- *
- * @param sessionId The session ID to close
- * @param halReason The HAL reason
- */
- private void halCloseEndpointSession(int sessionId, byte halReason) throws RemoteException {
- try {
- mHubInterface.closeEndpointSession(sessionId, halReason);
- } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
- throw e;
- }
- }
-
- /** Same as halCloseEndpointSession but does not throw the exception */
- private void halCloseEndpointSessionNoThrow(int sessionId, byte halReason) {
- try {
- halCloseEndpointSession(sessionId, halReason);
- } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
- Log.e(TAG, "Exception while calling HAL closeEndpointSession", e);
- }
- }
-
- /**
* Cleans up resources related to a session with the provided ID.
*
* @param sessionId The session ID to clean up resources for
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
index 8ab581e1fb7a..e1561599517d 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
@@ -29,6 +29,7 @@ import android.hardware.contexthub.IContextHubEndpoint;
import android.hardware.contexthub.IContextHubEndpointCallback;
import android.hardware.contexthub.IEndpointCommunication;
import android.hardware.contexthub.MessageDeliveryStatus;
+import android.hardware.contexthub.Reason;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;
@@ -42,6 +43,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
@@ -316,6 +318,11 @@ import java.util.function.Consumer;
}
}
+ /** Returns if a sessionId can be allocated for the service hub. */
+ private boolean isSessionIdAllocatedForService(int sessionId) {
+ return sessionId > mMaxSessionId || sessionId < mMinSessionId;
+ }
+
/**
* Unregisters an endpoint given its ID.
*
@@ -337,8 +344,7 @@ import java.util.function.Consumer;
}
}
- @Override
- public void onEndpointSessionOpenRequest(
+ private Optional<Byte> onEndpointSessionOpenRequestInternal(
int sessionId,
HubEndpointInfo.HubEndpointIdentifier destination,
HubEndpointInfo.HubEndpointIdentifier initiator,
@@ -348,7 +354,7 @@ import java.util.function.Consumer;
TAG,
"onEndpointSessionOpenRequest: invalid destination hub ID: "
+ destination.getHub());
- return;
+ return Optional.of(Reason.ENDPOINT_INVALID);
}
ContextHubEndpointBroker broker = mEndpointMap.get(destination.getEndpoint());
if (broker == null) {
@@ -356,7 +362,7 @@ import java.util.function.Consumer;
TAG,
"onEndpointSessionOpenRequest: unknown destination endpoint ID: "
+ destination.getEndpoint());
- return;
+ return Optional.of(Reason.ENDPOINT_INVALID);
}
HubEndpointInfo initiatorInfo = mHubInfoRegistry.getEndpointInfo(initiator);
if (initiatorInfo == null) {
@@ -364,9 +370,29 @@ import java.util.function.Consumer;
TAG,
"onEndpointSessionOpenRequest: unknown initiator endpoint ID: "
+ initiator.getEndpoint());
- return;
+ return Optional.of(Reason.ENDPOINT_INVALID);
+ }
+ if (!isSessionIdAllocatedForService(sessionId)) {
+ Log.e(
+ TAG,
+ "onEndpointSessionOpenRequest: invalid session ID, rejected:"
+ + " sessionId="
+ + sessionId);
+ return Optional.of(Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
}
- broker.onEndpointSessionOpenRequest(sessionId, initiatorInfo, serviceDescriptor);
+ return broker.onEndpointSessionOpenRequest(sessionId, initiatorInfo, serviceDescriptor);
+ }
+
+ @Override
+ public void onEndpointSessionOpenRequest(
+ int sessionId,
+ HubEndpointInfo.HubEndpointIdentifier destination,
+ HubEndpointInfo.HubEndpointIdentifier initiator,
+ String serviceDescriptor) {
+ Optional<Byte> errorOptional =
+ onEndpointSessionOpenRequestInternal(
+ sessionId, destination, initiator, serviceDescriptor);
+ errorOptional.ifPresent((error) -> halCloseEndpointSessionNoThrow(sessionId, error));
}
@Override
@@ -418,6 +444,30 @@ import java.util.function.Consumer;
}
}
+ /**
+ * Calls the HAL closeEndpointSession API.
+ *
+ * @param sessionId The session ID to close
+ * @param halReason The HAL reason
+ */
+ /* package */ void halCloseEndpointSession(int sessionId, byte halReason)
+ throws RemoteException {
+ try {
+ mHubInterface.closeEndpointSession(sessionId, halReason);
+ } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+ throw e;
+ }
+ }
+
+ /** Same as halCloseEndpointSession but does not throw the exception */
+ /* package */ void halCloseEndpointSessionNoThrow(int sessionId, byte halReason) {
+ try {
+ halCloseEndpointSession(sessionId, halReason);
+ } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+ Log.e(TAG, "Exception while calling HAL closeEndpointSession", e);
+ }
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java b/services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java
index 959609309da1..cd6a01478eef 100644
--- a/services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java
@@ -156,6 +156,8 @@ class AppCompatSafeRegionPolicy {
void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
if (mNeedsSafeRegionBounds) {
pw.println(prefix + " mNeedsSafeRegionBounds=true");
+ pw.println(
+ prefix + " latestSafeRegionBoundsOnActivity=" + getLatestSafeRegionBounds());
}
if (isLetterboxedForSafeRegionOnlyAllowed()) {
pw.println(prefix + " isLetterboxForSafeRegionOnlyAllowed=true");
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 2798e843d6dd..ab87459da01a 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -218,6 +218,11 @@ class Dimmer {
*/
protected void adjustAppearance(@NonNull WindowState dimmingContainer,
float alpha, int blurRadius) {
+ if (!mHost.isVisibleRequested()) {
+ // If the host is already going away, there is no point in keeping dimming
+ return;
+ }
+
if (mDimState != null || (alpha != 0 || blurRadius != 0)) {
final DimState d = obtainDimState(dimmingContainer);
d.prepareLookChange(alpha, blurRadius);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 543e32fae55f..9ff6eb66064f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -34,6 +34,7 @@ import android.app.admin.PackagePolicyKey;
import android.app.admin.PolicyKey;
import android.app.admin.PolicyValue;
import android.app.admin.UserRestrictionPolicyKey;
+import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.content.Context;
import android.content.IntentFilter;
@@ -282,7 +283,9 @@ final class PolicyDefinition<V> {
static PolicyDefinition<Set<String>> PERMITTED_INPUT_METHODS = new PolicyDefinition<>(
new NoArgsPolicyKey(DevicePolicyIdentifiers.PERMITTED_INPUT_METHODS_POLICY),
- new MostRecent<>(),
+ (Flags.usePolicyIntersectionForPermittedInputMethods()
+ ? new StringSetIntersection()
+ : new MostRecent<>()),
POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_INHERITABLE,
PolicyEnforcerCallbacks::noOp,
new PackageSetPolicySerializer());
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/StringSetIntersection.java b/services/devicepolicy/java/com/android/server/devicepolicy/StringSetIntersection.java
new file mode 100644
index 000000000000..bc075b02b141
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/StringSetIntersection.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2025 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.server.devicepolicy;
+
+import android.annotation.NonNull;
+import android.app.admin.PolicyValue;
+import android.app.admin.PackageSetPolicyValue;
+
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Objects;
+import java.util.Set;
+
+final class StringSetIntersection extends ResolutionMechanism<Set<String>> {
+
+ @Override
+ PolicyValue<Set<String>> resolve(
+ @NonNull LinkedHashMap<EnforcingAdmin, PolicyValue<Set<String>>> adminPolicies) {
+ Objects.requireNonNull(adminPolicies);
+ Set<String> intersectionOfPolicies = null;
+ for (PolicyValue<Set<String>> policy : adminPolicies.values()) {
+ if (intersectionOfPolicies == null) {
+ intersectionOfPolicies = new HashSet<>(policy.getValue());
+ } else {
+ intersectionOfPolicies.retainAll(policy.getValue());
+ }
+ }
+ if (intersectionOfPolicies == null) {
+ return null;
+ }
+ // Note that the resulting set below may be empty, but that's fine:
+ // particular policy should decide what is the meaning of an empty set.
+ return new PackageSetPolicyValue(intersectionOfPolicies);
+ }
+
+ @Override
+ android.app.admin.StringSetIntersection getParcelableResolutionMechanism() {
+ return new android.app.admin.StringSetIntersection();
+ }
+
+ @Override
+ public String toString() {
+ return "StringSetIntersection {}";
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
index a4c71bd6094e..2227eebdb128 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
@@ -251,6 +251,35 @@ public class DeviceSelectActionFromTvTest {
}
@Test
+ public void testDeviceSelect_DeviceAssertsActiveSource_singleSetStreamPathMessage() {
+ // TV was watching playback2 device connected at port 2, and wants to select
+ // playback1.
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ DeviceSelectActionFromTv action = createDeviceSelectAction(actionTimer, callback,
+ /*isCec20=*/false);
+ mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2,
+ "testDeviceSelect");
+ action.start();
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
+ mNativeWrapper.clearResultMessages();
+ mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYBACK_1,
+ "testDeviceSelect");
+ mTestLooper.dispatchAll();
+
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_POWER_STATE_CHANGE);
+ action.handleTimerEvent(STATE_WAIT_FOR_POWER_STATE_CHANGE);
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
+ action.processCommand(REPORT_POWER_STATUS_ON);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
public void testDeviceSelect_DeviceInStandbyStatus_Cec14b() {
mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2,
"testDeviceSelect");
diff --git a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
index 4d2dcf65bfeb..43b1ec393bf6 100644
--- a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -67,12 +68,15 @@ public class ContextHubEndpointTest {
private static final int SESSION_ID_RANGE = ContextHubEndpointManager.SERVICE_SESSION_RANGE;
private static final int MIN_SESSION_ID = 0;
private static final int MAX_SESSION_ID = MIN_SESSION_ID + SESSION_ID_RANGE - 1;
+ private static final int SESSION_ID_FOR_OPEN_REQUEST = MAX_SESSION_ID + 1;
+ private static final int INVALID_SESSION_ID_FOR_OPEN_REQUEST = MIN_SESSION_ID + 1;
private static final String ENDPOINT_NAME = "Example test endpoint";
private static final int ENDPOINT_ID = 1;
private static final String ENDPOINT_PACKAGE_NAME = "com.android.server.location.contexthub";
private static final String TARGET_ENDPOINT_NAME = "Example target endpoint";
+ private static final String ENDPOINT_SERVICE_DESCRIPTOR = "serviceDescriptor";
private static final int TARGET_ENDPOINT_ID = 1;
private static final int SAMPLE_MESSAGE_TYPE = 1234;
@@ -225,6 +229,105 @@ public class ContextHubEndpointTest {
}
@Test
+ public void testEndpointSessionOpenRequest() throws RemoteException {
+ assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE);
+ IContextHubEndpoint endpoint = registerExampleEndpoint();
+
+ HubEndpointInfo targetInfo =
+ new HubEndpointInfo(
+ TARGET_ENDPOINT_NAME,
+ TARGET_ENDPOINT_ID,
+ ENDPOINT_PACKAGE_NAME,
+ Collections.emptyList());
+ mHubInfoRegistry.onEndpointStarted(new HubEndpointInfo[] {targetInfo});
+ mEndpointManager.onEndpointSessionOpenRequest(
+ SESSION_ID_FOR_OPEN_REQUEST,
+ endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+ targetInfo.getIdentifier(),
+ ENDPOINT_SERVICE_DESCRIPTOR);
+
+ verify(mMockCallback)
+ .onSessionOpenRequest(
+ SESSION_ID_FOR_OPEN_REQUEST, targetInfo, ENDPOINT_SERVICE_DESCRIPTOR);
+
+ // Accept
+ endpoint.openSessionRequestComplete(SESSION_ID_FOR_OPEN_REQUEST);
+ verify(mMockEndpointCommunications)
+ .endpointSessionOpenComplete(SESSION_ID_FOR_OPEN_REQUEST);
+
+ unregisterExampleEndpoint(endpoint);
+ }
+
+ @Test
+ public void testEndpointSessionOpenRequestWithInvalidSessionId() throws RemoteException {
+ assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE);
+ IContextHubEndpoint endpoint = registerExampleEndpoint();
+
+ HubEndpointInfo targetInfo =
+ new HubEndpointInfo(
+ TARGET_ENDPOINT_NAME,
+ TARGET_ENDPOINT_ID,
+ ENDPOINT_PACKAGE_NAME,
+ Collections.emptyList());
+ mHubInfoRegistry.onEndpointStarted(new HubEndpointInfo[] {targetInfo});
+ mEndpointManager.onEndpointSessionOpenRequest(
+ INVALID_SESSION_ID_FOR_OPEN_REQUEST,
+ endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+ targetInfo.getIdentifier(),
+ ENDPOINT_SERVICE_DESCRIPTOR);
+ verify(mMockEndpointCommunications)
+ .closeEndpointSession(
+ INVALID_SESSION_ID_FOR_OPEN_REQUEST,
+ Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
+ verify(mMockCallback, never())
+ .onSessionOpenRequest(
+ INVALID_SESSION_ID_FOR_OPEN_REQUEST,
+ targetInfo,
+ ENDPOINT_SERVICE_DESCRIPTOR);
+
+ unregisterExampleEndpoint(endpoint);
+ }
+
+ @Test
+ public void testEndpointSessionOpenRequest_duplicatedSessionId_noopWhenSessionIsActive()
+ throws RemoteException {
+ assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE);
+ IContextHubEndpoint endpoint = registerExampleEndpoint();
+
+ HubEndpointInfo targetInfo =
+ new HubEndpointInfo(
+ TARGET_ENDPOINT_NAME,
+ TARGET_ENDPOINT_ID,
+ ENDPOINT_PACKAGE_NAME,
+ Collections.emptyList());
+ mHubInfoRegistry.onEndpointStarted(new HubEndpointInfo[] {targetInfo});
+ mEndpointManager.onEndpointSessionOpenRequest(
+ SESSION_ID_FOR_OPEN_REQUEST,
+ endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+ targetInfo.getIdentifier(),
+ ENDPOINT_SERVICE_DESCRIPTOR);
+ endpoint.openSessionRequestComplete(SESSION_ID_FOR_OPEN_REQUEST);
+ // Now session with id SESSION_ID_FOR_OPEN_REQUEST is active
+
+ // Duplicated session open request
+ mEndpointManager.onEndpointSessionOpenRequest(
+ SESSION_ID_FOR_OPEN_REQUEST,
+ endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+ targetInfo.getIdentifier(),
+ ENDPOINT_SERVICE_DESCRIPTOR);
+
+ // Client API is only invoked once
+ verify(mMockCallback, times(1))
+ .onSessionOpenRequest(
+ SESSION_ID_FOR_OPEN_REQUEST, targetInfo, ENDPOINT_SERVICE_DESCRIPTOR);
+ // HAL still receives two open complete notifications
+ verify(mMockEndpointCommunications, times(2))
+ .endpointSessionOpenComplete(SESSION_ID_FOR_OPEN_REQUEST);
+
+ unregisterExampleEndpoint(endpoint);
+ }
+
+ @Test
public void testMessageTransaction() throws RemoteException {
IContextHubEndpoint endpoint = registerExampleEndpoint();
testMessageTransactionInternal(endpoint, /* deliverMessageStatus= */ true);
diff --git a/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java b/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java
index d9bb7db17685..5419d9444abd 100644
--- a/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java
+++ b/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java
@@ -110,7 +110,7 @@ public class TextToSpeechTests extends InstrumentationTestCase {
blockingCallSpeak("foo bar", delegate);
ArgumentCaptor<SynthesisRequest> req = ArgumentCaptor.forClass(SynthesisRequest.class);
Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req.capture(),
- Mockito.<SynthesisCallback>anyObject());
+ Mockito.<SynthesisCallback>any());
assertEquals("eng", req.getValue().getLanguage());
assertEquals("USA", req.getValue().getCountry());
@@ -133,7 +133,7 @@ public class TextToSpeechTests extends InstrumentationTestCase {
blockingCallSpeak("le fou barre", delegate);
ArgumentCaptor<SynthesisRequest> req2 = ArgumentCaptor.forClass(SynthesisRequest.class);
Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req2.capture(),
- Mockito.<SynthesisCallback>anyObject());
+ Mockito.<SynthesisCallback>any());
// The params are basically unchanged.
assertEquals("eng", req2.getValue().getLanguage());
@@ -177,7 +177,7 @@ public class TextToSpeechTests extends InstrumentationTestCase {
blockingCallSpeak("foo bar", delegate);
ArgumentCaptor<SynthesisRequest> req = ArgumentCaptor.forClass(SynthesisRequest.class);
Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req.capture(),
- Mockito.<SynthesisCallback>anyObject());
+ Mockito.<SynthesisCallback>any());
assertEquals(defaultLocale.getISO3Language(), req.getValue().getLanguage());
assertEquals(defaultLocale.getISO3Country(), req.getValue().getCountry());
@@ -189,8 +189,8 @@ public class TextToSpeechTests extends InstrumentationTestCase {
private void blockingCallSpeak(String speech, IDelegate mock) throws
InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
- doCountDown(latch).when(mock).onSynthesizeText(Mockito.<SynthesisRequest>anyObject(),
- Mockito.<SynthesisCallback>anyObject());
+ doCountDown(latch).when(mock).onSynthesizeText(Mockito.<SynthesisRequest>any(),
+ Mockito.<SynthesisCallback>any());
mTts.speak(speech, TextToSpeech.QUEUE_ADD, null);
awaitCountDown(latch, 5, TimeUnit.SECONDS);