summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/servertransaction/LaunchActivityItem.java4
-rw-r--r--core/java/android/app/servertransaction/WindowStateInsetsControlChangeItem.java38
-rw-r--r--core/java/android/app/servertransaction/WindowStateResizeItem.java35
-rw-r--r--core/java/android/app/servertransaction/WindowStateTransactionItem.java118
-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/KeyboardShortcutInfo.java15
-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.java113
-rw-r--r--core/java/android/window/DisplayWindowPolicyController.java4
-rw-r--r--core/java/android/window/SnapshotDrawerUtils.java16
-rw-r--r--core/java/android/window/flags/large_screen_experiences_app_compat.aconfig7
-rw-r--r--core/java/android/window/flags/windowing_frontend.aconfig11
-rw-r--r--core/java/android/window/flags/windowing_sdk.aconfig11
-rw-r--r--core/java/com/android/internal/accessibility/AccessibilityShortcutController.java2
-rw-r--r--core/java/com/android/internal/accessibility/common/ShortcutConstants.java6
-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/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java11
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java88
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java24
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java133
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java4
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java13
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java8
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java25
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java119
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java33
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java24
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java151
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java1
-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/DesktopModeVisualIndicator.java1
-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/pip/PipTransition.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java3
-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/accessibility.aconfig10
-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/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt3
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationHeadsUpHeight.kt87
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt9
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt16
-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/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt46
-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/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableListTest.kt5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java57
-rw-r--r--packages/SystemUI/res/drawable/ic_widgets.xml27
-rw-r--r--packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml31
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_open_hub_chip.xml26
-rw-r--r--packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml37
-rw-r--r--packages/SystemUI/res/layout/hearing_tool_item.xml53
-rw-r--r--packages/SystemUI/res/values/config.xml14
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-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/accessibility/data/repository/AccessibilityQsShortcutsRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java80
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java129
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt46
-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/pipeline/domain/autoaddable/A11yShortcutAutoAddableList.kt5
-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/KeyboardShortcutListSearch.java102
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt4
-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/phone/HeadsUpManagerPhone.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java22
-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/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java75
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java134
-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/KeyboardShortcutListSearchTest.java68
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java87
-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.java333
-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/inputmethod/InputMethodManagerService.java24
-rw-r--r--services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java2
-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/AbsAppSnapshotController.java12
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java14
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java12
-rw-r--r--services/core/java/com/android/server/wm/InputManagerCallback.java1
-rw-r--r--services/core/java/com/android/server/wm/StartingSurfaceController.java14
-rw-r--r--services/core/java/com/android/server/wm/Task.java17
-rw-r--r--services/core/java/com/android/server/wm/Transition.java3
-rw-r--r--services/core/java/com/android/server/wm/TrustedOverlayHost.java1
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java15
-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
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java21
187 files changed, 4291 insertions, 1243 deletions
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 7dcbebaeba0b..7819e1ef94c6 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -104,8 +104,8 @@ public class LaunchActivityItem extends ClientTransactionItem {
public void execute(@NonNull ClientTransactionHandler client,
@NonNull PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
- ActivityClientRecord r = new ActivityClientRecord(mActivityToken, mIntent, mIdent, mInfo,
- mOverrideConfig, mReferrer, mVoiceInteractor, mState, mPersistentState,
+ final ActivityClientRecord r = new ActivityClientRecord(mActivityToken, mIntent, mIdent,
+ mInfo, mOverrideConfig, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mSceneTransitionInfo, mIsForward,
mProfilerInfo, client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble,
mTaskFragmentToken, mInitialCallerInfoAccessToken, mActivityWindowInfo);
diff --git a/core/java/android/app/servertransaction/WindowStateInsetsControlChangeItem.java b/core/java/android/app/servertransaction/WindowStateInsetsControlChangeItem.java
index ee04f8cbe3c7..1c8e497edd0a 100644
--- a/core/java/android/app/servertransaction/WindowStateInsetsControlChangeItem.java
+++ b/core/java/android/app/servertransaction/WindowStateInsetsControlChangeItem.java
@@ -16,8 +16,6 @@
package android.app.servertransaction;
-import static java.util.Objects.requireNonNull;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ClientTransactionHandler;
@@ -35,23 +33,19 @@ import java.util.Objects;
* Message to deliver window insets control change info.
* @hide
*/
-public class WindowStateInsetsControlChangeItem extends ClientTransactionItem {
+public class WindowStateInsetsControlChangeItem extends WindowStateTransactionItem {
private static final String TAG = "WindowStateInsetsControlChangeItem";
- private IWindow mWindow;
private InsetsState mInsetsState;
private InsetsSourceControl.Array mActiveControls;
@Override
- public void execute(@NonNull ClientTransactionHandler client,
+ public void execute(@NonNull ClientTransactionHandler client, @NonNull IWindow window,
@NonNull PendingTransactionActions pendingActions) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "windowInsetsControlChanged");
- if (mWindow instanceof InsetsControlChangeListener listener) {
- listener.onExecutingWindowStateInsetsControlChangeItem();
- }
try {
- mWindow.insetsControlChanged(mInsetsState, mActiveControls);
+ window.insetsControlChanged(mInsetsState, mActiveControls);
} catch (RemoteException e) {
// Should be a local call.
// An exception could happen if the process is restarted. It is safe to ignore since
@@ -73,7 +67,7 @@ public class WindowStateInsetsControlChangeItem extends ClientTransactionItem {
if (instance == null) {
instance = new WindowStateInsetsControlChangeItem();
}
- instance.mWindow = requireNonNull(window);
+ instance.setWindow(window);
instance.mInsetsState = new InsetsState(insetsState, true /* copySources */);
instance.mActiveControls = new InsetsSourceControl.Array(activeControls);
@@ -82,7 +76,7 @@ public class WindowStateInsetsControlChangeItem extends ClientTransactionItem {
@Override
public void recycle() {
- mWindow = null;
+ super.recycle();
mInsetsState = null;
mActiveControls = null;
ObjectPool.recycle(this);
@@ -93,14 +87,14 @@ public class WindowStateInsetsControlChangeItem extends ClientTransactionItem {
/** Writes to Parcel. */
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeStrongBinder(mWindow.asBinder());
+ super.writeToParcel(dest, flags);
dest.writeTypedObject(mInsetsState, flags);
dest.writeTypedObject(mActiveControls, flags);
}
/** Reads from Parcel. */
private WindowStateInsetsControlChangeItem(@NonNull Parcel in) {
- mWindow = IWindow.Stub.asInterface(in.readStrongBinder());
+ super(in);
mInsetsState = in.readTypedObject(InsetsState.CREATOR);
mActiveControls = in.readTypedObject(InsetsSourceControl.Array.CREATOR);
@@ -122,19 +116,18 @@ public class WindowStateInsetsControlChangeItem extends ClientTransactionItem {
if (this == o) {
return true;
}
- if (o == null || getClass() != o.getClass()) {
+ if (!super.equals(o)) {
return false;
}
final WindowStateInsetsControlChangeItem other = (WindowStateInsetsControlChangeItem) o;
- return Objects.equals(mWindow, other.mWindow)
- && Objects.equals(mInsetsState, other.mInsetsState)
+ return Objects.equals(mInsetsState, other.mInsetsState)
&& Objects.equals(mActiveControls, other.mActiveControls);
}
@Override
public int hashCode() {
int result = 17;
- result = 31 * result + Objects.hashCode(mWindow);
+ result = 31 * result + super.hashCode();
result = 31 * result + Objects.hashCode(mInsetsState);
result = 31 * result + Objects.hashCode(mActiveControls);
return result;
@@ -142,15 +135,6 @@ public class WindowStateInsetsControlChangeItem extends ClientTransactionItem {
@Override
public String toString() {
- return "WindowStateInsetsControlChangeItem{window=" + mWindow + "}";
- }
-
- /** The interface for IWindow to perform insets control change directly if possible. */
- public interface InsetsControlChangeListener {
- /**
- * Notifies that IWindow#insetsControlChanged is going to be called from
- * WindowStateInsetsControlChangeItem.
- */
- void onExecutingWindowStateInsetsControlChangeItem();
+ return "WindowStateInsetsControlChangeItem{" + super.toString() + "}";
}
}
diff --git a/core/java/android/app/servertransaction/WindowStateResizeItem.java b/core/java/android/app/servertransaction/WindowStateResizeItem.java
index da99096f022a..3c1fa4b83340 100644
--- a/core/java/android/app/servertransaction/WindowStateResizeItem.java
+++ b/core/java/android/app/servertransaction/WindowStateResizeItem.java
@@ -18,8 +18,6 @@ package android.app.servertransaction;
import static android.view.Display.INVALID_DISPLAY;
-import static java.util.Objects.requireNonNull;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ClientTransactionHandler;
@@ -39,11 +37,10 @@ import java.util.Objects;
* Message to deliver window resize info.
* @hide
*/
-public class WindowStateResizeItem extends ClientTransactionItem {
+public class WindowStateResizeItem extends WindowStateTransactionItem {
private static final String TAG = "WindowStateResizeItem";
- private IWindow mWindow;
private ClientWindowFrames mFrames;
private boolean mReportDraw;
private MergedConfiguration mConfiguration;
@@ -59,15 +56,12 @@ public class WindowStateResizeItem extends ClientTransactionItem {
private ActivityWindowInfo mActivityWindowInfo;
@Override
- public void execute(@NonNull ClientTransactionHandler client,
+ public void execute(@NonNull ClientTransactionHandler client, @NonNull IWindow window,
@NonNull PendingTransactionActions pendingActions) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
mReportDraw ? "windowResizedReport" : "windowResized");
- if (mWindow instanceof ResizeListener listener) {
- listener.onExecutingWindowStateResizeItem();
- }
try {
- mWindow.resized(mFrames, mReportDraw, mConfiguration, mInsetsState, mForceLayout,
+ window.resized(mFrames, mReportDraw, mConfiguration, mInsetsState, mForceLayout,
mAlwaysConsumeSystemBars, mDisplayId, mSyncSeqId, mDragResizing,
mActivityWindowInfo);
} catch (RemoteException e) {
@@ -94,7 +88,7 @@ public class WindowStateResizeItem extends ClientTransactionItem {
if (instance == null) {
instance = new WindowStateResizeItem();
}
- instance.mWindow = requireNonNull(window);
+ instance.setWindow(window);
instance.mFrames = new ClientWindowFrames(frames);
instance.mReportDraw = reportDraw;
instance.mConfiguration = new MergedConfiguration(configuration);
@@ -113,7 +107,7 @@ public class WindowStateResizeItem extends ClientTransactionItem {
@Override
public void recycle() {
- mWindow = null;
+ super.recycle();
mFrames = null;
mReportDraw = false;
mConfiguration = null;
@@ -132,7 +126,7 @@ public class WindowStateResizeItem extends ClientTransactionItem {
/** Writes to Parcel. */
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeStrongBinder(mWindow.asBinder());
+ super.writeToParcel(dest, flags);
dest.writeTypedObject(mFrames, flags);
dest.writeBoolean(mReportDraw);
dest.writeTypedObject(mConfiguration, flags);
@@ -147,7 +141,7 @@ public class WindowStateResizeItem extends ClientTransactionItem {
/** Reads from Parcel. */
private WindowStateResizeItem(@NonNull Parcel in) {
- mWindow = IWindow.Stub.asInterface(in.readStrongBinder());
+ super(in);
mFrames = in.readTypedObject(ClientWindowFrames.CREATOR);
mReportDraw = in.readBoolean();
mConfiguration = in.readTypedObject(MergedConfiguration.CREATOR);
@@ -175,12 +169,11 @@ public class WindowStateResizeItem extends ClientTransactionItem {
if (this == o) {
return true;
}
- if (o == null || getClass() != o.getClass()) {
+ if (!super.equals(o)) {
return false;
}
final WindowStateResizeItem other = (WindowStateResizeItem) o;
- return Objects.equals(mWindow, other.mWindow)
- && Objects.equals(mFrames, other.mFrames)
+ return Objects.equals(mFrames, other.mFrames)
&& mReportDraw == other.mReportDraw
&& Objects.equals(mConfiguration, other.mConfiguration)
&& Objects.equals(mInsetsState, other.mInsetsState)
@@ -195,7 +188,7 @@ public class WindowStateResizeItem extends ClientTransactionItem {
@Override
public int hashCode() {
int result = 17;
- result = 31 * result + Objects.hashCode(mWindow);
+ result = 31 * result + super.hashCode();
result = 31 * result + Objects.hashCode(mFrames);
result = 31 * result + (mReportDraw ? 1 : 0);
result = 31 * result + Objects.hashCode(mConfiguration);
@@ -211,16 +204,10 @@ public class WindowStateResizeItem extends ClientTransactionItem {
@Override
public String toString() {
- return "WindowStateResizeItem{window=" + mWindow
+ return "WindowStateResizeItem{" + super.toString()
+ ", reportDrawn=" + mReportDraw
+ ", configuration=" + mConfiguration
+ ", activityWindowInfo=" + mActivityWindowInfo
+ "}";
}
-
- /** The interface for IWindow to perform resize directly if possible. */
- public interface ResizeListener {
- /** Notifies that IWindow#resized is going to be called from WindowStateResizeItem. */
- void onExecutingWindowStateResizeItem();
- }
}
diff --git a/core/java/android/app/servertransaction/WindowStateTransactionItem.java b/core/java/android/app/servertransaction/WindowStateTransactionItem.java
new file mode 100644
index 000000000000..d556363dd947
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowStateTransactionItem.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 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 android.app.servertransaction;
+
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ClientTransactionHandler;
+import android.os.Parcel;
+import android.view.IWindow;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+
+/**
+ * {@link ClientTransactionItem} to report changes to a window.
+ *
+ * @hide
+ */
+public abstract class WindowStateTransactionItem extends ClientTransactionItem {
+
+ /** The interface for IWindow to perform callback directly if possible. */
+ public interface TransactionListener {
+ /** Notifies that the transaction item is going to be executed. */
+ void onExecutingWindowStateTransactionItem();
+ }
+
+ /** Target window. */
+ private IWindow mWindow;
+
+ WindowStateTransactionItem() {}
+
+ @Override
+ public final void execute(@NonNull ClientTransactionHandler client,
+ @NonNull PendingTransactionActions pendingActions) {
+ if (mWindow instanceof TransactionListener listener) {
+ listener.onExecutingWindowStateTransactionItem();
+ }
+ execute(client, mWindow, pendingActions);
+ }
+
+ /**
+ * Like {@link #execute(ClientTransactionHandler, PendingTransactionActions)},
+ * but take non-null {@link IWindow} as a parameter.
+ */
+ @VisibleForTesting(visibility = PACKAGE)
+ public abstract void execute(@NonNull ClientTransactionHandler client,
+ @NonNull IWindow window, @NonNull PendingTransactionActions pendingActions);
+
+ void setWindow(@NonNull IWindow window) {
+ mWindow = requireNonNull(window);
+ }
+
+ // To be overridden
+
+ WindowStateTransactionItem(@NonNull Parcel in) {
+ mWindow = IWindow.Stub.asInterface(in.readStrongBinder());
+ }
+
+ @CallSuper
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeStrongBinder(mWindow.asBinder());
+ }
+
+ @CallSuper
+ @Override
+ public void recycle() {
+ mWindow = null;
+ }
+
+ // Subclass must override and call super.equals to compare the mActivityToken.
+ @SuppressWarnings("EqualsGetClass")
+ @CallSuper
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final WindowStateTransactionItem other = (WindowStateTransactionItem) o;
+ return Objects.equals(mWindow, other.mWindow);
+ }
+
+ @CallSuper
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mWindow);
+ }
+
+ @CallSuper
+ @Override
+ public String toString() {
+ return "mWindow=" + mWindow;
+ }
+}
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/KeyboardShortcutInfo.java b/core/java/android/view/KeyboardShortcutInfo.java
index 118b03ce5504..3f6fd646994c 100644
--- a/core/java/android/view/KeyboardShortcutInfo.java
+++ b/core/java/android/view/KeyboardShortcutInfo.java
@@ -28,8 +28,8 @@ import android.os.Parcelable;
* Information about a Keyboard Shortcut.
*/
public final class KeyboardShortcutInfo implements Parcelable {
- private final CharSequence mLabel;
- private final Icon mIcon;
+ @Nullable private final CharSequence mLabel;
+ @Nullable private Icon mIcon;
private final char mBaseCharacter;
private final int mKeycode;
private final int mModifiers;
@@ -116,6 +116,15 @@ public final class KeyboardShortcutInfo implements Parcelable {
}
/**
+ * Removes an icon that was previously set.
+ *
+ * @hide
+ */
+ public void clearIcon() {
+ mIcon = null;
+ }
+
+ /**
* Returns the base keycode that, combined with the modifiers, triggers this shortcut. If the
* base character was set instead, returns {@link KeyEvent#KEYCODE_UNKNOWN}. Valid keycodes are
* defined as constants in {@link KeyEvent}.
@@ -165,4 +174,4 @@ public final class KeyboardShortcutInfo implements Parcelable {
return new KeyboardShortcutInfo[size];
}
};
-} \ No newline at end of file
+}
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 8e2b7f1d4dd2..139285a44817 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -143,7 +143,7 @@ import android.app.ICompatCameraControlCallback;
import android.app.ResourcesManager;
import android.app.WindowConfiguration;
import android.app.compat.CompatChanges;
-import android.app.servertransaction.WindowStateResizeItem;
+import android.app.servertransaction.WindowStateTransactionItem;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
import android.content.ClipDescription;
@@ -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).
@@ -7273,7 +7317,8 @@ public final class ViewRootImpl implements ViewParent,
if (dispatcher.isBackGestureInProgress()) {
return FINISH_NOT_HANDLED;
}
- if (topCallback instanceof OnBackAnimationCallback) {
+ if (topCallback instanceof OnBackAnimationCallback
+ && !(topCallback instanceof ImeBackAnimationController)) {
final OnBackAnimationCallback animationCallback =
(OnBackAnimationCallback) topCallback;
switch (keyEvent.getAction()) {
@@ -11201,10 +11246,10 @@ public final class ViewRootImpl implements ViewParent,
}
}
- static class W extends IWindow.Stub implements WindowStateResizeItem.ResizeListener {
+ static class W extends IWindow.Stub implements WindowStateTransactionItem.TransactionListener {
private final WeakReference<ViewRootImpl> mViewAncestor;
private final IWindowSession mWindowSession;
- private boolean mIsFromResizeItem;
+ private boolean mIsFromTransactionItem;
W(ViewRootImpl viewAncestor) {
mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
@@ -11212,8 +11257,8 @@ public final class ViewRootImpl implements ViewParent,
}
@Override
- public void onExecutingWindowStateResizeItem() {
- mIsFromResizeItem = true;
+ public void onExecutingWindowStateTransactionItem() {
+ mIsFromTransactionItem = true;
}
@Override
@@ -11221,8 +11266,8 @@ public final class ViewRootImpl implements ViewParent,
MergedConfiguration mergedConfiguration, InsetsState insetsState,
boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int syncSeqId,
boolean dragResizing, @Nullable ActivityWindowInfo activityWindowInfo) {
- final boolean isFromResizeItem = mIsFromResizeItem;
- mIsFromResizeItem = false;
+ final boolean isFromResizeItem = mIsFromTransactionItem;
+ mIsFromTransactionItem = false;
// Although this is a AIDL method, it will only be triggered in local process through
// either WindowStateResizeItem or WindowlessWindowManager.
final ViewRootImpl viewAncestor = mViewAncestor.get();
@@ -11259,10 +11304,13 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl.Array activeControls) {
+ final boolean isFromInsetsControlChangeItem = mIsFromTransactionItem;
+ mIsFromTransactionItem = false;
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchInsetsControlChanged(insetsState, activeControls.get());
}
+ // TODO(b/339380439): no need to post if the call is from InsetsControlChangeItem
}
@Override
@@ -12577,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() {
@@ -12757,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
@@ -12979,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);
+ }
}
/**
@@ -12997,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/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java
index 29bb32e6443f..f928f509bdb6 100644
--- a/core/java/android/window/SnapshotDrawerUtils.java
+++ b/core/java/android/window/SnapshotDrawerUtils.java
@@ -50,7 +50,6 @@ import android.app.ActivityManager;
import android.app.ActivityThread;
import android.content.Context;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.GraphicBuffer;
import android.graphics.Paint;
import android.graphics.PixelFormat;
@@ -68,6 +67,7 @@ import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.DecorView;
+import com.android.window.flags.Flags;
/**
* Utils class to help draw a snapshot on a surface.
@@ -181,7 +181,8 @@ public class SnapshotDrawerUtils {
// We consider nearly matched dimensions as there can be rounding errors and the user
// won't notice very minute differences from scaling one dimension more than the other
- boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshotW, mSnapshotH);
+ boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshotW, mSnapshotH)
+ && !Flags.drawSnapshotAspectRatioMatch();
// Keep a reference to it such that it doesn't get destroyed when finalized.
SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session)
@@ -382,8 +383,8 @@ public class SnapshotDrawerUtils {
}
final SnapshotSurface drawSurface = new SnapshotSurface(
rootSurface, snapshot, lp.getTitle());
-
- final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams;
+ final WindowManager.LayoutParams attrs = Flags.drawSnapshotAspectRatioMatch()
+ ? info.mainWindowLayoutParams : info.topOpaqueWindowLayoutParams;
final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
final ActivityManager.TaskDescription taskDescription =
getOrCreateTaskDescription(runningTaskInfo);
@@ -400,7 +401,8 @@ public class SnapshotDrawerUtils {
public static WindowManager.LayoutParams createLayoutParameters(StartingWindowInfo info,
CharSequence title, @WindowManager.LayoutParams.WindowType int windowType,
int pixelFormat, IBinder token) {
- final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams;
+ final WindowManager.LayoutParams attrs = Flags.drawSnapshotAspectRatioMatch()
+ ? info.mainWindowLayoutParams : info.topOpaqueWindowLayoutParams;
final WindowManager.LayoutParams mainWindowParams = info.mainWindowLayoutParams;
final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState;
if (attrs == null || mainWindowParams == null || topWindowInsetsState == null) {
@@ -527,7 +529,7 @@ public class SnapshotDrawerUtils {
void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame,
int statusBarHeight) {
- if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0
+ if (statusBarHeight > 0 && alpha(mStatusBarColor) != 0
&& (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) {
final int rightInset = (int) (mSystemBarInsets.right * mScale);
final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0;
@@ -541,7 +543,7 @@ public class SnapshotDrawerUtils {
getNavigationBarRect(c.getWidth(), c.getHeight(), mSystemBarInsets, navigationBarRect,
mScale);
final boolean visible = isNavigationBarColorViewVisible();
- if (visible && Color.alpha(mNavigationBarColor) != 0
+ if (visible && alpha(mNavigationBarColor) != 0
&& !navigationBarRect.isEmpty()) {
c.drawRect(navigationBarRect, mNavigationBarPaint);
}
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_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index f08f5b8fddbe..d6f65f8c9d8b 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -171,4 +171,15 @@ flag {
description: "Actively release task snapshot memory"
bug: "238206323"
is_fixed_read_only: true
+}
+
+flag {
+ name: "draw_snapshot_aspect_ratio_match"
+ namespace: "windowing_frontend"
+ description: "The aspect ratio should always match when drawing snapshot"
+ bug: "341020277"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
} \ No newline at end of file
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/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 33b4e4a7dd0b..75ddb58590a1 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -103,6 +103,8 @@ public class AccessibilityShortcutController {
// The component name for the sub setting of Hearing aids in Accessibility settings
public static final ComponentName ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "HearingAids");
+ public static final ComponentName ACCESSIBILITY_HEARING_AIDS_TILE_COMPONENT_NAME =
+ new ComponentName("com.android.server.accessibility", "HearingDevicesTile");
public static final ComponentName COLOR_INVERSION_TILE_COMPONENT_NAME =
new ComponentName("com.android.server.accessibility", "ColorInversionTile");
diff --git a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
index c08968dabc89..6420620adda9 100644
--- a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
+++ b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
@@ -16,6 +16,8 @@
package com.android.internal.accessibility.common;
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_TILE_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_TILE_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
@@ -160,6 +162,8 @@ public final class ShortcutConstants {
DALTONIZER_COMPONENT_NAME, DALTONIZER_TILE_COMPONENT_NAME,
ONE_HANDED_COMPONENT_NAME, ONE_HANDED_TILE_COMPONENT_NAME,
REDUCE_BRIGHT_COLORS_COMPONENT_NAME, REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME,
- FONT_SIZE_COMPONENT_NAME, FONT_SIZE_TILE_COMPONENT_NAME
+ FONT_SIZE_COMPONENT_NAME, FONT_SIZE_TILE_COMPONENT_NAME,
+ ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME,
+ ACCESSIBILITY_HEARING_AIDS_TILE_COMPONENT_NAME
);
}
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/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
index 29936cc2cac3..6b957114c00b 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -514,7 +514,12 @@ class DividerPresenter implements View.OnTouchListener {
mProperties.mDividerAttributes, mProperties.mIsVerticalSplit,
calculateMinPosition(), calculateMaxPosition());
mRenderer.setDividerPosition(mDividerPosition);
- switch (event.getAction()) {
+
+ // Convert to use screen-based coordinates to prevent lost track of motion events
+ // while moving divider bar and calculating dragging velocity.
+ event.setLocation(event.getRawX(), event.getRawY());
+ final int action = event.getAction() & MotionEvent.ACTION_MASK;
+ switch (action) {
case MotionEvent.ACTION_DOWN:
onStartDragging(event);
break;
@@ -713,9 +718,9 @@ class DividerPresenter implements View.OnTouchListener {
return snap(dividerPosition, possiblePositions);
}
if (velocity < 0) {
- return 0;
+ return minPosition;
} else {
- return fullyExpandedPosition;
+ return maxPosition;
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 9aa12aa824df..f78e2b5170fc 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -1322,7 +1322,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
mPresenter.expandTaskFragment(wct, container);
} else {
// Put activity into a new expanded container.
- final TaskFragmentContainer newContainer = newContainer(activity, getTaskId(activity));
+ final TaskFragmentContainer newContainer =
+ new TaskFragmentContainer.Builder(this, getTaskId(activity), activity)
+ .setPendingAppearedActivity(activity).build();
mPresenter.expandActivity(wct, newContainer.getTaskFragmentToken(), activity);
}
}
@@ -1738,9 +1740,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Can't find any activity in the Task that we can use as the owner activity.
return null;
}
- final TaskFragmentContainer container = newContainer(null /* pendingAppearedActivity */,
- intent, activityInTask, taskId, null /* pairedPrimaryContainer*/, overlayTag,
- launchOptions, associateLaunchingActivity);
+ final TaskFragmentContainer container =
+ new TaskFragmentContainer.Builder(this, taskId, activityInTask)
+ .setPendingAppearedIntent(intent)
+ .setOverlayTag(overlayTag)
+ .setLaunchOptions(launchOptions)
+ .setAssociatedActivity(associateLaunchingActivity ? activityInTask : null)
+ .build();
final IBinder taskFragmentToken = container.getTaskFragmentToken();
// Note that taskContainer will not exist before calling #newContainer if the container
// is the first embedded TF in the task.
@@ -1818,74 +1824,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return null;
}
- @GuardedBy("mLock")
- TaskFragmentContainer newContainer(@NonNull Activity pendingAppearedActivity, int taskId) {
- return newContainer(pendingAppearedActivity, pendingAppearedActivity, taskId);
- }
-
- @GuardedBy("mLock")
- TaskFragmentContainer newContainer(@NonNull Activity pendingAppearedActivity,
- @NonNull Activity activityInTask, int taskId) {
- return newContainer(pendingAppearedActivity, null /* pendingAppearedIntent */,
- activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */,
- null /* launchOptions */, false /* associateLaunchingActivity */);
- }
-
- @GuardedBy("mLock")
- TaskFragmentContainer newContainer(@NonNull Intent pendingAppearedIntent,
- @NonNull Activity activityInTask, int taskId) {
- return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
- activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */,
- null /* launchOptions */, false /* associateLaunchingActivity */);
- }
-
- @GuardedBy("mLock")
- TaskFragmentContainer newContainer(@NonNull Intent pendingAppearedIntent,
- @NonNull Activity activityInTask, int taskId,
- @NonNull TaskFragmentContainer pairedPrimaryContainer) {
- return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
- activityInTask, taskId, pairedPrimaryContainer, null /* tag */,
- null /* launchOptions */, false /* associateLaunchingActivity */);
- }
-
- /**
- * Creates and registers a new organized container with an optional activity that will be
- * re-parented to it in a WCT.
- *
- * @param pendingAppearedActivity the activity that will be reparented to the TaskFragment.
- * @param pendingAppearedIntent the Intent that will be started in the TaskFragment.
- * @param activityInTask activity in the same Task so that we can get the Task bounds
- * if needed.
- * @param taskId parent Task of the new TaskFragment.
- * @param pairedContainer the paired primary {@link TaskFragmentContainer}. When it is
- * set, the new container will be added right above it.
- * @param overlayTag The tag for the new created overlay container. It must be
- * needed if {@code isOverlay} is {@code true}. Otherwise,
- * it should be {@code null}.
- * @param launchOptions The launch options bundle to create a container. Must be
- * specified for overlay container.
- * @param associateLaunchingActivity {@code true} to indicate this overlay container
- * should associate with launching activity.
- */
- @GuardedBy("mLock")
- TaskFragmentContainer newContainer(@Nullable Activity pendingAppearedActivity,
- @Nullable Intent pendingAppearedIntent, @NonNull Activity activityInTask, int taskId,
- @Nullable TaskFragmentContainer pairedContainer, @Nullable String overlayTag,
- @Nullable Bundle launchOptions, boolean associateLaunchingActivity) {
- if (activityInTask == null) {
- throw new IllegalArgumentException("activityInTask must not be null,");
- }
- if (!mTaskContainers.contains(taskId)) {
- mTaskContainers.put(taskId, new TaskContainer(taskId, activityInTask));
- mDividerPresenters.put(taskId, new DividerPresenter(taskId, this, mExecutor));
- }
- final TaskContainer taskContainer = mTaskContainers.get(taskId);
- final TaskFragmentContainer container = new TaskFragmentContainer(pendingAppearedActivity,
- pendingAppearedIntent, taskContainer, this, pairedContainer, overlayTag,
- launchOptions, associateLaunchingActivity ? activityInTask : null);
- return container;
- }
-
/**
* Creates and registers a new split with the provided containers and configuration. Finishes
* existing secondary containers if found for the given primary container.
@@ -2581,6 +2519,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return mTaskContainers.get(taskId);
}
+ @GuardedBy("mLock")
+ void addTaskContainer(int taskId, TaskContainer taskContainer) {
+ mTaskContainers.put(taskId, taskContainer);
+ mDividerPresenters.put(taskId, new DividerPresenter(taskId, this, mExecutor));
+ }
+
Handler getHandler() {
return mHandler;
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 1cb410e90a76..eade86e50659 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -186,8 +186,9 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
// Create new empty task fragment
final int taskId = primaryContainer.getTaskId();
- final TaskFragmentContainer secondaryContainer = mController.newContainer(
- secondaryIntent, primaryActivity, taskId);
+ final TaskFragmentContainer secondaryContainer =
+ new TaskFragmentContainer.Builder(mController, taskId, primaryActivity)
+ .setPendingAppearedIntent(secondaryIntent).build();
final Rect secondaryRelBounds = getRelBoundsForPosition(POSITION_END, taskProperties,
splitAttributes);
final int windowingMode = mController.getTaskContainer(taskId)
@@ -261,7 +262,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
TaskFragmentContainer container = mController.getContainerWithActivity(activity);
final int taskId = container != null ? container.getTaskId() : activity.getTaskId();
if (container == null || container == containerToAvoid) {
- container = mController.newContainer(activity, taskId);
+ container = new TaskFragmentContainer.Builder(mController, taskId, activity)
+ .setPendingAppearedActivity(activity).build();
final int windowingMode = mController.getTaskContainer(taskId)
.getWindowingModeForTaskFragment(relBounds);
final IBinder reparentActivityToken = activity.getActivityToken();
@@ -304,15 +306,19 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
TaskFragmentContainer primaryContainer = mController.getContainerWithActivity(
launchingActivity);
if (primaryContainer == null) {
- primaryContainer = mController.newContainer(launchingActivity,
- launchingActivity.getTaskId());
+ primaryContainer = new TaskFragmentContainer.Builder(mController,
+ launchingActivity.getTaskId(), launchingActivity)
+ .setPendingAppearedActivity(launchingActivity).build();
}
final int taskId = primaryContainer.getTaskId();
- final TaskFragmentContainer secondaryContainer = mController.newContainer(activityIntent,
- launchingActivity, taskId,
- // Pass in the primary container to make sure it is added right above the primary.
- primaryContainer);
+ final TaskFragmentContainer secondaryContainer =
+ new TaskFragmentContainer.Builder(mController, taskId, launchingActivity)
+ .setPendingAppearedIntent(activityIntent)
+ // Pass in the primary container to make sure it is added right above the
+ // primary.
+ .setPairedPrimaryContainer(primaryContainer)
+ .build();
final TaskContainer taskContainer = mController.getTaskContainer(taskId);
final int windowingMode = taskContainer.getWindowingModeForTaskFragment(
primaryRelBounds);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index d0b6a01bb51e..7173b0c95230 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -195,20 +195,6 @@ class TaskFragmentContainer {
private boolean mLastDimOnTask;
/**
- * @see #TaskFragmentContainer(Activity, Intent, TaskContainer, SplitController,
- * TaskFragmentContainer, String, Bundle, Activity)
- */
- TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
- @Nullable Intent pendingAppearedIntent,
- @NonNull TaskContainer taskContainer,
- @NonNull SplitController controller,
- @Nullable TaskFragmentContainer pairedPrimaryContainer) {
- this(pendingAppearedActivity, pendingAppearedIntent, taskContainer,
- controller, pairedPrimaryContainer, null /* overlayTag */,
- null /* launchOptions */, null /* associatedActivity */);
- }
-
- /**
* Creates a container with an existing activity that will be re-parented to it in a window
* container transaction.
* @param pairedPrimaryContainer when it is set, the new container will be add right above it
@@ -218,7 +204,7 @@ class TaskFragmentContainer {
* @param associatedActivity the associated activity of the overlay container. Must be
* {@code null} for a non-overlay container.
*/
- TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
+ private TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
@Nullable Intent pendingAppearedIntent, @NonNull TaskContainer taskContainer,
@NonNull SplitController controller,
@Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag,
@@ -232,12 +218,6 @@ class TaskFragmentContainer {
mToken = new Binder("TaskFragmentContainer");
mTaskContainer = taskContainer;
mOverlayTag = overlayTag;
- if (overlayTag != null) {
- Objects.requireNonNull(launchOptions);
- } else if (associatedActivity != null) {
- throw new IllegalArgumentException("Associated activity must be null for "
- + "non-overlay activity.");
- }
mAssociatedActivityToken = associatedActivity != null
? associatedActivity.getActivityToken() : null;
@@ -1116,6 +1096,117 @@ class TaskFragmentContainer {
return sb.append("]").toString();
}
+ static final class Builder {
+ @NonNull
+ private final SplitController mSplitController;
+
+ // The parent Task id of the new TaskFragment.
+ private final int mTaskId;
+
+ // The activity in the same Task so that we can get the Task bounds if needed.
+ @NonNull
+ private final Activity mActivityInTask;
+
+ // The activity that will be reparented to the TaskFragment.
+ @Nullable
+ private Activity mPendingAppearedActivity;
+
+ // The Intent that will be started in the TaskFragment.
+ @Nullable
+ private Intent mPendingAppearedIntent;
+
+ // The paired primary {@link TaskFragmentContainer}. When it is set, the new container
+ // will be added right above it.
+ @Nullable
+ private TaskFragmentContainer mPairedPrimaryContainer;
+
+ // The launch options bundle to create a container. Must be specified for overlay container.
+ @Nullable
+ private Bundle mLaunchOptions;
+
+ // The tag for the new created overlay container. This is required when creating an
+ // overlay container.
+ @Nullable
+ private String mOverlayTag;
+
+ // The associated activity of the overlay container. Must be {@code null} for a
+ // non-overlay container.
+ @Nullable
+ private Activity mAssociatedActivity;
+
+ Builder(@NonNull SplitController splitController, int taskId,
+ @Nullable Activity activityInTask) {
+ if (taskId <= 0) {
+ throw new IllegalArgumentException("taskId is invalid, " + taskId);
+ }
+
+ mSplitController = splitController;
+ mTaskId = taskId;
+ mActivityInTask = activityInTask;
+ }
+
+ @NonNull
+ Builder setPendingAppearedActivity(@Nullable Activity pendingAppearedActivity) {
+ mPendingAppearedActivity = pendingAppearedActivity;
+ return this;
+ }
+
+ @NonNull
+ Builder setPendingAppearedIntent(@Nullable Intent pendingAppearedIntent) {
+ mPendingAppearedIntent = pendingAppearedIntent;
+ return this;
+ }
+
+ @NonNull
+ Builder setPairedPrimaryContainer(@Nullable TaskFragmentContainer pairedPrimaryContainer) {
+ mPairedPrimaryContainer = pairedPrimaryContainer;
+ return this;
+ }
+
+ @NonNull
+ Builder setLaunchOptions(@Nullable Bundle launchOptions) {
+ mLaunchOptions = launchOptions;
+ return this;
+ }
+
+ @NonNull
+ Builder setOverlayTag(@Nullable String overlayTag) {
+ mOverlayTag = overlayTag;
+ return this;
+ }
+
+ @NonNull
+ Builder setAssociatedActivity(@Nullable Activity associatedActivity) {
+ mAssociatedActivity = associatedActivity;
+ return this;
+ }
+
+ @NonNull
+ TaskFragmentContainer build() {
+ if (mOverlayTag != null) {
+ Objects.requireNonNull(mLaunchOptions);
+ } else if (mAssociatedActivity != null) {
+ throw new IllegalArgumentException("Associated activity must be null for "
+ + "non-overlay activity.");
+ }
+
+ TaskContainer taskContainer = mSplitController.getTaskContainer(mTaskId);
+ if (taskContainer == null && mActivityInTask == null) {
+ throw new IllegalArgumentException("mActivityInTask must be set.");
+ }
+
+ if (taskContainer == null) {
+ // Adding a TaskContainer if no existed one.
+ taskContainer = new TaskContainer(mTaskId, mActivityInTask);
+ mSplitController.addTaskContainer(mTaskId, taskContainer);
+ }
+
+ return new TaskFragmentContainer(mPendingAppearedActivity, mPendingAppearedIntent,
+ taskContainer, mSplitController, mPairedPrimaryContainer, mOverlayTag,
+ mLaunchOptions, mAssociatedActivity);
+ }
+ }
+
static class OverlayContainerRestoreParams {
/** The token of the overlay container */
@NonNull
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
index 746607c8094c..20626c79714e 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
@@ -720,7 +720,7 @@ public class DividerPresenterTest {
// Divider position is greater than minPosition and the velocity is enough for fling
assertEquals(
- 0, // Closed position
+ 30, // minPosition
DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
50 /* dividerPosition */,
30 /* minPosition */,
@@ -731,7 +731,7 @@ public class DividerPresenterTest {
// Divider position is less than maxPosition and the velocity is enough for fling
assertEquals(
- 1200, // Fully expanded position
+ 900, // maxPosition
DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
800 /* dividerPosition */,
30 /* minPosition */,
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
index a069ac7256d6..d649c6d57137 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
@@ -248,4 +248,17 @@ public class EmbeddingTestUtils {
return new SplitPlaceholderRule.Builder(placeholderIntent, activityPredicate,
intentPredicate, windowMetricsPredicate);
}
+
+ @NonNull
+ static TaskFragmentContainer createTfContainer(
+ @NonNull SplitController splitController, @NonNull Activity activity) {
+ return createTfContainer(splitController, TASK_ID, activity);
+ }
+
+ @NonNull
+ static TaskFragmentContainer createTfContainer(
+ @NonNull SplitController splitController, int taskId, @NonNull Activity activity) {
+ return new TaskFragmentContainer.Builder(splitController, taskId, activity)
+ .setPendingAppearedActivity(activity).build();
+ }
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
index 76e6a0ff2c21..7b473b04548c 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
@@ -25,6 +25,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -105,8 +106,11 @@ public class JetpackTaskFragmentOrganizerTest {
@Test
public void testExpandTaskFragment() {
final TaskContainer taskContainer = createTestTaskContainer();
- final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
- new Intent(), taskContainer, mSplitController, null /* pairedPrimaryContainer */);
+ doReturn(taskContainer).when(mSplitController).getTaskContainer(anyInt());
+ final TaskFragmentContainer container = new TaskFragmentContainer.Builder(mSplitController,
+ taskContainer.getTaskId(), null /* activityInTask */)
+ .setPendingAppearedIntent(new Intent())
+ .build();
final TaskFragmentInfo info = createMockInfo(container);
mOrganizer.mFragmentInfos.put(container.getTaskFragmentToken(), info);
container.setInfo(mTransaction, info);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 86b7e88a0c1a..0972d40f33e3 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -29,6 +29,7 @@ import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMock
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPlaceholderRuleBuilder;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTfContainer;
import static androidx.window.extensions.embedding.SplitPresenter.sanitizeBounds;
import static androidx.window.extensions.embedding.WindowAttributes.DIM_AREA_ON_TASK;
@@ -530,8 +531,8 @@ public class OverlayPresentationTest {
@Test
public void testUpdateActivityStackAttributes_nullContainer_earlyReturn() {
- final TaskFragmentContainer container = mSplitController.newContainer(mActivity,
- mActivity.getTaskId());
+ final TaskFragmentContainer container = createTfContainer(mSplitController,
+ mActivity.getTaskId(), mActivity);
mSplitController.updateActivityStackAttributes(
ActivityStack.Token.createFromBinder(container.getTaskFragmentToken()),
new ActivityStackAttributes.Builder().build());
@@ -837,8 +838,9 @@ public class OverlayPresentationTest {
final Intent intent = new Intent();
final IBinder fillTaskActivityToken = new Binder();
final IBinder lastOverlayToken = new Binder();
- final TaskFragmentContainer overlayContainer = mSplitController.newContainer(intent,
- mActivity, TASK_ID);
+ final TaskFragmentContainer overlayContainer =
+ new TaskFragmentContainer.Builder(mSplitController, TASK_ID, mActivity)
+ .setPendingAppearedIntent(intent).build();
final TaskFragmentContainer.OverlayContainerRestoreParams params = mock(
TaskFragmentContainer.OverlayContainerRestoreParams.class);
doReturn(params).when(mSplitController).getOverlayContainerRestoreParams(any(), any());
@@ -884,8 +886,8 @@ public class OverlayPresentationTest {
@NonNull
private TaskFragmentContainer createMockTaskFragmentContainer(
@NonNull Activity activity, boolean isVisible) {
- final TaskFragmentContainer container = mSplitController.newContainer(activity,
- activity.getTaskId());
+ final TaskFragmentContainer container = createTfContainer(mSplitController,
+ activity.getTaskId(), activity);
setupTaskFragmentInfo(container, activity, isVisible);
return container;
}
@@ -918,10 +920,13 @@ public class OverlayPresentationTest {
@Nullable Activity launchingActivity) {
final Activity activity = launchingActivity != null
? launchingActivity : createMockActivity();
- TaskFragmentContainer overlayContainer = mSplitController.newContainer(
- null /* pendingAppearedActivity */, mIntent, activity, taskId,
- null /* pairedPrimaryContainer */, tag, Bundle.EMPTY,
- associateLaunchingActivity);
+ TaskFragmentContainer overlayContainer =
+ new TaskFragmentContainer.Builder(mSplitController, taskId, activity)
+ .setPendingAppearedIntent(mIntent)
+ .setOverlayTag(tag)
+ .setLaunchOptions(Bundle.EMPTY)
+ .setAssociatedActivity(associateLaunchingActivity ? activity : null)
+ .build();
setupTaskFragmentInfo(overlayContainer, createMockActivity(), isVisible);
return overlayContainer;
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 35353dbe36be..640b1fced455 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -41,6 +41,7 @@ import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSpli
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPlaceholderRuleBuilder;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTfContainer;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds;
import static androidx.window.extensions.embedding.SplitRule.FINISH_ALWAYS;
@@ -59,7 +60,6 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.clearInvocations;
@@ -198,7 +198,7 @@ public class SplitControllerTest {
@Test
public void testOnTaskFragmentVanished() {
- final TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID);
+ final TaskFragmentContainer tf = createTfContainer(mSplitController, mActivity);
doReturn(tf.getTaskFragmentToken()).when(mInfo).getFragmentToken();
// The TaskFragment has been removed in the server, we only need to cleanup the reference.
@@ -213,7 +213,7 @@ public class SplitControllerTest {
public void testOnTaskFragmentAppearEmptyTimeout() {
// Setup to make sure a transaction record is started.
mTransactionManager.startNewTransaction();
- final TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID);
+ final TaskFragmentContainer tf = createTfContainer(mSplitController, mActivity);
doCallRealMethod().when(mSplitController).onTaskFragmentAppearEmptyTimeout(any(), any());
mSplitController.onTaskFragmentAppearEmptyTimeout(mTransaction, tf);
@@ -224,7 +224,7 @@ public class SplitControllerTest {
@Test
public void testOnActivityDestroyed() {
doReturn(new Binder()).when(mActivity).getActivityToken();
- final TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID);
+ final TaskFragmentContainer tf = createTfContainer(mSplitController, mActivity);
assertTrue(tf.hasActivity(mActivity.getActivityToken()));
@@ -245,12 +245,9 @@ public class SplitControllerTest {
public void testNewContainer() {
// Must pass in a valid activity.
assertThrows(IllegalArgumentException.class, () ->
- mSplitController.newContainer(null /* activity */, TASK_ID));
- assertThrows(IllegalArgumentException.class, () ->
- mSplitController.newContainer(mActivity, null /* launchingActivity */, TASK_ID));
+ createTfContainer(mSplitController, null /* activity */));
- final TaskFragmentContainer tf = mSplitController.newContainer(mActivity, mActivity,
- TASK_ID);
+ final TaskFragmentContainer tf = createTfContainer(mSplitController, mActivity);
final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID);
assertNotNull(tf);
@@ -263,7 +260,7 @@ public class SplitControllerTest {
public void testUpdateContainer() {
// Make SplitController#launchPlaceholderIfNecessary(TaskFragmentContainer) return true
// and verify if shouldContainerBeExpanded() not called.
- final TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID);
+ final TaskFragmentContainer tf = createTfContainer(mSplitController, mActivity);
spyOn(tf);
doReturn(mActivity).when(tf).getTopNonFinishingActivity();
doReturn(true).when(tf).isEmpty();
@@ -369,8 +366,12 @@ public class SplitControllerTest {
public void testOnStartActivityResultError() {
final Intent intent = new Intent();
final TaskContainer taskContainer = createTestTaskContainer();
- final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
- intent, taskContainer, mSplitController, null /* pairedPrimaryContainer */);
+ final int taskId = taskContainer.getTaskId();
+ mSplitController.addTaskContainer(taskId, taskContainer);
+ final TaskFragmentContainer container = new TaskFragmentContainer.Builder(mSplitController,
+ taskId, null /* activityInTask */)
+ .setPendingAppearedIntent(intent)
+ .build();
final SplitController.ActivityStartMonitor monitor =
mSplitController.getActivityStartMonitor();
@@ -410,7 +411,8 @@ public class SplitControllerTest {
@Test
public void testOnActivityReparentedToTask_diffProcess() {
// Create an empty TaskFragment to initialize for the Task.
- mSplitController.newContainer(new Intent(), mActivity, TASK_ID);
+ new TaskFragmentContainer.Builder(mSplitController, TASK_ID, mActivity)
+ .setPendingAppearedIntent(new Intent()).build();
final IBinder activityToken = new Binder();
final Intent intent = new Intent();
@@ -595,8 +597,9 @@ public class SplitControllerTest {
verify(mTransaction, never()).reparentActivityToTaskFragment(any(), any());
// Place in the top container if there is no other rule matched.
- final TaskFragmentContainer topContainer = mSplitController
- .newContainer(new Intent(), mActivity, TASK_ID);
+ final TaskFragmentContainer topContainer =
+ new TaskFragmentContainer.Builder(mSplitController, TASK_ID, mActivity)
+ .setPendingAppearedIntent(new Intent()).build();
mSplitController.placeActivityInTopContainer(mTransaction, mActivity);
verify(mTransaction).reparentActivityToTaskFragment(topContainer.getTaskFragmentToken(),
@@ -604,7 +607,7 @@ public class SplitControllerTest {
// Not reparent if activity is in a TaskFragment.
clearInvocations(mTransaction);
- mSplitController.newContainer(mActivity, TASK_ID);
+ createTfContainer(mSplitController, mActivity);
mSplitController.placeActivityInTopContainer(mTransaction, mActivity);
verify(mTransaction, never()).reparentActivityToTaskFragment(any(), any());
@@ -616,8 +619,7 @@ public class SplitControllerTest {
false /* isOnReparent */);
assertFalse(result);
- verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any(),
- anyString(), any(), anyBoolean());
+ verify(mSplitController, never()).addTaskContainer(anyInt(), any());
}
@Test
@@ -632,7 +634,6 @@ public class SplitControllerTest {
assertTrue(result);
assertNotNull(container);
- verify(mSplitController).newContainer(mActivity, TASK_ID);
verify(mSplitPresenter).expandActivity(mTransaction, container.getTaskFragmentToken(),
mActivity);
}
@@ -642,7 +643,7 @@ public class SplitControllerTest {
setupExpandRule(mActivity);
// When the activity is not in any TaskFragment, create a new expanded TaskFragment for it.
- final TaskFragmentContainer container = mSplitController.newContainer(mActivity, TASK_ID);
+ final TaskFragmentContainer container = createTfContainer(mSplitController, mActivity);
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
@@ -692,8 +693,8 @@ public class SplitControllerTest {
// Don't launch placeholder if the activity is not in the topmost active TaskFragment.
final Activity activity = createMockActivity();
- mSplitController.newContainer(mActivity, TASK_ID);
- mSplitController.newContainer(activity, TASK_ID);
+ createTfContainer(mSplitController, mActivity);
+ createTfContainer(mSplitController, activity);
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
@@ -711,7 +712,7 @@ public class SplitControllerTest {
(SplitPlaceholderRule) mSplitController.getSplitRules().get(0);
// Launch placeholder if the activity is in the topmost expanded TaskFragment.
- mSplitController.newContainer(mActivity, TASK_ID);
+ createTfContainer(mSplitController, mActivity);
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
@@ -763,10 +764,11 @@ public class SplitControllerTest {
final SplitPairRule splitRule = (SplitPairRule) mSplitController.getSplitRules().get(0);
// Activity is already in primary split, no need to create new split.
- final TaskFragmentContainer primaryContainer = mSplitController.newContainer(mActivity,
- TASK_ID);
- final TaskFragmentContainer secondaryContainer = mSplitController.newContainer(
- secondaryIntent, mActivity, TASK_ID);
+ final TaskFragmentContainer primaryContainer =
+ createTfContainer(mSplitController, mActivity);
+ final TaskFragmentContainer secondaryContainer =
+ new TaskFragmentContainer.Builder(mSplitController, TASK_ID, mActivity)
+ .setPendingAppearedIntent(secondaryIntent).build();
mSplitController.registerSplit(
mTransaction,
primaryContainer,
@@ -779,8 +781,6 @@ public class SplitControllerTest {
false /* isOnReparent */);
assertTrue(result);
- verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any(),
- anyString(), any(), anyBoolean());
verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any(), any());
}
@@ -792,10 +792,11 @@ public class SplitControllerTest {
// The new launched activity is in primary split, but there is no rule for it to split with
// the secondary, so return false.
- final TaskFragmentContainer primaryContainer = mSplitController.newContainer(mActivity,
- TASK_ID);
- final TaskFragmentContainer secondaryContainer = mSplitController.newContainer(
- secondaryIntent, mActivity, TASK_ID);
+ final TaskFragmentContainer primaryContainer =
+ createTfContainer(mSplitController, mActivity);
+ final TaskFragmentContainer secondaryContainer =
+ new TaskFragmentContainer.Builder(mSplitController, TASK_ID, mActivity)
+ .setPendingAppearedIntent(secondaryIntent).build();
mSplitController.registerSplit(
mTransaction,
primaryContainer,
@@ -822,8 +823,6 @@ public class SplitControllerTest {
false /* isOnReparent */);
assertTrue(result);
- verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any(),
- anyString(), any(), anyBoolean());
verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any(), any());
}
@@ -852,10 +851,10 @@ public class SplitControllerTest {
doReturn(PLACEHOLDER_INTENT).when(mActivity).getIntent();
// Activity is a placeholder.
- final TaskFragmentContainer primaryContainer = mSplitController.newContainer(
- primaryActivity, TASK_ID);
- final TaskFragmentContainer secondaryContainer = mSplitController.newContainer(mActivity,
- TASK_ID);
+ final TaskFragmentContainer primaryContainer =
+ createTfContainer(mSplitController, primaryActivity);
+ final TaskFragmentContainer secondaryContainer =
+ createTfContainer(mSplitController, mActivity);
mSplitController.registerSplit(
mTransaction,
primaryContainer,
@@ -874,8 +873,7 @@ public class SplitControllerTest {
final Activity activityBelow = createMockActivity();
setupSplitRule(activityBelow, mActivity);
- final TaskFragmentContainer container = mSplitController.newContainer(activityBelow,
- TASK_ID);
+ final TaskFragmentContainer container = createTfContainer(mSplitController, activityBelow);
container.addPendingAppearedActivity(mActivity);
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
@@ -890,8 +888,7 @@ public class SplitControllerTest {
setupSplitRule(mActivity, activityBelow);
// Disallow to split as primary.
- final TaskFragmentContainer container = mSplitController.newContainer(activityBelow,
- TASK_ID);
+ final TaskFragmentContainer container = createTfContainer(mSplitController, activityBelow);
container.addPendingAppearedActivity(mActivity);
boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
@@ -961,8 +958,7 @@ public class SplitControllerTest {
doReturn(createActivityInfoWithMinDimensions()).when(mActivity).getActivityInfo();
- final TaskFragmentContainer container = mSplitController.newContainer(activityBelow,
- TASK_ID);
+ final TaskFragmentContainer container = createTfContainer(mSplitController, activityBelow);
container.addPendingAppearedActivity(mActivity);
// Allow to split as primary.
@@ -980,8 +976,7 @@ public class SplitControllerTest {
doReturn(createActivityInfoWithMinDimensions()).when(mActivity).getActivityInfo();
- final TaskFragmentContainer container = mSplitController.newContainer(activityBelow,
- TASK_ID);
+ final TaskFragmentContainer container = createTfContainer(mSplitController, activityBelow);
container.addPendingAppearedActivity(mActivity);
boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
@@ -1044,8 +1039,8 @@ public class SplitControllerTest {
public void testResolveActivityToContainer_skipIfNonTopOrPinned() {
final TaskFragmentContainer container = createMockTaskFragmentContainer(mActivity);
final Activity pinnedActivity = createMockActivity();
- final TaskFragmentContainer topContainer = mSplitController.newContainer(pinnedActivity,
- TASK_ID);
+ final TaskFragmentContainer topContainer =
+ createTfContainer(mSplitController, pinnedActivity);
final TaskContainer taskContainer = container.getTaskContainer();
spyOn(taskContainer);
doReturn(container).when(taskContainer).getTopNonFinishingTaskFragmentContainer(false);
@@ -1351,7 +1346,7 @@ public class SplitControllerTest {
// Launch placeholder for activity in top TaskFragment.
setupPlaceholderRule(mActivity);
mTransactionManager.startNewTransaction();
- final TaskFragmentContainer container = mSplitController.newContainer(mActivity, TASK_ID);
+ final TaskFragmentContainer container = createTfContainer(mSplitController, mActivity);
mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity,
true /* isOnCreated */);
@@ -1365,9 +1360,10 @@ public class SplitControllerTest {
// Do not launch placeholder for invisible activity below the top TaskFragment.
setupPlaceholderRule(mActivity);
mTransactionManager.startNewTransaction();
- final TaskFragmentContainer bottomTf = mSplitController.newContainer(mActivity, TASK_ID);
- final TaskFragmentContainer topTf = mSplitController.newContainer(new Intent(), mActivity,
- TASK_ID);
+ final TaskFragmentContainer bottomTf = createTfContainer(mSplitController, mActivity);
+ final TaskFragmentContainer topTf =
+ new TaskFragmentContainer.Builder(mSplitController, TASK_ID, mActivity)
+ .setPendingAppearedIntent(new Intent()).build();
bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity,
false /* isVisible */));
topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity()));
@@ -1383,9 +1379,10 @@ public class SplitControllerTest {
// Launch placeholder for visible activity below the top TaskFragment.
setupPlaceholderRule(mActivity);
mTransactionManager.startNewTransaction();
- final TaskFragmentContainer bottomTf = mSplitController.newContainer(mActivity, TASK_ID);
- final TaskFragmentContainer topTf = mSplitController.newContainer(new Intent(), mActivity,
- TASK_ID);
+ final TaskFragmentContainer bottomTf = createTfContainer(mSplitController, mActivity);
+ final TaskFragmentContainer topTf =
+ new TaskFragmentContainer.Builder(mSplitController, TASK_ID, mActivity)
+ .setPendingAppearedIntent(new Intent()).build();
bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity,
true /* isVisible */));
topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity()));
@@ -1412,7 +1409,7 @@ public class SplitControllerTest {
@Test
public void testFinishActivityStacks_finishSingleActivityStack() {
- TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID);
+ TaskFragmentContainer tf = createTfContainer(mSplitController, mActivity);
tf.setInfo(mTransaction, createMockTaskFragmentInfo(tf, mActivity));
final TaskContainer taskContainer = mSplitController.mTaskContainers.get(TASK_ID);
@@ -1426,8 +1423,8 @@ public class SplitControllerTest {
@Test
public void testFinishActivityStacks_finishActivityStacksInOrder() {
- TaskFragmentContainer bottomTf = mSplitController.newContainer(mActivity, TASK_ID);
- TaskFragmentContainer topTf = mSplitController.newContainer(mActivity, TASK_ID);
+ TaskFragmentContainer bottomTf = createTfContainer(mSplitController, mActivity);
+ TaskFragmentContainer topTf = createTfContainer(mSplitController, mActivity);
bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity));
topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity()));
@@ -1687,8 +1684,8 @@ public class SplitControllerTest {
/** Creates a mock TaskFragment that has been registered and appeared in the organizer. */
private TaskFragmentContainer createMockTaskFragmentContainer(@NonNull Activity activity) {
- final TaskFragmentContainer container = mSplitController.newContainer(activity,
- activity.getTaskId());
+ final TaskFragmentContainer container = createTfContainer(mSplitController,
+ activity.getTaskId(), activity);
setupTaskFragmentInfo(container, activity);
return container;
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index 3fbce9ec31a5..816e2dae1e5b 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -31,6 +31,7 @@ import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActi
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTfContainer;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createWindowLayoutInfo;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds;
import static androidx.window.extensions.embedding.SplitPresenter.EXPAND_CONTAINERS_ATTRIBUTES;
@@ -139,7 +140,7 @@ public class SplitPresenterTest {
@Test
public void testCreateTaskFragment() {
- final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
+ final TaskFragmentContainer container = createTfContainer(mController, mActivity);
mPresenter.createTaskFragment(mTransaction, container.getTaskFragmentToken(),
mActivity.getActivityToken(), TASK_BOUNDS, WINDOWING_MODE_MULTI_WINDOW);
@@ -150,7 +151,7 @@ public class SplitPresenterTest {
@Test
public void testResizeTaskFragment() {
- final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
+ final TaskFragmentContainer container = createTfContainer(mController, mActivity);
mPresenter.mFragmentInfos.put(container.getTaskFragmentToken(), mTaskFragmentInfo);
mPresenter.resizeTaskFragment(mTransaction, container.getTaskFragmentToken(), TASK_BOUNDS);
@@ -166,7 +167,7 @@ public class SplitPresenterTest {
@Test
public void testUpdateWindowingMode() {
- final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
+ final TaskFragmentContainer container = createTfContainer(mController, mActivity);
mPresenter.mFragmentInfos.put(container.getTaskFragmentToken(), mTaskFragmentInfo);
mPresenter.updateWindowingMode(mTransaction, container.getTaskFragmentToken(),
WINDOWING_MODE_MULTI_WINDOW);
@@ -184,8 +185,8 @@ public class SplitPresenterTest {
@Test
public void testSetAdjacentTaskFragments() {
- final TaskFragmentContainer container0 = mController.newContainer(mActivity, TASK_ID);
- final TaskFragmentContainer container1 = mController.newContainer(mActivity, TASK_ID);
+ final TaskFragmentContainer container0 = createTfContainer(mController, mActivity);
+ final TaskFragmentContainer container1 = createTfContainer(mController, mActivity);
mPresenter.setAdjacentTaskFragments(mTransaction, container0.getTaskFragmentToken(),
container1.getTaskFragmentToken(), null /* adjacentParams */);
@@ -202,8 +203,8 @@ public class SplitPresenterTest {
@Test
public void testClearAdjacentTaskFragments() {
- final TaskFragmentContainer container0 = mController.newContainer(mActivity, TASK_ID);
- final TaskFragmentContainer container1 = mController.newContainer(mActivity, TASK_ID);
+ final TaskFragmentContainer container0 = createTfContainer(mController, mActivity);
+ final TaskFragmentContainer container1 = createTfContainer(mController, mActivity);
// No request to clear as it is not set by default.
mPresenter.clearAdjacentTaskFragments(mTransaction, container0.getTaskFragmentToken());
@@ -224,8 +225,8 @@ public class SplitPresenterTest {
@Test
public void testSetCompanionTaskFragment() {
- final TaskFragmentContainer container0 = mController.newContainer(mActivity, TASK_ID);
- final TaskFragmentContainer container1 = mController.newContainer(mActivity, TASK_ID);
+ final TaskFragmentContainer container0 = createTfContainer(mController, mActivity);
+ final TaskFragmentContainer container1 = createTfContainer(mController, mActivity);
mPresenter.setCompanionTaskFragment(mTransaction, container0.getTaskFragmentToken(),
container1.getTaskFragmentToken());
@@ -242,7 +243,7 @@ public class SplitPresenterTest {
@Test
public void testSetTaskFragmentDimOnTask() {
- final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
+ final TaskFragmentContainer container = createTfContainer(mController, mActivity);
mPresenter.setTaskFragmentDimOnTask(mTransaction, container.getTaskFragmentToken(), true);
verify(mTransaction).addTaskFragmentOperation(eq(container.getTaskFragmentToken()), any());
@@ -255,7 +256,7 @@ public class SplitPresenterTest {
@Test
public void testUpdateAnimationParams() {
- final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
+ final TaskFragmentContainer container = createTfContainer(mController, mActivity);
// Verify the default.
assertTrue(container.areLastRequestedAnimationParamsEqual(
@@ -287,7 +288,7 @@ public class SplitPresenterTest {
@Test
public void testSetTaskFragmentPinned() {
- final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
+ final TaskFragmentContainer container = createTfContainer(mController, mActivity);
// Verify the default.
assertFalse(container.isPinned());
@@ -667,8 +668,8 @@ public class SplitPresenterTest {
public void testExpandSplitContainerIfNeeded() {
Activity secondaryActivity = createMockActivity();
SplitRule splitRule = createSplitRule(mActivity, secondaryActivity);
- TaskFragmentContainer primaryTf = mController.newContainer(mActivity, TASK_ID);
- TaskFragmentContainer secondaryTf = mController.newContainer(secondaryActivity, TASK_ID);
+ TaskFragmentContainer primaryTf = createTfContainer(mController, mActivity);
+ TaskFragmentContainer secondaryTf = createTfContainer(mController, secondaryActivity);
SplitContainer splitContainer = new SplitContainer(primaryTf, secondaryActivity,
secondaryTf, splitRule, SPLIT_ATTRIBUTES);
@@ -710,8 +711,8 @@ public class SplitPresenterTest {
@Test
public void testCreateNewSplitContainer_secondaryAbovePrimary() {
final Activity secondaryActivity = createMockActivity();
- final TaskFragmentContainer bottomTf = mController.newContainer(secondaryActivity, TASK_ID);
- final TaskFragmentContainer primaryTf = mController.newContainer(mActivity, TASK_ID);
+ final TaskFragmentContainer bottomTf = createTfContainer(mController, secondaryActivity);
+ final TaskFragmentContainer primaryTf = createTfContainer(mController, mActivity);
final SplitPairRule rule = createSplitPairRuleBuilder(pair ->
pair.first == mActivity && pair.second == secondaryActivity, pair -> false,
metrics -> true)
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index 8913b22115e9..284723279b80 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -29,6 +29,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -57,6 +58,9 @@ import java.util.List;
* Build/Install/Run:
* atest WMJetpackUnitTests:TaskContainerTest
*/
+
+// Suppress GuardedBy warning on unit tests
+@SuppressWarnings("GuardedBy")
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -126,8 +130,11 @@ public class TaskContainerTest {
assertTrue(taskContainer.isEmpty());
- final TaskFragmentContainer tf = new TaskFragmentContainer(null /* activity */,
- new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */);
+ doReturn(taskContainer).when(mController).getTaskContainer(anyInt());
+ final TaskFragmentContainer tf = new TaskFragmentContainer.Builder(mController,
+ taskContainer.getTaskId(), null /* activityInTask */)
+ .setPendingAppearedIntent(new Intent())
+ .build();
assertFalse(taskContainer.isEmpty());
@@ -142,12 +149,17 @@ public class TaskContainerTest {
final TaskContainer taskContainer = createTestTaskContainer();
assertNull(taskContainer.getTopNonFinishingTaskFragmentContainer());
- final TaskFragmentContainer tf0 = new TaskFragmentContainer(null /* activity */,
- new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */);
+ doReturn(taskContainer).when(mController).getTaskContainer(anyInt());
+ final TaskFragmentContainer tf0 = new TaskFragmentContainer.Builder(mController,
+ taskContainer.getTaskId(), null /* activityInTask */)
+ .setPendingAppearedIntent(new Intent())
+ .build();
assertEquals(tf0, taskContainer.getTopNonFinishingTaskFragmentContainer());
- final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */,
- new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer tf1 = new TaskFragmentContainer.Builder(mController,
+ taskContainer.getTaskId(), null /* activityInTask */)
+ .setPendingAppearedIntent(new Intent())
+ .build();
assertEquals(tf1, taskContainer.getTopNonFinishingTaskFragmentContainer());
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
index 44ab2c458e39..7fab371cb790 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
@@ -100,24 +100,27 @@ public class TaskFragmentContainerTest {
@Test
public void testNewContainer() {
final TaskContainer taskContainer = createTestTaskContainer();
+ mController.addTaskContainer(taskContainer.getTaskId(), taskContainer);
// One of the activity and the intent must be non-null
assertThrows(IllegalArgumentException.class,
- () -> new TaskFragmentContainer(null, null, taskContainer, mController,
- null /* pairedPrimaryContainer */));
+ () -> new TaskFragmentContainer.Builder(mController, taskContainer.getTaskId(),
+ null /* activityInTask */).build());
// One of the activity and the intent must be null.
assertThrows(IllegalArgumentException.class,
- () -> new TaskFragmentContainer(mActivity, mIntent, taskContainer, mController,
- null /* pairedPrimaryContainer */));
+ () -> new TaskFragmentContainer.Builder(mController, taskContainer.getTaskId(),
+ null /* activityInTask */)
+ .setPendingAppearedActivity(createMockActivity())
+ .setPendingAppearedIntent(mIntent)
+ .build());
}
@Test
public void testFinish() {
final TaskContainer taskContainer = createTestTaskContainer();
- final TaskFragmentContainer container = new TaskFragmentContainer(mActivity,
- null /* pendingAppearedIntent */, taskContainer, mController,
- null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer container = createTaskFragmentContainer(taskContainer,
+ mActivity, null /* pendingAppearedIntent */);
doReturn(container).when(mController).getContainerWithActivity(mActivity);
// Only remove the activity, but not clear the reference until appeared.
@@ -148,15 +151,13 @@ public class TaskFragmentContainerTest {
@Test
public void testFinish_notFinishActivityThatIsReparenting() {
final TaskContainer taskContainer = createTestTaskContainer();
- final TaskFragmentContainer container0 = new TaskFragmentContainer(mActivity,
- null /* pendingAppearedIntent */, taskContainer, mController,
- null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer container0 = createTaskFragmentContainer(taskContainer,
+ mActivity, null /* pendingAppearedIntent */);
final TaskFragmentInfo info = createMockTaskFragmentInfo(container0, mActivity);
container0.setInfo(mTransaction, info);
// Request to reparent the activity to a new TaskFragment.
- final TaskFragmentContainer container1 = new TaskFragmentContainer(mActivity,
- null /* pendingAppearedIntent */, taskContainer, mController,
- null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer container1 = createTaskFragmentContainer(taskContainer,
+ mActivity, null /* pendingAppearedIntent */);
doReturn(container1).when(mController).getContainerWithActivity(mActivity);
// The activity is requested to be reparented, so don't finish it.
@@ -171,15 +172,13 @@ public class TaskFragmentContainerTest {
public void testFinish_alwaysFinishPlaceholder() {
// Register container1 as a placeholder
final TaskContainer taskContainer = createTestTaskContainer();
- final TaskFragmentContainer container0 = new TaskFragmentContainer(mActivity,
- null /* pendingAppearedIntent */, taskContainer, mController,
- null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer container0 = createTaskFragmentContainer(taskContainer,
+ mActivity, null /* pendingAppearedIntent */);
final TaskFragmentInfo info0 = createMockTaskFragmentInfo(container0, mActivity);
container0.setInfo(mTransaction, info0);
final Activity placeholderActivity = createMockActivity();
- final TaskFragmentContainer container1 = new TaskFragmentContainer(placeholderActivity,
- null /* pendingAppearedIntent */, taskContainer, mController,
- null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer container1 = createTaskFragmentContainer(taskContainer,
+ placeholderActivity, null /* pendingAppearedIntent */);
final TaskFragmentInfo info1 = createMockTaskFragmentInfo(container1, placeholderActivity);
container1.setInfo(mTransaction, info1);
final SplitAttributes splitAttributes = new SplitAttributes.Builder().build();
@@ -207,9 +206,8 @@ public class TaskFragmentContainerTest {
public void testSetInfo() {
final TaskContainer taskContainer = createTestTaskContainer();
// Pending activity should be cleared when it has appeared on server side.
- final TaskFragmentContainer pendingActivityContainer = new TaskFragmentContainer(mActivity,
- null /* pendingAppearedIntent */, taskContainer, mController,
- null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer pendingActivityContainer = createTaskFragmentContainer(
+ taskContainer, mActivity, null /* pendingAppearedIntent */);
assertTrue(pendingActivityContainer.mPendingAppearedActivities.contains(
mActivity.getActivityToken()));
@@ -221,9 +219,8 @@ public class TaskFragmentContainerTest {
assertTrue(pendingActivityContainer.mPendingAppearedActivities.isEmpty());
// Pending intent should be cleared when the container becomes non-empty.
- final TaskFragmentContainer pendingIntentContainer = new TaskFragmentContainer(
- null /* pendingAppearedActivity */, mIntent, taskContainer, mController,
- null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer pendingIntentContainer = createTaskFragmentContainer(
+ taskContainer, null /* pendingAppearedActivity */, mIntent);
assertEquals(mIntent, pendingIntentContainer.getPendingAppearedIntent());
@@ -237,8 +234,8 @@ public class TaskFragmentContainerTest {
@Test
public void testIsWaitingActivityAppear() {
final TaskContainer taskContainer = createTestTaskContainer();
- final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
- mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer container = createTaskFragmentContainer(
+ taskContainer, null /* pendingAppearedActivity */, mIntent);
assertTrue(container.isWaitingActivityAppear());
@@ -259,8 +256,8 @@ public class TaskFragmentContainerTest {
public void testAppearEmptyTimeout() {
doNothing().when(mController).onTaskFragmentAppearEmptyTimeout(any(), any());
final TaskContainer taskContainer = createTestTaskContainer();
- final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
- mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer container = createTaskFragmentContainer(
+ taskContainer, null /* pendingAppearedActivity */, mIntent);
assertNull(container.mAppearEmptyTimeout);
@@ -299,8 +296,8 @@ public class TaskFragmentContainerTest {
@Test
public void testCollectNonFinishingActivities() {
final TaskContainer taskContainer = createTestTaskContainer();
- final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
- mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer container = createTaskFragmentContainer(
+ taskContainer, null /* pendingAppearedActivity */, mIntent);
List<Activity> activities = container.collectNonFinishingActivities();
assertTrue(activities.isEmpty());
@@ -327,8 +324,8 @@ public class TaskFragmentContainerTest {
@Test
public void testCollectNonFinishingActivities_checkIfStable() {
final TaskContainer taskContainer = createTestTaskContainer();
- final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
- mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer container = createTaskFragmentContainer(
+ taskContainer, null /* pendingAppearedActivity */, mIntent);
// In case mInfo is null, collectNonFinishingActivities(true) should return null.
List<Activity> activities =
@@ -353,8 +350,8 @@ public class TaskFragmentContainerTest {
@Test
public void testAddPendingActivity() {
final TaskContainer taskContainer = createTestTaskContainer();
- final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
- mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer container = createTaskFragmentContainer(
+ taskContainer, null /* pendingAppearedActivity */, mIntent);
container.addPendingAppearedActivity(mActivity);
assertEquals(1, container.collectNonFinishingActivities().size());
@@ -367,10 +364,10 @@ public class TaskFragmentContainerTest {
@Test
public void testIsAbove() {
final TaskContainer taskContainer = createTestTaskContainer();
- final TaskFragmentContainer container0 = new TaskFragmentContainer(null /* activity */,
- mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
- final TaskFragmentContainer container1 = new TaskFragmentContainer(null /* activity */,
- mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer container0 = createTaskFragmentContainer(
+ taskContainer, null /* pendingAppearedActivity */, mIntent);
+ final TaskFragmentContainer container1 = createTaskFragmentContainer(
+ taskContainer, null /* pendingAppearedActivity */, mIntent);
assertTrue(container1.isAbove(container0));
assertFalse(container0.isAbove(container1));
@@ -379,8 +376,8 @@ public class TaskFragmentContainerTest {
@Test
public void testGetBottomMostActivity() {
final TaskContainer taskContainer = createTestTaskContainer();
- final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
- mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer container = createTaskFragmentContainer(
+ taskContainer, null /* pendingAppearedActivity */, mIntent);
container.addPendingAppearedActivity(mActivity);
assertEquals(mActivity, container.getBottomMostActivity());
@@ -396,8 +393,8 @@ public class TaskFragmentContainerTest {
@Test
public void testOnActivityDestroyed() {
final TaskContainer taskContainer = createTestTaskContainer(mController);
- final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
- mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer container = createTaskFragmentContainer(
+ taskContainer, null /* pendingAppearedActivity */, mIntent);
container.addPendingAppearedActivity(mActivity);
final List<IBinder> activities = new ArrayList<>();
activities.add(mActivity.getActivityToken());
@@ -416,8 +413,8 @@ public class TaskFragmentContainerTest {
public void testIsInIntermediateState() {
// True if no info set.
final TaskContainer taskContainer = createTestTaskContainer();
- final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
- mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer container = createTaskFragmentContainer(
+ taskContainer, null /* pendingAppearedActivity */, mIntent);
spyOn(taskContainer);
doReturn(true).when(taskContainer).isVisible();
@@ -479,8 +476,8 @@ public class TaskFragmentContainerTest {
@Test
public void testHasAppearedActivity() {
final TaskContainer taskContainer = createTestTaskContainer();
- final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
- mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer container = createTaskFragmentContainer(
+ taskContainer, null /* pendingAppearedActivity */, mIntent);
container.addPendingAppearedActivity(mActivity);
assertFalse(container.hasAppearedActivity(mActivity.getActivityToken()));
@@ -496,8 +493,8 @@ public class TaskFragmentContainerTest {
@Test
public void testHasPendingAppearedActivity() {
final TaskContainer taskContainer = createTestTaskContainer();
- final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
- mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer container = createTaskFragmentContainer(
+ taskContainer, null /* pendingAppearedActivity */, mIntent);
container.addPendingAppearedActivity(mActivity);
assertTrue(container.hasPendingAppearedActivity(mActivity.getActivityToken()));
@@ -513,10 +510,10 @@ public class TaskFragmentContainerTest {
@Test
public void testHasActivity() {
final TaskContainer taskContainer = createTestTaskContainer(mController);
- final TaskFragmentContainer container1 = new TaskFragmentContainer(null /* activity */,
- mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
- final TaskFragmentContainer container2 = new TaskFragmentContainer(null /* activity */,
- mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+ final TaskFragmentContainer container1 = createTaskFragmentContainer(
+ taskContainer, null /* pendingAppearedActivity */, mIntent);
+ final TaskFragmentContainer container2 = createTaskFragmentContainer(
+ taskContainer, null /* pendingAppearedActivity */, mIntent);
// Activity is pending appeared on container2.
container2.addPendingAppearedActivity(mActivity);
@@ -550,17 +547,19 @@ public class TaskFragmentContainerTest {
@Test
public void testNewContainerWithPairedPrimaryContainer() {
final TaskContainer taskContainer = createTestTaskContainer();
- final TaskFragmentContainer tf0 = new TaskFragmentContainer(
- null /* pendingAppearedActivity */, new Intent(), taskContainer, mController,
- null /* pairedPrimaryTaskFragment */);
- final TaskFragmentContainer tf1 = new TaskFragmentContainer(
- null /* pendingAppearedActivity */, new Intent(), taskContainer, mController,
- null /* pairedPrimaryTaskFragment */);
+ mController.addTaskContainer(taskContainer.getTaskId(), taskContainer);
+ final TaskFragmentContainer tf0 = createTaskFragmentContainer(
+ taskContainer, null /* pendingAppearedActivity */, new Intent());
+ final TaskFragmentContainer tf1 = createTaskFragmentContainer(
+ taskContainer, null /* pendingAppearedActivity */, new Intent());
// When tf2 is created with using tf0 as pairedPrimaryContainer, tf2 should be inserted
// right above tf0.
- final TaskFragmentContainer tf2 = new TaskFragmentContainer(
- null /* pendingAppearedActivity */, new Intent(), taskContainer, mController, tf0);
+ final TaskFragmentContainer tf2 = new TaskFragmentContainer.Builder(mController,
+ taskContainer.getTaskId(), null /* activityInTask */)
+ .setPendingAppearedIntent(new Intent())
+ .setPairedPrimaryContainer(tf0)
+ .build();
assertEquals(0, taskContainer.indexOf(tf0));
assertEquals(1, taskContainer.indexOf(tf2));
assertEquals(2, taskContainer.indexOf(tf1));
@@ -569,18 +568,15 @@ public class TaskFragmentContainerTest {
@Test
public void testNewContainerWithPairedPendingAppearedActivity() {
final TaskContainer taskContainer = createTestTaskContainer();
- final TaskFragmentContainer tf0 = new TaskFragmentContainer(
- createMockActivity(), null /* pendingAppearedIntent */, taskContainer, mController,
- null /* pairedPrimaryTaskFragment */);
- final TaskFragmentContainer tf1 = new TaskFragmentContainer(
- null /* pendingAppearedActivity */, new Intent(), taskContainer, mController,
- null /* pairedPrimaryTaskFragment */);
+ final TaskFragmentContainer tf0 = createTaskFragmentContainer(taskContainer,
+ createMockActivity(), null /* pendingAppearedIntent */);
+ final TaskFragmentContainer tf1 = createTaskFragmentContainer(taskContainer,
+ null /* pendingAppearedActivity */, new Intent());
// When tf2 is created with pendingAppearedActivity, tf2 should be inserted below any
// TaskFragment without any Activity.
- final TaskFragmentContainer tf2 = new TaskFragmentContainer(
- createMockActivity(), null /* pendingAppearedIntent */, taskContainer, mController,
- null /* pairedPrimaryTaskFragment */);
+ final TaskFragmentContainer tf2 = createTaskFragmentContainer(taskContainer,
+ createMockActivity(), null /* pendingAppearedIntent */);
assertEquals(0, taskContainer.indexOf(tf0));
assertEquals(1, taskContainer.indexOf(tf2));
assertEquals(2, taskContainer.indexOf(tf1));
@@ -589,9 +585,8 @@ public class TaskFragmentContainerTest {
@Test
public void testIsVisible() {
final TaskContainer taskContainer = createTestTaskContainer();
- final TaskFragmentContainer container = new TaskFragmentContainer(
- null /* pendingAppearedActivity */, new Intent(), taskContainer, mController,
- null /* pairedPrimaryTaskFragment */);
+ final TaskFragmentContainer container = createTaskFragmentContainer(taskContainer,
+ null /* pendingAppearedActivity */, new Intent());
// Not visible when there is not appeared.
assertFalse(container.isVisible());
@@ -617,4 +612,14 @@ public class TaskFragmentContainerTest {
doReturn(activity).when(mController).getActivity(activityToken);
return activity;
}
+
+ private TaskFragmentContainer createTaskFragmentContainer(TaskContainer taskContainer,
+ Activity pendingAppearedActivity, Intent pendingAppearedIntent) {
+ final int taskId = taskContainer.getTaskId();
+ mController.addTaskContainer(taskId, taskContainer);
+ return new TaskFragmentContainer.Builder(mController, taskId, pendingAppearedActivity)
+ .setPendingAppearedActivity(pendingAppearedActivity)
+ .setPendingAppearedIntent(pendingAppearedIntent)
+ .build();
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index f2095b130989..3ded7d246499 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -175,6 +175,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
.setName("home_task_overlay_container")
.setContainerLayer()
.setHidden(false)
+ .setCallsite("ShellTaskOrganizer.mHomeTaskOverlayContainer")
.build();
/**
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/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 38db1ebdc47b..ed0d2b87b03f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -223,6 +223,7 @@ public class DesktopModeVisualIndicator {
mLeash = builder
.setName("Desktop Mode Visual Indicator")
.setContainerLayer()
+ .setCallsite("DesktopModeVisualIndicator.createView")
.build();
t.show(mLeash);
final WindowManager.LayoutParams lp =
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/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index c7f693d8de50..6fcea1fe5560 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -675,6 +675,7 @@ public class PipTransition extends PipTransitionController {
.setContainerLayer()
.setHidden(false)
.setParent(root.getLeash())
+ .setCallsite("PipTransition.startExitAnimation")
.build();
startTransaction.reparent(activitySurface, pipLeash);
// Put the activity at local position with offset in case it is letterboxed.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index c2f4d72a1ddf..ca0d61f8fc9b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -233,6 +233,7 @@ public class TvPipTransition extends PipTransitionController {
.setContainerLayer()
.setHidden(false)
.setParent(root.getLeash())
+ .setCallsite("TvPipTransition.startAnimation")
.build();
startTransaction.reparent(activitySurface, pipLeash);
// Put the activity at local position with offset in case it is letterboxed.
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/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index 1be85d05c16e..ad4f02d13cc6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -280,6 +280,7 @@ public class TransitionAnimationHelper {
.setParent(rootLeash)
.setColorLayer()
.setOpaque(true)
+ .setCallsite("TransitionAnimationHelper.addBackgroundToTransition")
.build();
startTransaction
.setLayer(animationBackgroundSurface, Integer.MIN_VALUE)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 5379ca6cd51d..badce6e93d67 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -132,6 +132,7 @@ class DragResizeInputListener implements AutoCloseable {
.setName("TaskInputSink of " + decorationSurface)
.setContainerLayer()
.setParent(mDecorationSurface)
+ .setCallsite("DragResizeInputListener.constructor")
.build();
mSurfaceControlTransactionSupplier.get()
.setLayer(mInputSinkSurface, WindowDecoration.INPUT_SINK_Z_ORDER)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 2cbe47212c63..0dc512835d65 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -268,6 +268,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
.setName("Decor container of Task=" + mTaskInfo.taskId)
.setContainerLayer()
.setParent(mTaskSurface)
+ .setCallsite("WindowDecoration.relayout_1")
.build();
startT.setTrustedOverlay(mDecorationContainerSurface, true)
@@ -285,6 +286,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
.setName("Caption container of Task=" + mTaskInfo.taskId)
.setContainerLayer()
.setParent(mDecorationContainerSurface)
+ .setCallsite("WindowDecoration.relayout_2")
.build();
}
@@ -575,6 +577,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
.setName(namePrefix + " of Task=" + mTaskInfo.taskId)
.setContainerLayer()
.setParent(mDecorationContainerSurface)
+ .setCallsite("WindowDecoration.addWindow")
.build();
View v = LayoutInflater.from(mDecorWindowContext).inflate(layoutId, null);
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/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 55edff6d9518..d201071620e4 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -81,3 +81,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "hearing_devices_dialog_related_tools"
+ namespace: "accessibility"
+ description: "Shows the related tools for hearing devices dialog."
+ bug: "341648471"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
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/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index c19c08e09349..b8f9ca82f072 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -66,6 +66,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.input.key.onKeyEvent
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
@@ -137,7 +138,7 @@ fun BouncerContent(
// Despite the keyboard only being part of the password bouncer, adding it at this level is
// both necessary to properly handle the keyboard in all layouts and harmless in cases when
// the keyboard isn't used (like the PIN or pattern auth methods).
- modifier = modifier.imePadding(),
+ modifier = modifier.imePadding().onKeyEvent(viewModel::onKeyEvent),
) {
when (layout) {
BouncerSceneLayout.STANDARD_BOUNCER ->
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationHeadsUpHeight.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationHeadsUpHeight.kt
new file mode 100644
index 000000000000..75a565b2d2a0
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationHeadsUpHeight.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.notifications.ui.composable
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.invalidateMeasurement
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntOffset
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
+
+/**
+ * Modify element, which updates the height to the height of current top heads up notification, or
+ * to 0 if there is none.
+ *
+ * @param view Notification stack scroll view
+ */
+fun Modifier.notificationHeadsUpHeight(view: NotificationScrollView) =
+ this then HeadsUpLayoutElement(view)
+
+private data class HeadsUpLayoutElement(
+ val view: NotificationScrollView,
+) : ModifierNodeElement<HeadsUpLayoutNode>() {
+
+ override fun create(): HeadsUpLayoutNode = HeadsUpLayoutNode(view)
+
+ override fun update(node: HeadsUpLayoutNode) {
+ check(view == node.view) { "Trying to reuse the node with a new View." }
+ }
+}
+
+private class HeadsUpLayoutNode(val view: NotificationScrollView) :
+ LayoutModifierNode, Modifier.Node() {
+
+ private val headsUpHeightChangedListener = Runnable { invalidateMeasureIfAttached() }
+
+ override fun onAttach() {
+ super.onAttach()
+ view.addHeadsUpHeightChangedListener(headsUpHeightChangedListener)
+ }
+
+ override fun onDetach() {
+ super.onDetach()
+ view.removeHeadsUpHeightChangedListener(headsUpHeightChangedListener)
+ }
+
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints
+ ): MeasureResult {
+ // TODO(b/339181697) make sure, that the row is already measured.
+ val contentHeight = view.topHeadsUpHeight
+ val placeable =
+ measurable.measure(
+ constraints.copy(minHeight = contentHeight, maxHeight = contentHeight)
+ )
+ return layout(placeable.width, placeable.height) { placeable.place(IntOffset.Zero) }
+ }
+
+ override fun toString(): String {
+ return "HeadsUpLayoutNode(view=$view)"
+ }
+
+ fun invalidateMeasureIfAttached() {
+ if (isAttached) {
+ this.invalidateMeasurement()
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index e6132c6a2a59..c26259f3287c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -67,7 +67,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.NestedScrollBehavior
import com.android.compose.animation.scene.SceneScope
-import com.android.compose.modifiers.height
import com.android.compose.modifiers.thenIf
import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight
import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
@@ -108,18 +107,17 @@ object Notifications {
*/
@Composable
fun SceneScope.HeadsUpNotificationSpace(
+ stackScrollView: NotificationScrollView,
viewModel: NotificationsPlaceholderViewModel,
modifier: Modifier = Modifier,
isPeekFromBottom: Boolean = false,
) {
- val headsUpHeight = viewModel.headsUpHeight.collectAsStateWithLifecycle()
-
Element(
Notifications.Elements.HeadsUpNotificationPlaceholder,
modifier =
modifier
- .height { headsUpHeight.value.roundToInt() }
.fillMaxWidth()
+ .notificationHeadsUpHeight(stackScrollView)
.debugBackground(viewModel, DEBUG_HUN_COLOR)
.onGloballyPositioned { coordinates: LayoutCoordinates ->
val boundsInWindow = coordinates.boundsInWindow()
@@ -152,6 +150,7 @@ fun SceneScope.ConstrainedNotificationStack(
modifier = Modifier.fillMaxSize(),
)
HeadsUpNotificationSpace(
+ stackScrollView = stackScrollView,
viewModel = viewModel,
modifier = Modifier.align(Alignment.TopCenter),
)
@@ -358,7 +357,7 @@ fun SceneScope.NotificationScrollingStack(
.onSizeChanged { size -> stackHeight.intValue = size.height },
)
}
- HeadsUpNotificationSpace(viewModel = viewModel)
+ HeadsUpNotificationSpace(stackScrollView = stackScrollView, viewModel = viewModel)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index d76b19f3fa82..0ee485c496be 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -42,7 +42,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
-import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
@@ -60,7 +59,6 @@ import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.colorResource
-import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
@@ -79,14 +77,13 @@ import com.android.systemui.media.controls.ui.composable.MediaCarousel
import com.android.systemui.media.controls.ui.controller.MediaCarouselController
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.dagger.MediaModule
-import com.android.systemui.notifications.ui.composable.NotificationScrollingStack
+import com.android.systemui.notifications.ui.composable.HeadsUpNotificationSpace
import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility
import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneViewModel
import com.android.systemui.res.R
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.ComposableScene
-import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
import com.android.systemui.shade.ui.composable.Shade
@@ -99,7 +96,6 @@ import com.android.systemui.statusbar.phone.ui.TintedIconManager
import dagger.Lazy
import javax.inject.Inject
import javax.inject.Named
-import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn
@@ -368,15 +364,11 @@ private fun SceneScope.QuickSettingsScene(
Modifier.align(Alignment.CenterHorizontally).sysuiResTag("qs_footer_actions"),
)
}
- NotificationScrollingStack(
+ HeadsUpNotificationSpace(
stackScrollView = notificationStackScrollView,
viewModel = notificationsPlaceholderViewModel,
- shadeSession = shadeSession,
- maxScrimTop = { screenHeight },
- shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
- shadeMode = ShadeMode.Single,
- modifier =
- Modifier.fillMaxWidth().offset { IntOffset(x = 0, y = screenHeight.roundToInt()) },
+ modifier = Modifier.align(Alignment.BottomCenter),
+ isPeekFromBottom = true,
)
}
}
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/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 256687b56f4e..89bafb952211 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -16,6 +16,12 @@
package com.android.systemui.bouncer.ui.viewmodel
+import android.view.KeyEvent.KEYCODE_0
+import android.view.KeyEvent.KEYCODE_4
+import android.view.KeyEvent.KEYCODE_A
+import android.view.KeyEvent.KEYCODE_DEL
+import android.view.KeyEvent.KEYCODE_NUMPAD_0
+import androidx.compose.ui.input.key.KeyEventType
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.SceneKey
@@ -34,6 +40,8 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlin.random.Random
+import kotlin.random.nextInt
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -444,6 +452,44 @@ class PinBouncerViewModelTest : SysuiTestCase() {
assertThat(pin).hasSize(FakeAuthenticationRepository.HINTING_PIN_LENGTH + 1)
}
+ @Test
+ fun onKeyboardInput_pinInput_isUpdated() =
+ testScope.runTest {
+ val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
+ lockDeviceAndOpenPinBouncer()
+ val random = Random(System.currentTimeMillis())
+ // Generate a random 4 digit PIN
+ val expectedPin =
+ with(random) { arrayOf(nextInt(0..9), nextInt(0..9), nextInt(0..9), nextInt(0..9)) }
+
+ // Enter the PIN using NUM pad and normal number keyboard events
+ underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_0 + expectedPin[0])
+ underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_0 + expectedPin[0])
+
+ underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_NUMPAD_0 + expectedPin[1])
+ underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_NUMPAD_0 + expectedPin[1])
+
+ underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_0 + expectedPin[2])
+ underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_0 + expectedPin[2])
+
+ // Enter an additional digit in between and delete it
+ underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_4)
+ underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_4)
+
+ // Delete that additional digit
+ underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_DEL)
+ underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_DEL)
+
+ // Try entering a non digit character, this should be ignored.
+ underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_A)
+ underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_A)
+
+ underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_NUMPAD_0 + expectedPin[3])
+ underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_NUMPAD_0 + expectedPin[3])
+
+ assertThat(pin).containsExactly(*expectedPin)
+ }
+
private fun TestScope.switchToScene(toScene: SceneKey) {
val currentScene by collectLastValue(sceneInteractor.currentScene)
val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer
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/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableListTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableListTest.kt
index 311122d7f8d5..16f30feb7e3b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableListTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableListTest.kt
@@ -27,6 +27,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.ColorCorrectionTile
import com.android.systemui.qs.tiles.ColorInversionTile
+import com.android.systemui.qs.tiles.HearingDevicesTile
import com.android.systemui.qs.tiles.OneHandedModeTile
import com.android.systemui.qs.tiles.ReduceBrightColorsTile
import com.android.systemui.util.mockito.mock
@@ -77,6 +78,10 @@ class A11yShortcutAutoAddableListTest : SysuiTestCase() {
TileSpec.create(ReduceBrightColorsTile.TILE_SPEC),
AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME
),
+ factory.create(
+ TileSpec.create(HearingDevicesTile.TILE_SPEC),
+ AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME
+ ),
)
val autoAddables = A11yShortcutAutoAddableList.getA11yShortcutAutoAddables(factory)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 0f66a93bcbec..3bfc046e46b4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -38,6 +38,7 @@ import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Person;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.FlagsParameterization;
import android.testing.TestableLooper;
@@ -150,6 +151,62 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase {
}
@Test
+ public void testHasNotifications_headsUpManagerMapNotEmpty_true() {
+ final BaseHeadsUpManager bhum = createHeadsUpManager();
+ final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
+ bhum.showNotification(entry);
+
+ assertThat(bhum.mHeadsUpEntryMap).isNotEmpty();
+ assertThat(bhum.hasNotifications()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ public void testHasNotifications_avalancheMapNotEmpty_true() {
+ final BaseHeadsUpManager bhum = createHeadsUpManager();
+ final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
+ mContext);
+ final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry);
+ mAvalancheController.addToNext(headsUpEntry, () -> {});
+
+ assertThat(mAvalancheController.getWaitingEntryList()).isNotEmpty();
+ assertThat(bhum.hasNotifications()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ public void testHasNotifications_false() {
+ final BaseHeadsUpManager bhum = createHeadsUpManager();
+ assertThat(bhum.mHeadsUpEntryMap).isEmpty();
+ assertThat(mAvalancheController.getWaitingEntryList()).isEmpty();
+ assertThat(bhum.hasNotifications()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ public void testGetHeadsUpEntryList_includesAvalancheEntryList() {
+ final BaseHeadsUpManager bhum = createHeadsUpManager();
+ final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
+ mContext);
+ final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry);
+ mAvalancheController.addToNext(headsUpEntry, () -> {});
+
+ assertThat(bhum.getHeadsUpEntryList()).contains(headsUpEntry);
+ }
+
+ @Test
+ @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+ public void testGetHeadsUpEntry_returnsAvalancheEntry() {
+ final BaseHeadsUpManager bhum = createHeadsUpManager();
+ final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
+ mContext);
+ final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry);
+ mAvalancheController.addToNext(headsUpEntry, () -> {});
+
+ assertThat(bhum.getHeadsUpEntry(notifEntry.getKey())).isEqualTo(headsUpEntry);
+ }
+
+ @Test
public void testShowNotification_addsEntry() {
final BaseHeadsUpManager alm = createHeadsUpManager();
final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
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/drawable/qs_hearing_devices_related_tools_background.xml b/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml
new file mode 100644
index 000000000000..627b92b8a779
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<ripple
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/transparent"/>
+ <corners android:radius="@dimen/hearing_devices_preset_spinner_background_radius"/>
+ <stroke
+ android:width="1dp"
+ android:color="?androidprv:attr/textColorTertiary" />
+ </shape>
+ </item>
+</ripple>
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/res/layout/hearing_devices_tile_dialog.xml b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
index 2bf6f80c32d0..4a7bef9f48b9 100644
--- a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
@@ -36,9 +36,8 @@
style="@style/BluetoothTileDialog.Device"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="@dimen/hearing_devices_preset_spinner_height"
android:layout_marginTop="@dimen/hearing_devices_layout_margin"
- android:layout_marginBottom="@dimen/hearing_devices_layout_margin"
+ android:minHeight="@dimen/hearing_devices_preset_spinner_height"
android:gravity="center_vertical"
android:background="@drawable/hearing_devices_preset_spinner_background"
android:popupBackground="@drawable/hearing_devices_preset_spinner_popup_background"
@@ -54,9 +53,10 @@
android:visibility="gone"/>
<androidx.constraintlayout.widget.Barrier
- android:id="@+id/device_barrier"
+ android:id="@+id/device_function_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ app:barrierAllowsGoneWidgets="false"
app:barrierDirection="bottom"
app:constraint_referenced_ids="device_list,preset_spinner" />
@@ -71,7 +71,8 @@
android:contentDescription="@string/accessibility_hearing_device_pair_new_device"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@id/device_barrier"
+ app:layout_constraintTop_toBottomOf="@id/device_function_barrier"
+ app:layout_constraintBottom_toTopOf="@id/related_tools_scroll"
android:drawableStart="@drawable/ic_add"
android:drawablePadding="20dp"
android:drawableTint="?android:attr/textColorPrimary"
@@ -83,4 +84,32 @@
android:maxLines="1"
android:ellipsize="end" />
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/device_barrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierAllowsGoneWidgets="false"
+ app:barrierDirection="bottom"
+ app:constraint_referenced_ids="device_function_barrier, pair_new_device_button" />
+
+ <HorizontalScrollView
+ android:id="@+id/related_tools_scroll"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/bluetooth_dialog_layout_margin"
+ android:layout_marginEnd="@dimen/bluetooth_dialog_layout_margin"
+ android:layout_marginTop="@dimen/hearing_devices_layout_margin"
+ android:scrollbars="none"
+ android:fillViewport="true"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/preset_spinner">
+ <LinearLayout
+ android:id="@+id/related_tools_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+ </LinearLayout>
+ </HorizontalScrollView>
+
</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/hearing_tool_item.xml b/packages/SystemUI/res/layout/hearing_tool_item.xml
new file mode 100644
index 000000000000..84462d08d4a0
--- /dev/null
+++ b/packages/SystemUI/res/layout/hearing_tool_item.xml
@@ -0,0 +1,53 @@
+<!--
+ 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/tool_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:focusable="true"
+ android:clickable="true"
+ android:layout_weight="1">
+ <FrameLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="@dimen/hearing_devices_tool_icon_frame_width"
+ android:layout_height="@dimen/hearing_devices_tool_icon_frame_height"
+ android:background="@drawable/qs_hearing_devices_related_tools_background"
+ android:focusable="false" >
+ <ImageView
+ android:id="@+id/tool_icon"
+ android:layout_width="@dimen/hearing_devices_tool_icon_size"
+ android:layout_height="@dimen/hearing_devices_tool_icon_size"
+ android:layout_gravity="center"
+ android:scaleType="fitCenter"
+ android:focusable="false" />
+ </FrameLayout>
+ <TextView
+ android:id="@+id/tool_name"
+ android:textDirection="locale"
+ android:textAlignment="center"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/hearing_devices_layout_margin"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textSize="12sp"
+ android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
+ android:focusable="false" />
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 638785402055..fb883640c9a9 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -125,6 +125,20 @@
<!-- Use collapsed layout for media player in landscape QQS -->
<bool name="config_quickSettingsMediaLandscapeCollapsed">true</bool>
+ <!-- For hearing devices related tool list. Need to be in ComponentName format (package/class).
+ Should be activity to be launched.
+ Already contains tool that holds intent: "com.android.settings.action.live_caption".
+ Maximum number is 3. -->
+ <string-array name="config_quickSettingsHearingDevicesRelatedToolName" translatable="false">
+ </string-array>
+
+ <!-- The drawable resource names. If provided, it will replace the corresponding icons in
+ config_quickSettingsHearingDevicesRelatedToolName. Can be empty to use original icons.
+ Already contains tool that holds intent: "com.android.settings.action.live_caption".
+ Maximum number is 3. -->
+ <string-array name="config_quickSettingsHearingDevicesRelatedToolIcon" translatable="false">
+ </string-array>
+
<!-- Show indicator for Wifi on but not connected. -->
<bool name="config_showWifiIndicatorWhenEnabled">false</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7d7a5d4dbf14..edd3d77555f7 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1778,6 +1778,9 @@
<dimen name="hearing_devices_preset_spinner_text_padding_vertical">15dp</dimen>
<dimen name="hearing_devices_preset_spinner_arrow_size">24dp</dimen>
<dimen name="hearing_devices_preset_spinner_background_radius">28dp</dimen>
+ <dimen name="hearing_devices_tool_icon_frame_width">94dp</dimen>
+ <dimen name="hearing_devices_tool_icon_frame_height">64dp</dimen>
+ <dimen name="hearing_devices_tool_icon_size">28dp</dimen>
<!-- Height percentage of the parent container occupied by the communal view -->
<item name="communal_source_height_percentage" format="float" type="dimen">0.80</item>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6f2806d80ef3..c038a8207d43 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -917,6 +917,8 @@
<string name="hearing_devices_presets_error">Couldn\'t update preset</string>
<!-- QuickSettings: Title for hearing aids presets. Preset is a set of hearing aid settings. User can apply different settings in different environments (e.g. Outdoor, Restaurant, Home) [CHAR LIMIT=40]-->
<string name="hearing_devices_preset_label">Preset</string>
+ <!-- QuickSettings: Tool name for hearing devices dialog related tools [CHAR LIMIT=40]-->
+ <string name="live_caption_title">Live Caption</string>
<!--- Title of dialog triggered if the microphone is disabled but an app tried to access it. [CHAR LIMIT=150] -->
<string name="sensor_privacy_start_use_mic_dialog_title">Unblock device microphone?</string>
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/accessibility/data/repository/AccessibilityQsShortcutsRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepository.kt
index 4069cece7790..63791f9228c0 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepository.kt
@@ -30,6 +30,7 @@ import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.ColorCorrectionTile
import com.android.systemui.qs.tiles.ColorInversionTile
import com.android.systemui.qs.tiles.FontScalingTile
+import com.android.systemui.qs.tiles.HearingDevicesTile
import com.android.systemui.qs.tiles.OneHandedModeTile
import com.android.systemui.qs.tiles.ReduceBrightColorsTile
import javax.inject.Inject
@@ -74,6 +75,8 @@ constructor(
.REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME,
FontScalingTile.TILE_SPEC to
AccessibilityShortcutController.FONT_SIZE_TILE_COMPONENT_NAME,
+ HearingDevicesTile.TILE_SPEC to
+ AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_TILE_COMPONENT_NAME
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index 28dd2338ff2b..961d6aa1b821 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -25,10 +25,14 @@ import android.bluetooth.BluetoothHapClient;
import android.bluetooth.BluetoothHapPresetInfo;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.Visibility;
@@ -36,7 +40,10 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.Spinner;
+import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
@@ -69,6 +76,7 @@ import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -78,12 +86,15 @@ import java.util.stream.Collectors;
*/
public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
HearingDeviceItemCallback, BluetoothCallback {
-
+ private static final String TAG = "HearingDevicesDialogDelegate";
@VisibleForTesting
static final String ACTION_BLUETOOTH_DEVICE_DETAILS =
"com.android.settings.BLUETOOTH_DEVICE_DETAIL_SETTINGS";
private static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
private static final String KEY_BLUETOOTH_ADDRESS = "device_address";
+ @VisibleForTesting
+ static final Intent LIVE_CAPTION_INTENT = new Intent(
+ "com.android.settings.action.live_caption");
private final SystemUIDialog.Factory mSystemUIDialogFactory;
private final DialogTransitionAnimator mDialogTransitionAnimator;
private final ActivityStarter mActivityStarter;
@@ -102,6 +113,7 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
private Spinner mPresetSpinner;
private ArrayAdapter<String> mPresetInfoAdapter;
private Button mPairButton;
+ private LinearLayout mRelatedToolsContainer;
private final HearingDevicesPresetsController.PresetCallback mPresetCallback =
new HearingDevicesPresetsController.PresetCallback() {
@Override
@@ -253,10 +265,14 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
mPairButton = dialog.requireViewById(R.id.pair_new_device_button);
mDeviceList = dialog.requireViewById(R.id.device_list);
mPresetSpinner = dialog.requireViewById(R.id.preset_spinner);
+ mRelatedToolsContainer = dialog.requireViewById(R.id.related_tools_container);
setupDeviceListView(dialog);
setupPresetSpinner(dialog);
setupPairNewDeviceButton(dialog, mShowPairNewDevice ? VISIBLE : GONE);
+ if (com.android.systemui.Flags.hearingDevicesDialogRelatedTools()) {
+ setupRelatedToolsView(dialog);
+ }
}
@Override
@@ -351,6 +367,34 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
}
}
+ private void setupRelatedToolsView(SystemUIDialog dialog) {
+ final Context context = dialog.getContext();
+ final List<ToolItem> toolItemList = new ArrayList<>();
+ final String[] toolNameArray;
+ final String[] toolIconArray;
+
+ ToolItem preInstalledItem = getLiveCaption(context);
+ if (preInstalledItem != null) {
+ toolItemList.add(preInstalledItem);
+ }
+ try {
+ toolNameArray = context.getResources().getStringArray(
+ R.array.config_quickSettingsHearingDevicesRelatedToolName);
+ toolIconArray = context.getResources().getStringArray(
+ R.array.config_quickSettingsHearingDevicesRelatedToolIcon);
+ toolItemList.addAll(
+ HearingDevicesToolItemParser.parseStringArray(context, toolNameArray,
+ toolIconArray));
+ } catch (Resources.NotFoundException e) {
+ Log.i(TAG, "No hearing devices related tool config resource");
+ }
+ final int listSize = toolItemList.size();
+ for (int i = 0; i < listSize; i++) {
+ View view = createHearingToolView(context, toolItemList.get(i));
+ mRelatedToolsContainer.addView(view);
+ }
+ }
+
private void refreshPresetInfoAdapter(List<BluetoothHapPresetInfo> presetInfos,
int activePresetIndex) {
mPresetInfoAdapter.clear();
@@ -400,6 +444,40 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
return null;
}
+ @NonNull
+ private View createHearingToolView(Context context, ToolItem item) {
+ View view = LayoutInflater.from(context).inflate(R.layout.hearing_tool_item,
+ mRelatedToolsContainer, false);
+ ImageView icon = view.requireViewById(R.id.tool_icon);
+ TextView text = view.requireViewById(R.id.tool_name);
+ view.setContentDescription(item.getToolName());
+ icon.setImageDrawable(item.getToolIcon());
+ text.setText(item.getToolName());
+ Intent intent = item.getToolIntent();
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ view.setOnClickListener(
+ v -> {
+ dismissDialogIfExists();
+ mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0,
+ mDialogTransitionAnimator.createActivityTransitionController(view));
+ });
+ return view;
+ }
+
+ private ToolItem getLiveCaption(Context context) {
+ final PackageManager packageManager = context.getPackageManager();
+ LIVE_CAPTION_INTENT.setPackage(packageManager.getSystemCaptionsServicePackageName());
+ final List<ResolveInfo> resolved = packageManager.queryIntentActivities(LIVE_CAPTION_INTENT,
+ /* flags= */ 0);
+ if (!resolved.isEmpty()) {
+ return new ToolItem(context.getString(R.string.live_caption_title),
+ context.getDrawable(R.drawable.ic_volume_odi_captions),
+ LIVE_CAPTION_INTENT);
+ }
+
+ return null;
+ }
+
private void dismissDialogIfExists() {
if (mDialog != null) {
mDialog.dismiss();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java
new file mode 100644
index 000000000000..2006726e6847
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java
@@ -0,0 +1,129 @@
+/*
+ * 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.accessibility.hearingaid;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Utility class for managing and parsing tool items related to hearing devices.
+ */
+public class HearingDevicesToolItemParser {
+ private static final String TAG = "HearingDevicesToolItemParser";
+ private static final String SPLIT_DELIMITER = "/";
+ private static final String RES_TYPE = "drawable";
+ @VisibleForTesting
+ static final int MAX_NUM = 3;
+
+ /**
+ * Parses the string arrays to create a list of {@link ToolItem}.
+ *
+ * This method validates the structure of {@code toolNameArray} and {@code toolIconArray}.
+ * If {@code toolIconArray} is empty or mismatched in length with {@code toolNameArray}, the
+ * icon from {@link ActivityInfo#loadIcon(PackageManager)} will be used instead.
+ *
+ * @param context A valid context.
+ * @param toolNameArray An array of tool names in the format of {@link ComponentName}.
+ * @param toolIconArray An optional array of resource names for tool icons (can be empty).
+ * @return A list of {@link ToolItem} or an empty list if there are errors during parsing.
+ */
+ public static ImmutableList<ToolItem> parseStringArray(Context context, String[] toolNameArray,
+ String[] toolIconArray) {
+ if (toolNameArray.length == 0) {
+ Log.i(TAG, "Empty hearing device related tool name in array.");
+ return ImmutableList.of();
+ }
+ // For the performance concern, especially `getIdentifier` in `parseValidIcon`, we will
+ // limit the maximum number.
+ String[] nameArrayCpy = Arrays.copyOfRange(toolNameArray, 0,
+ Math.min(toolNameArray.length, MAX_NUM));
+ String[] iconArrayCpy = Arrays.copyOfRange(toolIconArray, 0,
+ Math.min(toolIconArray.length, MAX_NUM));
+
+ final PackageManager packageManager = context.getPackageManager();
+ final ImmutableList.Builder<ToolItem> toolItemList = ImmutableList.builder();
+ final List<ActivityInfo> activityInfoList = parseValidActivityInfo(context, nameArrayCpy);
+ final List<Drawable> iconList = parseValidIcon(context, iconArrayCpy);
+ final int size = activityInfoList.size();
+ // Only use custom icon if provided icon's list size is equal to provided name's list size.
+ final boolean useCustomIcons = (size == iconList.size());
+
+ for (int i = 0; i < size; i++) {
+ toolItemList.add(new ToolItem(
+ activityInfoList.get(i).loadLabel(packageManager).toString(),
+ useCustomIcons ? iconList.get(i)
+ : activityInfoList.get(i).loadIcon(packageManager),
+ new Intent(Intent.ACTION_MAIN).setComponent(
+ activityInfoList.get(i).getComponentName())
+ ));
+ }
+
+ return toolItemList.build();
+ }
+
+ private static List<ActivityInfo> parseValidActivityInfo(Context context,
+ String[] toolNameArray) {
+ final PackageManager packageManager = context.getPackageManager();
+ final List<ActivityInfo> activityInfoList = new ArrayList<>();
+ for (String toolName : toolNameArray) {
+ String[] nameParts = toolName.split(SPLIT_DELIMITER);
+ if (nameParts.length == 2) {
+ ComponentName componentName = ComponentName.unflattenFromString(toolName);
+ try {
+ ActivityInfo activityInfo = packageManager.getActivityInfo(
+ componentName, /* flags= */ 0);
+ activityInfoList.add(activityInfo);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Unable to find hearing device related tool: "
+ + componentName.flattenToString());
+ }
+ } else {
+ Log.e(TAG, "Malformed hearing device related tool name item in array: "
+ + toolName);
+ }
+ }
+ return activityInfoList;
+ }
+
+ private static List<Drawable> parseValidIcon(Context context, String[] toolIconArray) {
+ final List<Drawable> drawableList = new ArrayList<>();
+ for (String icon : toolIconArray) {
+ int resId = context.getResources().getIdentifier(icon, RES_TYPE,
+ context.getPackageName());
+ try {
+ drawableList.add(context.getDrawable(resId));
+ } catch (Resources.NotFoundException e) {
+ Log.e(TAG, "Resource does not exist: " + icon);
+ }
+ }
+ return drawableList;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt
new file mode 100644
index 000000000000..66bb2b5e2328
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.accessibility.hearingaid
+
+import android.content.Intent
+import android.graphics.drawable.Drawable
+
+data class ToolItem(
+ var toolName: String = "",
+ var toolIcon: Drawable,
+ var toolIntent: Intent,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 7c41b75d7105..40a141dcec77 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -20,6 +20,8 @@ import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyResources
import android.content.Context
import android.graphics.Bitmap
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.type
import androidx.core.graphics.drawable.toBitmap
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.SceneKey
@@ -326,7 +328,8 @@ class BouncerViewModel(
{ message },
failedAttempts,
remainingAttempts,
- ) ?: message
+ )
+ ?: message
} else {
message
}
@@ -343,7 +346,8 @@ class BouncerViewModel(
.KEYGUARD_DIALOG_FAILED_ATTEMPTS_ERASING_PROFILE,
{ message },
failedAttempts,
- ) ?: message
+ )
+ ?: message
} else {
message
}
@@ -377,6 +381,19 @@ class BouncerViewModel(
Swipe(SwipeDirection.Down) to UserActionResult(prevScene),
)
+ /**
+ * Notifies that a key event has occurred.
+ *
+ * @return `true` when the [KeyEvent] was consumed as user input on bouncer; `false` otherwise.
+ */
+ fun onKeyEvent(keyEvent: KeyEvent): Boolean {
+ return (authMethodViewModel.value as? PinBouncerViewModel)?.onKeyEvent(
+ keyEvent.type,
+ keyEvent.nativeKeyEvent.keyCode
+ )
+ ?: false
+ }
+
data class DialogViewModel(
val text: String,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index 4c2380c5e4db..aa447ffac154 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -19,6 +19,14 @@
package com.android.systemui.bouncer.ui.viewmodel
import android.content.Context
+import android.view.KeyEvent.KEYCODE_0
+import android.view.KeyEvent.KEYCODE_9
+import android.view.KeyEvent.KEYCODE_DEL
+import android.view.KeyEvent.KEYCODE_NUMPAD_0
+import android.view.KeyEvent.KEYCODE_NUMPAD_9
+import android.view.KeyEvent.isConfirmKey
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.KeyEventType
import com.android.keyguard.PinShapeAdapter
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
@@ -196,6 +204,44 @@ class PinBouncerViewModel(
else -> ActionButtonAppearance.Shown
}
}
+
+ /**
+ * Notifies that a key event has occurred.
+ *
+ * @return `true` when the [KeyEvent] was consumed as user input on bouncer; `false` otherwise.
+ */
+ fun onKeyEvent(type: KeyEventType, keyCode: Int): Boolean {
+ return when (type) {
+ KeyEventType.KeyUp -> {
+ if (isConfirmKey(keyCode)) {
+ onAuthenticateButtonClicked()
+ true
+ } else {
+ false
+ }
+ }
+ KeyEventType.KeyDown -> {
+ when (keyCode) {
+ KEYCODE_DEL -> {
+ onBackspaceButtonClicked()
+ true
+ }
+ in KEYCODE_0..KEYCODE_9 -> {
+ onPinButtonClicked(keyCode - KEYCODE_0)
+ true
+ }
+ in KEYCODE_NUMPAD_0..KEYCODE_NUMPAD_9 -> {
+ onPinButtonClicked(keyCode - KEYCODE_NUMPAD_0)
+ true
+ }
+ else -> {
+ false
+ }
+ }
+ }
+ else -> false
+ }
+ }
}
/** Appearance of pin-pad action buttons. */
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/pipeline/domain/autoaddable/A11yShortcutAutoAddableList.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableList.kt
index 08e39204386e..a0c9737de0ee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableList.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableList.kt
@@ -22,6 +22,7 @@ import com.android.systemui.qs.pipeline.domain.model.AutoAddable
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.ColorCorrectionTile
import com.android.systemui.qs.tiles.ColorInversionTile
+import com.android.systemui.qs.tiles.HearingDevicesTile
import com.android.systemui.qs.tiles.OneHandedModeTile
import com.android.systemui.qs.tiles.ReduceBrightColorsTile
@@ -50,6 +51,10 @@ object A11yShortcutAutoAddableList {
TileSpec.create(ReduceBrightColorsTile.TILE_SPEC),
AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME
),
+ factory.create(
+ TileSpec.create(HearingDevicesTile.TILE_SPEC),
+ AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME
+ )
)
} else {
emptySet()
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/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index 0d8030f02948..526800954427 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -19,9 +19,14 @@ package com.android.systemui.statusbar;
import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+import static com.android.systemui.Flags.validateKeyboardShortcutHelperIconUri;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.AppGlobals;
+import android.app.SynchronousUserSwitchObserver;
+import android.app.UserSwitchObserver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -37,6 +42,7 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.hardware.input.InputManagerGlobal;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.RemoteException;
import android.text.Editable;
@@ -136,6 +142,8 @@ public final class KeyboardShortcutListSearch {
};
private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final HandlerThread mHandlerThread = new HandlerThread("KeyboardShortcutHelper");
+ @VisibleForTesting Handler mBackgroundHandler;
@VisibleForTesting public Context mContext;
private final IPackageManager mPackageManager;
@@ -143,6 +151,13 @@ public final class KeyboardShortcutListSearch {
private KeyCharacterMap mKeyCharacterMap;
private KeyCharacterMap mBackupKeyCharacterMap;
+ private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) throws RemoteException {
+ dismiss();
+ }
+ };
+
@VisibleForTesting
KeyboardShortcutListSearch(Context context, WindowManager windowManager) {
this.mContext = new ContextThemeWrapper(
@@ -413,36 +428,75 @@ public final class KeyboardShortcutListSearch {
private boolean mAppShortcutsReceived;
private boolean mImeShortcutsReceived;
- @VisibleForTesting
- public void showKeyboardShortcuts(int deviceId) {
- retrieveKeyCharacterMap(deviceId);
- mAppShortcutsReceived = false;
- mImeShortcutsReceived = false;
- mWindowManager.requestAppKeyboardShortcuts(result -> {
- // Add specific app shortcuts
+ private void onAppSpecificShortcutsReceived(List<KeyboardShortcutGroup> result) {
+ // Add specific app shortcuts
+ if (result != null) {
if (result.isEmpty()) {
mCurrentAppPackageName = null;
mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, false);
} else {
mCurrentAppPackageName = result.get(0).getPackageName();
- mSpecificAppGroup.addAll(reMapToKeyboardShortcutMultiMappingGroup(result));
+ if (validateKeyboardShortcutHelperIconUri()) {
+ KeyboardShortcuts.sanitiseShortcuts(result);
+ }
+ mSpecificAppGroup.addAll(
+ reMapToKeyboardShortcutMultiMappingGroup(result));
mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, true);
}
- mAppShortcutsReceived = true;
- if (mImeShortcutsReceived) {
- mergeAndShowKeyboardShortcutsGroups();
- }
- }, deviceId);
- mWindowManager.requestImeKeyboardShortcuts(result -> {
- // Add specific Ime shortcuts
+ }
+ mAppShortcutsReceived = true;
+ if (mImeShortcutsReceived) {
+ mergeAndShowKeyboardShortcutsGroups();
+ }
+ }
+
+ private void onImeSpecificShortcutsReceived(List<KeyboardShortcutGroup> result) {
+ // Add specific Ime shortcuts
+ if (result != null) {
if (!result.isEmpty()) {
- mInputGroup.addAll(reMapToKeyboardShortcutMultiMappingGroup(result));
+ if (validateKeyboardShortcutHelperIconUri()) {
+ KeyboardShortcuts.sanitiseShortcuts(result);
+ }
+ mInputGroup.addAll(
+ reMapToKeyboardShortcutMultiMappingGroup(result));
}
- mImeShortcutsReceived = true;
- if (mAppShortcutsReceived) {
- mergeAndShowKeyboardShortcutsGroups();
+ }
+ mImeShortcutsReceived = true;
+ if (mAppShortcutsReceived) {
+ mergeAndShowKeyboardShortcutsGroups();
+ }
+ }
+
+ @VisibleForTesting
+ public void showKeyboardShortcuts(int deviceId) {
+ if (mBackgroundHandler == null) {
+ mHandlerThread.start();
+ mBackgroundHandler = new Handler(mHandlerThread.getLooper());
+ }
+
+ if (validateKeyboardShortcutHelperIconUri()) {
+ try {
+ ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
+ } catch (RemoteException e) {
+ Log.e(TAG, "could not register user switch observer", e);
}
- }, deviceId);
+ }
+
+ retrieveKeyCharacterMap(deviceId);
+ mAppShortcutsReceived = false;
+ mImeShortcutsReceived = false;
+ mWindowManager.requestAppKeyboardShortcuts(
+ result -> {
+ mBackgroundHandler.post(() -> {
+ onAppSpecificShortcutsReceived(result);
+ });
+ }, deviceId);
+ mWindowManager.requestImeKeyboardShortcuts(
+ result -> {
+ mBackgroundHandler.post(() -> {
+ onImeSpecificShortcutsReceived(result);
+ });
+ }, deviceId);
}
private void mergeAndShowKeyboardShortcutsGroups() {
@@ -508,6 +562,14 @@ public final class KeyboardShortcutListSearch {
mKeyboardShortcutsBottomSheetDialog.dismiss();
mKeyboardShortcutsBottomSheetDialog = null;
}
+ mHandlerThread.quit();
+ if (validateKeyboardShortcutHelperIconUri()) {
+ try {
+ ActivityManager.getService().unregisterUserSwitchObserver(mUserSwitchObserver);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not unregister user switch observer", e);
+ }
+ }
}
private KeyboardShortcutMultiMappingGroup getMultiMappingSystemShortcuts(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 21f608e13f5c..d00916a1c1a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -20,11 +20,16 @@ import static android.content.Context.LAYOUT_INFLATER_SERVICE;
import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+import static com.android.systemui.Flags.validateKeyboardShortcutHelperIconUri;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.AppGlobals;
import android.app.Dialog;
+import android.app.SynchronousUserSwitchObserver;
+import android.app.UserSwitchObserver;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
@@ -39,6 +44,7 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.hardware.input.InputManager;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
@@ -93,6 +99,8 @@ public final class KeyboardShortcuts {
};
private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final HandlerThread mHandlerThread = new HandlerThread("KeyboardShortcutHelper");
+ @VisibleForTesting Handler mBackgroundHandler;
@VisibleForTesting public Context mContext;
private final IPackageManager mPackageManager;
private final OnClickListener mDialogCloseListener = new DialogInterface.OnClickListener() {
@@ -129,6 +137,13 @@ public final class KeyboardShortcuts {
@Nullable private List<KeyboardShortcutGroup> mReceivedAppShortcutGroups = null;
@Nullable private List<KeyboardShortcutGroup> mReceivedImeShortcutGroups = null;
+ private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) throws RemoteException {
+ dismiss();
+ }
+ };
+
@VisibleForTesting
KeyboardShortcuts(Context context, WindowManager windowManager) {
this.mContext = new ContextThemeWrapper(
@@ -374,21 +389,68 @@ public final class KeyboardShortcuts {
@VisibleForTesting
public void showKeyboardShortcuts(int deviceId) {
+ if (mBackgroundHandler == null) {
+ mHandlerThread.start();
+ mBackgroundHandler = new Handler(mHandlerThread.getLooper());
+ }
+
+ if (validateKeyboardShortcutHelperIconUri()) {
+ try {
+ ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
+ } catch (RemoteException e) {
+ Log.e(TAG, "could not register user switch observer", e);
+ }
+ }
+
retrieveKeyCharacterMap(deviceId);
+
mReceivedAppShortcutGroups = null;
mReceivedImeShortcutGroups = null;
+
mWindowManager.requestAppKeyboardShortcuts(
result -> {
- mReceivedAppShortcutGroups = result;
- maybeMergeAndShowKeyboardShortcuts();
+ mBackgroundHandler.post(() -> {
+ onAppSpecificShortcutsReceived(result);
+ });
}, deviceId);
mWindowManager.requestImeKeyboardShortcuts(
result -> {
- mReceivedImeShortcutGroups = result;
- maybeMergeAndShowKeyboardShortcuts();
+ mBackgroundHandler.post(() -> {
+ onImeSpecificShortcutsReceived(result);
+ });
}, deviceId);
}
+ private void onAppSpecificShortcutsReceived(List<KeyboardShortcutGroup> result) {
+ mReceivedAppShortcutGroups =
+ result == null ? Collections.emptyList() : result;
+
+ if (validateKeyboardShortcutHelperIconUri()) {
+ sanitiseShortcuts(mReceivedAppShortcutGroups);
+ }
+
+ maybeMergeAndShowKeyboardShortcuts();
+ }
+
+ private void onImeSpecificShortcutsReceived(List<KeyboardShortcutGroup> result) {
+ mReceivedImeShortcutGroups =
+ result == null ? Collections.emptyList() : result;
+
+ if (validateKeyboardShortcutHelperIconUri()) {
+ sanitiseShortcuts(mReceivedImeShortcutGroups);
+ }
+
+ maybeMergeAndShowKeyboardShortcuts();
+ }
+
+ static void sanitiseShortcuts(List<KeyboardShortcutGroup> shortcutGroups) {
+ for (KeyboardShortcutGroup group : shortcutGroups) {
+ for (KeyboardShortcutInfo info : group.getItems()) {
+ info.clearIcon();
+ }
+ }
+ }
+
private void maybeMergeAndShowKeyboardShortcuts() {
if (mReceivedAppShortcutGroups == null || mReceivedImeShortcutGroups == null) {
return;
@@ -413,6 +475,14 @@ public final class KeyboardShortcuts {
mKeyboardShortcutsDialog.dismiss();
mKeyboardShortcutsDialog = null;
}
+ mHandlerThread.quit();
+ if (validateKeyboardShortcutHelperIconUri()) {
+ try {
+ ActivityManager.getService().unregisterUserSwitchObserver(mUserSwitchObserver);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not unregister user switch observer", e);
+ }
+ }
}
private KeyboardShortcutGroup getSystemShortcuts() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index edd2961fe119..99ce454126cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2843,14 +2843,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mIsSystemChildExpanded = expanded;
}
- public void setLayoutListener(LayoutListener listener) {
+ public void setLayoutListener(@Nullable LayoutListener listener) {
mLayoutListener = listener;
}
- public void removeListener() {
- mLayoutListener = null;
- }
-
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Trace.beginSection(appendTraceStyleTag("ExpNotRow#onLayout"));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index c10c09c49c6b..bdfbc4b53943 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -242,7 +242,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
public void onLayout() {
mIconsPlaced = false; // Force icons to be re-placed
setMenuLocation();
- mParent.removeListener();
+ mParent.setLayoutListener(null);
}
private void createMenuViews(boolean resetState) {
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 a9d7cc003b79..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;
@@ -255,7 +254,8 @@ public class NotificationStackScrollLayout
* The raw amount of the overScroll on the bottom, which is not rubber-banded.
*/
private float mOverScrolledBottomPixels;
- private ListenerSet<Runnable> mStackHeightChangedListeners = new ListenerSet<>();
+ private final ListenerSet<Runnable> mStackHeightChangedListeners = new ListenerSet<>();
+ private final ListenerSet<Runnable> mHeadsUpHeightChangedListeners = new ListenerSet<>();
private NotificationLogger.OnChildLocationsChangedListener mListener;
private OnNotificationLocationsChangedListener mLocationsChangedListener;
private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
@@ -1114,6 +1114,28 @@ public class NotificationStackScrollLayout
mStackHeightChangedListeners.remove(runnable);
}
+ private void notifyHeadsUpHeightChangedForView(View view) {
+ if (mTopHeadsUpRow == view) {
+ notifyHeadsUpHeightChangedListeners();
+ }
+ }
+
+ private void notifyHeadsUpHeightChangedListeners() {
+ for (Runnable listener : mHeadsUpHeightChangedListeners) {
+ listener.run();
+ }
+ }
+
+ @Override
+ public void addHeadsUpHeightChangedListener(@NonNull Runnable runnable) {
+ mHeadsUpHeightChangedListeners.addIfAbsent(runnable);
+ }
+
+ @Override
+ public void removeHeadsUpHeightChangedListener(@NonNull Runnable runnable) {
+ mHeadsUpHeightChangedListeners.remove(runnable);
+ }
+
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (!mSuppressChildrenMeasureAndLayout) {
@@ -2444,6 +2466,11 @@ public class NotificationStackScrollLayout
return mScrollViewFields.getIntrinsicStackHeight();
}
+ @Override
+ public int getTopHeadsUpHeight() {
+ return getTopHeadsUpPinnedHeight();
+ }
+
/**
* Calculate the gap height between two different views
*
@@ -2816,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
@@ -4193,12 +4212,14 @@ public class NotificationStackScrollLayout
requestAnimationOnViewResize(row);
}
requestChildrenUpdate();
+ notifyHeadsUpHeightChangedForView(view);
mAnimateStackYForContentHeightChange = previouslyNeededAnimation;
}
void onChildHeightReset(ExpandableView view) {
updateAnimationState(view);
updateChronometerForChild(view);
+ notifyHeadsUpHeightChangedForView(view);
}
private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
@@ -5573,6 +5594,7 @@ public class NotificationStackScrollLayout
*/
public void setTopHeadsUpRow(@Nullable ExpandableNotificationRow topHeadsUpRow) {
mTopHeadsUpRow = topHeadsUpRow;
+ notifyHeadsUpHeightChangedListeners();
}
public boolean getIsExpanded() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
index 463c631db32f..f6d9351952f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
@@ -27,9 +27,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
@SysUISingleton
class NotificationViewHeightRepository @Inject constructor() {
- /** The height in px of the current heads up notification. */
- val headsUpHeight = MutableStateFlow(0f)
-
/**
* The amount in px that the notification stack should scroll due to internal expansion. This
* should only happen when a notification expansion hits the bottom of the screen, so it is
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
index e7acbe3ab0b0..afcf3ae7d5b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
@@ -65,9 +65,6 @@ constructor(
}
.distinctUntilChanged()
- /** The height in px of the contents of the HUN. */
- val headsUpHeight: StateFlow<Float> = viewHeightRepository.headsUpHeight.asStateFlow()
-
/** The alpha of the Notification Stack for the brightness mirror */
val alphaForBrightnessMirror: StateFlow<Float> =
placeholderRepository.alphaForBrightnessMirror.asStateFlow()
@@ -110,11 +107,6 @@ constructor(
placeholderRepository.shadeScrimBounds.value = bounds
}
- /** Sets the height of heads up notification. */
- fun setHeadsUpHeight(height: Float) {
- viewHeightRepository.headsUpHeight.value = height
- }
-
/** Sets whether the notification stack is scrolled to the top. */
fun setScrolledToTop(scrolledToTop: Boolean) {
placeholderRepository.scrolledToTop.value = scrolledToTop
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
index 14b882f974d2..eaaa9a1523c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
@@ -32,6 +32,9 @@ interface NotificationScrollView {
*/
val intrinsicStackHeight: Int
+ /** Height in pixels required to display the top HeadsUp Notification. */
+ val topHeadsUpHeight: Int
+
/**
* Since this is an interface rather than a literal View, this provides cast-like access to the
* underlying view.
@@ -72,9 +75,18 @@ interface NotificationScrollView {
/** Sets whether the view is displayed in doze mode. */
fun setDozing(dozing: Boolean)
- /** Sets a listener to be notified, when the stack height might have changed. */
+ /** Adds a listener to be notified, when the stack height might have changed. */
fun addStackHeightChangedListener(runnable: Runnable)
/** @see addStackHeightChangedListener */
fun removeStackHeightChangedListener(runnable: Runnable)
+
+ /**
+ * Adds a listener to be notified, when the height of the top heads up notification might have
+ * changed.
+ */
+ fun addHeadsUpHeightChangedListener(runnable: Runnable)
+
+ /** @see addHeadsUpHeightChangedListener */
+ fun removeHeadsUpHeightChangedListener(runnable: Runnable)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index 622d8e7b2307..fd08e898fce3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -80,7 +80,6 @@ constructor(
launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } }
launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
- launch { viewModel.headsUpTop.collect { view.setHeadsUpTop(it) } }
launch { viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) } }
launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } }
launch { viewModel.isDozing.collect { isDozing -> view.setDozing(isDozing) } }
@@ -88,11 +87,9 @@ constructor(
launchAndDispose {
view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
view.setCurrentGestureOverscrollConsumer(viewModel.currentGestureOverscrollConsumer)
- view.setHeadsUpHeightConsumer(viewModel.headsUpHeightConsumer)
DisposableHandle {
view.setSyntheticScrollConsumer(null)
view.setCurrentGestureOverscrollConsumer(null)
- view.setHeadsUpHeightConsumer(null)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index c2ce1144fe1b..a99fbfcc7907 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -150,8 +150,6 @@ constructor(
*/
val currentGestureOverscrollConsumer: (Boolean) -> Unit =
stackAppearanceInteractor::setCurrentGestureOverscroll
- /** Receives the height of the heads up notification. */
- val headsUpHeightConsumer: (Float) -> Unit = stackAppearanceInteractor::setHeadsUpHeight
/** Whether the notification stack is scrollable or not. */
val isScrollable: Flow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index 97b86e3371f5..ea33be0ea4ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -29,7 +29,6 @@ import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrim
import com.android.systemui.util.kotlin.FlowDumperImpl
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.StateFlow
/**
* ViewModel used by the Notification placeholders inside the scene container to update the
@@ -74,9 +73,6 @@ constructor(
val shadeScrimRounding: Flow<ShadeScrimRounding> =
interactor.shadeScrimRounding.dumpWhileCollecting("shadeScrimRounding")
- /** The height in px of the contents of the HUN. */
- val headsUpHeight: StateFlow<Float> = interactor.headsUpHeight.dumpValue("headsUpHeight")
-
/**
* The amount [0-1] that the shade or quick settings has been opened. At 0, the shade is closed;
* at 1, either the shade or quick settings is open.
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/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index ffc859eb9197..4bf122dd3b6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -86,6 +86,8 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements
private final List<OnHeadsUpPhoneListenerChange> mHeadsUpPhoneListeners = new ArrayList<>();
private final VisualStabilityProvider mVisualStabilityProvider;
+ private final AvalancheController mAvalancheController;
+
// TODO(b/328393698) move the topHeadsUpRow logic to an interactor
private final MutableStateFlow<HeadsUpRowRepository> mTopHeadsUpRow =
StateFlowKt.MutableStateFlow(null);
@@ -155,6 +157,7 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements
mBypassController = bypassController;
mGroupMembershipManager = groupMembershipManager;
mVisualStabilityProvider = visualStabilityProvider;
+ mAvalancheController = avalancheController;
updateResources();
configurationController.addCallback(new ConfigurationController.ConfigurationListener() {
@@ -653,9 +656,10 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements
boolean wasKeyguard = mStatusBarState == StatusBarState.KEYGUARD;
boolean isKeyguard = newState == StatusBarState.KEYGUARD;
mStatusBarState = newState;
+
if (wasKeyguard && !isKeyguard && mBypassController.getBypassEnabled()) {
ArrayList<String> keysToRemove = new ArrayList<>();
- for (HeadsUpEntry entry : mHeadsUpEntryMap.values()) {
+ for (HeadsUpEntry entry : getHeadsUpEntryList()) {
if (entry.mEntry != null && entry.mEntry.isBubble() && !entry.isSticky()) {
keysToRemove.add(entry.mEntry.getKey());
}
@@ -671,7 +675,7 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements
if (!isDozing) {
// Let's make sure all huns we got while dozing time out within the normal timeout
// duration. Otherwise they could get stuck for a very long time
- for (HeadsUpEntry entry : mHeadsUpEntryMap.values()) {
+ for (HeadsUpEntry entry : getHeadsUpEntryList()) {
entry.updateEntry(true /* updatePostTime */, "onDozingChanged(false)");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
index 21691540fc56..eb09e6ef39cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -254,6 +254,13 @@ constructor(
return null
}
+ fun getWaitingEntryList(): List<HeadsUpEntry> {
+ if (!NotificationThrottleHun.isEnabled) {
+ return mutableListOf()
+ }
+ return nextMap.keys.toList()
+ }
+
private fun isShowing(entry: HeadsUpEntry): Boolean {
return headsUpEntryShowing != null && entry.mEntry?.key == headsUpEntryShowing?.mEntry?.key
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index 2ee98bb61d80..4bd868179faf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -279,7 +279,6 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager {
* Returns the entry if it is managed by this manager.
* @param key key of notification
* @return the entry
- * TODO(b/315362456) See if caller needs to check AvalancheController waiting entries
*/
@Nullable
public NotificationEntry getEntry(@NonNull String key) {
@@ -294,8 +293,13 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager {
@NonNull
@Override
public Stream<NotificationEntry> getAllEntries() {
- // TODO(b/315362456) See if callers need to check AvalancheController
- return mHeadsUpEntryMap.values().stream().map(headsUpEntry -> headsUpEntry.mEntry);
+ return getHeadsUpEntryList().stream().map(headsUpEntry -> headsUpEntry.mEntry);
+ }
+
+ public List<HeadsUpEntry> getHeadsUpEntryList() {
+ List<HeadsUpEntry> entryList = new ArrayList<>(mHeadsUpEntryMap.values());
+ entryList.addAll(mAvalancheController.getWaitingEntryList());
+ return entryList;
}
/**
@@ -304,7 +308,8 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager {
*/
@Override
public boolean hasNotifications() {
- return !mHeadsUpEntryMap.isEmpty();
+ return !mHeadsUpEntryMap.isEmpty()
+ || !mAvalancheController.getWaitingEntryList().isEmpty();
}
/**
@@ -507,8 +512,10 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager {
@Nullable
protected HeadsUpEntry getHeadsUpEntry(@NonNull String key) {
- // TODO(b/315362456) See if callers need to check AvalancheController
- return mHeadsUpEntryMap.get(key);
+ if (mHeadsUpEntryMap.containsKey(key)) {
+ return mHeadsUpEntryMap.get(key);
+ }
+ return mAvalancheController.getWaitingEntry(key);
}
/**
@@ -688,8 +695,7 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager {
*/
@Override
public boolean isSticky(String key) {
- // TODO(b/315362456) See if callers need to check AvalancheController
- HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(key);
+ HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
if (headsUpEntry != null) {
return headsUpEntry.isSticky();
}
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/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt
index bd446b9ed190..1d1329ac550c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt
@@ -29,6 +29,7 @@ import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.ColorCorrectionTile
import com.android.systemui.qs.tiles.ColorInversionTile
import com.android.systemui.qs.tiles.FontScalingTile
+import com.android.systemui.qs.tiles.HearingDevicesTile
import com.android.systemui.qs.tiles.OneHandedModeTile
import com.android.systemui.qs.tiles.ReduceBrightColorsTile
import com.android.systemui.util.mockito.whenever
@@ -94,7 +95,7 @@ class AccessibilityQsShortcutsRepositoryImplForDeviceTest : SysuiTestCase() {
fun testTileSpecToComponentMappingContent() {
val mapping = AccessibilityQsShortcutsRepositoryImpl.TILE_SPEC_TO_COMPONENT_MAPPING
- assertThat(mapping.size).isEqualTo(5)
+ assertThat(mapping.size).isEqualTo(6)
assertThat(mapping[ColorCorrectionTile.TILE_SPEC])
.isEqualTo(AccessibilityShortcutController.DALTONIZER_TILE_COMPONENT_NAME)
assertThat(mapping[ColorInversionTile.TILE_SPEC])
@@ -107,6 +108,10 @@ class AccessibilityQsShortcutsRepositoryImplForDeviceTest : SysuiTestCase() {
)
assertThat(mapping[FontScalingTile.TILE_SPEC])
.isEqualTo(AccessibilityShortcutController.FONT_SIZE_TILE_COMPONENT_NAME)
+ assertThat(mapping[HearingDevicesTile.TILE_SPEC])
+ .isEqualTo(
+ AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_TILE_COMPONENT_NAME
+ )
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index 8895a5e1a97c..0db0de2bcd7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.accessibility.hearingaid;
+import static com.android.systemui.accessibility.hearingaid.HearingDevicesDialogDelegate.LIVE_CAPTION_INTENT;
import static com.android.systemui.statusbar.phone.SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK;
import static com.google.common.truth.Truth.assertThat;
@@ -24,16 +25,24 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.os.Handler;
+import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
+import android.widget.LinearLayout;
import androidx.test.filters.SmallTest;
@@ -44,6 +53,7 @@ import com.android.settingslib.bluetooth.HapClientProfile;
import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.bluetooth.qsdialog.DeviceItem;
@@ -54,6 +64,7 @@ import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -75,6 +86,10 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
public MockitoRule mockito = MockitoJUnit.rule();
private static final String DEVICE_ADDRESS = "AA:BB:CC:DD:EE:FF";
+ private static final String TEST_PKG = "pkg";
+ private static final String TEST_CLS = "cls";
+ private static final ComponentName TEST_COMPONENT = new ComponentName(TEST_PKG, TEST_CLS);
+ private static final String TEST_LABEL = "label";
@Mock
private SystemUIDialog.Factory mSystemUIDialogFactory;
@@ -104,6 +119,12 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
private CachedBluetoothDevice mCachedDevice;
@Mock
private DeviceItem mHearingDeviceItem;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private ActivityInfo mActivityInfo;
+ @Mock
+ private Drawable mDrawable;
private SystemUIDialog mDialog;
private HearingDevicesDialogDelegate mDialogDelegate;
private TestableLooper mTestableLooper;
@@ -122,6 +143,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
when(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState);
when(mCachedDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
when(mHearingDeviceItem.getCachedBluetoothDevice()).thenReturn(mCachedDevice);
+ mContext.setMockPackageManager(mPackageManager);
setUpPairNewDeviceDialog();
@@ -170,6 +192,45 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
verify(mCachedDevice).disconnect();
}
+ @Test
+ @EnableFlags(Flags.FLAG_HEARING_DEVICES_DIALOG_RELATED_TOOLS)
+ public void showDialog_hasLiveCaption_noRelatedToolsInConfig_showOneRelatedTool() {
+ when(mPackageManager.queryIntentActivities(
+ eq(LIVE_CAPTION_INTENT), anyInt())).thenReturn(
+ List.of(new ResolveInfo()));
+ mContext.getOrCreateTestableResources().addOverride(
+ R.array.config_quickSettingsHearingDevicesRelatedToolName, new String[]{});
+
+ setUpPairNewDeviceDialog();
+ mDialog.show();
+
+ LinearLayout relatedToolsView = (LinearLayout) getRelatedToolsView(mDialog);
+ assertThat(relatedToolsView.getChildCount()).isEqualTo(1);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HEARING_DEVICES_DIALOG_RELATED_TOOLS)
+ public void showDialog_hasLiveCaption_oneRelatedToolInConfig_showTwoRelatedTools()
+ throws PackageManager.NameNotFoundException {
+ when(mPackageManager.queryIntentActivities(
+ eq(LIVE_CAPTION_INTENT), anyInt())).thenReturn(
+ List.of(new ResolveInfo()));
+ mContext.getOrCreateTestableResources().addOverride(
+ R.array.config_quickSettingsHearingDevicesRelatedToolName,
+ new String[]{TEST_PKG + "/" + TEST_CLS});
+ when(mPackageManager.getActivityInfo(eq(TEST_COMPONENT), anyInt())).thenReturn(
+ mActivityInfo);
+ when(mActivityInfo.loadLabel(mPackageManager)).thenReturn(TEST_LABEL);
+ when(mActivityInfo.loadIcon(mPackageManager)).thenReturn(mDrawable);
+ when(mActivityInfo.getComponentName()).thenReturn(TEST_COMPONENT);
+
+ setUpPairNewDeviceDialog();
+ mDialog.show();
+
+ LinearLayout relatedToolsView = (LinearLayout) getRelatedToolsView(mDialog);
+ assertThat(relatedToolsView.getChildCount()).isEqualTo(2);
+ }
+
private void setUpPairNewDeviceDialog() {
mDialogDelegate = new HearingDevicesDialogDelegate(
mContext,
@@ -219,4 +280,18 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
private View getPairNewDeviceButton(SystemUIDialog dialog) {
return dialog.requireViewById(R.id.pair_new_device_button);
}
+
+ private View getRelatedToolsView(SystemUIDialog dialog) {
+ return dialog.requireViewById(R.id.related_tools_container);
+ }
+
+ @After
+ public void reset() {
+ if (mDialogDelegate != null) {
+ mDialogDelegate = null;
+ }
+ if (mDialog != null) {
+ mDialog.dismiss();
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
new file mode 100644
index 000000000000..717292378913
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.accessibility.hearingaid;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import static java.util.Collections.emptyList;
+
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+
+/**
+ * Tests for {@link HearingDevicesToolItemParser}.
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class HearingDevicesToolItemParserTest extends SysuiTestCase {
+ @Rule
+ public MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private ActivityInfo mActivityInfo;
+ @Mock
+ private Drawable mDrawable;
+ private static final String TEST_PKG = "pkg";
+ private static final String TEST_CLS = "cls";
+ private static final ComponentName TEST_COMPONENT = new ComponentName(TEST_PKG, TEST_CLS);
+ private static final String TEST_NO_EXIST_PKG = "NoPkg";
+ private static final String TEST_NO_EXIST_CLS = "NoCls";
+ private static final ComponentName TEST_NO_EXIST_COMPONENT = new ComponentName(
+ TEST_NO_EXIST_PKG, TEST_NO_EXIST_CLS);
+
+ private static final String TEST_LABEL = "label";
+
+ @Before
+ public void setUp() throws PackageManager.NameNotFoundException {
+ mContext.setMockPackageManager(mPackageManager);
+
+ when(mPackageManager.getActivityInfo(eq(TEST_COMPONENT), anyInt())).thenReturn(
+ mActivityInfo);
+ when(mPackageManager.getActivityInfo(eq(TEST_NO_EXIST_COMPONENT), anyInt())).thenThrow(
+ new PackageManager.NameNotFoundException());
+ when(mActivityInfo.loadLabel(mPackageManager)).thenReturn(TEST_LABEL);
+ when(mActivityInfo.loadIcon(mPackageManager)).thenReturn(mDrawable);
+ when(mActivityInfo.getComponentName()).thenReturn(TEST_COMPONENT);
+ }
+
+ @Test
+ public void parseStringArray_noString_emptyResult() {
+ assertThat(HearingDevicesToolItemParser.parseStringArray(mContext, new String[]{},
+ new String[]{})).isEqualTo(emptyList());
+ }
+
+ @Test
+ public void parseStringArray_oneToolName_oneExpectedToolItem() {
+ String[] toolName = new String[]{TEST_PKG + "/" + TEST_CLS};
+
+ List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext,
+ toolName, new String[]{});
+
+ assertThat(toolItemList.size()).isEqualTo(1);
+ assertThat(toolItemList.get(0).getToolName()).isEqualTo(TEST_LABEL);
+ assertThat(toolItemList.get(0).getToolIntent().getComponent()).isEqualTo(TEST_COMPONENT);
+ }
+
+ @Test
+ public void parseStringArray_fourToolName_maxThreeToolItem() {
+ String componentNameString = TEST_PKG + "/" + TEST_CLS;
+ String[] fourToolName =
+ new String[]{componentNameString, componentNameString, componentNameString,
+ componentNameString};
+
+ List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext,
+ fourToolName, new String[]{});
+ assertThat(toolItemList.size()).isEqualTo(HearingDevicesToolItemParser.MAX_NUM);
+ }
+
+ @Test
+ public void parseStringArray_oneWrongFormatToolName_noToolItem() {
+ String[] wrongFormatToolName = new String[]{TEST_PKG};
+
+ List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext,
+ wrongFormatToolName, new String[]{});
+ assertThat(toolItemList.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void parseStringArray_oneNotExistToolName_noToolItem() {
+ String[] notExistToolName = new String[]{TEST_NO_EXIST_PKG + "/" + TEST_NO_EXIST_CLS};
+
+ List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext,
+ notExistToolName, new String[]{});
+ assertThat(toolItemList.size()).isEqualTo(0);
+ }
+}
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/KeyboardShortcutListSearchTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
index 22c9e45d48af..6985a27a59c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
@@ -20,14 +20,21 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.graphics.drawable.Icon;
+import android.os.Handler;
+import android.platform.test.annotations.EnableFlags;
+import android.view.KeyboardShortcutGroup;
+import android.view.KeyboardShortcutInfo;
import android.view.WindowManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.google.android.material.bottomsheet.BottomSheetDialog;
@@ -36,10 +43,14 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.Arrays;
+import java.util.Collections;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class KeyboardShortcutListSearchTest extends SysuiTestCase {
@@ -51,6 +62,7 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase {
@Mock private BottomSheetDialog mBottomSheetDialog;
@Mock WindowManager mWindowManager;
+ @Mock Handler mHandler;
@Before
public void setUp() {
@@ -58,6 +70,7 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase {
mKeyboardShortcutListSearch.sInstance = mKeyboardShortcutListSearch;
mKeyboardShortcutListSearch.mKeyboardShortcutsBottomSheetDialog = mBottomSheetDialog;
mKeyboardShortcutListSearch.mContext = mContext;
+ mKeyboardShortcutListSearch.mBackgroundHandler = mHandler;
}
@Test
@@ -78,4 +91,59 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase {
verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt());
verify(mWindowManager).requestImeKeyboardShortcuts(any(), anyInt());
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI)
+ public void requestAppKeyboardShortcuts_callback_sanitisesIcons() {
+ KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
+
+ mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID);
+
+ ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor =
+ ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class);
+ ArgumentCaptor<Runnable> handlerRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mWindowManager).requestAppKeyboardShortcuts(callbackCaptor.capture(), anyInt());
+ callbackCaptor.getValue().onKeyboardShortcutsReceived(Collections.singletonList(group));
+ verify(mHandler).post(handlerRunnableCaptor.capture());
+ handlerRunnableCaptor.getValue().run();
+
+ verify(group.getItems().get(0)).clearIcon();
+ verify(group.getItems().get(1)).clearIcon();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI)
+ public void requestImeKeyboardShortcuts_callback_sanitisesIcons() {
+ KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
+
+ mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID);
+
+ ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor =
+ ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class);
+ ArgumentCaptor<Runnable> handlerRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mWindowManager).requestImeKeyboardShortcuts(callbackCaptor.capture(), anyInt());
+ callbackCaptor.getValue().onKeyboardShortcutsReceived(Collections.singletonList(group));
+ verify(mHandler).post(handlerRunnableCaptor.capture());
+ handlerRunnableCaptor.getValue().run();
+
+ verify(group.getItems().get(0)).clearIcon();
+ verify(group.getItems().get(1)).clearIcon();
+
+ }
+
+ private KeyboardShortcutGroup createKeyboardShortcutGroupForIconTests() {
+ Icon icon = mock(Icon.class);
+
+ KeyboardShortcutInfo info1 = mock(KeyboardShortcutInfo.class);
+ KeyboardShortcutInfo info2 = mock(KeyboardShortcutInfo.class);
+ when(info1.getIcon()).thenReturn(icon);
+ when(info2.getIcon()).thenReturn(icon);
+ when(info1.getLabel()).thenReturn("label");
+ when(info2.getLabel()).thenReturn("label");
+
+ KeyboardShortcutGroup group = new KeyboardShortcutGroup("label",
+ Arrays.asList(new KeyboardShortcutInfo[]{ info1, info2}));
+ group.setPackageName("com.example");
+ return group;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
index a3ecde0fe976..2b3f13986113 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
@@ -20,25 +20,36 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Dialog;
+import android.graphics.drawable.Icon;
+import android.os.Handler;
+import android.platform.test.annotations.EnableFlags;
+import android.view.KeyboardShortcutGroup;
+import android.view.KeyboardShortcutInfo;
import android.view.WindowManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.Arrays;
+import java.util.Collections;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class KeyboardShortcutsTest extends SysuiTestCase {
@@ -50,6 +61,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
@Mock private Dialog mDialog;
@Mock WindowManager mWindowManager;
+ @Mock Handler mHandler;
@Before
public void setUp() {
@@ -57,6 +69,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
mKeyboardShortcuts.sInstance = mKeyboardShortcuts;
mKeyboardShortcuts.mKeyboardShortcutsDialog = mDialog;
mKeyboardShortcuts.mContext = mContext;
+ mKeyboardShortcuts.mBackgroundHandler = mHandler;
}
@Test
@@ -77,4 +90,78 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt());
verify(mWindowManager).requestImeKeyboardShortcuts(any(), anyInt());
}
+
+ @Test
+ public void sanitiseShortcuts_clearsIcons() {
+ KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
+
+ KeyboardShortcuts.sanitiseShortcuts(Collections.singletonList(group));
+
+ verify(group.getItems().get(0)).clearIcon();
+ verify(group.getItems().get(1)).clearIcon();
+ }
+
+ @Test
+ public void sanitiseShortcuts_nullPackage_clearsIcons() {
+ KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
+ group.setPackageName(null);
+
+ KeyboardShortcuts.sanitiseShortcuts(Collections.singletonList(group));
+
+ verify(group.getItems().get(0)).clearIcon();
+ verify(group.getItems().get(1)).clearIcon();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI)
+ public void requestAppKeyboardShortcuts_callback_sanitisesIcons() {
+ KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
+
+ mKeyboardShortcuts.toggle(mContext, DEVICE_ID);
+
+ ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor =
+ ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class);
+ ArgumentCaptor<Runnable> handlerRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mWindowManager).requestAppKeyboardShortcuts(callbackCaptor.capture(), anyInt());
+ callbackCaptor.getValue().onKeyboardShortcutsReceived(Collections.singletonList(group));
+ verify(mHandler).post(handlerRunnableCaptor.capture());
+ handlerRunnableCaptor.getValue().run();
+
+ verify(group.getItems().get(0)).clearIcon();
+ verify(group.getItems().get(1)).clearIcon();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI)
+ public void requestImeKeyboardShortcuts_callback_sanitisesIcons() {
+ KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
+
+ mKeyboardShortcuts.toggle(mContext, DEVICE_ID);
+
+ ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor =
+ ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class);
+ ArgumentCaptor<Runnable> handlerRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mWindowManager).requestImeKeyboardShortcuts(callbackCaptor.capture(), anyInt());
+ callbackCaptor.getValue().onKeyboardShortcutsReceived(Collections.singletonList(group));
+ verify(mHandler).post(handlerRunnableCaptor.capture());
+ handlerRunnableCaptor.getValue().run();
+
+ verify(group.getItems().get(0)).clearIcon();
+ verify(group.getItems().get(1)).clearIcon();
+
+ }
+
+ private KeyboardShortcutGroup createKeyboardShortcutGroupForIconTests() {
+ Icon icon = mock(Icon.class);
+
+ KeyboardShortcutInfo info1 = mock(KeyboardShortcutInfo.class);
+ KeyboardShortcutInfo info2 = mock(KeyboardShortcutInfo.class);
+ when(info1.getIcon()).thenReturn(icon);
+ when(info2.getIcon()).thenReturn(icon);
+
+ KeyboardShortcutGroup group = new KeyboardShortcutGroup("label",
+ Arrays.asList(new KeyboardShortcutInfo[]{ info1, info2}));
+ group.setPackageName("com.example");
+ return group;
+ }
}
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 72c52544a44a..2addf6f9ec96 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -16,7 +16,20 @@
package com.android.server.audio;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.Manifest.permission.BLUETOOTH_STACK;
+import static android.Manifest.permission.CALL_AUDIO_INTERCEPTION;
+import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
+import static android.Manifest.permission.CAPTURE_AUDIO_OUTPUT;
+import static android.Manifest.permission.CAPTURE_MEDIA_OUTPUT;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.MODIFY_AUDIO_ROUTING;
+import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS;
import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED;
+import static android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS;
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
+import static android.Manifest.permission.QUERY_AUDIO_STATE;
+import static android.Manifest.permission.WRITE_SETTINGS;
import static android.app.BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT;
import static android.media.AudioDeviceInfo.TYPE_BLE_HEADSET;
import static android.media.AudioDeviceInfo.TYPE_BLE_SPEAKER;
@@ -1888,7 +1901,6 @@ public class AudioService extends IAudioService.Stub
}
mSpatializerHelper.reset(/* featureEnabled */ mHasSpatializerEffect);
- mSoundDoseHelper.reset();
// Restore rotation information.
if (mMonitorRotation) {
@@ -1899,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);
@@ -2075,7 +2089,7 @@ public class AudioService extends IAudioService.Stub
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
/**
* @see AudioManager#setSupportedSystemUsages(int[])
*/
@@ -2090,7 +2104,7 @@ public class AudioService extends IAudioService.Stub
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
/**
* @see AudioManager#getSupportedSystemUsages()
*/
@@ -2110,7 +2124,7 @@ public class AudioService extends IAudioService.Stub
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
/**
* @return the {@link android.media.audiopolicy.AudioProductStrategy} discovered from the
* platform configuration file.
@@ -2124,9 +2138,7 @@ public class AudioService extends IAudioService.Stub
}
@android.annotation.EnforcePermission(anyOf = {
- MODIFY_AUDIO_SETTINGS_PRIVILEGED,
- android.Manifest.permission.MODIFY_AUDIO_ROUTING
- })
+ MODIFY_AUDIO_SETTINGS_PRIVILEGED, MODIFY_AUDIO_ROUTING })
/**
* @return the List of {@link android.media.audiopolicy.AudioVolumeGroup} discovered from the
* platform configuration file.
@@ -2584,7 +2596,7 @@ public class AudioService extends IAudioService.Stub
Log.w(TAG, "audioFormat to enable is not a surround format.");
return false;
}
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.WRITE_SETTINGS)
+ if (mContext.checkCallingOrSelfPermission(WRITE_SETTINGS)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Missing WRITE_SETTINGS permission");
}
@@ -2608,7 +2620,7 @@ public class AudioService extends IAudioService.Stub
return true;
}
- @android.annotation.EnforcePermission(android.Manifest.permission.WRITE_SETTINGS)
+ @android.annotation.EnforcePermission(WRITE_SETTINGS)
/** @see AudioManager#setEncodedSurroundMode(int) */
@Override
public boolean setEncodedSurroundMode(@AudioManager.EncodedSurroundOutputMode int mode) {
@@ -2786,7 +2798,7 @@ public class AudioService extends IAudioService.Stub
if (!TextUtils.isEmpty(packageName)) {
PackageManager pm = mContext.getPackageManager();
- if (pm.checkPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD, packageName)
+ if (pm.checkPermission(CAPTURE_AUDIO_HOTWORD, packageName)
== PackageManager.PERMISSION_GRANTED) {
try {
assistantUid = pm.getPackageUidAsUser(packageName, getCurrentUserId());
@@ -2973,7 +2985,7 @@ public class AudioService extends IAudioService.Stub
* @see AudioManager#setPreferredDevicesForStrategy(AudioProductStrategy,
* List<AudioDeviceAttributes>)
*/
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
public int setPreferredDevicesForStrategy(int strategy, List<AudioDeviceAttributes> devices) {
super.setPreferredDevicesForStrategy_enforcePermission();
if (devices == null) {
@@ -3001,7 +3013,7 @@ public class AudioService extends IAudioService.Stub
return status;
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
/** @see AudioManager#removePreferredDeviceForStrategy(AudioProductStrategy) */
public int removePreferredDevicesForStrategy(int strategy) {
super.removePreferredDevicesForStrategy_enforcePermission();
@@ -3017,7 +3029,7 @@ public class AudioService extends IAudioService.Stub
return status;
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
/**
* @see AudioManager#getPreferredDeviceForStrategy(AudioProductStrategy)
* @see AudioManager#getPreferredDevicesForStrategy(AudioProductStrategy)
@@ -3049,7 +3061,7 @@ public class AudioService extends IAudioService.Stub
* @see AudioManager#setDeviceAsNonDefaultForStrategy(AudioProductStrategy,
* List<AudioDeviceAttributes>)
*/
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
public int setDeviceAsNonDefaultForStrategy(int strategy,
@NonNull AudioDeviceAttributes device) {
super.setDeviceAsNonDefaultForStrategy_enforcePermission();
@@ -3078,7 +3090,7 @@ public class AudioService extends IAudioService.Stub
* @see AudioManager#removeDeviceAsNonDefaultForStrategy(AudioProductStrategy,
* AudioDeviceAttributes)
*/
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
public int removeDeviceAsNonDefaultForStrategy(int strategy,
AudioDeviceAttributes device) {
super.removeDeviceAsNonDefaultForStrategy_enforcePermission();
@@ -3104,7 +3116,7 @@ public class AudioService extends IAudioService.Stub
/**
* @see AudioManager#getNonDefaultDevicesForStrategy(AudioProductStrategy)
*/
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
public List<AudioDeviceAttributes> getNonDefaultDevicesForStrategy(int strategy) {
super.getNonDefaultDevicesForStrategy_enforcePermission();
List<AudioDeviceAttributes> devices = new ArrayList<>();
@@ -3205,7 +3217,7 @@ public class AudioService extends IAudioService.Stub
return status;
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
/** @see AudioManager#clearPreferredDevicesForCapturePreset(int) */
public int clearPreferredDevicesForCapturePreset(int capturePreset) {
super.clearPreferredDevicesForCapturePreset_enforcePermission();
@@ -3221,7 +3233,7 @@ public class AudioService extends IAudioService.Stub
return status;
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
/**
* @see AudioManager#getPreferredDevicesForCapturePreset(int)
*/
@@ -3555,7 +3567,7 @@ public class AudioService extends IAudioService.Stub
if (isMuteAdjust &&
(streamType == AudioSystem.STREAM_VOICE_CALL ||
streamType == AudioSystem.STREAM_BLUETOOTH_SCO) &&
- mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
+ mContext.checkPermission(MODIFY_PHONE_STATE, pid, uid)
!= PackageManager.PERMISSION_GRANTED) {
Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: adjustStreamVolume from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
@@ -3566,7 +3578,7 @@ public class AudioService extends IAudioService.Stub
// make sure that the calling app have the MODIFY_AUDIO_ROUTING permission.
if (streamType == AudioSystem.STREAM_ASSISTANT &&
mContext.checkPermission(
- android.Manifest.permission.MODIFY_AUDIO_ROUTING, pid, uid)
+ MODIFY_AUDIO_ROUTING, pid, uid)
!= PackageManager.PERMISSION_GRANTED) {
Log.w(TAG, "MODIFY_AUDIO_ROUTING Permission Denial: adjustStreamVolume from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
@@ -3997,50 +4009,25 @@ public class AudioService extends IAudioService.Stub
}
private void enforceModifyAudioRoutingPermission() {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ if (mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_ROUTING)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Missing MODIFY_AUDIO_ROUTING permission");
}
}
- private void enforceAccessUltrasoundPermission() {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.ACCESS_ULTRASOUND)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Missing ACCESS_ULTRASOUND permission");
- }
- }
-
- private void enforceQueryStatePermission() {
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.QUERY_AUDIO_STATE)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Missing QUERY_AUDIO_STATE permissions");
- }
- }
-
private void enforceQueryStateOrModifyRoutingPermission() {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ if (mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_ROUTING)
!= PackageManager.PERMISSION_GRANTED
- && mContext.checkCallingOrSelfPermission(Manifest.permission.QUERY_AUDIO_STATE)
+ && mContext.checkCallingOrSelfPermission(QUERY_AUDIO_STATE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException(
"Missing MODIFY_AUDIO_ROUTING or QUERY_AUDIO_STATE permissions");
}
}
- private void enforceCallAudioInterceptionPermission() {
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.CALL_AUDIO_INTERCEPTION)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Missing CALL_AUDIO_INTERCEPTION permission");
- }
- }
-
-
@Override
@android.annotation.EnforcePermission(anyOf = {
- MODIFY_AUDIO_SETTINGS_PRIVILEGED,
- android.Manifest.permission.MODIFY_AUDIO_ROUTING
- })
+ MODIFY_AUDIO_SETTINGS_PRIVILEGED, MODIFY_AUDIO_ROUTING })
/** @see AudioManager#setVolumeGroupVolumeIndex(int, int, int) */
public void setVolumeGroupVolumeIndex(int groupId, int index, int flags,
String callingPackage, String attributionTag) {
@@ -4074,9 +4061,7 @@ public class AudioService extends IAudioService.Stub
@Override
@android.annotation.EnforcePermission(anyOf = {
- MODIFY_AUDIO_SETTINGS_PRIVILEGED,
- android.Manifest.permission.MODIFY_AUDIO_ROUTING
- })
+ MODIFY_AUDIO_SETTINGS_PRIVILEGED, MODIFY_AUDIO_ROUTING })
/** @see AudioManager#getVolumeGroupVolumeIndex(int) */
public int getVolumeGroupVolumeIndex(int groupId) {
super.getVolumeGroupVolumeIndex_enforcePermission();
@@ -4093,9 +4078,7 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#getVolumeGroupMaxVolumeIndex(int) */
@android.annotation.EnforcePermission(anyOf = {
- MODIFY_AUDIO_SETTINGS_PRIVILEGED,
- android.Manifest.permission.MODIFY_AUDIO_ROUTING
- })
+ MODIFY_AUDIO_SETTINGS_PRIVILEGED, MODIFY_AUDIO_ROUTING })
public int getVolumeGroupMaxVolumeIndex(int groupId) {
super.getVolumeGroupMaxVolumeIndex_enforcePermission();
synchronized (VolumeStreamState.class) {
@@ -4109,9 +4092,7 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#getVolumeGroupMinVolumeIndex(int) */
@android.annotation.EnforcePermission(anyOf = {
- MODIFY_AUDIO_SETTINGS_PRIVILEGED,
- android.Manifest.permission.MODIFY_AUDIO_ROUTING
- })
+ MODIFY_AUDIO_SETTINGS_PRIVILEGED, MODIFY_AUDIO_ROUTING })
public int getVolumeGroupMinVolumeIndex(int groupId) {
super.getVolumeGroupMinVolumeIndex_enforcePermission();
synchronized (VolumeStreamState.class) {
@@ -4125,9 +4106,7 @@ public class AudioService extends IAudioService.Stub
@Override
@android.annotation.EnforcePermission(anyOf = {
- android.Manifest.permission.MODIFY_AUDIO_ROUTING,
- android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
- })
+ MODIFY_AUDIO_ROUTING, MODIFY_AUDIO_SETTINGS_PRIVILEGED })
/** @see AudioDeviceVolumeManager#setDeviceVolume(VolumeInfo, AudioDeviceAttributes)
* Part of service interface, check permissions and parameters here
* Note calling package is for logging purposes only, not to be trusted
@@ -4253,7 +4232,7 @@ public class AudioService extends IAudioService.Stub
}
/** @see AudioManager#getLastAudibleVolumeForVolumeGroup(int) */
- @android.annotation.EnforcePermission(android.Manifest.permission.QUERY_AUDIO_STATE)
+ @android.annotation.EnforcePermission(QUERY_AUDIO_STATE)
public int getLastAudibleVolumeForVolumeGroup(int groupId) {
super.getLastAudibleVolumeForVolumeGroup_enforcePermission();
synchronized (VolumeStreamState.class) {
@@ -4318,16 +4297,14 @@ public class AudioService extends IAudioService.Stub
return;
}
if ((streamType == AudioManager.STREAM_VOICE_CALL) && (index == 0)
- && (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_PHONE_STATE)
+ && (mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED)) {
Log.w(TAG, "Trying to call setStreamVolume() for STREAM_VOICE_CALL and index 0 without"
+ " MODIFY_PHONE_STATE callingPackage=" + callingPackage);
return;
}
if ((streamType == AudioManager.STREAM_ASSISTANT)
- && (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ && (mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_ROUTING)
!= PackageManager.PERMISSION_GRANTED)) {
Log.w(TAG, "Trying to call setStreamVolume() for STREAM_ASSISTANT without"
+ " MODIFY_AUDIO_ROUTING callingPackage=" + callingPackage);
@@ -4348,7 +4325,7 @@ public class AudioService extends IAudioService.Stub
canChangeMuteAndUpdateController);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_ULTRASOUND)
+ @android.annotation.EnforcePermission(Manifest.permission.ACCESS_ULTRASOUND)
/** @see AudioManager#isUltrasoundSupported() */
public boolean isUltrasoundSupported() {
super.isUltrasoundSupported_enforcePermission();
@@ -4356,8 +4333,8 @@ public class AudioService extends IAudioService.Stub
return AudioSystem.isUltrasoundSupported();
}
- /** @see AudioManager#isHotwordStreamSupported() */
- @android.annotation.EnforcePermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD)
+ /** @see AudioManager#isHotwordStreamSupported(boolean) */
+ @android.annotation.EnforcePermission(CAPTURE_AUDIO_HOTWORD)
public boolean isHotwordStreamSupported(boolean lookbackAudio) {
super.isHotwordStreamSupported_enforcePermission();
try {
@@ -4373,7 +4350,7 @@ public class AudioService extends IAudioService.Stub
private boolean canChangeAccessibilityVolume() {
synchronized (mAccessibilityServiceUidsLock) {
if (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_ACCESSIBILITY_VOLUME)) {
+ Manifest.permission.CHANGE_ACCESSIBILITY_VOLUME)) {
return true;
}
if (mAccessibilityServiceUids != null) {
@@ -4874,7 +4851,7 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#forceVolumeControlStream(int) */
public void forceVolumeControlStream(int streamType, IBinder cb) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ if (mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
return;
}
@@ -5127,7 +5104,7 @@ public class AudioService extends IAudioService.Stub
return;
}
if ((PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.CAPTURE_AUDIO_OUTPUT))) {
+ CAPTURE_AUDIO_OUTPUT))) {
Log.w(TAG, "Trying to call forceRemoteSubmixFullVolume() without CAPTURE_AUDIO_OUTPUT");
return;
}
@@ -5174,8 +5151,7 @@ public class AudioService extends IAudioService.Stub
return;
}
if (userId != UserHandle.getCallingUserId() &&
- mContext.checkPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- pid, uid)
+ mContext.checkPermission(INTERACT_ACROSS_USERS_FULL, pid, uid)
!= PackageManager.PERMISSION_GRANTED) {
return;
}
@@ -5216,7 +5192,7 @@ public class AudioService extends IAudioService.Stub
return mMasterMute.get();
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
/** @see AudioManager#setMasterMute(boolean, int) */
public void setMasterMute(boolean mute, int flags, String callingPackage, int userId,
String attributionTag) {
@@ -5252,9 +5228,7 @@ public class AudioService extends IAudioService.Stub
@Override
@android.annotation.EnforcePermission(anyOf = {
- android.Manifest.permission.MODIFY_AUDIO_ROUTING,
- android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
- })
+ MODIFY_AUDIO_ROUTING, MODIFY_AUDIO_SETTINGS_PRIVILEGED })
/**
* @see AudioDeviceVolumeManager#getDeviceVolume(VolumeInfo, AudioDeviceAttributes)
*/
@@ -5302,12 +5276,12 @@ public class AudioService extends IAudioService.Stub
final boolean isPrivileged =
Binder.getCallingUid() == Process.SYSTEM_UID
|| callingHasAudioSettingsPermission()
- || (mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+ || (mContext.checkCallingPermission(MODIFY_AUDIO_ROUTING)
== PackageManager.PERMISSION_GRANTED);
return (mStreamStates[streamType].getMinIndex(isPrivileged) + 5) / 10;
}
- @android.annotation.EnforcePermission(android.Manifest.permission.QUERY_AUDIO_STATE)
+ @android.annotation.EnforcePermission(QUERY_AUDIO_STATE)
/** Get last audible volume before stream was muted. */
public int getLastAudibleStreamVolume(int streamType) {
super.getLastAudibleStreamVolume_enforcePermission();
@@ -5469,8 +5443,7 @@ public class AudioService extends IAudioService.Stub
return;
}
if (userId != UserHandle.getCallingUserId() &&
- mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ mContext.checkCallingOrSelfPermission(INTERACT_ACROSS_USERS_FULL)
!= PackageManager.PERMISSION_GRANTED) {
mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission").record();
return;
@@ -6060,7 +6033,7 @@ public class AudioService extends IAudioService.Stub
}
final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_PHONE_STATE)
+ MODIFY_PHONE_STATE)
== PackageManager.PERMISSION_GRANTED;
if ((mode == AudioSystem.MODE_IN_CALL
|| mode == AudioSystem.MODE_CALL_REDIRECT
@@ -6267,7 +6240,7 @@ public class AudioService extends IAudioService.Stub
mModeDispatchers.unregister(dispatcher);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION)
+ @android.annotation.EnforcePermission(CALL_AUDIO_INTERCEPTION)
/** @see AudioManager#isPstnCallAudioInterceptable() */
public boolean isPstnCallAudioInterceptable() {
@@ -6293,7 +6266,7 @@ public class AudioService extends IAudioService.Stub
@Override
public void setRttEnabled(boolean rttEnabled) {
if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_PHONE_STATE)
+ MODIFY_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setRttEnabled from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
@@ -6585,8 +6558,7 @@ public class AudioService extends IAudioService.Stub
? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED)
.record();
}
- final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_PHONE_STATE)
+ final boolean isPrivileged = mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
== PackageManager.PERMISSION_GRANTED;
final long ident = Binder.clearCallingIdentity();
try {
@@ -6636,8 +6608,7 @@ public class AudioService extends IAudioService.Stub
if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
return;
}
- final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_PHONE_STATE)
+ final boolean isPrivileged = mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
== PackageManager.PERMISSION_GRANTED;
// for logging only
@@ -6704,7 +6675,7 @@ public class AudioService extends IAudioService.Stub
}
/** @see AudioManager#setA2dpSuspended(boolean) */
- @android.annotation.EnforcePermission(android.Manifest.permission.BLUETOOTH_STACK)
+ @android.annotation.EnforcePermission(BLUETOOTH_STACK)
public void setA2dpSuspended(boolean enable) {
super.setA2dpSuspended_enforcePermission();
final String eventSource = new StringBuilder("setA2dpSuspended(").append(enable)
@@ -6714,7 +6685,7 @@ public class AudioService extends IAudioService.Stub
}
/** @see AudioManager#setA2dpSuspended(boolean) */
- @android.annotation.EnforcePermission(android.Manifest.permission.BLUETOOTH_STACK)
+ @android.annotation.EnforcePermission(BLUETOOTH_STACK)
public void setLeAudioSuspended(boolean enable) {
super.setLeAudioSuspended_enforcePermission();
final String eventSource = new StringBuilder("setLeAudioSuspended(").append(enable)
@@ -6819,8 +6790,7 @@ public class AudioService extends IAudioService.Stub
mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission or systemReady").record();
return;
}
- final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_PHONE_STATE)
+ final boolean isPrivileged = mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
== PackageManager.PERMISSION_GRANTED;
final long ident = Binder.clearCallingIdentity();
try {
@@ -6843,8 +6813,7 @@ public class AudioService extends IAudioService.Stub
final String eventSource = new StringBuilder("stopBluetoothSco()")
.append(") from u/pid:").append(uid).append("/")
.append(pid).toString();
- final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_PHONE_STATE)
+ final boolean isPrivileged = mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
== PackageManager.PERMISSION_GRANTED;
final long ident = Binder.clearCallingIdentity();
try {
@@ -7325,17 +7294,17 @@ public class AudioService extends IAudioService.Stub
}
private boolean callingOrSelfHasAudioSettingsPermission() {
- return mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
+ return mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_SETTINGS)
== PackageManager.PERMISSION_GRANTED;
}
private boolean callingHasAudioSettingsPermission() {
- return mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
+ return mContext.checkCallingPermission(MODIFY_AUDIO_SETTINGS)
== PackageManager.PERMISSION_GRANTED;
}
private boolean hasAudioSettingsPermission(int uid, int pid) {
- return mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
+ return mContext.checkPermission(MODIFY_AUDIO_SETTINGS, pid, uid)
== PackageManager.PERMISSION_GRANTED;
}
@@ -7527,17 +7496,16 @@ public class AudioService extends IAudioService.Stub
* @param register Whether the listener is to be registered or unregistered. If false, the
* device adopts variable volume behavior.
*/
- @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED })
+ @RequiresPermission(anyOf = { MODIFY_AUDIO_ROUTING, BLUETOOTH_PRIVILEGED })
public void registerDeviceVolumeDispatcherForAbsoluteVolume(boolean register,
IAudioDeviceVolumeDispatcher cb, String packageName,
AudioDeviceAttributes device, List<VolumeInfo> volumes,
boolean handlesVolumeAdjustment,
@AudioManager.AbsoluteDeviceVolumeBehavior int deviceVolumeBehavior) {
// verify permissions
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ if (mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_ROUTING)
!= PackageManager.PERMISSION_GRANTED
- && mContext.checkCallingOrSelfPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ && mContext.checkCallingOrSelfPermission(BLUETOOTH_PRIVILEGED)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException(
"Missing MODIFY_AUDIO_ROUTING or BLUETOOTH_PRIVILEGED permissions");
@@ -7595,9 +7563,7 @@ public class AudioService extends IAudioService.Stub
* @param deviceVolumeBehavior one of the device behaviors
*/
@android.annotation.EnforcePermission(anyOf = {
- android.Manifest.permission.MODIFY_AUDIO_ROUTING,
- android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
- })
+ MODIFY_AUDIO_ROUTING, MODIFY_AUDIO_SETTINGS_PRIVILEGED })
public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
@AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior, @Nullable String pkgName) {
// verify permissions
@@ -7680,9 +7646,7 @@ public class AudioService extends IAudioService.Stub
* @return the volume behavior for the device
*/
@android.annotation.EnforcePermission(anyOf = {
- android.Manifest.permission.MODIFY_AUDIO_ROUTING,
- android.Manifest.permission.QUERY_AUDIO_STATE,
- android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+ MODIFY_AUDIO_ROUTING, QUERY_AUDIO_STATE, MODIFY_AUDIO_SETTINGS_PRIVILEGED
})
public @AudioManager.DeviceVolumeBehavior
int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
@@ -7770,7 +7734,7 @@ public class AudioService extends IAudioService.Stub
*/
private static final byte[] DEFAULT_ARC_AUDIO_DESCRIPTOR = new byte[]{0x09, 0x7f, 0x07};
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
/**
* see AudioManager.setWiredDeviceConnectionState()
*/
@@ -7880,7 +7844,7 @@ public class AudioService extends IAudioService.Stub
public @interface BtProfile {}
- @android.annotation.EnforcePermission(android.Manifest.permission.BLUETOOTH_STACK)
+ @android.annotation.EnforcePermission(BLUETOOTH_STACK)
/**
* See AudioManager.handleBluetoothActiveDeviceChanged(...)
*/
@@ -10156,8 +10120,8 @@ public class AudioService extends IAudioService.Stub
if (AudioAttributes.isSystemUsage(usage)) {
if ((usage == AudioAttributes.USAGE_CALL_ASSISTANT
&& (audioAttributes.getAllFlags() & AudioAttributes.FLAG_CALL_REDIRECTION) != 0
- && callerHasPermission(Manifest.permission.CALL_AUDIO_INTERCEPTION))
- || callerHasPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)) {
+ && callerHasPermission(CALL_AUDIO_INTERCEPTION))
+ || callerHasPermission(MODIFY_AUDIO_ROUTING)) {
if (!isSupportedSystemUsage(usage)) {
throw new IllegalArgumentException(
"Unsupported usage " + AudioAttributes.usageToString(usage));
@@ -10175,8 +10139,8 @@ public class AudioService extends IAudioService.Stub
&& ((usage == AudioAttributes.USAGE_CALL_ASSISTANT
&& (audioAttributes.getAllFlags()
& AudioAttributes.FLAG_CALL_REDIRECTION) != 0
- && callerHasPermission(Manifest.permission.CALL_AUDIO_INTERCEPTION))
- || callerHasPermission(Manifest.permission.MODIFY_AUDIO_ROUTING));
+ && callerHasPermission(CALL_AUDIO_INTERCEPTION))
+ || callerHasPermission(MODIFY_AUDIO_ROUTING));
}
return true;
}
@@ -10207,7 +10171,7 @@ public class AudioService extends IAudioService.Stub
if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) == AudioManager.AUDIOFOCUS_FLAG_LOCK) {
if (AudioSystem.IN_VOICE_COMM_FOCUS_ID.equals(clientId)) {
if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_PHONE_STATE)) {
+ MODIFY_PHONE_STATE)) {
final String reason = "Invalid permission to (un)lock audio focus";
Log.e(TAG, reason, new Exception());
mmi.set(MediaMetrics.Property.EARLY_RETURN, reason)
@@ -10239,10 +10203,9 @@ public class AudioService extends IAudioService.Stub
// does caller have system privileges to bypass HardeningEnforcer
boolean permissionOverridesCheck = false;
- if ((mContext.checkCallingOrSelfPermission(
- Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ if ((mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
== PackageManager.PERMISSION_GRANTED)
- || (mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+ || (mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_ROUTING)
== PackageManager.PERMISSION_GRANTED)) {
permissionOverridesCheck = true;
} else if (uid < UserHandle.AID_APP_START) {
@@ -10376,7 +10339,7 @@ public class AudioService extends IAudioService.Stub
* such as another freeze currently used.
*/
@Override
- @EnforcePermission("android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ @EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
public boolean enterAudioFocusFreezeForTest(IBinder cb, int[] exemptedUids) {
super.enterAudioFocusFreezeForTest_enforcePermission();
Objects.requireNonNull(exemptedUids);
@@ -10392,7 +10355,7 @@ public class AudioService extends IAudioService.Stub
* such as the freeze already having ended, or not started.
*/
@Override
- @EnforcePermission("android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ @EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
public boolean exitAudioFocusFreezeForTest(IBinder cb) {
super.exitAudioFocusFreezeForTest_enforcePermission();
Objects.requireNonNull(cb);
@@ -10438,8 +10401,7 @@ public class AudioService extends IAudioService.Stub
private static final boolean SPATIAL_AUDIO_ENABLED_DEFAULT = true;
private void enforceModifyDefaultAudioEffectsPermission() {
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ if (mContext.checkCallingOrSelfPermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Missing MODIFY_DEFAULT_AUDIO_EFFECTS permission");
}
@@ -10463,7 +10425,7 @@ public class AudioService extends IAudioService.Stub
return mSpatializerHelper.isAvailable();
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#isAvailableForDevice(AudioDeviceAttributes) */
public boolean isSpatializerAvailableForDevice(@NonNull AudioDeviceAttributes device) {
super.isSpatializerAvailableForDevice_enforcePermission();
@@ -10471,7 +10433,7 @@ public class AudioService extends IAudioService.Stub
return mSpatializerHelper.isAvailableForDevice(Objects.requireNonNull(device));
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#hasHeadTracker(AudioDeviceAttributes) */
public boolean hasHeadTracker(@NonNull AudioDeviceAttributes device) {
super.hasHeadTracker_enforcePermission();
@@ -10479,7 +10441,7 @@ public class AudioService extends IAudioService.Stub
return mSpatializerHelper.hasHeadTracker(Objects.requireNonNull(device));
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#setHeadTrackerEnabled(boolean, AudioDeviceAttributes) */
public void setHeadTrackerEnabled(boolean enabled, @NonNull AudioDeviceAttributes device) {
super.setHeadTrackerEnabled_enforcePermission();
@@ -10487,7 +10449,7 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.setHeadTrackerEnabled(enabled, Objects.requireNonNull(device));
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#isHeadTrackerEnabled(AudioDeviceAttributes) */
public boolean isHeadTrackerEnabled(@NonNull AudioDeviceAttributes device) {
super.isHeadTrackerEnabled_enforcePermission();
@@ -10500,7 +10462,7 @@ public class AudioService extends IAudioService.Stub
return mSpatializerHelper.isHeadTrackerAvailable();
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#setSpatializerEnabled(boolean) */
public void setSpatializerEnabled(boolean enabled) {
super.setSpatializerEnabled_enforcePermission();
@@ -10530,7 +10492,7 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.unregisterStateCallback(cb);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#SpatializerHeadTrackingDispatcherStub */
public void registerSpatializerHeadTrackingCallback(
@NonNull ISpatializerHeadTrackingModeCallback cb) {
@@ -10540,7 +10502,7 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.registerHeadTrackingModeCallback(cb);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#SpatializerHeadTrackingDispatcherStub */
public void unregisterSpatializerHeadTrackingCallback(
@NonNull ISpatializerHeadTrackingModeCallback cb) {
@@ -10557,7 +10519,7 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.registerHeadTrackerAvailableCallback(cb, register);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#setOnHeadToSoundstagePoseUpdatedListener */
public void registerHeadToSoundstagePoseCallback(
@NonNull ISpatializerHeadToSoundStagePoseCallback cb) {
@@ -10567,7 +10529,7 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.registerHeadToSoundstagePoseCallback(cb);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#clearOnHeadToSoundstagePoseUpdatedListener */
public void unregisterHeadToSoundstagePoseCallback(
@NonNull ISpatializerHeadToSoundStagePoseCallback cb) {
@@ -10577,7 +10539,7 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.unregisterHeadToSoundstagePoseCallback(cb);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#getSpatializerCompatibleAudioDevices() */
public @NonNull List<AudioDeviceAttributes> getSpatializerCompatibleAudioDevices() {
super.getSpatializerCompatibleAudioDevices_enforcePermission();
@@ -10585,7 +10547,7 @@ public class AudioService extends IAudioService.Stub
return mSpatializerHelper.getCompatibleAudioDevices();
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#addSpatializerCompatibleAudioDevice(AudioDeviceAttributes) */
public void addSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
super.addSpatializerCompatibleAudioDevice_enforcePermission();
@@ -10594,7 +10556,7 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.addCompatibleAudioDevice(ada);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#removeSpatializerCompatibleAudioDevice(AudioDeviceAttributes) */
public void removeSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
super.removeSpatializerCompatibleAudioDevice_enforcePermission();
@@ -10603,7 +10565,7 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.removeCompatibleAudioDevice(ada);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#getSupportedHeadTrackingModes() */
public int[] getSupportedHeadTrackingModes() {
super.getSupportedHeadTrackingModes_enforcePermission();
@@ -10611,7 +10573,7 @@ public class AudioService extends IAudioService.Stub
return mSpatializerHelper.getSupportedHeadTrackingModes();
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#getHeadTrackingMode() */
public int getActualHeadTrackingMode() {
super.getActualHeadTrackingMode_enforcePermission();
@@ -10619,7 +10581,7 @@ public class AudioService extends IAudioService.Stub
return mSpatializerHelper.getActualHeadTrackingMode();
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#getDesiredHeadTrackingMode() */
public int getDesiredHeadTrackingMode() {
super.getDesiredHeadTrackingMode_enforcePermission();
@@ -10627,7 +10589,7 @@ public class AudioService extends IAudioService.Stub
return mSpatializerHelper.getDesiredHeadTrackingMode();
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#setGlobalTransform */
public void setSpatializerGlobalTransform(@NonNull float[] transform) {
super.setSpatializerGlobalTransform_enforcePermission();
@@ -10636,7 +10598,7 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.setGlobalTransform(transform);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#recenterHeadTracker() */
public void recenterHeadTracker() {
super.recenterHeadTracker_enforcePermission();
@@ -10644,7 +10606,7 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.recenterHeadTracker();
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#setDesiredHeadTrackingMode */
public void setDesiredHeadTrackingMode(@Spatializer.HeadTrackingModeSet int mode) {
super.setDesiredHeadTrackingMode_enforcePermission();
@@ -10660,7 +10622,7 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.setDesiredHeadTrackingMode(mode);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#setEffectParameter */
public void setSpatializerParameter(int key, @NonNull byte[] value) {
super.setSpatializerParameter_enforcePermission();
@@ -10669,7 +10631,7 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.setEffectParameter(key, value);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#getEffectParameter */
public void getSpatializerParameter(int key, @NonNull byte[] value) {
super.getSpatializerParameter_enforcePermission();
@@ -10678,7 +10640,7 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.getEffectParameter(key, value);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#getOutput */
public int getSpatializerOutput() {
super.getSpatializerOutput_enforcePermission();
@@ -10686,7 +10648,7 @@ public class AudioService extends IAudioService.Stub
return mSpatializerHelper.getOutput();
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#setOnSpatializerOutputChangedListener */
public void registerSpatializerOutputCallback(ISpatializerOutputCallback cb) {
super.registerSpatializerOutputCallback_enforcePermission();
@@ -10695,7 +10657,7 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.registerSpatializerOutputCallback(cb);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
/** @see Spatializer#clearOnSpatializerOutputChangedListener */
public void unregisterSpatializerOutputCallback(ISpatializerOutputCallback cb) {
super.unregisterSpatializerOutputCallback_enforcePermission();
@@ -10742,7 +10704,7 @@ public class AudioService extends IAudioService.Stub
private boolean isBluetoothPrividged() {
return PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.BLUETOOTH_CONNECT)
+ Manifest.permission.BLUETOOTH_CONNECT)
|| Binder.getCallingUid() == Process.SYSTEM_UID;
}
@@ -10949,7 +10911,7 @@ public class AudioService extends IAudioService.Stub
});
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
/** @see AudioManager#getMutingExpectedDevice */
public @Nullable AudioDeviceAttributes getMutingExpectedDevice() {
super.getMutingExpectedDevice_enforcePermission();
@@ -11000,7 +10962,7 @@ public class AudioService extends IAudioService.Stub
final RemoteCallbackList<IMuteAwaitConnectionCallback> mMuteAwaitConnectionDispatchers =
new RemoteCallbackList<IMuteAwaitConnectionCallback>();
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
/** @see AudioManager#registerMuteAwaitConnectionCallback */
public void registerMuteAwaitConnectionDispatcher(@NonNull IMuteAwaitConnectionCallback cb,
boolean register) {
@@ -11175,7 +11137,7 @@ public class AudioService extends IAudioService.Stub
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.REMOTE_AUDIO_PLAYBACK)
+ @android.annotation.EnforcePermission(Manifest.permission.REMOTE_AUDIO_PLAYBACK)
@Override
public void setRingtonePlayer(IRingtonePlayer player) {
setRingtonePlayer_enforcePermission();
@@ -11376,7 +11338,6 @@ public class AudioService extends IAudioService.Stub
@Override
@android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
- @AudioDeviceCategory
public boolean isBluetoothAudioDeviceCategoryFixed(@NonNull String address) {
super.isBluetoothAudioDeviceCategoryFixed_enforcePermission();
if (!automaticBtDeviceType()) {
@@ -11892,7 +11853,7 @@ public class AudioService extends IAudioService.Stub
}
private void enforceVolumeController(String action) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.STATUS_BAR_SERVICE,
"Only SystemUI can " + action);
}
@@ -12396,8 +12357,8 @@ public class AudioService extends IAudioService.Stub
}
if (requireCaptureAudioOrMediaOutputPerm
- && !callerHasPermission(android.Manifest.permission.CAPTURE_MEDIA_OUTPUT)
- && !callerHasPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT)) {
+ && !callerHasPermission(CAPTURE_MEDIA_OUTPUT)
+ && !callerHasPermission(CAPTURE_AUDIO_OUTPUT)) {
Log.e(TAG, "Privileged audio capture requires CAPTURE_MEDIA_OUTPUT or "
+ "CAPTURE_AUDIO_OUTPUT system permission");
return false;
@@ -12405,7 +12366,7 @@ public class AudioService extends IAudioService.Stub
if (voiceCommunicationCaptureMixes != null && voiceCommunicationCaptureMixes.size() > 0) {
if (!callerHasPermission(
- android.Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT)) {
+ Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT)) {
Log.e(TAG, "Audio capture for voice communication requires "
+ "CAPTURE_VOICE_COMMUNICATION_OUTPUT system permission");
return false;
@@ -12422,13 +12383,12 @@ public class AudioService extends IAudioService.Stub
}
if (requireModifyRouting
- && !callerHasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)) {
+ && !callerHasPermission(MODIFY_AUDIO_ROUTING)) {
Log.e(TAG, "Can not capture audio without MODIFY_AUDIO_ROUTING");
return false;
}
- if (requireCallAudioInterception
- && !callerHasPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION)) {
+ if (requireCallAudioInterception && !callerHasPermission(CALL_AUDIO_INTERCEPTION)) {
Log.e(TAG, "Can not capture audio without CALL_AUDIO_INTERCEPTION");
return false;
}
@@ -12541,7 +12501,7 @@ public class AudioService extends IAudioService.Stub
// permission check
final boolean hasPermissionForPolicy =
(PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission(
- android.Manifest.permission.MODIFY_AUDIO_ROUTING));
+ MODIFY_AUDIO_ROUTING));
if (!hasPermissionForPolicy) {
Slog.w(TAG, errorMsg + " for pid " +
+ Binder.getCallingPid() + " / uid "
@@ -12625,7 +12585,7 @@ public class AudioService extends IAudioService.Stub
* @return {@link AudioManager#SUCCESS} iff the mixing rules were updated successfully,
* {@link AudioManager#ERROR} otherwise.
*/
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
public int updateMixingRulesForPolicy(
@NonNull AudioMix[] mixesToUpdate,
@NonNull AudioMixingRule[] updatedMixingRules,
@@ -12753,7 +12713,7 @@ public class AudioService extends IAudioService.Stub
return AudioManager.SUCCESS;
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
/** @see AudioPolicy#getFocusStack() */
public List<AudioFocusInfo> getFocusStack() {
super.getFocusStack_enforcePermission();
@@ -12779,8 +12739,7 @@ public class AudioService extends IAudioService.Stub
/**
* see {@link AudioPolicy#setFadeManagerConfigurationForFocusLoss(FadeManagerConfiguration)}
*/
- @android.annotation.EnforcePermission(
- android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
public int setFadeManagerConfigurationForFocusLoss(
@NonNull FadeManagerConfiguration fmcForFocusLoss) {
super.setFadeManagerConfigurationForFocusLoss_enforcePermission();
@@ -12796,8 +12755,7 @@ public class AudioService extends IAudioService.Stub
/**
* see {@link AudioPolicy#clearFadeManagerConfigurationForFocusLoss()}
*/
- @android.annotation.EnforcePermission(
- android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
public int clearFadeManagerConfigurationForFocusLoss() {
super.clearFadeManagerConfigurationForFocusLoss_enforcePermission();
ensureFadeManagerConfigIsEnabled();
@@ -12808,8 +12766,7 @@ public class AudioService extends IAudioService.Stub
/**
* see {@link AudioPolicy#getFadeManagerConfigurationForFocusLoss()}
*/
- @android.annotation.EnforcePermission(
- android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
public FadeManagerConfiguration getFadeManagerConfigurationForFocusLoss() {
super.getFadeManagerConfigurationForFocusLoss_enforcePermission();
ensureFadeManagerConfigIsEnabled();
@@ -12970,7 +12927,7 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#supportsBluetoothVariableLatency() */
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
public boolean supportsBluetoothVariableLatency() {
super.supportsBluetoothVariableLatency_enforcePermission();
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
@@ -12979,7 +12936,7 @@ public class AudioService extends IAudioService.Stub
}
/** @see AudioManager#setBluetoothVariableLatencyEnabled(boolean) */
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
public void setBluetoothVariableLatencyEnabled(boolean enabled) {
super.setBluetoothVariableLatencyEnabled_enforcePermission();
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
@@ -12988,7 +12945,7 @@ public class AudioService extends IAudioService.Stub
}
/** @see AudioManager#isBluetoothVariableLatencyEnabled(boolean) */
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
public boolean isBluetoothVariableLatencyEnabled() {
super.isBluetoothVariableLatencyEnabled_enforcePermission();
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
@@ -13075,7 +13032,7 @@ public class AudioService extends IAudioService.Stub
public void registerRecordingCallback(IRecordingConfigDispatcher rcdb) {
final boolean isPrivileged =
(PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission(
- android.Manifest.permission.MODIFY_AUDIO_ROUTING));
+ MODIFY_AUDIO_ROUTING));
mRecordMonitor.registerRecordingCallback(rcdb, isPrivileged);
}
@@ -13086,7 +13043,7 @@ public class AudioService extends IAudioService.Stub
public List<AudioRecordingConfiguration> getActiveRecordingConfigurations() {
final boolean isPrivileged = Binder.getCallingUid() == Process.SYSTEM_UID
|| (PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission(
- android.Manifest.permission.MODIFY_AUDIO_ROUTING));
+ MODIFY_AUDIO_ROUTING));
return mRecordMonitor.getActiveRecordingConfigurations(isPrivileged);
}
@@ -13122,7 +13079,7 @@ public class AudioService extends IAudioService.Stub
public void registerPlaybackCallback(IPlaybackConfigDispatcher pcdb) {
final boolean isPrivileged =
(PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_AUDIO_ROUTING));
+ MODIFY_AUDIO_ROUTING));
mPlaybackMonitor.registerPlaybackCallback(pcdb, isPrivileged);
}
@@ -13133,7 +13090,7 @@ public class AudioService extends IAudioService.Stub
public List<AudioPlaybackConfiguration> getActivePlaybackConfigurations() {
final boolean isPrivileged =
(PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_AUDIO_ROUTING));
+ MODIFY_AUDIO_ROUTING));
return mPlaybackMonitor.getActivePlaybackConfigurations(isPrivileged);
}
@@ -13724,8 +13681,7 @@ public class AudioService extends IAudioService.Stub
* see {@link AudioManager#dispatchAudioFocusChangeWithFade(AudioFocusInfo, int, AudioPolicy,
* List, FadeManagerConfiguration)}
*/
- @android.annotation.EnforcePermission(
- android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
public int dispatchFocusChangeWithFade(AudioFocusInfo afi, int focusChange,
IAudioPolicyCallback pcb, List<AudioFocusInfo> otherActiveAfis,
FadeManagerConfiguration transientFadeMgrConfig) {
@@ -13762,8 +13718,7 @@ public class AudioService extends IAudioService.Stub
/**
* @see AudioManager#shouldNotificationSoundPlay(AudioAttributes)
*/
- @android.annotation.EnforcePermission(
- android.Manifest.permission.QUERY_AUDIO_STATE)
+ @android.annotation.EnforcePermission(QUERY_AUDIO_STATE)
public boolean shouldNotificationSoundPlay(@NonNull final AudioAttributes aa) {
super.shouldNotificationSoundPlay_enforcePermission();
Objects.requireNonNull(aa);
@@ -13823,12 +13778,10 @@ public class AudioService extends IAudioService.Stub
new HashMap<IBinder, AsdProxy>();
private void checkMonitorAudioServerStatePermission() {
- if (!(mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_PHONE_STATE) ==
- PackageManager.PERMISSION_GRANTED ||
- mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_AUDIO_ROUTING) ==
- PackageManager.PERMISSION_GRANTED)) {
+ if (!(mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
+ == PackageManager.PERMISSION_GRANTED
+ || mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_ROUTING)
+ == PackageManager.PERMISSION_GRANTED)) {
throw new SecurityException("Not allowed to monitor audioserver state");
}
}
@@ -13923,7 +13876,7 @@ public class AudioService extends IAudioService.Stub
AudioSystem.setAudioHalPids(pidsArray);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
//======================
// Multi Audio Focus
//======================
@@ -13964,7 +13917,7 @@ public class AudioService extends IAudioService.Stub
* or the delay is not in range of {@link #getMaxAdditionalOutputDeviceDelay()}.
*/
@Override
- //@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ //@RequiresPermission(MODIFY_AUDIO_ROUTING)
public boolean setAdditionalOutputDeviceDelay(
@NonNull AudioDeviceAttributes device, @IntRange(from = 0) long delayMillis) {
Objects.requireNonNull(device, "device must not be null");
@@ -14037,7 +13990,7 @@ public class AudioService extends IAudioService.Stub
return delayMillis;
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
/** @see AudioManager#addAssistantServicesUids(int []) */
@Override
public void addAssistantServicesUids(int [] assistantUids) {
@@ -14050,7 +14003,7 @@ public class AudioService extends IAudioService.Stub
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
/** @see AudioManager#removeAssistantServicesUids(int []) */
@Override
public void removeAssistantServicesUids(int [] assistantUids) {
@@ -14062,7 +14015,7 @@ public class AudioService extends IAudioService.Stub
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
/** @see AudioManager#getAssistantServicesUids() */
@Override
public int[] getAssistantServicesUids() {
@@ -14075,7 +14028,7 @@ public class AudioService extends IAudioService.Stub
return assistantUids;
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
/** @see AudioManager#setActiveAssistantServiceUids(int []) */
@Override
public void setActiveAssistantServiceUids(int [] activeAssistantUids) {
@@ -14088,7 +14041,7 @@ public class AudioService extends IAudioService.Stub
updateActiveAssistantServiceUids();
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
/** @see AudioManager#getActiveAssistantServiceUids() */
@Override
public int[] getActiveAssistantServiceUids() {
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/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 976399d3917a..5843d72f346a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4098,17 +4098,17 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
if (additionalSubtypeMap != newAdditionalSubtypeMap) {
AdditionalSubtypeMapRepository.putAndSave(userId, newAdditionalSubtypeMap,
settings.getMethodMap());
- final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
- userId, AdditionalSubtypeMapRepository.get(userId),
- DirectBootAwareness.AUTO);
- InputMethodSettingsRepository.put(userId, newSettings);
- if (isCurrentUser) {
- final long ident = Binder.clearCallingIdentity();
- try {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ final InputMethodSettings newSettings = queryInputMethodServicesInternal(
+ mContext, userId, AdditionalSubtypeMapRepository.get(userId),
+ DirectBootAwareness.AUTO);
+ InputMethodSettingsRepository.put(userId, newSettings);
+ if (isCurrentUser) {
postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */);
- } finally {
- Binder.restoreCallingIdentity(ident);
}
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
}
@@ -4969,6 +4969,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
final int flags = PackageManager.GET_META_DATA
| PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
| directBootAwarenessFlags;
+
+ // Beware that package visibility filtering will be enforced based on the effective calling
+ // identity (Binder.getCallingUid()), but our use case always expect Binder.getCallingUid()
+ // to return Process.SYSTEM_UID here. The actual filtering is implemented separately with
+ // canCallerAccessInputMethod().
+ // TODO(b/343108534): Use PackageManagerInternal#queryIntentServices() to pass SYSTEM_UID.
final List<ResolveInfo> services = userAwareContext.getPackageManager().queryIntentServices(
new Intent(InputMethod.SERVICE_INTERFACE),
PackageManager.ResolveInfoFlags.of(flags));
diff --git a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
index d932bd4e6d20..563f93e96331 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
@@ -434,7 +434,7 @@ class LocaleManagerBackupHelper {
ATTR_PACKAGE_NAME);
String languageTags = parser.getAttributeValue(/* namespace= */ null, ATTR_LOCALES);
boolean delegateSelector = parser.getAttributeBoolean(/* namespace= */ null,
- ATTR_DELEGATE_SELECTOR);
+ ATTR_DELEGATE_SELECTOR, false);
if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(languageTags)) {
LocalesInfo localesInfo = new LocalesInfo(languageTags, delegateSelector);
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/AbsAppSnapshotController.java b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
index fd4b06148c5f..0e0b78fb3206 100644
--- a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
+++ b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
@@ -466,17 +466,17 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
}
/**
- * @return The {@link WindowInsetsController.Appearance} flags for the top fullscreen opaque
- * window in the given {@param TYPE}.
+ * @return The {@link WindowInsetsController.Appearance} flags for the top main app window in
+ * the given {@param TYPE}.
*/
@WindowInsetsController.Appearance
private int getAppearance(TYPE source) {
final ActivityRecord topFullscreenActivity = getTopFullscreenActivity(source);
- final WindowState topFullscreenOpaqueWindow = topFullscreenActivity != null
- ? topFullscreenActivity.getTopFullscreenOpaqueWindow()
+ final WindowState topFullscreenWindow = topFullscreenActivity != null
+ ? topFullscreenActivity.findMainWindow()
: null;
- if (topFullscreenOpaqueWindow != null) {
- return topFullscreenOpaqueWindow.mAttrs.insetsFlags.appearance;
+ if (topFullscreenWindow != null) {
+ return topFullscreenWindow.mAttrs.insetsFlags.appearance;
}
return 0;
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index eb6262c95d84..434e92f7978a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -7660,20 +7660,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
- /**
- * @return The to top most child window for which {@link LayoutParams#isFullscreen()} returns
- * true and isn't fully transparent.
- */
- WindowState getTopFullscreenOpaqueWindow() {
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState win = mChildren.get(i);
- if (win != null && win.mAttrs.isFullscreen() && !win.isFullyTransparent()) {
- return win;
- }
- }
- return null;
- }
-
WindowState findMainWindow() {
return findMainWindow(true);
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 0e4f0335118d..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;
@@ -1270,6 +1268,8 @@ class BackNavigationController {
.setContainerLayer()
.setHidden(false)
.setParent(task.getSurfaceControl())
+ .setCallsite(
+ "BackWindowAnimationAdaptorWrapper.getOrCreateAnimationTarget")
.build();
mCloseTransaction = new SurfaceControl.Transaction();
mCloseTransaction.reparent(leashSurface, null);
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 22ca82a29d59..1e7de2be87fa 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -286,6 +286,7 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
.setName(name)
.setCallsite("createSurfaceForGestureMonitor")
.setParent(inputOverlay)
+ .setCallsite("InputManagerCallback.createSurfaceForGestureMonitor")
.build();
}
}
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index 05eeeb381d8a..cff40c768381 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -140,23 +140,15 @@ public class StartingSurfaceController {
}
StartingSurface createTaskSnapshotSurface(ActivityRecord activity, TaskSnapshot taskSnapshot) {
- final WindowState topFullscreenOpaqueWindow;
final Task task = activity.getTask();
if (task == null) {
Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find task for activity="
+ activity);
return null;
}
- final ActivityRecord topFullscreenActivity = task.getTopFullscreenActivity();
- if (topFullscreenActivity == null) {
- Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find top fullscreen for task="
- + task);
- return null;
- }
- topFullscreenOpaqueWindow = topFullscreenActivity.getTopFullscreenOpaqueWindow();
- if (topFullscreenOpaqueWindow == null) {
- Slog.w(TAG, "TaskSnapshotSurface.create: no opaque window in "
- + topFullscreenActivity);
+ final WindowState mainWindow = activity.findMainWindow(false);
+ if (mainWindow == null) {
+ Slog.w(TAG, "TaskSnapshotSurface.create: no main window in " + activity);
return null;
}
if (activity.mDisplayContent.getRotation() != taskSnapshot.getRotation()) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a555388ab233..cd22591d3629 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3624,14 +3624,15 @@ class Task extends TaskFragment {
// If the developer has persist a different configuration, we need to override it to the
// starting window because persisted configuration does not effect to Task.
info.taskInfo.configuration.setTo(activity.getConfiguration());
- final ActivityRecord topFullscreenActivity = getTopFullscreenActivity();
- if (topFullscreenActivity != null) {
- final WindowState topFullscreenOpaqueWindow =
- topFullscreenActivity.getTopFullscreenOpaqueWindow();
- if (topFullscreenOpaqueWindow != null) {
- info.topOpaqueWindowInsetsState =
- topFullscreenOpaqueWindow.getInsetsStateWithVisibilityOverride();
- info.topOpaqueWindowLayoutParams = topFullscreenOpaqueWindow.getAttrs();
+ if (!Flags.drawSnapshotAspectRatioMatch()) {
+ final ActivityRecord topFullscreenActivity = getTopFullscreenActivity();
+ if (topFullscreenActivity != null) {
+ final WindowState mainWindow = topFullscreenActivity.findMainWindow(false);
+ if (mainWindow != null) {
+ info.topOpaqueWindowInsetsState =
+ mainWindow.getInsetsStateWithVisibilityOverride();
+ info.topOpaqueWindowLayoutParams = mainWindow.getAttrs();
+ }
}
}
return info;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 7ec31d5a8ecb..28369fa74527 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -2639,7 +2639,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
}
}
final SurfaceControl rootLeash = leashReference.makeAnimationLeash().setName(
- "Transition Root: " + leashReference.getName()).build();
+ "Transition Root: " + leashReference.getName())
+ .setCallsite("Transition.calculateTransitionRoots").build();
rootLeash.setUnreleasedWarningCallSite("Transition.calculateTransitionRoots");
// Update layers to start transaction because we prevent assignment during collect, so
// the layer of transition root can be correct.
diff --git a/services/core/java/com/android/server/wm/TrustedOverlayHost.java b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
index debe7946dc95..9b868bebd868 100644
--- a/services/core/java/com/android/server/wm/TrustedOverlayHost.java
+++ b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
@@ -54,6 +54,7 @@ class TrustedOverlayHost {
final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(null)
.setContainerLayer()
.setHidden(true)
+ .setCallsite("TrustedOverlayHost.requireOverlaySurfaceControl")
.setName("Overlay Host Leash");
mSurfaceControl = b.build();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 90c287c056e8..6953c60d0d74 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -188,6 +188,7 @@ import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyCache;
+import android.app.servertransaction.WindowStateInsetsControlChangeItem;
import android.app.servertransaction.WindowStateResizeItem;
import android.content.Context;
import android.content.res.Configuration;
@@ -3818,11 +3819,17 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
final InsetsStateController stateController =
getDisplayContent().getInsetsStateController();
+ final InsetsState insetsState = getCompatInsetsState();
mLastReportedActiveControls.set(stateController.getControlsForDispatch(this));
- try {
- mClient.insetsControlChanged(getCompatInsetsState(), mLastReportedActiveControls);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to deliver inset control state change to w=" + this, e);
+ if (Flags.insetsControlChangedItem()) {
+ getProcess().scheduleClientTransactionItem(WindowStateInsetsControlChangeItem.obtain(
+ mClient, insetsState, mLastReportedActiveControls));
+ } else {
+ try {
+ mClient.insetsControlChanged(insetsState, mLastReportedActiveControls);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to deliver inset control state change to w=" + this, e);
+ }
}
}
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));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 70319754253b..4a9760bc3317 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2057,7 +2057,7 @@ public class ActivityRecordTests extends WindowTestsBase {
final ActivityRecord activity = createActivityWithTask();
// TaskSnapshotSurface requires a fullscreen opaque window.
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
- WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
+ TYPE_BASE_APPLICATION);
params.width = params.height = WindowManager.LayoutParams.MATCH_PARENT;
final TestWindowState w = new TestWindowState(
mAtm.mWindowManager, getTestSession(), new TestIWindow(), params, activity);
@@ -2504,25 +2504,6 @@ public class ActivityRecordTests extends WindowTestsBase {
activity.removeImmediately();
}
- @Test
- @Presubmit
- public void testGetTopFullscreenOpaqueWindow() {
- final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
- assertNull(activity.getTopFullscreenOpaqueWindow());
-
- final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "window1");
- final WindowState window11 = createWindow(null, TYPE_APPLICATION, activity, "window11");
- final WindowState window12 = createWindow(null, TYPE_APPLICATION, activity, "window12");
- assertEquals(window12, activity.getTopFullscreenOpaqueWindow());
- window12.mAttrs.width = 500;
- assertEquals(window11, activity.getTopFullscreenOpaqueWindow());
- window11.mAttrs.width = 500;
- assertEquals(window1, activity.getTopFullscreenOpaqueWindow());
- window1.mAttrs.alpha = 0f;
- assertNull(activity.getTopFullscreenOpaqueWindow());
- activity.removeImmediately();
- }
-
@SetupWindows(addWindows = W_ACTIVITY)
@Test
public void testLandscapeSeascapeRotationByApp() {