summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/companion/virtual/flags/flags.aconfig13
-rw-r--r--core/java/android/view/InsetsFlags.java12
-rw-r--r--core/java/android/view/NativeVectorDrawableAnimator.java5
-rw-r--r--core/java/android/view/ViewAnimationHostBridge.java29
-rw-r--r--core/java/android/view/ViewRootImpl.java93
-rw-r--r--core/java/android/window/DisplayWindowPolicyController.java4
-rw-r--r--core/java/android/window/flags/large_screen_experiences_app_compat.aconfig7
-rw-r--r--core/java/android/window/flags/windowing_sdk.aconfig11
-rw-r--r--core/java/com/android/internal/policy/ScreenDecorationsUtils.java23
-rw-r--r--core/res/AndroidManifest.xml5
-rw-r--r--core/res/res/values/config_telephony.xml6
-rw-r--r--core/tests/coretests/src/android/view/ViewFrameRateTest.java158
-rw-r--r--data/etc/preinstalled-packages-platform.xml15
-rw-r--r--graphics/java/android/graphics/RenderNode.java5
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt49
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java19
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt55
-rw-r--r--media/tests/MediaRouter/Android.bp1
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig14
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt9
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt115
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt71
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt)173
-rw-r--r--packages/SystemUI/res/drawable/ic_widgets.xml27
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_open_hub_chip.xml26
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java212
-rw-r--r--packages/SystemUI/src/com/android/systemui/complication/dagger/OpenHubComplicationComponent.java138
-rw-r--r--packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListener.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt87
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractor.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSPreferencesInteractor.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepository.kt)16
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt65
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt47
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/panels/data/QSPreferencesRepositoryTest.kt130
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt90
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt27
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryKosmos.kt25
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/QSPreferencesInteractorKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt)6
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java16
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java2
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java3
-rw-r--r--services/core/java/com/android/server/audio/SoundDoseHelper.java9
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java3
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiUtils.java9
-rw-r--r--services/core/java/com/android/server/hdmi/RequestArcAction.java10
-rw-r--r--services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java7
-rw-r--r--services/core/java/com/android/server/hdmi/SystemAudioAction.java7
-rw-r--r--services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java8
-rw-r--r--services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java9
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2Provider.java56
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java85
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java2
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java14
109 files changed, 2280 insertions, 545 deletions
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 006226eb8c31..ed5d66227574 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -52,4 +52,15 @@ flag {
description: "Makes MediaDrm APIs device-aware"
bug: "303535376"
is_fixed_read_only: true
-} \ No newline at end of file
+}
+
+flag {
+ namespace: "virtual_devices"
+ name: "virtual_display_multi_window_mode_support"
+ description: "Add support for WINDOWING_MODE_MULTI_WINDOW to virtual displays by default"
+ is_fixed_read_only: true
+ bug: "341151395"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/view/InsetsFlags.java b/core/java/android/view/InsetsFlags.java
index ca8a7a8cf175..2fa57688f0cb 100644
--- a/core/java/android/view/InsetsFlags.java
+++ b/core/java/android/view/InsetsFlags.java
@@ -17,6 +17,7 @@
package android.view;
import static android.view.WindowInsetsController.APPEARANCE_FORCE_LIGHT_NAVIGATION_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_CAPTION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
@@ -24,6 +25,7 @@ import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_B
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND;
import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
@@ -69,7 +71,15 @@ public class InsetsFlags {
@ViewDebug.FlagToString(
mask = APPEARANCE_FORCE_LIGHT_NAVIGATION_BARS,
equals = APPEARANCE_FORCE_LIGHT_NAVIGATION_BARS,
- name = "FORCE_LIGHT_NAVIGATION_BARS")
+ name = "FORCE_LIGHT_NAVIGATION_BARS"),
+ @ViewDebug.FlagToString(
+ mask = APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND,
+ equals = APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND,
+ name = "APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND"),
+ @ViewDebug.FlagToString(
+ mask = APPEARANCE_LIGHT_CAPTION_BARS,
+ equals = APPEARANCE_LIGHT_CAPTION_BARS,
+ name = "APPEARANCE_LIGHT_CAPTION_BARS")
})
public @Appearance int appearance;
diff --git a/core/java/android/view/NativeVectorDrawableAnimator.java b/core/java/android/view/NativeVectorDrawableAnimator.java
index b0556a3f8a91..e92bd1f5d6b8 100644
--- a/core/java/android/view/NativeVectorDrawableAnimator.java
+++ b/core/java/android/view/NativeVectorDrawableAnimator.java
@@ -16,6 +16,8 @@
package android.view;
+import android.animation.Animator;
+
/**
* Exists just to allow for android.graphics & android.view package separation
*
@@ -26,4 +28,7 @@ package android.view;
public interface NativeVectorDrawableAnimator {
/** @hide */
long getAnimatorNativePtr();
+
+ /** @hide */
+ void setThreadedRendererAnimatorListener(Animator.AnimatorListener listener);
}
diff --git a/core/java/android/view/ViewAnimationHostBridge.java b/core/java/android/view/ViewAnimationHostBridge.java
index e0fae21bbdf6..62b2b6c053c4 100644
--- a/core/java/android/view/ViewAnimationHostBridge.java
+++ b/core/java/android/view/ViewAnimationHostBridge.java
@@ -16,14 +16,19 @@
package android.view;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.graphics.RenderNode;
+import androidx.annotation.NonNull;
+
/**
* Maps a View to a RenderNode's AnimationHost
*
* @hide
*/
-public class ViewAnimationHostBridge implements RenderNode.AnimationHost {
+public class ViewAnimationHostBridge extends AnimatorListenerAdapter
+ implements RenderNode.AnimationHost {
private final View mView;
/**
@@ -34,17 +39,35 @@ public class ViewAnimationHostBridge implements RenderNode.AnimationHost {
}
@Override
- public void registerAnimatingRenderNode(RenderNode animator) {
- mView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(animator);
+ public void registerAnimatingRenderNode(RenderNode renderNode, Animator animator) {
+ mView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(renderNode);
+ animator.addListener(this);
}
@Override
public void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator) {
mView.mAttachInfo.mViewRootImpl.registerVectorDrawableAnimator(animator);
+ animator.setThreadedRendererAnimatorListener(this);
}
@Override
public boolean isAttached() {
return mView.mAttachInfo != null;
}
+
+ @Override
+ public void onAnimationStart(@NonNull Animator animation) {
+ ViewRootImpl viewRoot = mView.getViewRootImpl();
+ if (viewRoot != null) {
+ viewRoot.addThreadedRendererView(mView);
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(@NonNull Animator animation) {
+ ViewRootImpl viewRoot = mView.getViewRootImpl();
+ if (viewRoot != null) {
+ viewRoot.removeThreadedRendererView(mView);
+ }
+ }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index bdada11c7073..139285a44817 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -427,6 +427,12 @@ public final class ViewRootImpl implements ViewParent,
private static final long NANOS_PER_SEC = 1000000000;
+ // If the ViewRootImpl has been idle for more than 750ms, clear the preferred
+ // frame rate category and frame rate.
+ private static final int IDLE_TIME_MILLIS = 750;
+
+ private static final long NANOS_PER_MILLI = 1_000_000;
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
@@ -659,6 +665,10 @@ public final class ViewRootImpl implements ViewParent,
private int mMinusOneFrameIntervalMillis = 0;
// VRR interval between the previous and the frame before
private int mMinusTwoFrameIntervalMillis = 0;
+ // VRR has the invalidation idle message been posted?
+ private boolean mInvalidationIdleMessagePosted = false;
+ // VRR: List of all Views that are animating with the threaded render
+ private ArrayList<View> mThreadedRendererViews = new ArrayList();
/**
* Update the Choreographer's FrameInfo object with the timing information for the current
@@ -1184,6 +1194,8 @@ public final class ViewRootImpl implements ViewParent,
toolkitFrameRateVelocityMappingReadOnly();
private static boolean sToolkitEnableInvalidateCheckThreadFlagValue =
Flags.enableInvalidateCheckThread();
+ private static boolean sSurfaceFlingerBugfixFlagValue =
+ com.android.graphics.surfaceflinger.flags.Flags.vrrBugfix24q4();
static {
sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly();
@@ -4261,8 +4273,13 @@ public final class ViewRootImpl implements ViewParent,
// when the values are applicable.
if (mDrawnThisFrame) {
mDrawnThisFrame = false;
+ if (!mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) {
+ mInvalidationIdleMessagePosted = true;
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS);
+ }
setCategoryFromCategoryCounts();
updateInfrequentCount();
+ updateFrameRateFromThreadedRendererViews();
setPreferredFrameRate(mPreferredFrameRate);
setPreferredFrameRateCategory(mPreferredFrameRateCategory);
if (mPreferredFrameRate > 0
@@ -6499,6 +6516,8 @@ public final class ViewRootImpl implements ViewParent,
return "MSG_WINDOW_TOUCH_MODE_CHANGED";
case MSG_KEEP_CLEAR_RECTS_CHANGED:
return "MSG_KEEP_CLEAR_RECTS_CHANGED";
+ case MSG_CHECK_INVALIDATION_IDLE:
+ return "MSG_CHECK_INVALIDATION_IDLE";
case MSG_REFRESH_POINTER_ICON:
return "MSG_REFRESH_POINTER_ICON";
case MSG_TOUCH_BOOST_TIMEOUT:
@@ -6759,6 +6778,31 @@ public final class ViewRootImpl implements ViewParent,
mNumPausedForSync = 0;
scheduleTraversals();
break;
+ case MSG_CHECK_INVALIDATION_IDLE: {
+ long delta;
+ if (mIsTouchBoosting || mIsFrameRateBoosting || mInsetsAnimationRunning) {
+ delta = 0;
+ } else {
+ delta = System.nanoTime() / NANOS_PER_MILLI - mLastUpdateTimeMillis;
+ }
+ if (delta >= IDLE_TIME_MILLIS) {
+ mFrameRateCategoryHighCount = 0;
+ mFrameRateCategoryHighHintCount = 0;
+ mFrameRateCategoryNormalCount = 0;
+ mFrameRateCategoryLowCount = 0;
+ mPreferredFrameRate = 0;
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ updateFrameRateFromThreadedRendererViews();
+ setPreferredFrameRate(mPreferredFrameRate);
+ setPreferredFrameRateCategory(mPreferredFrameRateCategory);
+ mInvalidationIdleMessagePosted = false;
+ } else {
+ mInvalidationIdleMessagePosted = true;
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
+ IDLE_TIME_MILLIS - delta);
+ }
+ break;
+ }
case MSG_TOUCH_BOOST_TIMEOUT:
/**
* Lower the frame rate after the boosting period (FRAME_RATE_TOUCH_BOOST_TIME).
@@ -12581,6 +12625,24 @@ public final class ViewRootImpl implements ViewParent,
}
/**
+ * Views that are animating with the ThreadedRenderer don't use the normal invalidation
+ * path, so the value won't be updated through performTraversals. This reads the votes
+ * from those views.
+ */
+ private void updateFrameRateFromThreadedRendererViews() {
+ ArrayList<View> views = mThreadedRendererViews;
+ for (int i = views.size() - 1; i >= 0; i--) {
+ View view = views.get(i);
+ View.AttachInfo attachInfo = view.mAttachInfo;
+ if (attachInfo == null || attachInfo.mViewRootImpl != this) {
+ views.remove(i);
+ } else {
+ view.votePreferredFrameRate();
+ }
+ }
+ }
+
+ /**
* Sets the mPreferredFrameRateCategory from the high, high_hint, normal, and low counts.
*/
private void setCategoryFromCategoryCounts() {
@@ -12761,6 +12823,31 @@ public final class ViewRootImpl implements ViewParent,
}
/**
+ * Mark a View as having an active ThreadedRenderer animation. This is used for
+ * RenderNodeAnimators and AnimatedVectorDrawables. When the animation stops,
+ * {@link #removeThreadedRendererView(View)} must be called.
+ * @param view The View with the ThreadedRenderer animation that started.
+ */
+ public void addThreadedRendererView(View view) {
+ if (!mThreadedRendererViews.contains(view)) {
+ mThreadedRendererViews.add(view);
+ }
+ }
+
+ /**
+ * When a ThreadedRenderer animation ends, the View that is associated with it using
+ * {@link #addThreadedRendererView(View)} must be removed with a call to this method.
+ * @param view The View whose ThreadedRender animation has stopped.
+ */
+ public void removeThreadedRendererView(View view) {
+ mThreadedRendererViews.remove(view);
+ if (!mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) {
+ mInvalidationIdleMessagePosted = true;
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS);
+ }
+ }
+
+ /**
* Returns {@link #INTERMITTENT_STATE_INTERMITTENT} when the ViewRootImpl has only been
* updated intermittently, {@link #INTERMITTENT_STATE_NOT_INTERMITTENT} when it is
* not updated intermittently, and {@link #INTERMITTENT_STATE_IN_TRANSITION} when it
@@ -12983,6 +13070,10 @@ public final class ViewRootImpl implements ViewParent,
private void removeVrrMessages() {
mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
+ if (mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) {
+ mInvalidationIdleMessagePosted = false;
+ mHandler.removeMessages(MSG_CHECK_INVALIDATION_IDLE);
+ }
}
/**
@@ -13001,7 +13092,7 @@ public final class ViewRootImpl implements ViewParent,
mMinusOneFrameIntervalMillis = timeIntervalMillis;
mLastUpdateTimeMillis = currentTimeMillis;
- if (timeIntervalMillis + mMinusTwoFrameIntervalMillis
+ if (mThreadedRendererViews.isEmpty() && timeIntervalMillis + mMinusTwoFrameIntervalMillis
>= INFREQUENT_UPDATE_INTERVAL_MILLIS) {
int infrequentUpdateCount = mInfrequentUpdateCount;
mInfrequentUpdateCount = infrequentUpdateCount == INFREQUENT_UPDATE_COUNTS
diff --git a/core/java/android/window/DisplayWindowPolicyController.java b/core/java/android/window/DisplayWindowPolicyController.java
index 8d71a8e998bd..9cd2a716e498 100644
--- a/core/java/android/window/DisplayWindowPolicyController.java
+++ b/core/java/android/window/DisplayWindowPolicyController.java
@@ -23,6 +23,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.WindowConfiguration;
+import android.companion.virtualdevice.flags.Flags;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -66,6 +67,9 @@ public abstract class DisplayWindowPolicyController {
public DisplayWindowPolicyController() {
synchronized (mSupportedWindowingModes) {
mSupportedWindowingModes.add(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
+ if (Flags.virtualDisplayMultiWindowModeSupport()) {
+ mSupportedWindowingModes.add(WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
+ }
}
}
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index 4b2beb903325..983f46c58c4b 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -2,13 +2,6 @@ package: "com.android.window.flags"
container: "system"
flag {
- name: "disable_thin_letterboxing_reachability"
- namespace: "large_screen_experiences_app_compat"
- description: "Whether reachability is disabled in case of thin letterboxing"
- bug: "334077350"
-}
-
-flag {
name: "disable_thin_letterboxing_policy"
namespace: "large_screen_experiences_app_compat"
description: "Whether reachability is disabled in case of thin letterboxing"
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 6af3d9e50d75..21aa4800237c 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -147,4 +147,15 @@ flag {
metadata {
purpose: PURPOSE_BUGFIX
}
+}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "insets_control_seq"
+ description: "Add seqId to InsetsControls to ensure the stale update is ignored"
+ bug: "339380439"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
} \ No newline at end of file
diff --git a/core/java/com/android/internal/policy/ScreenDecorationsUtils.java b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
index ec6283922807..067e5e8813a7 100644
--- a/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
+++ b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
@@ -18,6 +18,9 @@ package com.android.internal.policy;
import android.content.Context;
import android.content.res.Resources;
+import android.util.DisplayUtils;
+import android.view.Display;
+import android.view.DisplayInfo;
import android.view.RoundedCorners;
import com.android.internal.R;
@@ -57,11 +60,31 @@ public class ScreenDecorationsUtils {
bottomRadius = defaultRadius;
}
+ // If the physical pixels are scaled, apply it here
+ float scale = getPhysicalPixelDisplaySizeRatio(context);
+ if (scale != 1f) {
+ topRadius = topRadius * scale;
+ bottomRadius = bottomRadius * scale;
+ }
+
// Always use the smallest radius to make sure the rounded corners will
// completely cover the display.
return Math.min(topRadius, bottomRadius);
}
+ static float getPhysicalPixelDisplaySizeRatio(Context context) {
+ DisplayInfo displayInfo = new DisplayInfo();
+ context.getDisplay().getDisplayInfo(displayInfo);
+ final Display.Mode maxDisplayMode =
+ DisplayUtils.getMaximumResolutionDisplayMode(displayInfo.supportedModes);
+ if (maxDisplayMode == null) {
+ return 1f;
+ }
+ return DisplayUtils.getPhysicalPixelDisplaySizeRatio(
+ maxDisplayMode.getPhysicalWidth(), maxDisplayMode.getPhysicalHeight(),
+ displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight());
+ }
+
/**
* If live rounded corners are supported on windows.
*/
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7b9235cdc691..6dbe44b483d2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4702,6 +4702,11 @@
<permission android:name="android.permission.REQUEST_UNIQUE_ID_ATTESTATION"
android:protectionLevel="signature" />
+ <!-- Allows an application to use the RemoteKeyProvisioningService.
+ @hide -->
+ <permission android:name="android.permission.BIND_RKP_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to get enabled credential manager providers.
@hide -->
<permission android:name="android.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS"
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 04f6f5214e74..dcda5d8669a4 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -403,4 +403,10 @@
<integer name="config_wait_for_datagram_sending_response_for_last_message_timeout_millis">60000</integer>
<java-symbol type="integer" name="config_wait_for_datagram_sending_response_for_last_message_timeout_millis" />
+ <!-- Boolean indicating whether Telephony should force PhoneGlobals creation
+ regardless of FEATURE_TELEPHONY presence.
+ -->
+ <bool name="config_force_phone_globals_creation">false</bool>
+ <java-symbol type="bool" name="config_force_phone_globals_creation" />
+
</resources>
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index 0b1b40c8ba8b..07446e7617aa 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -41,11 +41,13 @@ import android.app.Instrumentation;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
+import android.platform.test.annotations.LargeTest;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.DisplayMetrics;
import android.widget.FrameLayout;
+import android.widget.ProgressBar;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
@@ -623,6 +625,162 @@ public class ViewFrameRateTest {
assertEquals(FRAME_RATE_CATEGORY_HIGH_HINT, mViewRoot.getLastPreferredFrameRateCategory());
}
+ @LargeTest
+ @Test
+ @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY,
+ com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
+ })
+ public void idleDetected() throws Throwable {
+ waitForFrameRateCategoryToSettle();
+ mActivityRule.runOnUiThread(() -> {
+ mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_HIGH);
+ mMovingView.setFrameContentVelocity(Float.MAX_VALUE);
+ mMovingView.invalidate();
+ runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_HIGH,
+ mViewRoot.getLastPreferredFrameRateCategory()));
+ });
+ waitForAfterDraw();
+
+ // Wait for idle timeout
+ Thread.sleep(1000);
+ assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ }
+
+ @LargeTest
+ @Test
+ @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY,
+ com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
+ })
+ public void vectorDrawableFrameRate() throws Throwable {
+ final ProgressBar[] progressBars = new ProgressBar[3];
+ final ViewGroup[] parents = new ViewGroup[1];
+ mActivityRule.runOnUiThread(() -> {
+ ViewGroup parent = (ViewGroup) mMovingView.getParent();
+ parents[0] = parent;
+ ProgressBar progressBar1 = new ProgressBar(mActivity);
+ parent.addView(progressBar1);
+ progressBar1.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
+ progressBar1.setIndeterminate(true);
+ progressBars[0] = progressBar1;
+
+ ProgressBar progressBar2 = new ProgressBar(mActivity);
+ parent.addView(progressBar2);
+ progressBar2.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NORMAL);
+ progressBar2.setIndeterminate(true);
+ progressBars[1] = progressBar2;
+
+ ProgressBar progressBar3 = new ProgressBar(mActivity);
+ parent.addView(progressBar3);
+ progressBar3.setRequestedFrameRate(45f);
+ progressBar3.setIndeterminate(true);
+ progressBars[2] = progressBar3;
+ });
+ waitForFrameRateCategoryToSettle();
+
+ // Wait for idle timeout
+ Thread.sleep(1000);
+ assertEquals(45f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(FRAME_RATE_CATEGORY_NORMAL, mViewRoot.getLastPreferredFrameRateCategory());
+
+ // Removing the vector drawable with NORMAL should drop the category to LOW
+ mActivityRule.runOnUiThread(() -> parents[0].removeView(progressBars[1]));
+ Thread.sleep(1000);
+ assertEquals(45f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(FRAME_RATE_CATEGORY_LOW,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ // Removing the one voting for frame rate should leave only the category
+ mActivityRule.runOnUiThread(() -> parents[0].removeView(progressBars[2]));
+ Thread.sleep(1000);
+ assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(FRAME_RATE_CATEGORY_LOW,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ // Removing the last one should leave it with no preference
+ mActivityRule.runOnUiThread(() -> parents[0].removeView(progressBars[0]));
+ Thread.sleep(1000);
+ assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ }
+
+ @LargeTest
+ @Test
+ @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY,
+ com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
+ })
+ public void renderNodeAnimatorFrameRateCanceled() throws Throwable {
+ mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ waitForFrameRateCategoryToSettle();
+
+ RenderNodeAnimator[] renderNodeAnimator = new RenderNodeAnimator[1];
+ renderNodeAnimator[0] = new RenderNodeAnimator(RenderNodeAnimator.ALPHA, 0f);
+ renderNodeAnimator[0].setDuration(100000);
+
+ mActivityRule.runOnUiThread(() -> {
+ renderNodeAnimator[0].setTarget(mMovingView);
+ renderNodeAnimator[0].start();
+ mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
+ runAfterDraw(() -> {
+ assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(FRAME_RATE_CATEGORY_LOW,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ });
+ });
+ waitForAfterDraw();
+
+ mActivityRule.runOnUiThread(() -> {
+ renderNodeAnimator[0].cancel();
+ });
+
+ // Wait for idle timeout
+ Thread.sleep(1000);
+ assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ }
+
+ @LargeTest
+ @Test
+ @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+ FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY,
+ com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
+ })
+ public void renderNodeAnimatorFrameRateRemoved() throws Throwable {
+ mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ waitForFrameRateCategoryToSettle();
+
+ RenderNodeAnimator[] renderNodeAnimator = new RenderNodeAnimator[1];
+ renderNodeAnimator[0] = new RenderNodeAnimator(RenderNodeAnimator.ALPHA, 0f);
+ renderNodeAnimator[0].setDuration(100000);
+
+ mActivityRule.runOnUiThread(() -> {
+ renderNodeAnimator[0].setTarget(mMovingView);
+ renderNodeAnimator[0].start();
+ mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
+ runAfterDraw(() -> {
+ assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(FRAME_RATE_CATEGORY_LOW,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ });
+ });
+ waitForAfterDraw();
+
+ mActivityRule.runOnUiThread(() -> {
+ ViewGroup parent = (ViewGroup) mMovingView.getParent();
+ assert parent != null;
+ parent.removeView(mMovingView);
+ });
+
+ Thread.sleep(1000);
+ assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+ assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
+ mViewRoot.getLastPreferredFrameRateCategory());
+ }
+
private void runAfterDraw(@NonNull Runnable runnable) {
Handler handler = new Handler(Looper.getMainLooper());
mAfterDrawLatch = new CountDownLatch(1);
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index f9fb84d2b31d..782327713fdc 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -134,19 +134,4 @@ to pre-existing users, but cannot uninstall pre-existing system packages from pr
<install-in-user-type package="com.android.avatarpicker">
<install-in user-type="FULL" />
</install-in-user-type>
-
- <!-- AiLabs Warp app pre-installed in hardware/google/pixel/common/pixel-common-device.mk -->
- <install-in-user-type package="com.google.android.apps.warp">
- <install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
- <do-not-install-in user-type="android.os.usertype.profile.PRIVATE" />
- </install-in-user-type>
-
- <!-- Google Home app pre-installed on tangor devices in vendor/google/products/tangor_common.mk
- -->
- <install-in-user-type package="com.google.android.apps.chromecast.app">
- <install-in user-type="FULL" />
- <install-in user-type="PROFILE" />
- <do-not-install-in user-type="android.os.usertype.profile.PRIVATE" />
- </install-in-user-type>
</config>
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 0650b7817729..211f74a47bdd 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.animation.Animator;
import android.annotation.BytesLong;
import android.annotation.ColorInt;
import android.annotation.FloatRange;
@@ -1639,7 +1640,7 @@ public final class RenderNode {
*/
public interface AnimationHost {
/** @hide */
- void registerAnimatingRenderNode(RenderNode animator);
+ void registerAnimatingRenderNode(RenderNode renderNode, Animator animator);
/** @hide */
void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator);
@@ -1654,7 +1655,7 @@ public final class RenderNode {
throw new IllegalStateException("Cannot start this animator on a detached view!");
}
nAddAnimator(mNativeRenderNode, animator.getNativeAnimator());
- mAnimationHost.registerAnimatingRenderNode(this);
+ mAnimationHost.registerAnimatingRenderNode(this, animator);
}
/** @hide */
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 55f205bb14a6..d4bb461c284e 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -1266,6 +1266,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
private final IntArray mPendingAnimationActions = new IntArray();
private final AnimatedVectorDrawable mDrawable;
private long mTotalDuration;
+ private AnimatorListener mThreadedRendererAnimatorListener;
VectorDrawableAnimatorRT(AnimatedVectorDrawable drawable) {
mDrawable = drawable;
@@ -1689,6 +1690,9 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
if (mListener != null) {
mListener.onAnimationStart(null);
}
+ if (mThreadedRendererAnimatorListener != null) {
+ mThreadedRendererAnimatorListener.onAnimationStart(null);
+ }
}
// This should only be called after animator has been added to the RenderNode target.
@@ -1717,6 +1721,9 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
if (mListener != null) {
mListener.onAnimationStart(null);
}
+ if (mThreadedRendererAnimatorListener != null) {
+ mThreadedRendererAnimatorListener.onAnimationStart(null);
+ }
}
@Override
@@ -1725,6 +1732,11 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
}
@Override
+ public void setThreadedRendererAnimatorListener(AnimatorListener animatorListener) {
+ mThreadedRendererAnimatorListener = animatorListener;
+ }
+
+ @Override
public boolean canReverse() {
return mIsReversible;
}
@@ -1788,6 +1800,9 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
if (mListener != null) {
mListener.onAnimationEnd(null);
}
+ if (mThreadedRendererAnimatorListener != null) {
+ mThreadedRendererAnimatorListener.onAnimationEnd(null);
+ }
}
// onFinished: should be called from native
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index fb0a1ab3062e..12bbd51b968d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -522,14 +522,16 @@ public abstract class WMShellModule {
RecentsTransitionHandler recentsTransitionHandler,
MultiInstanceHelper multiInstanceHelper,
@ShellMainThread ShellExecutor mainExecutor,
- Optional<DesktopTasksLimiter> desktopTasksLimiter) {
+ Optional<DesktopTasksLimiter> desktopTasksLimiter,
+ Optional<RecentTasksController> recentTasksController) {
return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
dragAndDropController, transitions, enterDesktopTransitionHandler,
exitDesktopTransitionHandler, toggleResizeDesktopTaskTransitionHandler,
dragToDesktopTransitionHandler, desktopModeTaskRepository,
desktopModeLoggerTransitionObserver, launchAdjacentController,
- recentsTransitionHandler, multiInstanceHelper, mainExecutor, desktopTasksLimiter);
+ recentsTransitionHandler, multiInstanceHelper,
+ mainExecutor, desktopTasksLimiter, recentTasksController.orElse(null));
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 6e45397411d7..ef384c74cb5e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -70,6 +70,7 @@ import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksLi
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener
import com.android.wm.shell.draganddrop.DragAndDropController
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.recents.RecentTasksController
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.shared.DesktopModeStatus
@@ -118,6 +119,7 @@ class DesktopTasksController(
private val multiInstanceHelper: MultiInstanceHelper,
@ShellMainThread private val mainExecutor: ShellExecutor,
private val desktopTasksLimiter: Optional<DesktopTasksLimiter>,
+ private val recentTasksController: RecentTasksController?
) :
RemoteCallable<DesktopTasksController>,
Transitions.TransitionHandler,
@@ -293,24 +295,49 @@ class DesktopTasksController(
taskId: Int,
wct: WindowContainerTransaction = WindowContainerTransaction()
): Boolean {
- shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task -> moveToDesktop(task, wct) }
- ?: return false
+ shellTaskOrganizer.getRunningTaskInfo(taskId)?.let {
+ moveToDesktop(it, wct)
+ } ?: moveToDesktopFromNonRunningTask(taskId, wct)
return true
}
- /** Move a task to desktop */
+ private fun moveToDesktopFromNonRunningTask(
+ taskId: Int,
+ wct: WindowContainerTransaction
+ ): Boolean {
+ recentTasksController?.findTaskInBackground(taskId)?.let {
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: moveToDesktopFromNonRunningTask taskId=%d",
+ taskId
+ )
+ // TODO(342378842): Instead of using default display, support multiple displays
+ val taskToMinimize =
+ bringDesktopAppsToFrontBeforeShowingNewTask(DEFAULT_DISPLAY, wct, taskId)
+ addMoveToDesktopChangesNonRunningTask(wct, taskId)
+ // TODO(343149901): Add DPI changes for task launch
+ val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct)
+ addPendingMinimizeTransition(transition, taskToMinimize)
+ return true
+ } ?: return false
+ }
+
+ private fun addMoveToDesktopChangesNonRunningTask(
+ wct: WindowContainerTransaction,
+ taskId: Int
+ ) {
+ val options = ActivityOptions.makeBasic()
+ options.launchWindowingMode = WINDOWING_MODE_FREEFORM
+ wct.startTask(taskId, options.toBundle())
+ }
+
+ /**
+ * Move a task to desktop
+ */
fun moveToDesktop(
task: RunningTaskInfo,
wct: WindowContainerTransaction = WindowContainerTransaction()
) {
- if (!DesktopModeStatus.canEnterDesktopMode(context)) {
- KtProtoLog.w(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: Cannot enter desktop, " +
- "display does not meet minimum size requirements"
- )
- return
- }
if (Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task)) {
KtProtoLog.w(
WM_SHELL_DESKTOP_MODE,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index d8f2c02b5399..863202d5e1c3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -446,6 +446,25 @@ public class RecentTasksController implements TaskStackListenerCallback,
return null;
}
+ /**
+ * Find the background task that match the given taskId.
+ */
+ @Nullable
+ public ActivityManager.RecentTaskInfo findTaskInBackground(int taskId) {
+ List<ActivityManager.RecentTaskInfo> tasks = mActivityTaskManager.getRecentTasks(
+ Integer.MAX_VALUE, ActivityManager.RECENT_IGNORE_UNAVAILABLE,
+ ActivityManager.getCurrentUser());
+ for (int i = 0; i < tasks.size(); i++) {
+ final ActivityManager.RecentTaskInfo task = tasks.get(i);
+ if (task.isVisible) {
+ continue;
+ }
+ if (taskId == task.taskId) {
+ return task;
+ }
+ }
+ return null;
+ }
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index ac67bd1fedd8..cf6cea2b34a7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.desktopmode
+import android.app.ActivityManager.RecentTaskInfo
import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
@@ -47,13 +48,16 @@ import android.view.WindowManager.TRANSIT_OPEN
import android.view.WindowManager.TRANSIT_TO_BACK
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.DisplayAreaInfo
+import android.window.IWindowContainerToken
import android.window.RemoteTransition
import android.window.TransitionRequestInfo
import android.window.WindowContainerToken
import android.window.WindowContainerTransaction
+import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
+import android.window.WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
@@ -78,6 +82,7 @@ import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFulls
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplitScreenTask
import com.android.wm.shell.draganddrop.DragAndDropController
+import com.android.wm.shell.recents.RecentTasksController
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.shared.DesktopModeStatus
@@ -93,6 +98,7 @@ import com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_DESKTOP_MODE
import com.android.wm.shell.transition.Transitions.TransitionHandler
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
+import java.util.Optional
import org.junit.After
import org.junit.Assume.assumeTrue
import org.junit.Before
@@ -115,7 +121,6 @@ import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.atLeastOnce
import org.mockito.kotlin.capture
import org.mockito.quality.Strictness
-import java.util.Optional
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.mockito.Mockito.`when` as whenever
@@ -154,6 +159,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Mock lateinit var multiInstanceHelper: MultiInstanceHelper
@Mock lateinit var desktopModeLoggerTransitionObserver: DesktopModeLoggerTransitionObserver
@Mock lateinit var desktopModeVisualIndicator: DesktopModeVisualIndicator
+ @Mock lateinit var recentTasksController: RecentTasksController
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var controller: DesktopTasksController
@@ -233,6 +239,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
multiInstanceHelper,
shellExecutor,
Optional.of(desktopTasksLimiter),
+ recentTasksController
)
}
@@ -622,7 +629,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
controller.moveToDesktop(task)
val wct = getLatestMoveToDesktopWct()
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
}
@Test
@@ -643,14 +650,17 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
- fun moveToDesktop_deviceNotSupported_doesNothing() {
- val task = setUpFullscreenTask()
+ fun moveToDesktop_nonRunningTask_launchesInFreeform() {
+ whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
- // Simulate non compatible device
- doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val task = createTaskInfo(1)
- controller.moveToDesktop(task)
- verifyWCTNotExecuted()
+ whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
+
+ controller.moveToDesktop(task.taskId)
+ with(getLatestMoveToDesktopWct()){
+ assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
+ }
}
@Test
@@ -666,6 +676,17 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ fun moveToDesktop_deviceNotSupported_doesNothing() {
+ val task = setUpFullscreenTask()
+
+ // Simulate non compatible device
+ doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+
+ controller.moveToDesktop(task)
+ verifyWCTNotExecuted()
+ }
+
+ @Test
fun moveToDesktop_deviceNotSupported_deviceRestrictionsOverridden_taskIsMovedToDesktop() {
val task = setUpFullscreenTask()
@@ -1834,6 +1855,20 @@ private fun WindowContainerTransaction.assertPendingIntentAt(index: Int, intent:
assertThat(op.pendingIntent?.intent?.component).isEqualTo(intent.component)
}
+private fun WindowContainerTransaction.assertLaunchTaskAt(
+ index: Int,
+ taskId: Int,
+ windowingMode: Int
+) {
+ val keyLaunchWindowingMode = "android.activity.windowingMode"
+
+ assertIndexInBounds(index)
+ val op = hierarchyOps[index]
+ assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_LAUNCH_TASK)
+ assertThat(op.launchOptions?.getInt(LAUNCH_KEY_TASK_ID)).isEqualTo(taskId)
+ assertThat(op.launchOptions?.getInt(keyLaunchWindowingMode, WINDOWING_MODE_UNDEFINED))
+ .isEqualTo(windowingMode)
+}
private fun WindowContainerTransaction?.anyDensityConfigChange(
token: WindowContainerToken
): Boolean {
@@ -1841,3 +1876,7 @@ private fun WindowContainerTransaction?.anyDensityConfigChange(
change.key == token.asBinder() && ((change.value.configSetMask and CONFIG_DENSITY) != 0)
} ?: false
}
+private fun createTaskInfo(id: Int) = RecentTaskInfo().apply {
+ taskId = id
+ token = WindowContainerToken(mock(IWindowContainerToken::class.java))
+}
diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp
index 61b18c88e734..d21cb9319885 100644
--- a/media/tests/MediaRouter/Android.bp
+++ b/media/tests/MediaRouter/Android.bp
@@ -9,6 +9,7 @@ package {
android_test {
name: "mediaroutertest",
+ team: "trendy_team_android_media_solutions",
srcs: ["**/*.java"],
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 36bad5e622cf..1df9c88e48ac 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -184,6 +184,13 @@ flag {
}
flag {
+ name: "notification_avalanche_throttle_hun"
+ namespace: "systemui"
+ description: "(currently unused) During notification avalanche, throttle HUNs showing in fast succession."
+ bug: "307288824"
+}
+
+flag {
name: "notification_avalanche_suppression"
namespace: "systemui"
description: "After notification avalanche floodgate event, suppress HUNs completely."
@@ -990,6 +997,13 @@ flag {
}
flag {
+ name: "glanceable_hub_shortcut_button"
+ namespace: "systemui"
+ description: "Shows a button over the dream and lock screen to open the glanceable hub"
+ bug: "339667383"
+}
+
+flag {
name: "glanceable_hub_gesture_handle"
namespace: "systemui"
description: "Shows a vertical bar at the right edge to indicate the user can swipe to open the glanceable hub"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index b1240252796f..978943ae7f7c 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -179,8 +179,15 @@ class TextAnimator(
private val fontVariationUtils = FontVariationUtils()
- fun updateLayout(layout: Layout) {
+ fun updateLayout(layout: Layout, textSize: Float = -1f) {
textInterpolator.layout = layout
+
+ if (textSize >= 0) {
+ textInterpolator.targetPaint.textSize = textSize
+ textInterpolator.basePaint.textSize = textSize
+ textInterpolator.onTargetPaintModified()
+ textInterpolator.onBasePaintModified()
+ }
}
fun isRunning(): Boolean {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index d2a1de3e4695..f0fb9f62fdad 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -371,96 +371,57 @@ private fun prepareInterruption(
transition: TransitionState.Transition,
previousTransition: TransitionState.Transition,
) {
- val previousUniqueState = reconcileStates(element, previousTransition)
- if (previousUniqueState == null) {
- reconcileStates(element, transition)
- return
- }
-
- val fromSceneState = element.sceneStates[transition.fromScene]
- val toSceneState = element.sceneStates[transition.toScene]
-
- if (
- fromSceneState == null ||
- toSceneState == null ||
- sharedElementTransformation(element.key, transition)?.enabled != false
- ) {
- // If there is only one copy of the element or if the element is shared, animate deltas in
- // both scenes.
- fromSceneState?.updateValuesBeforeInterruption(previousUniqueState)
- toSceneState?.updateValuesBeforeInterruption(previousUniqueState)
- }
+ val sceneStates = element.sceneStates
+ sceneStates[previousTransition.fromScene]?.selfUpdateValuesBeforeInterruption()
+ sceneStates[previousTransition.toScene]?.selfUpdateValuesBeforeInterruption()
+ sceneStates[transition.fromScene]?.selfUpdateValuesBeforeInterruption()
+ sceneStates[transition.toScene]?.selfUpdateValuesBeforeInterruption()
+
+ reconcileStates(element, previousTransition)
+ reconcileStates(element, transition)
}
/**
* Reconcile the state of [element] in the fromScene and toScene of [transition] so that the values
* before interruption have their expected values, taking shared transitions into account.
- *
- * If the element had a unique state, i.e. it is shared in [transition] or it is only present in one
- * of the scenes, return it.
*/
private fun reconcileStates(
element: Element,
transition: TransitionState.Transition,
-): Element.SceneState? {
- val fromSceneState = element.sceneStates[transition.fromScene]
- val toSceneState = element.sceneStates[transition.toScene]
- when {
- // Element is in both scenes.
- fromSceneState != null && toSceneState != null -> {
- val isSharedTransformationDisabled =
- sharedElementTransformation(element.key, transition)?.enabled == false
- when {
- // Element shared transition is disabled so the element is placed in both scenes.
- isSharedTransformationDisabled -> {
- fromSceneState.updateValuesBeforeInterruption(fromSceneState)
- toSceneState.updateValuesBeforeInterruption(toSceneState)
- return null
- }
-
- // Element is shared and placed in fromScene only.
- fromSceneState.lastOffset != Offset.Unspecified -> {
- fromSceneState.updateValuesBeforeInterruption(fromSceneState)
- toSceneState.updateValuesBeforeInterruption(fromSceneState)
- return fromSceneState
- }
-
- // Element is shared and placed in toScene only.
- toSceneState.lastOffset != Offset.Unspecified -> {
- fromSceneState.updateValuesBeforeInterruption(toSceneState)
- toSceneState.updateValuesBeforeInterruption(toSceneState)
- return toSceneState
- }
-
- // Element is in none of the scenes.
- else -> {
- fromSceneState.updateValuesBeforeInterruption(null)
- toSceneState.updateValuesBeforeInterruption(null)
- return null
- }
- }
- }
-
- // Element is only in fromScene.
- fromSceneState != null -> {
- fromSceneState.updateValuesBeforeInterruption(fromSceneState)
- return fromSceneState
- }
+) {
+ val fromSceneState = element.sceneStates[transition.fromScene] ?: return
+ val toSceneState = element.sceneStates[transition.toScene] ?: return
+ if (!isSharedElementEnabled(element.key, transition)) {
+ return
+ }
- // Element is only in toScene.
- toSceneState != null -> {
- toSceneState.updateValuesBeforeInterruption(toSceneState)
- return toSceneState
- }
- else -> return null
+ if (
+ fromSceneState.offsetBeforeInterruption != Offset.Unspecified &&
+ toSceneState.offsetBeforeInterruption == Offset.Unspecified
+ ) {
+ // Element is shared and placed in fromScene only.
+ toSceneState.updateValuesBeforeInterruption(fromSceneState)
+ } else if (
+ toSceneState.offsetBeforeInterruption != Offset.Unspecified &&
+ fromSceneState.offsetBeforeInterruption == Offset.Unspecified
+ ) {
+ // Element is shared and placed in toScene only.
+ fromSceneState.updateValuesBeforeInterruption(toSceneState)
}
}
-private fun Element.SceneState.updateValuesBeforeInterruption(lastState: Element.SceneState?) {
- offsetBeforeInterruption = lastState?.lastOffset ?: Offset.Unspecified
- sizeBeforeInterruption = lastState?.lastSize ?: Element.SizeUnspecified
- scaleBeforeInterruption = lastState?.lastScale ?: Scale.Unspecified
- alphaBeforeInterruption = lastState?.lastAlpha ?: Element.AlphaUnspecified
+private fun Element.SceneState.selfUpdateValuesBeforeInterruption() {
+ offsetBeforeInterruption = lastOffset
+ sizeBeforeInterruption = lastSize
+ scaleBeforeInterruption = lastScale
+ alphaBeforeInterruption = lastAlpha
+}
+
+private fun Element.SceneState.updateValuesBeforeInterruption(lastState: Element.SceneState) {
+ offsetBeforeInterruption = lastState.offsetBeforeInterruption
+ sizeBeforeInterruption = lastState.sizeBeforeInterruption
+ scaleBeforeInterruption = lastState.scaleBeforeInterruption
+ alphaBeforeInterruption = lastState.alphaBeforeInterruption
clearInterruptionDeltas()
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index bdeab797d165..079c1d922275 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -17,7 +17,6 @@ package com.android.systemui.shared.clocks
import android.animation.TimeInterpolator
import android.annotation.ColorInt
-import android.annotation.FloatRange
import android.annotation.IntRange
import android.annotation.SuppressLint
import android.content.Context
@@ -27,7 +26,7 @@ import android.text.TextUtils
import android.text.format.DateFormat
import android.util.AttributeSet
import android.util.MathUtils.constrainedMap
-import android.util.TypedValue
+import android.util.TypedValue.COMPLEX_UNIT_PX
import android.view.View
import android.view.View.MeasureSpec.EXACTLY
import android.widget.TextView
@@ -219,9 +218,7 @@ constructor(
override fun setTextSize(type: Int, size: Float) {
super.setTextSize(type, size)
- if (type == TypedValue.COMPLEX_UNIT_PX) {
- lastUnconstrainedTextSize = size
- }
+ lastUnconstrainedTextSize = if (type == COMPLEX_UNIT_PX) size else Float.MAX_VALUE
}
@SuppressLint("DrawAllocation")
@@ -234,23 +231,19 @@ constructor(
MeasureSpec.getMode(heightMeasureSpec) == EXACTLY
) {
// Call straight into TextView.setTextSize to avoid setting lastUnconstrainedTextSize
- super.setTextSize(
- TypedValue.COMPLEX_UNIT_PX,
- min(lastUnconstrainedTextSize, MeasureSpec.getSize(heightMeasureSpec) / 2F)
- )
+ val size = min(lastUnconstrainedTextSize, MeasureSpec.getSize(heightMeasureSpec) / 2F)
+ super.setTextSize(COMPLEX_UNIT_PX, size)
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
- val animator = textAnimator
- if (animator == null) {
- textAnimator =
- textAnimatorFactory(layout, ::invalidate)?.also {
- onTextAnimatorInitialized?.invoke(it)
- onTextAnimatorInitialized = null
- }
- } else {
- animator.updateLayout(layout)
- }
+ textAnimator?.let { animator -> animator.updateLayout(layout, textSize) }
+ ?: run {
+ textAnimator =
+ textAnimatorFactory(layout, ::invalidate).also {
+ onTextAnimatorInitialized?.invoke(it)
+ onTextAnimatorInitialized = null
+ }
+ }
if (migratedClocks && hasCustomPositionUpdatedAnimation) {
// Expand width to avoid clock being clipped during stepping animation
@@ -307,18 +300,18 @@ constructor(
logger.d("animateColorChange")
setTextStyle(
weight = lockScreenWeight,
- textSize = -1f,
color = null, /* using current color */
animate = false,
+ interpolator = null,
duration = 0,
delay = 0,
onAnimationEnd = null
)
setTextStyle(
weight = lockScreenWeight,
- textSize = -1f,
color = lockScreenColor,
animate = true,
+ interpolator = null,
duration = COLOR_ANIM_DURATION,
delay = 0,
onAnimationEnd = null
@@ -329,16 +322,15 @@ constructor(
logger.d("animateAppearOnLockscreen")
setTextStyle(
weight = dozingWeight,
- textSize = -1f,
color = lockScreenColor,
animate = false,
+ interpolator = null,
duration = 0,
delay = 0,
onAnimationEnd = null
)
setTextStyle(
weight = lockScreenWeight,
- textSize = -1f,
color = lockScreenColor,
animate = true,
duration = APPEAR_ANIM_DURATION,
@@ -356,16 +348,15 @@ constructor(
logger.d("animateFoldAppear")
setTextStyle(
weight = lockScreenWeightInternal,
- textSize = -1f,
color = lockScreenColor,
animate = false,
+ interpolator = null,
duration = 0,
delay = 0,
onAnimationEnd = null
)
setTextStyle(
weight = dozingWeightInternal,
- textSize = -1f,
color = dozingColor,
animate = animate,
interpolator = Interpolators.EMPHASIZED_DECELERATE,
@@ -385,9 +376,9 @@ constructor(
val startAnimPhase2 = Runnable {
setTextStyle(
weight = if (isDozing()) dozingWeight else lockScreenWeight,
- textSize = -1f,
color = null,
animate = true,
+ interpolator = null,
duration = CHARGE_ANIM_DURATION_PHASE_1,
delay = 0,
onAnimationEnd = null
@@ -395,9 +386,9 @@ constructor(
}
setTextStyle(
weight = if (isDozing()) lockScreenWeight else dozingWeight,
- textSize = -1f,
color = null,
animate = true,
+ interpolator = null,
duration = CHARGE_ANIM_DURATION_PHASE_0,
delay = chargeAnimationDelay.toLong(),
onAnimationEnd = startAnimPhase2
@@ -408,9 +399,9 @@ constructor(
logger.d("animateDoze")
setTextStyle(
weight = if (isDozing) dozingWeight else lockScreenWeight,
- textSize = -1f,
color = if (isDozing) dozingColor else lockScreenColor,
animate = animate,
+ interpolator = null,
duration = DOZE_ANIM_DURATION,
delay = 0,
onAnimationEnd = null
@@ -448,7 +439,6 @@ constructor(
*/
private fun setTextStyle(
@IntRange(from = 0, to = 1000) weight: Int,
- @FloatRange(from = 0.0) textSize: Float,
color: Int?,
animate: Boolean,
interpolator: TimeInterpolator?,
@@ -459,7 +449,6 @@ constructor(
textAnimator?.let {
it.setTextStyle(
weight = weight,
- textSize = textSize,
color = color,
animate = animate && isAnimationEnabled,
duration = duration,
@@ -474,7 +463,6 @@ constructor(
onTextAnimatorInitialized = { textAnimator ->
textAnimator.setTextStyle(
weight = weight,
- textSize = textSize,
color = color,
animate = false,
duration = duration,
@@ -487,27 +475,6 @@ constructor(
}
}
- private fun setTextStyle(
- @IntRange(from = 0, to = 1000) weight: Int,
- @FloatRange(from = 0.0) textSize: Float,
- color: Int?,
- animate: Boolean,
- duration: Long,
- delay: Long,
- onAnimationEnd: Runnable?
- ) {
- setTextStyle(
- weight = weight,
- textSize = textSize,
- color = color,
- animate = animate,
- interpolator = null,
- duration = duration,
- delay = delay,
- onAnimationEnd = onAnimationEnd
- )
- }
-
fun refreshFormat() = refreshFormat(DateFormat.is24HourFormat(context))
fun refreshFormat(use24HourFormat: Boolean) {
Patterns.update(context)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
index 29fbee01a18b..7936ccc1ddd1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
@@ -108,7 +108,7 @@ public class CommunalTouchHandlerTest extends SysuiTestCase {
mTouchHandler.onSessionStart(mTouchSession);
verify(mTouchSession).registerInputListener(inputEventListenerArgumentCaptor.capture());
inputEventListenerArgumentCaptor.getValue().onInputEvent(motionEvent);
- verify(mCentralSurfaces).handleDreamTouch(motionEvent);
+ verify(mCentralSurfaces).handleCommunalHubTouch(motionEvent);
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 26fcb234843d..49d039970a24 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -90,6 +90,7 @@ class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() {
.thenReturn(FakeSharedPreferences())
},
userTracker = FakeUserTracker(),
+ systemSettings = FakeSettings(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
settings = FakeSettings()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
index 99a01858471c..9ab1ac116b0d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
@@ -22,13 +22,14 @@ import android.content.SharedPreferences
import android.content.pm.UserInfo
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.backup.BackupHelper
+import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -80,6 +81,7 @@ class KeyguardQuickAffordanceLocalUserSelectionManagerTest : SysuiTestCase() {
context = context,
userFileManager = userFileManager,
userTracker = userTracker,
+ systemSettings = FakeSettings(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index 567e0a9717fc..159ce36bea2d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -21,7 +21,6 @@ import android.content.pm.UserInfo
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
@@ -32,6 +31,7 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
+import com.android.systemui.res.R
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient
@@ -91,6 +91,7 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ systemSettings = FakeSettings(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
client1 = FakeCustomizationProviderClient()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 2d77f4f1c436..78a116737349 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -148,6 +148,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ systemSettings = FakeSettings(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
val remoteUserSelectionManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index 2b8a644162c7..9dc930babc10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -27,18 +27,22 @@ import com.android.systemui.coroutines.collectValues
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.data.repository.setSceneTransition
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import junit.framework.Assert.assertEquals
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
@@ -192,6 +196,175 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
}
@Test
+ @EnableSceneContainer
+ fun surfaceBehindVisibility_fromLockscreenToGone_trueThroughout() =
+ testScope.runTest {
+ val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
+ val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+
+ // Before the transition, we start on Lockscreen so the surface should start invisible.
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Lockscreen))
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(isSurfaceBehindVisible).isFalse()
+
+ // Unlocked with fingerprint.
+ kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+
+ // Start the transition to Gone, the surface should become immediately visible.
+ kosmos.setSceneTransition(
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Gone,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ progress = flowOf(0.3f),
+ currentScene = flowOf(Scenes.Lockscreen),
+ )
+ )
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(isSurfaceBehindVisible).isTrue()
+
+ // Towards the end of the transition, the surface should continue to be visible.
+ kosmos.setSceneTransition(
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Gone,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ progress = flowOf(0.9f),
+ currentScene = flowOf(Scenes.Gone),
+ )
+ )
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(isSurfaceBehindVisible).isTrue()
+
+ // After the transition, settles on Gone. Surface behind should stay visible now.
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone))
+ kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ assertThat(isSurfaceBehindVisible).isTrue()
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun surfaceBehindVisibility_fromBouncerToGone_becomesTrue() =
+ testScope.runTest {
+ val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
+ val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+
+ // Before the transition, we start on Bouncer so the surface should start invisible.
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Bouncer))
+ kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "")
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
+ assertThat(isSurfaceBehindVisible).isFalse()
+
+ // Unlocked with fingerprint.
+ kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+
+ // Start the transition to Gone, the surface should remain invisible prior to hitting
+ // the
+ // threshold.
+ kosmos.setSceneTransition(
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Bouncer,
+ toScene = Scenes.Gone,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ progress =
+ flowOf(
+ FromPrimaryBouncerTransitionInteractor
+ .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
+ ),
+ currentScene = flowOf(Scenes.Bouncer),
+ )
+ )
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
+ assertThat(isSurfaceBehindVisible).isFalse()
+
+ // Once the transition passes the threshold, the surface should become visible.
+ kosmos.setSceneTransition(
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Bouncer,
+ toScene = Scenes.Gone,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ progress =
+ flowOf(
+ FromPrimaryBouncerTransitionInteractor
+ .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD + 0.01f
+ ),
+ currentScene = flowOf(Scenes.Gone),
+ )
+ )
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
+ assertThat(isSurfaceBehindVisible).isTrue()
+
+ // After the transition, settles on Gone. Surface behind should stay visible now.
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone))
+ kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ assertThat(isSurfaceBehindVisible).isTrue()
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun surfaceBehindVisibility_idleWhileUnlocked_alwaysTrue() =
+ testScope.runTest {
+ val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
+ val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+
+ // Unlocked with fingerprint.
+ kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+ SuccessFingerprintAuthenticationStatus(0, true)
+ )
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone))
+ kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+
+ listOf(
+ Scenes.Shade,
+ Scenes.QuickSettings,
+ Scenes.Shade,
+ Scenes.Gone,
+ )
+ .forEach { scene ->
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(scene))
+ kosmos.sceneInteractor.changeScene(scene, "")
+ assertThat(currentScene).isEqualTo(scene)
+ assertWithMessage("Unexpected visibility for scene \"${scene.debugName}\"")
+ .that(isSurfaceBehindVisible)
+ .isTrue()
+ }
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun surfaceBehindVisibility_idleWhileLocked_alwaysFalse() =
+ testScope.runTest {
+ val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
+ val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+
+ listOf(
+ Scenes.Shade,
+ Scenes.QuickSettings,
+ Scenes.Shade,
+ Scenes.Lockscreen,
+ )
+ .forEach { scene ->
+ kosmos.setSceneTransition(ObservableTransitionState.Idle(scene))
+ kosmos.sceneInteractor.changeScene(scene, "")
+ assertWithMessage("Unexpected visibility for scene \"${scene.debugName}\"")
+ .that(isSurfaceBehindVisible)
+ .isFalse()
+ }
+ }
+
+ @Test
@DisableSceneContainer
fun testUsingGoingAwayAnimation_duringTransitionToGone() =
testScope.runTest {
diff --git a/packages/SystemUI/res/drawable/ic_widgets.xml b/packages/SystemUI/res/drawable/ic_widgets.xml
new file mode 100644
index 000000000000..b21d047f9386
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_widgets.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- go/gm2-icons, from gs_widgets_vd_theme_24.xml -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/black"
+ android:pathData="M666,520L440,294L666,68L892,294L666,520ZM120,440L120,120L440,120L440,440L120,440ZM520,840L520,520L840,520L840,840L520,840ZM120,840L120,520L440,520L440,840L120,840ZM200,360L360,360L360,200L200,200L200,360ZM667,408L780,295L667,182L554,295L667,408ZM600,760L760,760L760,600L600,600L600,760ZM200,760L360,760L360,600L200,600L200,760ZM360,360L360,360L360,360L360,360L360,360ZM554,295L554,295L554,295L554,295L554,295ZM360,600L360,600L360,600L360,600L360,600ZM600,600L600,600L600,600L600,600L600,600Z" />
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_open_hub_chip.xml b/packages/SystemUI/res/layout/dream_overlay_open_hub_chip.xml
new file mode 100644
index 000000000000..be063a9631e8
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_open_hub_chip.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+<com.android.systemui.animation.view.LaunchableImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="@dimen/dream_overlay_bottom_affordance_height"
+ android:layout_width="@dimen/dream_overlay_bottom_affordance_width"
+ android:layout_gravity="bottom|start"
+ android:padding="@dimen/dream_overlay_bottom_affordance_padding"
+ android:scaleType="fitCenter"
+ android:tint="?android:attr/textColorPrimary"
+ android:src="@drawable/ic_widgets"
+ android:contentDescription="@string/accessibility_action_open_communal_hub" />
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 3f3bb0bc94b6..86c807bf9d07 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -15,12 +15,12 @@
*/
package com.android.keyguard
-import android.os.Trace
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Resources
+import android.os.Trace
import android.text.format.DateFormat
import android.util.Log
import android.util.TypedValue
@@ -466,15 +466,11 @@ constructor(
largeRegionSampler?.stopRegionSampler()
smallTimeListener?.stop()
largeTimeListener?.stop()
- clock
- ?.smallClock
- ?.view
- ?.removeOnAttachStateChangeListener(smallClockOnAttachStateChangeListener)
+ clock?.apply {
+ smallClock.view.removeOnAttachStateChangeListener(smallClockOnAttachStateChangeListener)
+ largeClock.view.removeOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
+ }
smallClockFrame?.viewTreeObserver?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
- clock
- ?.largeClock
- ?.view
- ?.removeOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
}
/**
@@ -505,15 +501,17 @@ constructor(
}
}
- private fun updateFontSizes() {
+ fun updateFontSizes() {
clock?.run {
- smallClock.events.onFontSettingChanged(
- resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
- )
+ smallClock.events.onFontSettingChanged(getSmallClockSizePx())
largeClock.events.onFontSettingChanged(getLargeClockSizePx())
}
}
+ private fun getSmallClockSizePx(): Float {
+ return resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
+ }
+
private fun getLargeClockSizePx(): Float {
return if (largeClockOnSecondaryDisplay) {
resources.getDimensionPixelSize(R.dimen.presentation_clock_text_size).toFloat()
@@ -549,9 +547,8 @@ constructor(
it.copy(value = 1f - it.value)
},
keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, AOD)),
- ).filter {
- it.transitionState != TransitionState.FINISHED
- }
+ )
+ .filter { it.transitionState != TransitionState.FINISHED }
.collect { handleDoze(it.value) }
}
}
@@ -574,28 +571,27 @@ constructor(
internal fun listenForAnyStateToLockscreenTransition(scope: CoroutineScope): Job {
return scope.launch {
keyguardTransitionInteractor
- .transitionStepsToState(LOCKSCREEN)
- .filter { it.transitionState == TransitionState.STARTED }
- .filter { it.from != AOD }
- .collect { handleDoze(0f) }
+ .transitionStepsToState(LOCKSCREEN)
+ .filter { it.transitionState == TransitionState.STARTED }
+ .filter { it.from != AOD }
+ .collect { handleDoze(0f) }
}
}
/**
- * When keyguard is displayed due to pulsing notifications when AOD is off,
- * we should make sure clock is in dozing state instead of LS state
+ * When keyguard is displayed due to pulsing notifications when AOD is off, we should make sure
+ * clock is in dozing state instead of LS state
*/
@VisibleForTesting
internal fun listenForAnyStateToDozingTransition(scope: CoroutineScope): Job {
return scope.launch {
keyguardTransitionInteractor
- .transitionStepsToState(DOZING)
- .filter { it.transitionState == TransitionState.FINISHED }
- .collect { handleDoze(1f) }
+ .transitionStepsToState(DOZING)
+ .filter { it.transitionState == TransitionState.FINISHED }
+ .collect { handleDoze(1f) }
}
}
-
@VisibleForTesting
internal fun listenForDozing(scope: CoroutineScope): Job {
return scope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 27b2b92ab899..63ad41a808dc 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -20,7 +20,6 @@ import static androidx.dynamicanimation.animation.DynamicAnimation.TRANSLATION_X
import static androidx.dynamicanimation.animation.FloatPropertyCompat.createFloatPropertyCompat;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
-import static com.android.systemui.flags.Flags.SWIPE_UNCLEARED_TRANSIENT_VIEW_FIX;
import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
import android.animation.Animator;
@@ -481,16 +480,11 @@ public class SwipeHelper implements Gefingerpoken, Dumpable {
updateSwipeProgressFromOffset(animView, canBeDismissed);
mDismissPendingMap.remove(animView);
boolean wasRemoved = false;
- if (animView instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) animView;
- if (mFeatureFlags.isEnabled(SWIPE_UNCLEARED_TRANSIENT_VIEW_FIX)) {
- // If the view is already removed from its parent and added as Transient,
- // we need to clean the transient view upon animation end
- wasRemoved = row.getTransientContainer() != null
- || row.getParent() == null || row.isRemoved();
- } else {
- wasRemoved = row.isRemoved();
- }
+ if (animView instanceof ExpandableNotificationRow row) {
+ // If the view is already removed from its parent and added as Transient,
+ // we need to clean the transient view upon animation end
+ wasRemoved = row.getTransientContainer() != null
+ || row.getParent() == null || row.isRemoved();
}
if (!mCancelled || wasRemoved) {
mCallback.onChildDismissed(animView);
diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
index afa23755d937..e284bc7752cf 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
@@ -18,6 +18,7 @@ package com.android.systemui.complication;
import static com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent.DreamHomeControlsModule.DREAM_HOME_CONTROLS_CHIP_VIEW;
import static com.android.systemui.complication.dagger.RegisteredComplicationsModule.DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS;
+import static com.android.systemui.complication.dagger.RegisteredComplicationsModule.OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS;
import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE;
import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK;
import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.UNAVAILABLE;
@@ -35,6 +36,7 @@ import androidx.annotation.Nullable;
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.Utils;
import com.android.systemui.CoreStartable;
+import com.android.systemui.Flags;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent;
import com.android.systemui.controls.ControlsServiceInfo;
@@ -89,6 +91,7 @@ public class DreamHomeControlsComplication implements Complication {
private final DreamHomeControlsComplication mComplication;
private final DreamOverlayStateController mDreamOverlayStateController;
private final ControlsComponent mControlsComponent;
+ private final boolean mReplacedByOpenHub;
private boolean mOverlayActive = false;
@@ -116,11 +119,13 @@ public class DreamHomeControlsComplication implements Complication {
public Registrant(DreamHomeControlsComplication complication,
DreamOverlayStateController dreamOverlayStateController,
ControlsComponent controlsComponent,
- @SystemUser Monitor monitor) {
+ @SystemUser Monitor monitor,
+ @Named(OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS) boolean replacedByOpenHub) {
super(monitor);
mComplication = complication;
mControlsComponent = controlsComponent;
mDreamOverlayStateController = dreamOverlayStateController;
+ mReplacedByOpenHub = replacedByOpenHub;
}
@Override
@@ -132,7 +137,9 @@ public class DreamHomeControlsComplication implements Complication {
private void updateHomeControlsComplication() {
mControlsComponent.getControlsListingController().ifPresent(c -> {
- if (isHomeControlsAvailable(c.getCurrentServices())) {
+ final boolean replacedWithOpenHub =
+ Flags.glanceableHubShortcutButton() && mReplacedByOpenHub;
+ if (isHomeControlsAvailable(c.getCurrentServices()) && !replacedWithOpenHub) {
mDreamOverlayStateController.addComplication(mComplication);
} else {
mDreamOverlayStateController.removeComplication(mComplication);
diff --git a/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java b/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java
new file mode 100644
index 000000000000..3cf22b1b55e4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.complication;
+
+import static com.android.systemui.complication.dagger.OpenHubComplicationComponent.OpenHubModule.OPEN_HUB_CHIP_VIEW;
+import static com.android.systemui.complication.dagger.RegisteredComplicationsModule.OPEN_HUB_CHIP_LAYOUT_PARAMS;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.CoreStartable;
+import com.android.systemui.Flags;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
+import com.android.systemui.communal.shared.model.CommunalScenes;
+import com.android.systemui.complication.dagger.OpenHubComplicationComponent;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.SystemUser;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.ViewController;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * A dream complication that shows a chip to open the glanceable hub.
+ */
+// TODO(b/339667383): delete or properly implement this once a product decision is made
+public class OpenHubComplication implements Complication {
+ private final Resources mResources;
+ private final OpenHubComplicationComponent.Factory mComponentFactory;
+
+ @Inject
+ public OpenHubComplication(
+ @Main Resources resources,
+ OpenHubComplicationComponent.Factory componentFactory) {
+ mResources = resources;
+ mComponentFactory = componentFactory;
+ }
+
+ @Override
+ public ViewHolder createView(ComplicationViewModel model) {
+ return mComponentFactory.create(mResources).getViewHolder();
+ }
+
+ @Override
+ public int getRequiredTypeAvailability() {
+ // TODO(b/339667383): create a new complication type if we decide to productionize this
+ return COMPLICATION_TYPE_HOME_CONTROLS;
+ }
+
+ /**
+ * {@link CoreStartable} for registering the complication with SystemUI on startup.
+ */
+ public static class Registrant extends ConditionalCoreStartable {
+ private final OpenHubComplication mComplication;
+ private final DreamOverlayStateController mDreamOverlayStateController;
+
+ private boolean mOverlayActive = false;
+
+ private final DreamOverlayStateController.Callback mOverlayStateCallback =
+ new DreamOverlayStateController.Callback() {
+ @Override
+ public void onStateChanged() {
+ if (mOverlayActive == mDreamOverlayStateController.isOverlayActive()) {
+ return;
+ }
+
+ mOverlayActive = !mOverlayActive;
+
+ if (mOverlayActive) {
+ updateOpenHubComplication();
+ }
+ }
+ };
+
+ @Inject
+ public Registrant(OpenHubComplication complication,
+ DreamOverlayStateController dreamOverlayStateController,
+ @SystemUser Monitor monitor) {
+ super(monitor);
+ mComplication = complication;
+ mDreamOverlayStateController = dreamOverlayStateController;
+ }
+
+ @Override
+ public void onStart() {
+ mDreamOverlayStateController.addCallback(mOverlayStateCallback);
+ }
+
+ private void updateOpenHubComplication() {
+ // TODO(b/339667383): don't show the complication if glanceable hub is disabled
+ if (Flags.glanceableHubShortcutButton()) {
+ mDreamOverlayStateController.addComplication(mComplication);
+ } else {
+ mDreamOverlayStateController.removeComplication(mComplication);
+ }
+ }
+ }
+
+ /**
+ * Contains values/logic associated with the dream complication view.
+ */
+ public static class OpenHubChipViewHolder implements ViewHolder {
+ private final ImageView mView;
+ private final ComplicationLayoutParams mLayoutParams;
+ private final OpenHubChipViewController mViewController;
+
+ @Inject
+ OpenHubChipViewHolder(
+ OpenHubChipViewController dreamOpenHubChipViewController,
+ @Named(OPEN_HUB_CHIP_VIEW) ImageView view,
+ @Named(OPEN_HUB_CHIP_LAYOUT_PARAMS) ComplicationLayoutParams layoutParams
+ ) {
+ mView = view;
+ mLayoutParams = layoutParams;
+ mViewController = dreamOpenHubChipViewController;
+ mViewController.init();
+ }
+
+ @Override
+ public ImageView getView() {
+ return mView;
+ }
+
+ @Override
+ public ComplicationLayoutParams getLayoutParams() {
+ return mLayoutParams;
+ }
+ }
+
+ /**
+ * Controls behavior of the dream complication.
+ */
+ static class OpenHubChipViewController extends ViewController<ImageView> {
+ private static final String TAG = "OpenHubCtrl";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final Context mContext;
+ private final ConfigurationController mConfigurationController;
+
+ private final ConfigurationController.ConfigurationListener mConfigurationListener =
+ new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onUiModeChanged() {
+ reloadResources();
+ }
+ };
+ private final CommunalInteractor mCommunalInteractor;
+
+ @Inject
+ OpenHubChipViewController(
+ @Named(OPEN_HUB_CHIP_VIEW) ImageView view,
+ Context context,
+ ConfigurationController configurationController,
+ CommunalInteractor communalInteractor) {
+ super(view);
+
+ mContext = context;
+ mConfigurationController = configurationController;
+ mCommunalInteractor = communalInteractor;
+ }
+
+ @Override
+ protected void onViewAttached() {
+ reloadResources();
+ mView.setOnClickListener(this::onClickOpenHub);
+ mConfigurationController.addCallback(mConfigurationListener);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mConfigurationController.removeCallback(mConfigurationListener);
+ }
+
+ private void reloadResources() {
+ mView.setImageTintList(Utils.getColorAttr(mContext, android.R.attr.textColorPrimary));
+ final Drawable background = mView.getBackground();
+ if (background != null) {
+ background.setTintList(
+ Utils.getColorAttr(mContext, com.android.internal.R.attr.colorSurface));
+ }
+ }
+
+ private void onClickOpenHub(View v) {
+ if (DEBUG) Log.d(TAG, "open hub complication tapped");
+
+ mCommunalInteractor.changeScene(CommunalScenes.Communal, null);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/OpenHubComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/OpenHubComplicationComponent.java
new file mode 100644
index 000000000000..501601ee32ba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/OpenHubComplicationComponent.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.complication.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.widget.ImageView;
+
+import com.android.systemui.complication.OpenHubComplication;
+import com.android.systemui.res.R;
+import com.android.systemui.shared.shadow.DoubleShadowIconDrawable;
+import com.android.systemui.shared.shadow.DoubleShadowTextHelper;
+
+import dagger.BindsInstance;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Named;
+import javax.inject.Scope;
+
+/**
+ * Responsible for generating dependencies for the {@link OpenHubComplication}.
+ */
+@Subcomponent(modules = OpenHubComplicationComponent.OpenHubModule.class)
+@OpenHubComplicationComponent.OpenHubComplicationScope
+public interface OpenHubComplicationComponent {
+ /**
+ * Creates a view holder for the open hub complication.
+ */
+ OpenHubComplication.OpenHubChipViewHolder getViewHolder();
+
+ /**
+ * Scope of the open hub complication.
+ */
+ @Documented
+ @Retention(RUNTIME)
+ @Scope
+ @interface OpenHubComplicationScope {
+ }
+
+ /**
+ * Factory that generates a {@link OpenHubComplicationComponent}.
+ */
+ @Subcomponent.Factory
+ interface Factory {
+ /**
+ * Creates an instance of {@link OpenHubComplicationComponent}.
+ */
+ OpenHubComplicationComponent create(@BindsInstance Resources resources);
+ }
+
+ /**
+ * Scoped injected values for the {@link OpenHubComplicationComponent}.
+ */
+ @Module
+ interface OpenHubModule {
+ String OPEN_HUB_CHIP_VIEW = "open_hub_chip_view";
+ String OPEN_HUB_BACKGROUND_DRAWABLE = "open_hub_background_drawable";
+
+ /**
+ * Provides the dream open hub chip view.
+ */
+ @Provides
+ @OpenHubComplicationScope
+ @Named(OPEN_HUB_CHIP_VIEW)
+ static ImageView provideOpenHubChipView(
+ LayoutInflater layoutInflater,
+ @Named(OPEN_HUB_BACKGROUND_DRAWABLE) Drawable backgroundDrawable) {
+ final ImageView chip =
+ (ImageView) layoutInflater.inflate(R.layout.dream_overlay_open_hub_chip,
+ null, false);
+ chip.setBackground(backgroundDrawable);
+
+ return chip;
+ }
+
+ /**
+ * Provides the background drawable for the open hub chip.
+ */
+ @Provides
+ @OpenHubComplicationScope
+ @Named(OPEN_HUB_BACKGROUND_DRAWABLE)
+ static Drawable providesOpenHubBackground(Context context, Resources resources) {
+ return new DoubleShadowIconDrawable(createShadowInfo(
+ resources,
+ R.dimen.dream_overlay_bottom_affordance_key_text_shadow_radius,
+ R.dimen.dream_overlay_bottom_affordance_key_text_shadow_dx,
+ R.dimen.dream_overlay_bottom_affordance_key_text_shadow_dy,
+ R.dimen.dream_overlay_bottom_affordance_key_shadow_alpha
+ ),
+ createShadowInfo(
+ resources,
+ R.dimen.dream_overlay_bottom_affordance_ambient_text_shadow_radius,
+ R.dimen.dream_overlay_bottom_affordance_ambient_text_shadow_dx,
+ R.dimen.dream_overlay_bottom_affordance_ambient_text_shadow_dy,
+ R.dimen.dream_overlay_bottom_affordance_ambient_shadow_alpha
+ ),
+ resources.getDrawable(R.drawable.dream_overlay_bottom_affordance_bg),
+ resources.getDimensionPixelOffset(
+ R.dimen.dream_overlay_bottom_affordance_width),
+ resources.getDimensionPixelSize(R.dimen.dream_overlay_bottom_affordance_inset)
+ );
+ }
+
+ private static DoubleShadowTextHelper.ShadowInfo createShadowInfo(Resources resources,
+ int blurId, int offsetXId, int offsetYId, int alphaId) {
+
+ return new DoubleShadowTextHelper.ShadowInfo(
+ resources.getDimension(blurId),
+ resources.getDimension(offsetXId),
+ resources.getDimension(offsetYId),
+ resources.getFloat(alphaId)
+ );
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
index 6f1b09829671..edb5ff7799be 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
@@ -25,6 +25,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.res.R;
+import com.android.systemui.util.settings.SystemSettings;
import dagger.Module;
import dagger.Provides;
@@ -39,6 +40,7 @@ import javax.inject.Named;
subcomponents = {
DreamClockTimeComplicationComponent.class,
DreamHomeControlsComplicationComponent.class,
+ OpenHubComplicationComponent.class,
DreamMediaEntryComplicationComponent.class
})
public interface RegisteredComplicationsModule {
@@ -46,6 +48,8 @@ public interface RegisteredComplicationsModule {
String DREAM_SMARTSPACE_LAYOUT_PARAMS = "smartspace_layout_params";
String DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS = "home_controls_chip_layout_params";
String DREAM_MEDIA_ENTRY_LAYOUT_PARAMS = "media_entry_layout_params";
+ String OPEN_HUB_CHIP_LAYOUT_PARAMS = "open_hub_chip_layout_params";
+ String OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS = "open_hub_chip_replace_home_controls";
int DREAM_CLOCK_TIME_COMPLICATION_WEIGHT = 1;
int DREAM_CLOCK_TIME_COMPLICATION_WEIGHT_NO_SMARTSPACE = 2;
@@ -109,6 +113,26 @@ public interface RegisteredComplicationsModule {
}
/**
+ * Provides layout parameters for the open hub complication.
+ */
+ @Provides
+ @Named(OPEN_HUB_CHIP_LAYOUT_PARAMS)
+ static ComplicationLayoutParams provideOpenHubLayoutParams(
+ @Named(OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS) boolean replaceHomeControls) {
+ int position = ComplicationLayoutParams.POSITION_BOTTOM | (replaceHomeControls
+ ? ComplicationLayoutParams.POSITION_START
+ : ComplicationLayoutParams.POSITION_END);
+ int direction = replaceHomeControls ? ComplicationLayoutParams.DIRECTION_END
+ : ComplicationLayoutParams.DIRECTION_START;
+ return new ComplicationLayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ position,
+ direction,
+ DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT);
+ }
+
+ /**
* Provides layout parameters for the smartspace complication.
*/
@Provides
@@ -124,4 +148,14 @@ public interface RegisteredComplicationsModule {
res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_padding),
res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_max_width));
}
+
+ /**
+ * If true, the home controls chip should not be shown and the open hub chip should be shown in
+ * its place.
+ */
+ @Provides
+ @Named(OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS)
+ static boolean providesOpenHubChipReplaceHomeControls(SystemSettings systemSettings) {
+ return systemSettings.getBool(OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS, false);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 3e98fc1b4a2a..7aab37e12b8c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -32,6 +32,8 @@ import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule;
+import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
+import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule;
import com.android.systemui.media.dagger.MediaModule;
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionCli;
import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
@@ -112,6 +114,8 @@ import javax.inject.Named;
GestureModule.class,
HeadsUpModule.class,
KeyboardShortcutsModule.class,
+ KeyguardBlueprintModule.class,
+ KeyguardSectionsModule.class,
MediaModule.class,
MediaMuteAwaitConnectionCli.StartableModule.class,
MultiUserUtilsModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 339e8f06e853..2ebb94f8bcf4 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -70,8 +70,6 @@ import com.android.systemui.inputmethod.InputMethodModule;
import com.android.systemui.keyboard.KeyboardModule;
import com.android.systemui.keyevent.data.repository.KeyEventRepositoryModule;
import com.android.systemui.keyguard.ui.composable.LockscreenContent;
-import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
-import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.log.dagger.MonitorLog;
import com.android.systemui.log.table.TableLogBuffer;
@@ -222,8 +220,6 @@ import javax.inject.Named;
InputMethodModule.class,
KeyEventRepositoryModule.class,
KeyboardModule.class,
- KeyguardBlueprintModule.class,
- KeyguardSectionsModule.class,
LetterboxModule.class,
LogModule.class,
MediaProjectionActivitiesModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
index 1c047ddcd3d8..04fda3313df6 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
@@ -98,7 +98,7 @@ public class CommunalTouchHandler implements TouchHandler {
// Notification shade window has its own logic to be visible if the hub is open, no need to
// do anything here other than send touch events over.
session.registerInputListener(ev -> {
- surfaces.handleDreamTouch((MotionEvent) ev);
+ surfaces.handleCommunalHubTouch((MotionEvent) ev);
if (ev != null && ((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) {
var unused = session.pop();
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 2e49919d489b..c08434015ab1 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -42,14 +42,6 @@ object Flags {
@JvmField val NULL_FLAG = unreleasedFlag("null_flag")
// 100 - notification
- // TODO(b/297792660): Tracking Bug
- @JvmField val UNCLEARED_TRANSIENT_HUN_FIX =
- releasedFlag("uncleared_transient_hun_fix")
-
- // TODO(b/298308067): Tracking Bug
- @JvmField val SWIPE_UNCLEARED_TRANSIENT_VIEW_FIX =
- releasedFlag("swipe_uncleared_transient_view_fix")
-
// TODO(b/254512751): Tracking Bug
val NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
unreleasedFlag("notification_pipeline_developer_logging")
@@ -170,12 +162,6 @@ object Flags {
val WALLPAPER_PICKER_GRID_APPLY_BUTTON =
unreleasedFlag("wallpaper_picker_grid_apply_button")
- /** Keyguard Migration */
-
- // TODO(b/297037052): Tracking bug.
- @JvmField
- val REMOVE_NPVC_BOTTOM_AREA_USAGE = unreleasedFlag("remove_npvc_bottom_area_usage")
-
/** Flag meant to guard the talkback fix for the KeyguardIndicationTextView */
// TODO(b/286563884): Tracking bug
@JvmField val KEYGUARD_TALKBACK_FIX = unreleasedFlag("keyguard_talkback_fix")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index c32c226441fe..a50cc8fbacb3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -53,7 +53,6 @@ import com.android.systemui.keyguard.ui.composable.LockscreenContent
import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.android.systemui.keyguard.ui.view.layout.KeyguardBlueprintCommandListener
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
@@ -89,7 +88,6 @@ constructor(
private val screenOffAnimationController: ScreenOffAnimationController,
private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
private val chipbarCoordinator: ChipbarCoordinator,
- private val keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener,
private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
private val configuration: ConfigurationState,
@@ -160,7 +158,6 @@ constructor(
)
}
}
- keyguardBlueprintCommandListener.start()
}
fun bindIndicationArea() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
index 80675d373b8e..0863cd737529 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
@@ -28,6 +28,8 @@ object BuiltInKeyguardQuickAffordanceKeys {
const val CREATE_NOTE = "create_note"
const val DO_NOT_DISTURB = "do_not_disturb"
const val FLASHLIGHT = "flashlight"
+ // TODO(b/339667383): delete or properly implement this once a product decision is made
+ const val GLANCEABLE_HUB = "glanceable_hub"
const val HOME_CONTROLS = "home"
const val MUTE = "mute"
const val QR_CODE_SCANNER = "qr_code_scanner"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
new file mode 100644
index 000000000000..d09b9f68ea60
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import com.android.systemui.Flags
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.communal.data.repository.CommunalSceneRepository
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+/** Shortcut that opens the glanceable hub. */
+// TODO(b/339667383): delete or properly implement this once a product decision is made
+@SysUISingleton
+class GlanceableHubQuickAffordanceConfig
+@Inject
+constructor(
+ private val communalRepository: CommunalSceneRepository,
+) : KeyguardQuickAffordanceConfig {
+
+ override val key: String = BuiltInKeyguardQuickAffordanceKeys.GLANCEABLE_HUB
+ override fun pickerName(): String = "Glanceable hub"
+
+ override val pickerIconResourceId = R.drawable.ic_widgets
+
+ override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> by lazy {
+ if (Flags.glanceableHubShortcutButton()) {
+ val contentDescription = ContentDescription.Loaded(pickerName())
+ val icon = Icon.Resource(pickerIconResourceId, contentDescription)
+ flowOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon))
+ } else {
+ flowOf(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ }
+ }
+
+ override fun onTriggered(
+ expandable: Expandable?
+ ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
+ communalRepository.changeScene(CommunalScenes.Communal, null)
+ return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
index 45561959a7df..93296f0ca24b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
@@ -36,6 +36,7 @@ interface KeyguardDataQuickAffordanceModule {
camera: CameraQuickAffordanceConfig,
doNotDisturb: DoNotDisturbQuickAffordanceConfig,
flashlight: FlashlightQuickAffordanceConfig,
+ glanceableHub: GlanceableHubQuickAffordanceConfig,
home: HomeControlsKeyguardQuickAffordanceConfig,
mute: MuteQuickAffordanceConfig,
quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
@@ -46,6 +47,7 @@ interface KeyguardDataQuickAffordanceModule {
camera,
doNotDisturb,
flashlight,
+ glanceableHub,
home,
mute,
quickAccessWallet,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
index deedbdb9e72b..0748979a2465 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt
@@ -20,15 +20,17 @@ package com.android.systemui.keyguard.data.quickaffordance
import android.content.Context
import android.content.IntentFilter
import android.content.SharedPreferences
-import com.android.systemui.res.R
+import com.android.systemui.Flags
import com.android.systemui.backup.BackupHelper
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.settings.SystemSettings
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
@@ -50,6 +52,7 @@ constructor(
@Application private val context: Context,
private val userFileManager: UserFileManager,
private val userTracker: UserTracker,
+ private val systemSettings: SystemSettings,
broadcastDispatcher: BroadcastDispatcher,
) : KeyguardQuickAffordanceSelectionManager {
@@ -70,6 +73,22 @@ constructor(
}
private val defaults: Map<String, List<String>> by lazy {
+ // Quick hack to allow testing out a lock screen shortcut to open the glanceable hub. This
+ // flag will not be rolled out and is only used for local testing.
+ // TODO(b/339667383): delete or properly implement this once a product decision is made
+ if (Flags.glanceableHubShortcutButton()) {
+ if (systemSettings.getBool("open_hub_chip_replace_home_controls", false)) {
+ return@lazy mapOf(
+ "bottom_start" to listOf("glanceable_hub"),
+ "bottom_end" to listOf("create_note")
+ )
+ } else {
+ return@lazy mapOf(
+ "bottom_start" to listOf("home"),
+ "bottom_end" to listOf("glanceable_hub")
+ )
+ }
+ }
context.resources
.getStringArray(R.array.config_keyguardQuickAffordanceDefaults)
.associate { item ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
index c11c49c7a8a0..b826a002b9d9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
@@ -91,9 +91,9 @@ constructor(
*/
fun refreshBlueprint(config: Config = Config.DEFAULT) {
fun scheduleCallback() {
- // We use a handler here instead of a CoroutineDipsatcher because the one provided by
+ // We use a handler here instead of a CoroutineDispatcher because the one provided by
// @Main CoroutineDispatcher is currently Dispatchers.Main.immediate, which doesn't
- // delay the callback, and instead runs it imemdiately.
+ // delay the callback, and instead runs it immediately.
handler.post {
assert.isMainThread()
targetTransitionConfig?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 53a0c3200f3d..76a822369b0c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -86,7 +86,7 @@ constructor(
return@combine null
}
- fromBouncerStep.value > 0.5f
+ fromBouncerStep.value > TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
}
.onStart {
// Default to null ("don't care, use a reasonable default").
@@ -232,5 +232,6 @@ constructor(
val TO_AOD_DURATION = DEFAULT_DURATION
val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
val TO_DOZING_DURATION = DEFAULT_DURATION
+ val TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD = 0.5f
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
index 7cee258dc39f..41c39597dc02 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
@@ -20,6 +20,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.content.Context
+import com.android.systemui.CoreStartable
import com.android.systemui.biometrics.domain.interactor.FingerprintPropertyInteractor
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
@@ -31,17 +32,17 @@ import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBl
import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
+import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection
+import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.launch
@SysUISingleton
@@ -53,9 +54,11 @@ constructor(
private val context: Context,
private val shadeInteractor: ShadeInteractor,
private val clockInteractor: KeyguardClockInteractor,
- configurationInteractor: ConfigurationInteractor,
- fingerprintPropertyInteractor: FingerprintPropertyInteractor,
-) {
+ private val configurationInteractor: ConfigurationInteractor,
+ private val fingerprintPropertyInteractor: FingerprintPropertyInteractor,
+ private val smartspaceSection: SmartspaceSection,
+ private val clockSection: ClockSection,
+) : CoreStartable {
/** The current blueprint for the lockscreen. */
val blueprint: StateFlow<KeyguardBlueprint> = keyguardBlueprintRepository.blueprint
@@ -75,15 +78,23 @@ constructor(
}
}
- private val refreshEvents: Flow<Unit> =
- merge(
- configurationInteractor.onAnyConfigurationChange,
- fingerprintPropertyInteractor.propertiesInitialized.filter { it }.map {},
- )
-
- init {
+ override fun start() {
applicationScope.launch { blueprintId.collect { transitionToBlueprint(it) } }
- applicationScope.launch { refreshEvents.collect { refreshBlueprint() } }
+ applicationScope.launch {
+ fingerprintPropertyInteractor.propertiesInitialized
+ .filter { it }
+ .collect { refreshBlueprint() }
+ }
+ applicationScope.launch {
+ val refreshConfig =
+ Config(
+ Type.NoTransition,
+ rebuildSections = listOf(smartspaceSection),
+ )
+ configurationInteractor.onAnyConfigurationChange.collect {
+ refreshBlueprint(refreshConfig)
+ }
+ }
}
/**
@@ -120,4 +131,8 @@ constructor(
fun getCurrentBlueprint(): KeyguardBlueprint {
return keyguardBlueprintRepository.blueprint.value
}
+
+ companion object {
+ private val TAG = "KeyguardBlueprintInteractor"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index fb65a6dacbe3..88e6602e56b7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
@@ -29,6 +30,7 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
import com.android.systemui.util.kotlin.sample
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -84,25 +86,52 @@ constructor(
}
.distinctUntilChanged()
+ private val isDeviceEntered: Flow<Boolean> by lazy {
+ deviceEntryInteractor.get().isDeviceEntered
+ }
+
+ private val isDeviceNotEntered: Flow<Boolean> by lazy { isDeviceEntered.map { !it } }
+
/**
- * Surface visibility, which is either determined by the default visibility in the FINISHED
- * KeyguardState, or the transition-specific visibility used during certain RUNNING transitions.
+ * Surface visibility, which is either determined by the default visibility when not
+ * transitioning between [KeyguardState]s or [Scenes] or the transition-specific visibility used
+ * during certain ongoing transitions.
*/
@OptIn(ExperimentalCoroutinesApi::class)
val surfaceBehindVisibility: Flow<Boolean> =
- transitionInteractor.isInTransitionToAnyState
- .flatMapLatest { isInTransition ->
- if (!isInTransition) {
- defaultSurfaceBehindVisibility
- } else {
- combine(
- transitionSpecificSurfaceBehindVisibility,
- defaultSurfaceBehindVisibility,
- ) { transitionVisibility, defaultVisibility ->
- // Defer to the transition-specific visibility since we're RUNNING a
- // transition, but fall back to the default visibility if the current
- // transition's interactor did not specify a visibility.
- transitionVisibility ?: defaultVisibility
+ if (SceneContainerFlag.isEnabled) {
+ sceneInteractor.get().transitionState.flatMapLatestConflated { transitionState ->
+ when (transitionState) {
+ is ObservableTransitionState.Transition ->
+ when {
+ transitionState.fromScene == Scenes.Lockscreen &&
+ transitionState.toScene == Scenes.Gone -> flowOf(true)
+ transitionState.fromScene == Scenes.Bouncer &&
+ transitionState.toScene == Scenes.Gone ->
+ transitionState.progress.map { progress ->
+ progress >
+ FromPrimaryBouncerTransitionInteractor
+ .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
+ }
+ else -> isDeviceEntered
+ }
+ is ObservableTransitionState.Idle -> isDeviceEntered
+ }
+ }
+ } else {
+ transitionInteractor.isInTransitionToAnyState.flatMapLatest { isInTransition ->
+ if (!isInTransition) {
+ defaultSurfaceBehindVisibility
+ } else {
+ combine(
+ transitionSpecificSurfaceBehindVisibility,
+ defaultSurfaceBehindVisibility,
+ ) { transitionVisibility, defaultVisibility ->
+ // Defer to the transition-specific visibility since we're RUNNING a
+ // transition, but fall back to the default visibility if the current
+ // transition's interactor did not specify a visibility.
+ transitionVisibility ?: defaultVisibility
+ }
}
}
}
@@ -162,7 +191,7 @@ constructor(
*/
val lockscreenVisibility: Flow<Boolean> =
if (SceneContainerFlag.isEnabled) {
- deviceEntryInteractor.get().isDeviceEntered.map { !it }
+ isDeviceNotEntered
} else {
transitionInteractor.currentKeyguardState
.sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
index 7ca2ebaeab20..6d579f3b2513 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
@@ -37,16 +37,32 @@ interface KeyguardBlueprint {
fun replaceViews(
constraintLayout: ConstraintLayout,
previousBlueprint: KeyguardBlueprint? = null,
+ rebuildSections: List<KeyguardSection> = listOf(),
bindData: Boolean = true
) {
- val prevSections =
- previousBlueprint?.let { prev ->
- prev.sections.subtract(sections).forEach { it.removeViews(constraintLayout) }
- prev.sections
+ val prevSections = previousBlueprint?.sections ?: listOf()
+ val skipSections = sections.intersect(prevSections).subtract(rebuildSections)
+ prevSections.subtract(skipSections).forEach { it.removeViews(constraintLayout) }
+ sections.subtract(skipSections).forEach {
+ it.addViews(constraintLayout)
+ if (bindData) {
+ it.bindData(constraintLayout)
}
- ?: listOf()
+ }
+ }
+
+ /** Rebuilds views for the target sections, or all of them if unspecified. */
+ fun rebuildViews(
+ constraintLayout: ConstraintLayout,
+ rebuildSections: List<KeyguardSection> = sections,
+ bindData: Boolean = true
+ ) {
+ if (rebuildSections.isEmpty()) {
+ return
+ }
- sections.subtract(prevSections).forEach {
+ rebuildSections.forEach { it.removeViews(constraintLayout) }
+ rebuildSections.forEach {
it.addViews(constraintLayout)
if (bindData) {
it.bindData(constraintLayout)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index 52d7519c2e3c..816033561e94 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -95,17 +95,8 @@ constructor(
null as KeyguardBlueprint?,
)
.collect { (prevBlueprint, blueprint) ->
- val cs =
- ConstraintSet().apply {
- clone(constraintLayout)
- val emptyLayout = ConstraintSet.Layout()
- knownIds.forEach {
- getConstraint(it).layout.copyFrom(emptyLayout)
- }
- blueprint.applyConstraints(this)
- }
-
- var transition =
+ val config = Config.DEFAULT
+ val transition =
if (
!KeyguardBottomAreaRefactor.isEnabled &&
prevBlueprint != null &&
@@ -114,23 +105,37 @@ constructor(
BaseBlueprintTransition(clockViewModel)
.addTransition(
IntraBlueprintTransition(
- Config.DEFAULT,
+ config,
clockViewModel,
smartspaceViewModel
)
)
} else {
IntraBlueprintTransition(
- Config.DEFAULT,
+ config,
clockViewModel,
smartspaceViewModel
)
}
- runTransition(constraintLayout, transition, Config.DEFAULT) {
- // Add and remove views of sections that are not contained by the
- // other.
- blueprint.replaceViews(constraintLayout, prevBlueprint)
+ runTransition(constraintLayout, transition, config) {
+ // Replace sections from the previous blueprint with the new ones
+ blueprint.replaceViews(
+ constraintLayout,
+ prevBlueprint,
+ config.rebuildSections
+ )
+
+ val cs =
+ ConstraintSet().apply {
+ clone(constraintLayout)
+ val emptyLayout = ConstraintSet.Layout()
+ knownIds.forEach {
+ getConstraint(it).layout.copyFrom(emptyLayout)
+ }
+ blueprint.applyConstraints(this)
+ }
+
logAlphaVisibilityOfAppliedConstraintSet(cs, clockViewModel)
cs.applyTo(constraintLayout)
}
@@ -138,22 +143,21 @@ constructor(
}
launch("$TAG#viewModel.refreshTransition") {
- viewModel.refreshTransition.collect { transition ->
- val cs =
- ConstraintSet().apply {
- clone(constraintLayout)
- viewModel.blueprint.value.applyConstraints(this)
- }
+ viewModel.refreshTransition.collect { config ->
+ val blueprint = viewModel.blueprint.value
runTransition(
constraintLayout,
- IntraBlueprintTransition(
- transition,
- clockViewModel,
- smartspaceViewModel
- ),
- transition,
+ IntraBlueprintTransition(config, clockViewModel, smartspaceViewModel),
+ config,
) {
+ blueprint.rebuildViews(constraintLayout, config.rebuildSections)
+
+ val cs =
+ ConstraintSet().apply {
+ clone(constraintLayout)
+ blueprint.applyConstraints(this)
+ }
logAlphaVisibilityOfAppliedConstraintSet(cs, clockViewModel)
cs.applyTo(constraintLayout)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListener.kt
index 962cdf10cf86..c0266567a63f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListener.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.view.layout
import androidx.core.text.isDigitsOnly
+import com.android.systemui.CoreStartable
import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.statusbar.commandline.Command
@@ -31,10 +32,10 @@ constructor(
private val commandRegistry: CommandRegistry,
private val keyguardBlueprintRepository: KeyguardBlueprintRepository,
private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
-) {
+) : CoreStartable {
private val layoutCommand = KeyguardLayoutManagerCommand()
- fun start() {
+ override fun start() {
commandRegistry.registerCommand(COMMAND) { layoutCommand }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
index 04ac7bf1178e..2dc930121a71 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
@@ -17,9 +17,14 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints
+import com.android.systemui.CoreStartable
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
+import com.android.systemui.keyguard.ui.view.layout.KeyguardBlueprintCommandListener
import dagger.Binds
import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
import dagger.multibindings.IntoSet
@Module
@@ -41,4 +46,18 @@ abstract class KeyguardBlueprintModule {
abstract fun bindShortcutsBesideUdfpsLockscreenBlueprint(
shortcutsBesideUdfpsLockscreenBlueprint: ShortcutsBesideUdfpsKeyguardBlueprint
): KeyguardBlueprint
+
+ @Binds
+ @IntoMap
+ @ClassKey(KeyguardBlueprintInteractor::class)
+ abstract fun bindsKeyguardBlueprintInteractor(
+ keyguardBlueprintInteractor: KeyguardBlueprintInteractor
+ ): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(KeyguardBlueprintCommandListener::class)
+ abstract fun bindsKeyguardBlueprintCommandListener(
+ keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener
+ ): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
index c69d868866d0..02e9ca5b6821 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints.transitions
import android.transition.TransitionSet
+import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.DefaultClockSteppingTransition
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
@@ -46,6 +47,7 @@ class IntraBlueprintTransition(
val type: Type,
val checkPriority: Boolean = true,
val terminatePrevious: Boolean = true,
+ val rebuildSections: List<KeyguardSection> = listOf(),
) {
companion object {
val DEFAULT = Config(Type.NoTransition)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index b367715f529e..34a1da54c123 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -32,6 +32,7 @@ import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.constraintlayout.widget.ConstraintSet.VISIBLE
import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
import com.android.systemui.customization.R as custR
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
@@ -57,6 +58,7 @@ internal fun ConstraintSet.setAlpha(
alpha: Float,
) = views.forEach { view -> this.setAlpha(view.id, alpha) }
+@SysUISingleton
class ClockSection
@Inject
constructor(
@@ -72,6 +74,7 @@ constructor(
if (!MigrateClocksToBlueprint.isEnabled) {
return
}
+
KeyguardClockViewBinder.bind(
this,
constraintLayout,
@@ -86,6 +89,7 @@ constructor(
if (!MigrateClocksToBlueprint.isEnabled) {
return
}
+
keyguardClockViewModel.currentClock.value?.let { clock ->
constraintSet.applyDeltaFrom(buildConstraints(clock, constraintSet))
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index 0b8376af811c..c5fab8f57822 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -219,5 +219,37 @@ constructor(
sensorRect.left
)
}
+
+ // This is only intended to be here until the KeyguardBottomAreaRefactor flag is enabled
+ // Without this logic, the lock icon location changes but the KeyguardBottomAreaView is not
+ // updated and visible ui layout jank occurs. This is due to AmbientIndicationContainer
+ // being in NPVC and laying out prior to the KeyguardRootView.
+ // Remove when both DeviceEntryUdfpsRefactor and KeyguardBottomAreaRefactor are enabled.
+ if (DeviceEntryUdfpsRefactor.isEnabled && !KeyguardBottomAreaRefactor.isEnabled) {
+ with(notificationPanelView) {
+ val isUdfpsSupported = deviceEntryIconViewModel.get().isUdfpsSupported.value
+ val bottomAreaViewRight = findViewById<View>(R.id.keyguard_bottom_area)?.right ?: 0
+ findViewById<View>(R.id.ambient_indication_container)?.let {
+ val (ambientLeft, ambientTop) = it.locationOnScreen
+ if (isUdfpsSupported) {
+ // make top of ambient indication view the bottom of the lock icon
+ it.layout(
+ ambientLeft,
+ sensorRect.bottom,
+ bottomAreaViewRight - ambientLeft,
+ ambientTop + it.measuredHeight
+ )
+ } else {
+ // make bottom of ambient indication view the top of the lock icon
+ it.layout(
+ ambientLeft,
+ sensorRect.top - it.measuredHeight,
+ bottomAreaViewRight - ambientLeft,
+ sensorRect.top
+ )
+ }
+ }
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index 487c2e918e5f..2d6690fbbc99 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -22,6 +22,7 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener
import androidx.constraintlayout.widget.Barrier
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
@@ -36,6 +37,7 @@ import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import dagger.Lazy
import javax.inject.Inject
+@SysUISingleton
open class SmartspaceSection
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index 486d4d46c767..aa93df7f1474 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -23,6 +23,7 @@ import android.graphics.drawable.Drawable
import android.media.MediaRouter2Manager
import android.media.RoutingSessionInfo
import android.media.session.MediaController
+import android.media.session.MediaController.PlaybackInfo
import android.text.TextUtils
import android.util.Log
import androidx.annotation.AnyThread
@@ -74,6 +75,11 @@ constructor(
private val listeners: MutableSet<Listener> = mutableSetOf()
private val entries: MutableMap<String, Entry> = mutableMapOf()
+ companion object {
+ private val EMPTY_AND_DISABLED_MEDIA_DEVICE_DATA =
+ MediaDeviceData(enabled = false, icon = null, name = null, showBroadcastButton = false)
+ }
+
/** Add a listener for changes to the media route (ie. device). */
fun addListener(listener: Listener) = listeners.add(listener)
@@ -333,28 +339,32 @@ constructor(
@WorkerThread
private fun updateCurrent() {
if (isLeAudioBroadcastEnabled()) {
- if (enableLeAudioSharing()) {
- current =
- MediaDeviceData(
- enabled = false,
- icon =
- context.getDrawable(
- com.android.settingslib.R.drawable.ic_bt_le_audio_sharing
- ),
- name = context.getString(R.string.audio_sharing_description),
- intent = null,
- showBroadcastButton = false
- )
+ current = getLeAudioBroadcastDeviceData()
+ } else if (Flags.usePlaybackInfoForRoutingControls()) {
+ val activeDevice: MediaDeviceData?
+
+ // LocalMediaManager provides the connected device based on PlaybackInfo.
+ // TODO (b/342197065): Simplify nullability once we make currentConnectedDevice
+ // non-null.
+ val connectedDevice = localMediaManager.currentConnectedDevice?.toMediaDeviceData()
+
+ if (controller?.playbackInfo?.playbackType == PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
+ val routingSession =
+ mr2manager.get().getRoutingSessionForMediaController(controller)
+
+ activeDevice =
+ routingSession?.let {
+ // For a remote session, always use the current device from
+ // LocalMediaManager. Override with routing session name if available to
+ // show dynamic group name.
+ connectedDevice?.copy(name = it.name ?: connectedDevice.name)
+ }
} else {
- current =
- MediaDeviceData(
- /* enabled */ true,
- /* icon */ context.getDrawable(R.drawable.settings_input_antenna),
- /* name */ broadcastDescription,
- /* intent */ null,
- /* showBroadcastButton */ showBroadcastButton = true
- )
+ // Prefer SASS if available when playback is local.
+ activeDevice = getSassDevice() ?: connectedDevice
}
+
+ current = activeDevice ?: EMPTY_AND_DISABLED_MEDIA_DEVICE_DATA
} else {
val aboutToConnect = aboutToConnectDeviceOverride
if (
@@ -389,6 +399,43 @@ constructor(
}
}
+ private fun getSassDevice(): MediaDeviceData? {
+ val sassDevice = aboutToConnectDeviceOverride ?: return null
+ return sassDevice.fullMediaDevice?.toMediaDeviceData()
+ ?: sassDevice.backupMediaDeviceData
+ }
+
+ private fun MediaDevice.toMediaDeviceData() =
+ MediaDeviceData(
+ enabled = true,
+ icon = iconWithoutBackground,
+ name = name,
+ id = id,
+ showBroadcastButton = false
+ )
+
+ private fun getLeAudioBroadcastDeviceData(): MediaDeviceData {
+ return if (enableLeAudioSharing()) {
+ MediaDeviceData(
+ enabled = false,
+ icon =
+ context.getDrawable(
+ com.android.settingslib.R.drawable.ic_bt_le_audio_sharing
+ ),
+ name = context.getString(R.string.audio_sharing_description),
+ intent = null,
+ showBroadcastButton = false
+ )
+ } else {
+ MediaDeviceData(
+ enabled = true,
+ icon = context.getDrawable(R.drawable.settings_input_antenna),
+ name = broadcastDescription,
+ intent = null,
+ showBroadcastButton = true
+ )
+ }
+ }
/** Return a display name for the current device / route, or null if not possible */
private fun getDeviceName(
device: MediaDevice?,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt
new file mode 100644
index 000000000000..f3e5b8f0c214
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.data.repository
+
+import android.content.Context
+import android.content.SharedPreferences
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.kotlin.SharedPreferencesExt.observe
+import com.android.systemui.util.kotlin.emitOnStart
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+/** Repository for QS user preferences. */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class QSPreferencesRepository
+@Inject
+constructor(
+ private val userFileManager: UserFileManager,
+ private val userRepository: UserRepository,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+ /** Whether to show the labels on icon tiles for the current user. */
+ val showLabels: Flow<Boolean> =
+ userRepository.selectedUserInfo
+ .flatMapLatest { userInfo ->
+ val prefs = getSharedPrefs(userInfo.id)
+ prefs.observe().emitOnStart().map { prefs.getBoolean(ICON_LABELS_KEY, false) }
+ }
+ .flowOn(backgroundDispatcher)
+
+ /** Sets for the current user whether to show the labels on icon tiles. */
+ fun setShowLabels(showLabels: Boolean) {
+ with(getSharedPrefs(userRepository.getSelectedUserInfo().id)) {
+ edit().putBoolean(ICON_LABELS_KEY, showLabels).apply()
+ }
+ }
+
+ private fun getSharedPrefs(userId: Int): SharedPreferences {
+ return userFileManager.getSharedPreferences(
+ FILE_NAME,
+ Context.MODE_PRIVATE,
+ userId,
+ )
+ }
+
+ companion object {
+ private const val ICON_LABELS_KEY = "show_icon_labels"
+ const val FILE_NAME = "quick_settings_prefs"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractor.kt
index a871531f283a..6a899b07b2a0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractor.kt
@@ -20,7 +20,6 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
-import com.android.systemui.qs.panels.data.repository.IconLabelVisibilityRepository
import com.android.systemui.qs.panels.shared.model.IconLabelVisibilityLog
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -33,17 +32,17 @@ import kotlinx.coroutines.flow.stateIn
class IconLabelVisibilityInteractor
@Inject
constructor(
- private val repo: IconLabelVisibilityRepository,
+ private val preferencesInteractor: QSPreferencesInteractor,
@IconLabelVisibilityLog private val logBuffer: LogBuffer,
@Application scope: CoroutineScope,
) {
val showLabels: StateFlow<Boolean> =
- repo.showLabels
+ preferencesInteractor.showLabels
.onEach { logChange(it) }
- .stateIn(scope, SharingStarted.WhileSubscribed(), repo.showLabels.value)
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
fun setShowLabels(showLabels: Boolean) {
- repo.setShowLabels(showLabels)
+ preferencesInteractor.setShowLabels(showLabels)
}
private fun logChange(showLabels: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSPreferencesInteractor.kt
index 686e5f49442b..811be80d23fa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSPreferencesInteractor.kt
@@ -14,22 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.qs.panels.domain.interactor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.panels.data.repository.QSPreferencesRepository
import javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.Flow
-/** Repository for whether to show the labels of icon tiles. */
@SysUISingleton
-class IconLabelVisibilityRepository @Inject constructor() {
- // TODO(b/341735914): Persist and back up showLabels
- private val _showLabels = MutableStateFlow(false)
- val showLabels: StateFlow<Boolean> = _showLabels.asStateFlow()
+class QSPreferencesInteractor @Inject constructor(private val repo: QSPreferencesRepository) {
+ val showLabels: Flow<Boolean> = repo.showLabels
fun setShowLabels(showLabels: Boolean) {
- _showLabels.value = showLabels
+ repo.setShowLabels(showLabels)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModel.kt
index 5d4b8f1773f2..12cbde2cbc91 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModel.kt
@@ -30,7 +30,9 @@ interface IconLabelVisibilityViewModel {
@SysUISingleton
class IconLabelVisibilityViewModelImpl
@Inject
-constructor(private val interactor: IconLabelVisibilityInteractor) : IconLabelVisibilityViewModel {
+constructor(
+ private val interactor: IconLabelVisibilityInteractor,
+) : IconLabelVisibilityViewModel {
override val showLabels: StateFlow<Boolean> = interactor.showLabels
override fun setShowLabels(showLabels: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 7bc76af68c6b..c091ac3dea50 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -936,6 +936,10 @@ public class InternetDialogController implements AccessPointController.AccessPoi
mHasActiveSubIdOnDds = false;
Log.e(TAG, "Can't get DDS subscriptionInfo");
return;
+ } else if (ddsSubInfo.isOnlyNonTerrestrialNetwork()) {
+ mHasActiveSubIdOnDds = false;
+ Log.d(TAG, "This is NTN, so do not show mobile data");
+ return;
}
mHasActiveSubIdOnDds = isEmbeddedSubscriptionVisible(ddsSubInfo);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 22aa492dbfe8..1d8b7e5b6155 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -43,6 +43,7 @@ import com.android.systemui.communal.ui.compose.CommunalContainer
import com.android.systemui.communal.ui.compose.CommunalContent
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.util.CommunalColors
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -64,6 +65,7 @@ import kotlinx.coroutines.launch
*
* This will be used until the glanceable hub is integrated into Flexiglass.
*/
+@SysUISingleton
class GlanceableHubContainerController
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 84b74782d09f..fe22cc628b5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -24,7 +24,6 @@ import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_ALL;
import static com.android.systemui.Flags.newAodTransition;
import static com.android.systemui.Flags.notificationOverExpansionClippingFix;
-import static com.android.systemui.flags.Flags.UNCLEARED_TRANSIENT_HUN_FIX;
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
import static com.android.systemui.util.DumpUtilsKt.println;
@@ -2844,23 +2843,15 @@ public class NotificationStackScrollLayout
mAddedHeadsUpChildren.remove(child);
return false;
}
- if (mFeatureFlags.isEnabled(UNCLEARED_TRANSIENT_HUN_FIX)) {
- // Skip adding animation for clicked heads up notifications when the
- // Shade is closed, because the animation event is generated in
- // generateHeadsUpAnimationEvents. Only report that an animation was
- // actually generated (thus requesting the transient view be added)
- // if a removal animation is in progress.
- if (!isExpanded() && isClickedHeadsUp(child)) {
- // An animation is already running, add it transiently
- mClearTransientViewsWhenFinished.add(child);
- return child.inRemovalAnimation();
- }
- } else {
- if (isClickedHeadsUp(child)) {
- // An animation is already running, add it transiently
- mClearTransientViewsWhenFinished.add(child);
- return true;
- }
+ // Skip adding animation for clicked heads up notifications when the
+ // Shade is closed, because the animation event is generated in
+ // generateHeadsUpAnimationEvents. Only report that an animation was
+ // actually generated (thus requesting the transient view be added)
+ // if a removal animation is in progress.
+ if (!isExpanded() && isClickedHeadsUp(child)) {
+ // An animation is already running, add it transiently
+ mClearTransientViewsWhenFinished.add(child);
+ return child.inRemovalAnimation();
}
if (mDebugRemoveAnimation) {
Log.d(TAG, "generateRemove " + key
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 7b7a35b4928f..05a43917f7e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -327,6 +327,11 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner, CoreStartable
@Deprecated
float getDisplayDensity();
+ /**
+ * Forwards touch events to communal hub
+ */
+ void handleCommunalHubTouch(MotionEvent event);
+
public static class KeyboardShortcutsMessage {
final int mDeviceId;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
index 5ab56ae4be4d..a7b54847cdf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
@@ -81,6 +81,7 @@ abstract class CentralSurfacesEmptyImpl : CentralSurfaces {
override fun shouldIgnoreTouch() = false
override fun isDeviceInteractive() = false
override fun handleDreamTouch(event: MotionEvent?) {}
+ override fun handleCommunalHubTouch(event: MotionEvent?) {}
override fun awakenDreams() {}
override fun isBouncerShowing() = false
override fun isBouncerShowingScrimmed() = false
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 e0da2fe584b6..78a803618c8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -172,6 +172,7 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor;
import com.android.systemui.shade.CameraLauncher;
+import com.android.systemui.shade.GlanceableHubContainerController;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shade.QuickSettingsController;
@@ -595,6 +596,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener =
(extractor, which) -> updateTheme();
private final BrightnessMirrorShowingInteractor mBrightnessMirrorShowingInteractor;
+ private final GlanceableHubContainerController mGlanceableHubContainerController;
/**
* Public constructor for CentralSurfaces.
@@ -707,7 +709,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
UserTracker userTracker,
Provider<FingerprintManager> fingerprintManager,
ActivityStarter activityStarter,
- BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor
+ BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor,
+ GlanceableHubContainerController glanceableHubContainerController
) {
mContext = context;
mNotificationsController = notificationsController;
@@ -802,6 +805,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mFingerprintManager = fingerprintManager;
mActivityStarter = activityStarter;
mBrightnessMirrorShowingInteractor = brightnessMirrorShowingInteractor;
+ mGlanceableHubContainerController = glanceableHubContainerController;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mStartingSurfaceOptional = startingSurfaceOptional;
@@ -2951,6 +2955,11 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
@Override
+ public void handleCommunalHubTouch(MotionEvent event) {
+ mGlanceableHubContainerController.onTouchEvent(event);
+ }
+
+ @Override
public void awakenDreams() {
mUiBgExecutor.execute(() -> {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
index 58b82f166623..da928a364984 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
@@ -32,6 +32,7 @@ import android.util.SparseBooleanArray;
import androidx.annotation.NonNull;
import com.android.internal.camera.flags.Flags;
+import com.android.systemui.util.ListenerSet;
import java.util.Set;
@@ -43,7 +44,7 @@ public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPr
private final SparseBooleanArray mSoftwareToggleState = new SparseBooleanArray();
private final SparseBooleanArray mHardwareToggleState = new SparseBooleanArray();
private Boolean mRequiresAuthentication;
- private final Set<Callback> mCallbacks = new ArraySet<>();
+ private final ListenerSet<Callback> mCallbacks = new ListenerSet<>();
public IndividualSensorPrivacyControllerImpl(
@NonNull SensorPrivacyManager sensorPrivacyManager) {
@@ -115,7 +116,7 @@ public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPr
@Override
public void addCallback(@NonNull Callback listener) {
- mCallbacks.add(listener);
+ mCallbacks.addIfAbsent(listener);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
index 03c8af9020e1..99f956489bc3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
@@ -97,7 +97,14 @@ constructor(
}
private fun showNewVolumePanel() {
- volumePanelGlobalStateInteractor.setVisible(true)
+ activityStarter.dismissKeyguardThenExecute(
+ {
+ volumePanelGlobalStateInteractor.setVisible(true)
+ false
+ },
+ {},
+ true
+ )
}
private fun createNewVolumePanelDialog(): Dialog {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
index 07c980bb6656..18bd960b30a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
@@ -132,7 +132,7 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase {
public void complicationAvailability_serviceNotAvailable_noFavorites_doNotAddComplication() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
- mDreamOverlayStateController, mControlsComponent, mMonitor);
+ mDreamOverlayStateController, mControlsComponent, mMonitor, false);
registrant.start();
setHaveFavorites(false);
@@ -145,7 +145,7 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase {
public void complicationAvailability_serviceAvailable_noFavorites_doNotAddComplication() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
- mDreamOverlayStateController, mControlsComponent, mMonitor);
+ mDreamOverlayStateController, mControlsComponent, mMonitor, false);
registrant.start();
setHaveFavorites(false);
@@ -158,7 +158,7 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase {
public void complicationAvailability_serviceAvailable_noFavorites_panel_addComplication() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
- mDreamOverlayStateController, mControlsComponent, mMonitor);
+ mDreamOverlayStateController, mControlsComponent, mMonitor, false);
registrant.start();
setHaveFavorites(false);
@@ -171,7 +171,7 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase {
public void complicationAvailability_serviceNotAvailable_haveFavorites_doNotAddComplication() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
- mDreamOverlayStateController, mControlsComponent, mMonitor);
+ mDreamOverlayStateController, mControlsComponent, mMonitor, false);
registrant.start();
setHaveFavorites(true);
@@ -184,7 +184,7 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase {
public void complicationAvailability_serviceAvailable_haveFavorites_addComplication() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
- mDreamOverlayStateController, mControlsComponent, mMonitor);
+ mDreamOverlayStateController, mControlsComponent, mMonitor, false);
registrant.start();
setHaveFavorites(true);
@@ -197,7 +197,7 @@ public class DreamHomeControlsComplicationTest extends SysuiTestCase {
public void complicationAvailability_checkAvailabilityWhenDreamOverlayBecomesActive() {
final DreamHomeControlsComplication.Registrant registrant =
new DreamHomeControlsComplication.Registrant(mComplication,
- mDreamOverlayStateController, mControlsComponent, mMonitor);
+ mDreamOverlayStateController, mControlsComponent, mMonitor, false);
registrant.start();
setServiceAvailable(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 36bfa092c042..90ac05fc1b2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -135,6 +135,7 @@ class CustomizationProviderTest : SysuiTestCase() {
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ systemSettings = FakeSettings(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
val remoteUserSelectionManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
index 0bdf47a51670..f0ad5103e9a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
@@ -38,6 +38,7 @@ import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -48,6 +49,9 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@ExperimentalCoroutinesApi
@@ -57,6 +61,7 @@ class KeyguardBlueprintInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val underTest = kosmos.keyguardBlueprintInteractor
+ private val keyguardBlueprintRepository = kosmos.keyguardBlueprintRepository
private val clockRepository by lazy { kosmos.fakeKeyguardClockRepository }
private val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
private val fingerprintPropertyRepository by lazy { kosmos.fakeFingerprintPropertyRepository }
@@ -103,44 +108,46 @@ class KeyguardBlueprintInteractorTest : SysuiTestCase() {
}
@Test
- @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
- fun fingerprintPropertyInitialized_updatesBlueprint() {
+ @EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
+ fun testDoesNotApplySplitShadeBlueprint() {
testScope.runTest {
- assertThat(kosmos.keyguardBlueprintRepository.targetTransitionConfig).isNull()
-
- fingerprintPropertyRepository.supportsUdfps() // initialize properties
+ val blueprintId by collectLastValue(underTest.blueprintId)
+ kosmos.shadeRepository.setShadeMode(ShadeMode.Split)
+ clockRepository.setCurrentClock(clockController)
+ configurationRepository.onConfigurationChange()
runCurrent()
advanceUntilIdle()
- assertThat(kosmos.keyguardBlueprintRepository.targetTransitionConfig).isNotNull()
+ assertThat(blueprintId).isEqualTo(DefaultKeyguardBlueprint.DEFAULT)
}
}
@Test
- @EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
- fun testDoesNotApplySplitShadeBlueprint() {
+ @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
+ fun fingerprintPropertyInitialized_updatesBlueprint() {
testScope.runTest {
- val blueprintId by collectLastValue(underTest.blueprintId)
- kosmos.shadeRepository.setShadeMode(ShadeMode.Split)
- clockRepository.setCurrentClock(clockController)
- configurationRepository.onConfigurationChange()
+ underTest.start()
+ reset(keyguardBlueprintRepository)
+
+ fingerprintPropertyRepository.supportsUdfps() // initialize properties
runCurrent()
advanceUntilIdle()
- assertThat(blueprintId).isEqualTo(DefaultKeyguardBlueprint.DEFAULT)
+ verify(keyguardBlueprintRepository, times(2)).refreshBlueprint(any())
}
}
@Test
fun testRefreshFromConfigChange() {
testScope.runTest {
- assertThat(kosmos.keyguardBlueprintRepository.targetTransitionConfig).isNull()
+ underTest.start()
+ reset(keyguardBlueprintRepository)
configurationRepository.onConfigurationChange()
runCurrent()
advanceUntilIdle()
- assertThat(kosmos.keyguardBlueprintRepository.targetTransitionConfig).isNotNull()
+ verify(keyguardBlueprintRepository, times(2)).refreshBlueprint(any())
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 35659c12fad5..14d954873f0f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -281,6 +281,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ systemSettings = FakeSettings(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
val remoteUserSelectionManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
index ef3183a8891f..ced3526f40be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
@@ -281,6 +281,7 @@ class KeyguardQuickAffordanceInteractorSceneContainerTest : SysuiTestCase() {
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ systemSettings = FakeSettings(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
val remoteUserSelectionManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index 616aac7ce460..344e0fc2bc6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -44,6 +44,7 @@ import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import com.android.systemui.util.mockito.whenever
import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -55,6 +56,7 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
+@ExperimentalCoroutinesApi
@SmallTest
class DefaultKeyguardBlueprintTest : SysuiTestCase() {
private lateinit var underTest: DefaultKeyguardBlueprint
@@ -112,17 +114,66 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() {
@Test
fun replaceViews_withPrevBlueprint() {
val prevBlueprint = mock(KeyguardBlueprint::class.java)
- val someSection = mock(KeyguardSection::class.java)
- whenever(prevBlueprint.sections)
- .thenReturn(underTest.sections.minus(mDefaultDeviceEntrySection).plus(someSection))
+ val removedSection = mock(KeyguardSection::class.java)
+ val addedSection = mDefaultDeviceEntrySection
+ val rebuildSection = clockSection
+ val prevSections = underTest.sections.minus(addedSection).plus(removedSection)
+ val unchangedSections = underTest.sections.subtract(listOf(addedSection, rebuildSection))
+ whenever(prevBlueprint.sections).thenReturn(prevSections)
+
val constraintLayout = ConstraintLayout(context, null)
underTest.replaceViews(constraintLayout, prevBlueprint)
- underTest.sections.minus(mDefaultDeviceEntrySection).forEach {
- verify(it, never())?.addViews(constraintLayout)
+
+ unchangedSections.forEach {
+ verify(it, never()).addViews(constraintLayout)
+ verify(it, never()).removeViews(constraintLayout)
+ }
+
+ verify(addedSection).addViews(constraintLayout)
+ verify(removedSection).removeViews(constraintLayout)
+ }
+
+ @Test
+ fun replaceViews_withPrevBlueprint_withRebuildTargets() {
+ val prevBlueprint = mock(KeyguardBlueprint::class.java)
+ val removedSection = mock(KeyguardSection::class.java)
+ val addedSection = mDefaultDeviceEntrySection
+ val rebuildSection = clockSection
+ val prevSections = underTest.sections.minus(addedSection).plus(removedSection)
+ val unchangedSections = underTest.sections.subtract(listOf(addedSection, rebuildSection))
+ whenever(prevBlueprint.sections).thenReturn(prevSections)
+
+ val constraintLayout = ConstraintLayout(context, null)
+ underTest.replaceViews(constraintLayout, prevBlueprint, listOf(rebuildSection))
+
+ unchangedSections.forEach {
+ verify(it, never()).addViews(constraintLayout)
+ verify(it, never()).removeViews(constraintLayout)
+ }
+
+ verify(addedSection).addViews(constraintLayout)
+ verify(rebuildSection).addViews(constraintLayout)
+ verify(rebuildSection).removeViews(constraintLayout)
+ verify(removedSection).removeViews(constraintLayout)
+ }
+
+ @Test
+ fun rebuildViews() {
+ val rebuildSections = listOf(mDefaultDeviceEntrySection, clockSection)
+ val unchangedSections = underTest.sections.subtract(rebuildSections)
+
+ val constraintLayout = ConstraintLayout(context, null)
+ underTest.rebuildViews(constraintLayout, rebuildSections)
+
+ unchangedSections.forEach {
+ verify(it, never()).addViews(constraintLayout)
+ verify(it, never()).removeViews(constraintLayout)
}
- verify(mDefaultDeviceEntrySection).addViews(constraintLayout)
- verify(someSection).removeViews(constraintLayout)
+ rebuildSections.forEach {
+ verify(it).addViews(constraintLayout)
+ verify(it).removeViews(constraintLayout)
+ }
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 16421a0f83b4..bdc5fc34158f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -178,6 +178,7 @@ class KeyguardBottomAreaViewModelTest(flags: FlagsParameterization) : SysuiTestC
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ systemSettings = FakeSettings(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
val remoteUserSelectionManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 833822076620..8a12a90efc4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -221,6 +221,7 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
+ systemSettings = FakeSettings(),
broadcastDispatcher = fakeBroadcastDispatcher,
)
val remoteUserSelectionManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index d2701dd0d3a3..16d8819f13fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
@@ -29,6 +29,7 @@ import android.media.session.MediaController.PlaybackInfo
import android.media.session.MediaSession
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.RequiresFlagsDisabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -40,6 +41,7 @@ import com.android.settingslib.flags.Flags
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.media.PhoneMediaDevice
+import com.android.settingslib.media.flags.Flags.FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.MediaTestUtils
import com.android.systemui.media.controls.shared.model.MediaData
@@ -101,7 +103,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Mock private lateinit var listener: MediaDeviceManager.Listener
@Mock private lateinit var device: MediaDevice
@Mock private lateinit var icon: Drawable
- @Mock private lateinit var route: RoutingSessionInfo
+ @Mock private lateinit var routingSession: RoutingSessionInfo
@Mock private lateinit var selectedRoute: MediaRoute2Info
@Mock private lateinit var controller: MediaController
@Mock private lateinit var playbackInfo: PlaybackInfo
@@ -141,7 +143,10 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
whenever(lmmFactory.create(PACKAGE)).thenReturn(lmm)
whenever(muteAwaitFactory.create(lmm)).thenReturn(muteAwaitManager)
whenever(lmm.getCurrentConnectedDevice()).thenReturn(device)
- whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(route)
+ whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(routingSession)
+
+ whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL)
+ whenever(controller.playbackInfo).thenReturn(playbackInfo)
// Create a media sesssion and notification for testing.
session = MediaSession(context, SESSION_KEY)
@@ -235,6 +240,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
reset(listener)
// WHEN media data is loaded with a different token
// AND that token results in a null route
+ whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
manager.onMediaDataLoaded(KEY, null, mediaData)
fakeBgExecutor.runAllReady()
@@ -394,9 +400,10 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
}
@Test
- fun deviceNameFromMR2RouteInfo() {
+ fun onMediaDataLoaded_withRemotePlaybackType_usesNonNullRoutingSessionName() {
// GIVEN that MR2Manager returns a valid routing session
- whenever(route.name).thenReturn(REMOTE_DEVICE_NAME)
+ whenever(routingSession.name).thenReturn(REMOTE_DEVICE_NAME)
+ whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
// WHEN a notification is added
manager.onMediaDataLoaded(KEY, null, mediaData)
fakeBgExecutor.runAllReady()
@@ -408,8 +415,9 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
}
@Test
- fun deviceDisabledWhenMR2ReturnsNullRouteInfo() {
+ fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
// GIVEN that MR2Manager returns null for routing session
+ whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
// WHEN a notification is added
manager.onMediaDataLoaded(KEY, null, mediaData)
@@ -422,13 +430,14 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
}
@Test
- fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceChanged() {
+ fun onSelectedDeviceStateChanged_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
// GIVEN a notif is added
manager.onMediaDataLoaded(KEY, null, mediaData)
fakeBgExecutor.runAllReady()
fakeFgExecutor.runAllReady()
reset(listener)
// AND MR2Manager returns null for routing session
+ whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
// WHEN the selected device changes state
val deviceCallback = captureCallback()
@@ -442,13 +451,14 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
}
@Test
- fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceListUpdate() {
+ fun onDeviceListUpdate_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
// GIVEN a notif is added
manager.onMediaDataLoaded(KEY, null, mediaData)
fakeBgExecutor.runAllReady()
fakeFgExecutor.runAllReady()
reset(listener)
// GIVEN that MR2Manager returns null for routing session
+ whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
// WHEN the selected device changes state
val deviceCallback = captureCallback()
@@ -461,15 +471,17 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
assertThat(data.name).isNull()
}
+ // With the flag enabled, MediaDeviceManager no longer gathers device name information directly.
+ @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
@Test
fun mr2ReturnsSystemRouteWithNullName_isPhone_usePhoneName() {
// When the routing session name is null, and is a system session for a PhoneMediaDevice
val phoneDevice = mock(PhoneMediaDevice::class.java)
whenever(phoneDevice.iconWithoutBackground).thenReturn(icon)
whenever(lmm.currentConnectedDevice).thenReturn(phoneDevice)
- whenever(route.isSystemSession).thenReturn(true)
+ whenever(routingSession.isSystemSession).thenReturn(true)
- whenever(route.name).thenReturn(null)
+ whenever(routingSession.name).thenReturn(null)
whenever(mr2.getSelectedRoutes(any())).thenReturn(listOf(selectedRoute))
whenever(selectedRoute.name).thenReturn(REMOTE_DEVICE_NAME)
whenever(selectedRoute.type).thenReturn(MediaRoute2Info.TYPE_BUILTIN_SPEAKER)
@@ -483,13 +495,15 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
assertThat(data.name).isEqualTo(PhoneMediaDevice.getMediaTransferThisDeviceName(context))
}
+ // With the flag enabled, MediaDeviceManager no longer gathers device name information directly.
+ @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
@Test
fun mr2ReturnsSystemRouteWithNullName_useSelectedRouteName() {
// When the routing session does not have a name, and is a system session
- whenever(route.name).thenReturn(null)
+ whenever(routingSession.name).thenReturn(null)
whenever(mr2.getSelectedRoutes(any())).thenReturn(listOf(selectedRoute))
whenever(selectedRoute.name).thenReturn(REMOTE_DEVICE_NAME)
- whenever(route.isSystemSession).thenReturn(true)
+ whenever(routingSession.isSystemSession).thenReturn(true)
manager.onMediaDataLoaded(KEY, null, mediaData)
fakeBgExecutor.runAllReady()
@@ -503,8 +517,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Test
fun mr2ReturnsNonSystemRouteWithNullName_useLocalDeviceName() {
// GIVEN that MR2Manager returns a routing session that does not have a name
- whenever(route.name).thenReturn(null)
- whenever(route.isSystemSession).thenReturn(false)
+ whenever(routingSession.name).thenReturn(null)
+ whenever(routingSession.isSystemSession).thenReturn(false)
// WHEN a notification is added
manager.onMediaDataLoaded(KEY, null, mediaData)
fakeBgExecutor.runAllReady()
@@ -534,7 +548,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
}
@Test
- fun audioInfoVolumeControlIdChanged() {
+ fun onAudioInfoChanged_withRemotePlaybackInfo_queriesRoutingSession() {
whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL)
whenever(playbackInfo.getVolumeControlId()).thenReturn(null)
whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo)
@@ -545,10 +559,11 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
reset(mr2)
// WHEN onAudioInfoChanged fires with a volume control id change
whenever(playbackInfo.getVolumeControlId()).thenReturn("placeholder id")
+ whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java)
verify(controller).registerCallback(captor.capture())
captor.value.onAudioInfoChanged(playbackInfo)
- // THEN the route is checked
+ // THEN the routing session is checked
verify(mr2).getRoutingSessionForMediaController(eq(controller))
}
@@ -654,7 +669,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
@Test
fun testRemotePlaybackDeviceOverride() {
- whenever(route.name).thenReturn(DEVICE_NAME)
+ whenever(routingSession.name).thenReturn(DEVICE_NAME)
val deviceData =
MediaDeviceData(false, null, REMOTE_DEVICE_NAME, null, showBroadcastButton = false)
val mediaDataWithDevice = mediaData.copy(device = deviceData)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/data/QSPreferencesRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/data/QSPreferencesRepositoryTest.kt
new file mode 100644
index 000000000000..b0aa6ddf44ce
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/data/QSPreferencesRepositoryTest.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.data
+
+import android.content.Context
+import android.content.SharedPreferences
+import android.content.pm.UserInfo
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.panels.data.repository.QSPreferencesRepository
+import com.android.systemui.qs.panels.data.repository.qsPreferencesRepository
+import com.android.systemui.settings.userFileManager
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.user.data.repository.userRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class QSPreferencesRepositoryTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val underTest = with(kosmos) { qsPreferencesRepository }
+
+ @Test
+ fun showLabels_updatesFromSharedPreferences() =
+ with(kosmos) {
+ testScope.runTest {
+ val latest by collectLastValue(underTest.showLabels)
+ assertThat(latest).isFalse()
+
+ setShowLabelsInSharedPreferences(true)
+ assertThat(latest).isTrue()
+
+ setShowLabelsInSharedPreferences(false)
+ assertThat(latest).isFalse()
+ }
+ }
+
+ @Test
+ fun showLabels_updatesFromUserChange() =
+ with(kosmos) {
+ testScope.runTest {
+ fakeUserRepository.setUserInfos(USERS)
+ val latest by collectLastValue(underTest.showLabels)
+
+ fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
+ setShowLabelsInSharedPreferences(false)
+
+ fakeUserRepository.setSelectedUserInfo(ANOTHER_USER)
+ setShowLabelsInSharedPreferences(true)
+
+ fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
+ assertThat(latest).isFalse()
+ }
+ }
+
+ @Test
+ fun setShowLabels_inSharedPreferences() {
+ underTest.setShowLabels(false)
+ assertThat(getShowLabelsFromSharedPreferences(true)).isFalse()
+
+ underTest.setShowLabels(true)
+ assertThat(getShowLabelsFromSharedPreferences(false)).isTrue()
+ }
+
+ @Test
+ fun setShowLabels_forDifferentUser() =
+ with(kosmos) {
+ testScope.runTest {
+ fakeUserRepository.setUserInfos(USERS)
+
+ fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
+ underTest.setShowLabels(false)
+ assertThat(getShowLabelsFromSharedPreferences(true)).isFalse()
+
+ fakeUserRepository.setSelectedUserInfo(ANOTHER_USER)
+ underTest.setShowLabels(true)
+ assertThat(getShowLabelsFromSharedPreferences(false)).isTrue()
+
+ fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
+ assertThat(getShowLabelsFromSharedPreferences(true)).isFalse()
+ }
+ }
+
+ private fun getSharedPreferences(): SharedPreferences =
+ with(kosmos) {
+ return userFileManager.getSharedPreferences(
+ QSPreferencesRepository.FILE_NAME,
+ Context.MODE_PRIVATE,
+ userRepository.getSelectedUserInfo().id,
+ )
+ }
+
+ private fun setShowLabelsInSharedPreferences(value: Boolean) {
+ getSharedPreferences().edit().putBoolean(ICON_LABELS_KEY, value).apply()
+ }
+
+ private fun getShowLabelsFromSharedPreferences(defaultValue: Boolean): Boolean {
+ return getSharedPreferences().getBoolean(ICON_LABELS_KEY, defaultValue)
+ }
+
+ companion object {
+ private const val ICON_LABELS_KEY = "show_icon_labels"
+ private const val PRIMARY_USER_ID = 0
+ private val PRIMARY_USER = UserInfo(PRIMARY_USER_ID, "user 0", UserInfo.FLAG_MAIN)
+ private const val ANOTHER_USER_ID = 1
+ private val ANOTHER_USER = UserInfo(ANOTHER_USER_ID, "user 1", UserInfo.FLAG_FULL)
+ private val USERS = listOf(PRIMARY_USER, ANOTHER_USER)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt
new file mode 100644
index 000000000000..9b08432e290f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.domain.interactor
+
+import android.content.pm.UserInfo
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class IconLabelVisibilityInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val underTest = with(kosmos) { iconLabelVisibilityInteractor }
+
+ @Before
+ fun setUp() {
+ with(kosmos) { fakeUserRepository.setUserInfos(USERS) }
+ }
+
+ @Test
+ fun changingShowLabels_receivesCorrectShowLabels() =
+ with(kosmos) {
+ testScope.runTest {
+ val showLabels by collectLastValue(underTest.showLabels)
+
+ underTest.setShowLabels(false)
+ runCurrent()
+ assertThat(showLabels).isFalse()
+
+ underTest.setShowLabels(true)
+ runCurrent()
+ assertThat(showLabels).isTrue()
+ }
+ }
+
+ @Test
+ fun changingUser_receivesCorrectShowLabels() =
+ with(kosmos) {
+ testScope.runTest {
+ val showLabels by collectLastValue(underTest.showLabels)
+
+ fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
+ underTest.setShowLabels(false)
+ runCurrent()
+ assertThat(showLabels).isFalse()
+
+ fakeUserRepository.setSelectedUserInfo(ANOTHER_USER)
+ underTest.setShowLabels(true)
+ runCurrent()
+ assertThat(showLabels).isTrue()
+
+ fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
+ runCurrent()
+ assertThat(showLabels).isFalse()
+ }
+ }
+
+ companion object {
+ private val PRIMARY_USER = UserInfo(0, "user 0", UserInfo.FLAG_MAIN)
+ private val ANOTHER_USER = UserInfo(1, "user 1", UserInfo.FLAG_FULL)
+ private val USERS = listOf(PRIMARY_USER, ANOTHER_USER)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
index 9798562ab5a8..29487cdace2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
@@ -1116,6 +1116,34 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase {
assertThat(mInternetDialogController.hasActiveSubIdOnDds()).isFalse();
}
+ @Test
+ public void hasActiveSubIdOnDds_activeDdsAndIsOnlyNonTerrestrialNetwork_returnFalse() {
+ when(SubscriptionManager.getDefaultDataSubscriptionId())
+ .thenReturn(SUB_ID);
+ SubscriptionInfo info = mock(SubscriptionInfo.class);
+ when(info.isEmbedded()).thenReturn(true);
+ when(info.isOnlyNonTerrestrialNetwork()).thenReturn(true);
+ when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(info);
+
+ mInternetDialogController.mOnSubscriptionsChangedListener.onSubscriptionsChanged();
+
+ assertFalse(mInternetDialogController.hasActiveSubIdOnDds());
+ }
+
+ @Test
+ public void hasActiveSubIdOnDds_activeDdsAndIsNotOnlyNonTerrestrialNetwork_returnTrue() {
+ when(SubscriptionManager.getDefaultDataSubscriptionId())
+ .thenReturn(SUB_ID);
+ SubscriptionInfo info = mock(SubscriptionInfo.class);
+ when(info.isEmbedded()).thenReturn(true);
+ when(info.isOnlyNonTerrestrialNetwork()).thenReturn(false);
+ when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(info);
+
+ mInternetDialogController.mOnSubscriptionsChangedListener.onSubscriptionsChanged();
+
+ assertTrue(mInternetDialogController.hasActiveSubIdOnDds());
+ }
+
private String getResourcesString(String name) {
return mContext.getResources().getString(getResourcesId(name));
}
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 f461e2f67d20..12f3ef3cf553 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
@@ -171,7 +171,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
// and then we would test both configurations, but currently they are all read
// in the constructor.
mSetFlagsRule.enableFlags(FLAG_NEW_AOD_TRANSITION);
- mFeatureFlags.setDefault(Flags.UNCLEARED_TRANSIENT_HUN_FIX);
// Inject dependencies before initializing the layout
mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
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 62804ed1412e..cb9790b2495a 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
@@ -138,6 +138,7 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor;
import com.android.systemui.shade.CameraLauncher;
+import com.android.systemui.shade.GlanceableHubContainerController;
import com.android.systemui.shade.NotificationPanelView;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowViewController;
@@ -337,6 +338,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private KeyboardShortcuts mKeyboardShortcuts;
@Mock private KeyboardShortcutListSearch mKeyboardShortcutListSearch;
@Mock private PackageManager mPackageManager;
+ @Mock private GlanceableHubContainerController mGlanceableHubContainerController;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
@@ -590,7 +592,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mUserTracker,
() -> mFingerprintManager,
mActivityStarter,
- mBrightnessMirrorShowingInteractor
+ mBrightnessMirrorShowingInteractor,
+ mGlanceableHubContainerController
);
mScreenLifecycle.addObserver(mCentralSurfaces.mScreenObserver);
mCentralSurfaces.initShadeVisibilityListener();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
index a6b40df8e81b..fb12897ead19 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
@@ -23,12 +23,14 @@ import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection
+import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
import com.android.systemui.keyguard.ui.viewmodel.keyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.keyguardSmartspaceViewModel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.util.mockito.mock
import java.util.Optional
+import org.mockito.Mockito.spy
val Kosmos.keyguardClockSection: ClockSection by
Kosmos.Fixture {
@@ -42,6 +44,9 @@ val Kosmos.keyguardClockSection: ClockSection by
)
}
+val Kosmos.keyguardSmartspaceSection: SmartspaceSection by
+ Kosmos.Fixture { mock<SmartspaceSection>() }
+
val Kosmos.defaultKeyguardBlueprint by
Kosmos.Fixture {
DefaultKeyguardBlueprint(
@@ -57,7 +62,7 @@ val Kosmos.defaultKeyguardBlueprint by
aodBurnInSection = mock(),
communalTutorialIndicatorSection = mock(),
clockSection = keyguardClockSection,
- smartspaceSection = mock(),
+ smartspaceSection = keyguardSmartspaceSection,
keyguardSliceViewSection = mock(),
udfpsAccessibilityOverlaySection = mock(),
accessibilityActionsSection = mock(),
@@ -80,7 +85,7 @@ val Kosmos.splitShadeBlueprint by
aodBurnInSection = mock(),
communalTutorialIndicatorSection = mock(),
clockSection = keyguardClockSection,
- smartspaceSection = mock(),
+ smartspaceSection = keyguardSmartspaceSection,
mediaSection = mock(),
accessibilityActionsSection = mock(),
)
@@ -88,13 +93,15 @@ val Kosmos.splitShadeBlueprint by
val Kosmos.keyguardBlueprintRepository by
Kosmos.Fixture {
- KeyguardBlueprintRepository(
- blueprints =
- setOf(
- defaultKeyguardBlueprint,
- splitShadeBlueprint,
- ),
- handler = fakeExecutorHandler,
- assert = mock(),
+ spy(
+ KeyguardBlueprintRepository(
+ blueprints =
+ setOf(
+ defaultKeyguardBlueprint,
+ splitShadeBlueprint,
+ ),
+ handler = fakeExecutorHandler,
+ assert = mock(),
+ )
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorKosmos.kt
index 5256ce4b9adf..4328ca153374 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorKosmos.kt
@@ -20,6 +20,8 @@ import android.content.applicationContext
import com.android.systemui.biometrics.domain.interactor.fingerprintPropertyInteractor
import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.keyguard.data.repository.keyguardBlueprintRepository
+import com.android.systemui.keyguard.data.repository.keyguardClockSection
+import com.android.systemui.keyguard.data.repository.keyguardSmartspaceSection
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.shade.domain.interactor.shadeInteractor
@@ -34,5 +36,7 @@ val Kosmos.keyguardBlueprintInteractor by
clockInteractor = keyguardClockInteractor,
configurationInteractor = configurationInteractor,
fingerprintPropertyInteractor = fingerprintPropertyInteractor,
+ clockSection = keyguardClockSection,
+ smartspaceSection = keyguardSmartspaceSection,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryKosmos.kt
new file mode 100644
index 000000000000..39ae5eb44c65
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryKosmos.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.settings.userFileManager
+import com.android.systemui.user.data.repository.userRepository
+
+val Kosmos.qsPreferencesRepository by
+ Kosmos.Fixture { QSPreferencesRepository(userFileManager, userRepository, testDispatcher) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorKosmos.kt
index 7b9e4a17e998..954084b874a0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorKosmos.kt
@@ -19,12 +19,11 @@ package com.android.systemui.qs.panels.domain.interactor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.log.core.FakeLogBuffer
-import com.android.systemui.qs.panels.data.repository.iconLabelVisibilityRepository
val Kosmos.iconLabelVisibilityInteractor by
Kosmos.Fixture {
IconLabelVisibilityInteractor(
- iconLabelVisibilityRepository,
+ qsPreferencesInteractor,
FakeLogBuffer.Factory.create(),
applicationCoroutineScope
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/QSPreferencesInteractorKosmos.kt
index 277dbb7016ad..eb83e325d79b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/QSPreferencesInteractorKosmos.kt
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.qs.panels.domain.interactor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.panels.data.repository.qsPreferencesRepository
-val Kosmos.iconLabelVisibilityRepository by Kosmos.Fixture { IconLabelVisibilityRepository() }
+val Kosmos.qsPreferencesInteractor by
+ Kosmos.Fixture { QSPreferencesInteractor(qsPreferencesRepository) }
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index dc1cfb92c3b8..ddccb3731cc1 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -4008,20 +4008,10 @@ public class UserBackupManagerService {
}
private PackageInfo getPackageInfoForBMMLogging(String packageName) {
- try {
- return mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId);
- } catch (NameNotFoundException e) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Asked to get PackageInfo for BMM logging of nonexistent pkg "
- + packageName));
-
- PackageInfo packageInfo = new PackageInfo();
- packageInfo.packageName = packageName;
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = packageName;
- return packageInfo;
- }
+ return packageInfo;
}
/** Hand off a restore session. */
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 4dd3a8f67b0d..b35959f1a6e8 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3980,7 +3980,7 @@ class StorageManagerService extends IStorageManager.Stub
if (resUuids.contains(rec.fsUuid)) continue;
// Treat as recent if mounted within the last week
- if (rec.lastSeenMillis > 0 && rec.lastSeenMillis < lastWeek) {
+ if (rec.lastSeenMillis > 0 && rec.lastSeenMillis >= lastWeek) {
final StorageVolume userVol = rec.buildStorageVolume(mContext);
res.add(userVol);
resUuids.add(userVol.getUuid());
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index baa7f2f389e8..2addf6f9ec96 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1901,7 +1901,6 @@ public class AudioService extends IAudioService.Stub
}
mSpatializerHelper.reset(/* featureEnabled */ mHasSpatializerEffect);
- mSoundDoseHelper.reset();
// Restore rotation information.
if (mMonitorRotation) {
@@ -1912,6 +1911,8 @@ public class AudioService extends IAudioService.Stub
// indicate the end of reconfiguration phase to audio HAL
AudioSystem.setParameters("restarting=false");
+ mSoundDoseHelper.reset(/*resetISoundDose=*/true);
+
sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_SERVER_STATE,
SENDMSG_QUEUE, 1, 0, null, 0);
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 9610034caf01..e28ae952e65a 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -856,11 +856,12 @@ public class SoundDoseHelper {
pw.println();
}
- /*package*/void reset() {
+ /*package*/void reset(boolean resetISoundDose) {
Log.d(TAG, "Reset the sound dose helper");
- mSoundDose.compareAndExchange(/*expectedValue=*/null,
- AudioSystem.getSoundDoseInterface(mSoundDoseCallback));
+ if (resetISoundDose) {
+ mSoundDose.set(AudioSystem.getSoundDoseInterface(mSoundDoseCallback));
+ }
synchronized (mCsdStateLock) {
try {
@@ -972,7 +973,7 @@ public class SoundDoseHelper {
}
}
- reset();
+ reset(/*resetISoundDose=*/false);
}
private void onConfigureSafeMedia(boolean force, String caller) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index d876a381ca7a..3c3bdd5b69f6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -269,8 +269,7 @@ public class HdmiCecMessageValidator {
addValidationInfo(Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR,
oneByteValidator, ADDR_NOT_UNREGISTERED, ADDR_DIRECT);
addValidationInfo(Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE,
- new MinimumOneByteRangeValidator(0x00, 0x01),
- ADDR_NOT_UNREGISTERED, ADDR_ALL);
+ new SingleByteRangeValidator(0x00, 0x01), ADDR_AUDIO_SYSTEM, ADDR_ALL);
addValidationInfo(Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS,
new SingleByteRangeValidator(0x00, 0x01), ADDR_NOT_UNREGISTERED,
ADDR_DIRECT);
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index 5646e1b9a9ef..0688fbf358ad 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -175,14 +175,15 @@ final class HdmiUtils {
*
* @param logicalAddress the logical address to verify
* @param deviceType the device type to check
- * @throws IllegalArgumentException
*/
- static void verifyAddressType(int logicalAddress, int deviceType) {
+ static boolean verifyAddressType(int logicalAddress, int deviceType) {
List<Integer> actualDeviceTypes = getTypeFromAddress(logicalAddress);
if (!actualDeviceTypes.contains(deviceType)) {
- throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType
- + ", Actual:" + actualDeviceTypes);
+ Slog.w(TAG,"Device type mismatch:[Expected:" + deviceType
+ + ", Actual:" + actualDeviceTypes + "]");
+ return false;
}
+ return true;
}
/**
diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java
index 54c8c00b8889..58e146ecaa78 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java
@@ -19,6 +19,7 @@ package com.android.server.hdmi;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
+import android.util.Slog;
/**
* Base feature action class for &lt;Request ARC Initiation&gt;/&lt;Request ARC Termination&gt;.
@@ -38,13 +39,14 @@ abstract class RequestArcAction extends HdmiCecFeatureAction {
* @param source {@link HdmiCecLocalDevice} instance
* @param avrAddress address of AV receiver. It should be AUDIO_SYSTEM type
* @param callback callback to inform about the status of the action
- * @throws IllegalArgumentException if device type of sourceAddress and avrAddress
- * is invalid
*/
RequestArcAction(HdmiCecLocalDevice source, int avrAddress, IHdmiControlCallback callback) {
super(source, callback);
- HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV);
- HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ if (!HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV) ||
+ !HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)) {
+ Slog.w(TAG, "Device type mismatch, stop the action.");
+ finish();
+ }
mAvrAddress = avrAddress;
}
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index 32e274ece9ab..5ab22e1dcd61 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -47,8 +47,11 @@ final class SetArcTransmissionStateAction extends HdmiCecFeatureAction {
SetArcTransmissionStateAction(HdmiCecLocalDevice source, int avrAddress,
boolean enabled) {
super(source);
- HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV);
- HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ if (!HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV) ||
+ !HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)) {
+ Slog.w(TAG, "Device type mismatch, stop the action.");
+ finish();
+ }
mAvrAddress = avrAddress;
mEnabled = enabled;
}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index e96963b9ae3f..f14cda1e6509 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -20,6 +20,7 @@ import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.util.Slog;
import java.util.List;
@@ -56,12 +57,14 @@ abstract class SystemAudioAction extends HdmiCecFeatureAction {
* @param avrAddress logical address of AVR device
* @param targetStatus Whether to enable the system audio mode or not
* @param callback callback interface to be notified when it's done
- * @throws IllegalArgumentException if device type of sourceAddress and avrAddress is invalid
*/
SystemAudioAction(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus,
IHdmiControlCallback callback) {
super(source, callback);
- HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ if (!HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)) {
+ Slog.w(TAG, "Device type mismatch, stop the action.");
+ finish();
+ }
mAvrLogicalAddress = avrAddress;
mTargetAudioStatus = targetStatus;
}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
index 99148c4ea114..08a938731dae 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
@@ -19,12 +19,14 @@ package com.android.server.hdmi;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
+import android.util.Slog;
/**
* Feature action that handles System Audio initiated by AVR devices.
*/
// Seq #33
final class SystemAudioActionFromAvr extends SystemAudioAction {
+ private static final String TAG = "SystemAudioActionFromAvr";
/**
* Constructor
*
@@ -32,12 +34,14 @@ final class SystemAudioActionFromAvr extends SystemAudioAction {
* @param avrAddress logical address of AVR device
* @param targetStatus Whether to enable the system audio mode or not
* @param callback callback interface to be notified when it's done
- * @throws IllegalArgumentException if device type of tvAddress and avrAddress is invalid
*/
SystemAudioActionFromAvr(HdmiCecLocalDevice source, int avrAddress,
boolean targetStatus, IHdmiControlCallback callback) {
super(source, avrAddress, targetStatus, callback);
- HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV);
+ if (!HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV)) {
+ Slog.w(TAG, "Device type mismatch, stop the action.");
+ finish();
+ }
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
index 5c0c272f59e0..675aa3171fbd 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
@@ -18,13 +18,14 @@ package com.android.server.hdmi;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
+import android.util.Slog;
/**
* Feature action that handles System Audio initiated by TV devices.
*/
final class SystemAudioActionFromTv extends SystemAudioAction {
-
+ private static final String TAG = "SystemAudioActionFromTv";
/**
* Constructor
*
@@ -32,12 +33,14 @@ final class SystemAudioActionFromTv extends SystemAudioAction {
* @param avrAddress logical address of AVR device
* @param targetStatus Whether to enable the system audio mode or not
* @param callback callback interface to be notified when it's done
- * @throws IllegalArgumentException if device type of tvAddress is invalid
*/
SystemAudioActionFromTv(HdmiCecLocalDevice sourceAddress, int avrAddress,
boolean targetStatus, IHdmiControlCallback callback) {
super(sourceAddress, avrAddress, targetStatus, callback);
- HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV);
+ if (!HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV)) {
+ Slog.w(TAG, "Device type mismatch, stop the action.");
+ finish();
+ }
}
@Override
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 1bc2a5eb1351..363684f618cc 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.content.ComponentName;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
+import android.media.MediaRouter2;
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.os.Bundle;
@@ -172,4 +173,59 @@ abstract class MediaRoute2Provider {
@NonNull RoutingSessionInfo sessionInfo);
void onRequestFailed(@NonNull MediaRoute2Provider provider, long requestId, int reason);
}
+
+ /**
+ * Holds session creation or transfer initiation information for a transfer in flight.
+ *
+ * <p>The initiator app is typically also the {@link RoutingSessionInfo#getClientPackageName()
+ * client app}, with the exception of the {@link MediaRouter2#getSystemController() system
+ * routing session} which is exceptional in that it's shared among all apps.
+ *
+ * <p>For the system routing session, the initiator app is the one that programmatically
+ * triggered the transfer (for example, via {@link MediaRouter2#transferTo}), or the target app
+ * of the proxy router that did the transfer.
+ *
+ * @see MediaRouter2.RoutingController#wasTransferInitiatedBySelf()
+ * @see RoutingSessionInfo#getTransferInitiatorPackageName()
+ * @see RoutingSessionInfo#getTransferInitiatorUserHandle()
+ */
+ protected static class SessionCreationOrTransferRequest {
+
+ /**
+ * The id of the request, or {@link
+ * android.media.MediaRoute2ProviderService#REQUEST_ID_NONE} if unknown.
+ */
+ public final long mRequestId;
+
+ /** The {@link MediaRoute2Info#getId() id} of the target route. */
+ @NonNull public final String mTargetRouteId;
+
+ @RoutingSessionInfo.TransferReason public final int mTransferReason;
+
+ /** The {@link android.os.UserHandle} on which the initiator app is running. */
+ @NonNull public final UserHandle mTransferInitiatorUserHandle;
+
+ @NonNull public final String mTransferInitiatorPackageName;
+
+ SessionCreationOrTransferRequest(
+ long requestId,
+ @NonNull String routeId,
+ @RoutingSessionInfo.TransferReason int transferReason,
+ @NonNull UserHandle transferInitiatorUserHandle,
+ @NonNull String transferInitiatorPackageName) {
+ mRequestId = requestId;
+ mTargetRouteId = routeId;
+ mTransferReason = transferReason;
+ mTransferInitiatorUserHandle = transferInitiatorUserHandle;
+ mTransferInitiatorPackageName = transferInitiatorPackageName;
+ }
+
+ public boolean isTargetRoute(@Nullable MediaRoute2Info route2Info) {
+ return route2Info != null && mTargetRouteId.equals(route2Info.getId());
+ }
+
+ public boolean isTargetRouteIdInList(@NonNull List<String> routesList) {
+ return routesList.stream().anyMatch(mTargetRouteId::equals);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 6ce3ab4b2d65..76930a003e46 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -79,12 +79,15 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
new AudioManagerBroadcastReceiver();
private final Object mRequestLock = new Object();
+
@GuardedBy("mRequestLock")
- private volatile SessionCreationRequest mPendingSessionCreationRequest;
+ private volatile SessionCreationOrTransferRequest mPendingSessionCreationOrTransferRequest;
private final Object mTransferLock = new Object();
+
@GuardedBy("mTransferLock")
- @Nullable private volatile SessionCreationRequest mPendingTransferRequest;
+ @Nullable
+ private volatile SessionCreationOrTransferRequest mPendingTransferRequest;
SystemMediaRoute2Provider(Context context, UserHandle user, Looper looper) {
super(COMPONENT_NAME);
@@ -180,12 +183,14 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
synchronized (mRequestLock) {
// Handle the previous request as a failure if exists.
- if (mPendingSessionCreationRequest != null) {
- mCallback.onRequestFailed(this, mPendingSessionCreationRequest.mRequestId,
+ if (mPendingSessionCreationOrTransferRequest != null) {
+ mCallback.onRequestFailed(
+ /* provider= */ this,
+ mPendingSessionCreationOrTransferRequest.mRequestId,
MediaRoute2ProviderService.REASON_UNKNOWN_ERROR);
}
- mPendingSessionCreationRequest =
- new SessionCreationRequest(
+ mPendingSessionCreationOrTransferRequest =
+ new SessionCreationOrTransferRequest(
requestId,
routeId,
RoutingSessionInfo.TRANSFER_REASON_FALLBACK,
@@ -247,7 +252,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
synchronized (mTransferLock) {
mPendingTransferRequest =
- new SessionCreationRequest(
+ new SessionCreationOrTransferRequest(
requestId,
routeId,
transferReason,
@@ -438,7 +443,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
boolean isTransferringToTheSelectedRoute =
mPendingTransferRequest.isTargetRoute(selectedRoute);
boolean canBePotentiallyTransferred =
- mPendingTransferRequest.isInsideOfRoutesList(transferableRoutes);
+ mPendingTransferRequest.isTargetRouteIdInList(transferableRoutes);
if (isTransferringToTheSelectedRoute) {
transferReason = mPendingTransferRequest.mTransferReason;
@@ -492,20 +497,20 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
@GuardedBy("mRequestLock")
private void reportPendingSessionRequestResultLockedIfNeeded(
RoutingSessionInfo newSessionInfo) {
- if (mPendingSessionCreationRequest == null) {
+ if (mPendingSessionCreationOrTransferRequest == null) {
// No pending request, nothing to report.
return;
}
- long pendingRequestId = mPendingSessionCreationRequest.mRequestId;
- if (TextUtils.equals(mSelectedRouteId, mPendingSessionCreationRequest.mRouteId)) {
+ long pendingRequestId = mPendingSessionCreationOrTransferRequest.mRequestId;
+ if (mPendingSessionCreationOrTransferRequest.mTargetRouteId.equals(mSelectedRouteId)) {
if (DEBUG) {
Slog.w(
TAG,
"Session creation success to route "
- + mPendingSessionCreationRequest.mRouteId);
+ + mPendingSessionCreationOrTransferRequest.mTargetRouteId);
}
- mPendingSessionCreationRequest = null;
+ mPendingSessionCreationOrTransferRequest = null;
mCallback.onSessionCreated(this, pendingRequestId, newSessionInfo);
} else {
boolean isRequestedRouteConnectedBtRoute = isRequestedRouteConnectedBtRoute();
@@ -515,16 +520,16 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
Slog.w(
TAG,
"Session creation failed to route "
- + mPendingSessionCreationRequest.mRouteId);
+ + mPendingSessionCreationOrTransferRequest.mTargetRouteId);
}
- mPendingSessionCreationRequest = null;
+ mPendingSessionCreationOrTransferRequest = null;
mCallback.onRequestFailed(
this, pendingRequestId, MediaRoute2ProviderService.REASON_UNKNOWN_ERROR);
} else if (DEBUG) {
Slog.w(
TAG,
"Session creation waiting state to route "
- + mPendingSessionCreationRequest.mRouteId);
+ + mPendingSessionCreationOrTransferRequest.mTargetRouteId);
}
}
}
@@ -535,7 +540,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
// where two BT routes are active so the transferable routes list is empty.
// See b/307723189 for context
for (MediaRoute2Info btRoute : mBluetoothRouteController.getAllBluetoothRoutes()) {
- if (TextUtils.equals(btRoute.getId(), mPendingSessionCreationRequest.mRouteId)) {
+ if (TextUtils.equals(
+ btRoute.getId(), mPendingSessionCreationOrTransferRequest.mTargetRouteId)) {
return true;
}
}
@@ -585,51 +591,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
mBluetoothRouteController.getClass().getSimpleName());
}
- private static class SessionCreationRequest {
- private final long mRequestId;
- @NonNull private final String mRouteId;
-
- @RoutingSessionInfo.TransferReason private final int mTransferReason;
-
- @NonNull private final UserHandle mTransferInitiatorUserHandle;
- @NonNull private final String mTransferInitiatorPackageName;
-
- SessionCreationRequest(
- long requestId,
- @NonNull String routeId,
- @RoutingSessionInfo.TransferReason int transferReason,
- @NonNull UserHandle transferInitiatorUserHandle,
- @NonNull String transferInitiatorPackageName) {
- mRequestId = requestId;
- mRouteId = routeId;
- mTransferReason = transferReason;
- mTransferInitiatorUserHandle = transferInitiatorUserHandle;
- mTransferInitiatorPackageName = transferInitiatorPackageName;
- }
-
- private boolean isTargetRoute(@Nullable MediaRoute2Info route2Info) {
- if (route2Info == null) {
- return false;
- }
-
- return isTargetRoute(route2Info.getId());
- }
-
- private boolean isTargetRoute(@Nullable String routeId) {
- return mRouteId.equals(routeId);
- }
-
- private boolean isInsideOfRoutesList(@NonNull List<String> routesList) {
- for (String routeId : routesList) {
- if (isTargetRoute(routeId)) {
- return true;
- }
- }
-
- return false;
- }
- }
-
void updateVolume() {
int devices = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
int volume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 57f6d2789dc5..a90473865ce5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -5153,6 +5153,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
// Okay to proceed
synchronized (mLock) {
+ assertCallerIsOwnerOrRoot();
+ assertPreparedAndNotSealedLocked("setPreVerifiedDomains");
mPreVerifiedDomains = preVerifiedDomains;
}
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 108c7fb5c3b5..f91ef1d41a0c 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -1099,10 +1099,6 @@ class BackNavigationController {
}
void finishPresentAnimations() {
- if (!mComposed) {
- return;
- }
-
if (mCloseAdaptor != null) {
mCloseAdaptor.mTarget.cancelAnimation();
mCloseAdaptor = null;
@@ -1131,8 +1127,10 @@ class BackNavigationController {
}
void clearBackAnimateTarget() {
- finishPresentAnimations();
- mComposed = false;
+ if (mComposed) {
+ mComposed = false;
+ finishPresentAnimations();
+ }
mWaitTransition = false;
mStartingSurfaceTargetMatch = false;
mSwitchType = UNKNOWN;
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
index ad68de84eace..9d8d520fcb53 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
@@ -15,6 +15,8 @@
*/
package com.android.server.power.batterysaver;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.NULL_DEFAULT;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -35,12 +37,14 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.os.PowerManager;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings.Global;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
@@ -65,6 +69,9 @@ public class BatterySaverStateMachineTest {
private DevicePersistedState mPersistedState;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(NULL_DEFAULT);
+
private class DevicePersistedState {
// Current battery level.
public int batteryLevel = 100;
@@ -171,6 +178,11 @@ public class BatterySaverStateMachineTest {
void triggerDynamicModeNotification() {
// Do nothing
}
+
+ @Override
+ void triggerDynamicModeNotificationV2() {
+ // Do nothing
+ }
}
@Before
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index 98e119cf0dad..473d1dc22d7a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -139,12 +139,13 @@ public class HdmiCecMessageValidatorTest {
@Test
public void isValid_setSystemAudioMode() {
- assertMessageValidity("40:72:00").isEqualTo(OK);
- assertMessageValidity("4F:72:01:03").isEqualTo(OK);
+ assertMessageValidity("50:72:00").isEqualTo(OK);
+ assertMessageValidity("50:72:01").isEqualTo(OK);
+ assertMessageValidity("5F:72:01:03").isEqualTo(ERROR_PARAMETER_LONG);
- assertMessageValidity("F0:72").isEqualTo(ERROR_SOURCE);
- assertMessageValidity("40:72").isEqualTo(ERROR_PARAMETER_SHORT);
- assertMessageValidity("40:72:02").isEqualTo(ERROR_PARAMETER);
+ assertMessageValidity("40:72:00").isEqualTo(ERROR_SOURCE);
+ assertMessageValidity("50:72").isEqualTo(ERROR_PARAMETER_SHORT);
+ assertMessageValidity("50:72:02").isEqualTo(ERROR_PARAMETER);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
index c89c32a03553..74583dd619c7 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
@@ -709,4 +709,18 @@ public class HdmiUtilsTest {
assertThat(HdmiUtils.buildMessage("40:32:65:6E:67").getParams()).isEqualTo(
new byte[]{0x65, 0x6E, 0x67});
}
+
+ @Test
+ public void testVerifyAddressType() {
+ assertTrue(HdmiUtils.verifyAddressType(Constants.ADDR_TV,
+ HdmiDeviceInfo.DEVICE_TV));
+ assertTrue(HdmiUtils.verifyAddressType(Constants.ADDR_AUDIO_SYSTEM,
+ HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM));
+ assertTrue(HdmiUtils.verifyAddressType(Constants.ADDR_PLAYBACK_1,
+ HdmiDeviceInfo.DEVICE_PLAYBACK));
+ assertFalse(HdmiUtils.verifyAddressType(Constants.ADDR_SPECIFIC_USE,
+ HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM));
+ assertFalse(HdmiUtils.verifyAddressType(Constants.ADDR_PLAYBACK_2,
+ HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR));
+ }
}