summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apct-tests/perftests/core/src/android/os/OWNERS5
-rw-r--r--core/java/android/app/ActivityThread.java13
-rw-r--r--core/java/android/app/ClientTransactionHandler.java7
-rw-r--r--core/java/android/app/ContextImpl.java6
-rw-r--r--core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java135
-rw-r--r--core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java112
-rw-r--r--core/java/android/app/servertransaction/WindowTokenClientController.java195
-rw-r--r--core/java/android/os/Handler.java4
-rw-r--r--core/java/android/text/TextUtils.java27
-rw-r--r--core/java/android/view/WindowManagerGlobal.java10
-rw-r--r--core/java/android/view/animation/AnimationUtils.java9
-rw-r--r--core/java/android/view/inputmethod/RemoteInputConnectionImpl.java45
-rw-r--r--core/java/android/window/WindowContextController.java8
-rw-r--r--core/java/android/window/WindowTokenClient.java105
-rw-r--r--core/java/com/android/internal/widget/LockPatternView.java7
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java50
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java65
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java59
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/WindowTokenClientControllerTest.java215
-rw-r--r--core/tests/coretests/src/android/content/TEST_MAPPING18
-rw-r--r--core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING18
-rw-r--r--core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING18
-rw-r--r--core/tests/coretests/src/android/window/WindowContextControllerTest.java19
-rw-r--r--libs/WindowManager/Shell/Android.bp2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/Android.bp2
-rw-r--r--libs/hwui/tests/common/TestUtils.h32
-rw-r--r--libs/hwui/tests/unit/CacheManagerTests.cpp2
-rw-r--r--libs/hwui/tests/unit/RenderNodeDrawableTests.cpp8
-rw-r--r--libs/hwui/tests/unit/ShaderCacheTests.cpp4
-rw-r--r--libs/hwui/tests/unit/SkiaDisplayListTests.cpp4
-rw-r--r--libs/hwui/tests/unit/SkiaPipelineTests.cpp22
-rw-r--r--libs/hwui/tests/unit/main.cpp64
-rw-r--r--media/java/android/media/OWNERS3
-rw-r--r--media/jni/OWNERS3
-rw-r--r--media/jni/android_media_MediaCodec.cpp2
-rw-r--r--packages/CredentialManager/Android.bp2
-rw-r--r--packages/EasterEgg/Android.bp2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java2
-rw-r--r--packages/SystemUI/Android.bp6
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt9
-rw-r--r--packages/SystemUI/compose/core/tests/Android.bp2
-rw-r--r--packages/SystemUI/compose/features/tests/Android.bp2
-rw-r--r--packages/SystemUI/customization/Android.bp2
-rw-r--r--packages/SystemUI/res/values/config.xml10
-rw-r--r--packages/SystemUI/shared/Android.bp2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt99
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt282
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt309
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java73
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/TunerService.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java42
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt56
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt26
-rw-r--r--packages/SystemUI/unfold/Android.bp2
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt4
-rw-r--r--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt6
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java106
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java121
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java4
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java10
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java35
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java150
-rw-r--r--services/core/java/com/android/server/pm/ArchiveManager.java21
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java9
-rw-r--r--services/core/java/com/android/server/pm/TEST_MAPPING10
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java158
-rw-r--r--services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java194
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java18
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java53
-rw-r--r--services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java68
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java2
-rw-r--r--services/core/java/com/android/server/wm/Transition.java30
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java13
-rw-r--r--services/incremental/TEST_MAPPING3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java32
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java116
-rw-r--r--services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING18
-rw-r--r--services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING18
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java177
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java15
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java41
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt3
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt11
145 files changed, 2983 insertions, 1309 deletions
diff --git a/apct-tests/perftests/core/src/android/os/OWNERS b/apct-tests/perftests/core/src/android/os/OWNERS
index a1719c9c31d1..76ab30334634 100644
--- a/apct-tests/perftests/core/src/android/os/OWNERS
+++ b/apct-tests/perftests/core/src/android/os/OWNERS
@@ -1 +1,4 @@
-per-file PackageParsingPerfTest.kt = file:/services/core/java/com/android/server/pm/OWNERS \ No newline at end of file
+per-file PackageParsingPerfTest.kt = file:/services/core/java/com/android/server/pm/OWNERS
+
+# Bug component: 345036
+per-file VibratorPerfTest.java = file:/services/core/java/com/android/server/vibrator/OWNERS \ No newline at end of file
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 853528f908bb..12ffdb37f106 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -65,6 +65,7 @@ import android.app.servertransaction.PendingTransactionActions.StopInfo;
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.TransactionExecutor;
import android.app.servertransaction.TransactionExecutorHelper;
+import android.app.servertransaction.WindowTokenClientController;
import android.bluetooth.BluetoothFrameworkInitializer;
import android.companion.virtual.VirtualDeviceManager;
import android.compat.annotation.UnsupportedAppUsage;
@@ -6221,6 +6222,18 @@ public final class ActivityThread extends ClientTransactionHandler
false /* clearPending */);
}
+ @Override
+ public void handleWindowContextConfigurationChanged(@NonNull IBinder clientToken,
+ @NonNull Configuration configuration, int displayId) {
+ WindowTokenClientController.getInstance().onWindowContextConfigurationChanged(clientToken,
+ configuration, displayId);
+ }
+
+ @Override
+ public void handleWindowContextWindowRemoval(@NonNull IBinder clientToken) {
+ WindowTokenClientController.getInstance().onWindowContextWindowRemoved(clientToken);
+ }
+
/**
* Sends windowing mode change callbacks to {@link Activity} if applicable.
*
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 49fb794a0a25..f7a43f42f2ef 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -163,6 +163,13 @@ public abstract class ClientTransactionHandler {
public abstract void handleActivityConfigurationChanged(@NonNull ActivityClientRecord r,
Configuration overrideConfig, int displayId);
+ /** Deliver {@link android.window.WindowContext} configuration change. */
+ public abstract void handleWindowContextConfigurationChanged(@NonNull IBinder clientToken,
+ @NonNull Configuration configuration, int displayId);
+
+ /** Deliver {@link android.window.WindowContext} window removal event. */
+ public abstract void handleWindowContextWindowRemoval(@NonNull IBinder clientToken);
+
/** Deliver result from another activity. */
public abstract void handleSendResult(
@NonNull ActivityClientRecord r, List<ResultInfo> results, String reason);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 59b0dacd7cd7..1f95497a7dba 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -27,6 +27,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UiContext;
+import android.app.servertransaction.WindowTokenClientController;
import android.companion.virtual.VirtualDeviceManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
@@ -3276,7 +3277,8 @@ class ContextImpl extends Context {
// if this Context is not a WindowContext. WindowContext finalization is handled in
// WindowContext class.
if (mToken instanceof WindowTokenClient && mOwnsToken) {
- ((WindowTokenClient) mToken).detachFromWindowContainerIfNeeded();
+ WindowTokenClientController.getInstance().detachIfNeeded(
+ (WindowTokenClient) mToken);
}
super.finalize();
}
@@ -3304,7 +3306,7 @@ class ContextImpl extends Context {
final WindowTokenClient token = new WindowTokenClient();
final ContextImpl context = systemContext.createWindowContextBase(token, displayId);
token.attachContext(context);
- token.attachToDisplayContent(displayId);
+ WindowTokenClientController.getInstance().attachToDisplayContent(token, displayId);
context.mContextType = CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI;
context.mOwnsToken = true;
diff --git a/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java b/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java
new file mode 100644
index 000000000000..3ac642fd4664
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package 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;
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.Parcel;
+
+import java.util.Objects;
+
+/**
+ * {@link android.window.WindowContext} configuration change message.
+ * @hide
+ */
+public class WindowContextConfigurationChangeItem extends ClientTransactionItem {
+
+ @Nullable
+ private IBinder mClientToken;
+ @Nullable
+ private Configuration mConfiguration;
+ private int mDisplayId;
+
+ @Override
+ public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+ @NonNull PendingTransactionActions pendingActions) {
+ client.handleWindowContextConfigurationChanged(mClientToken, mConfiguration, mDisplayId);
+ }
+
+ // ObjectPoolItem implementation
+
+ private WindowContextConfigurationChangeItem() {}
+
+ /** Obtains an instance initialized with provided params. */
+ public static WindowContextConfigurationChangeItem obtain(
+ @NonNull IBinder clientToken, @NonNull Configuration config, int displayId) {
+ WindowContextConfigurationChangeItem instance =
+ ObjectPool.obtain(WindowContextConfigurationChangeItem.class);
+ if (instance == null) {
+ instance = new WindowContextConfigurationChangeItem();
+ }
+ instance.mClientToken = requireNonNull(clientToken);
+ instance.mConfiguration = requireNonNull(config);
+ instance.mDisplayId = displayId;
+
+ return instance;
+ }
+
+ @Override
+ public void recycle() {
+ mClientToken = null;
+ mConfiguration = null;
+ mDisplayId = INVALID_DISPLAY;
+ ObjectPool.recycle(this);
+ }
+
+ // Parcelable implementation
+
+ /** Writes to Parcel. */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeStrongBinder(mClientToken);
+ dest.writeTypedObject(mConfiguration, flags);
+ dest.writeInt(mDisplayId);
+ }
+
+ /** Reads from Parcel. */
+ private WindowContextConfigurationChangeItem(@NonNull Parcel in) {
+ mClientToken = in.readStrongBinder();
+ mConfiguration = in.readTypedObject(Configuration.CREATOR);
+ mDisplayId = in.readInt();
+ }
+
+ public static final @NonNull Creator<WindowContextConfigurationChangeItem> CREATOR =
+ new Creator<>() {
+ public WindowContextConfigurationChangeItem createFromParcel(Parcel in) {
+ return new WindowContextConfigurationChangeItem(in);
+ }
+
+ public WindowContextConfigurationChangeItem[] newArray(int size) {
+ return new WindowContextConfigurationChangeItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final WindowContextConfigurationChangeItem other = (WindowContextConfigurationChangeItem) o;
+ return Objects.equals(mClientToken, other.mClientToken)
+ && Objects.equals(mConfiguration, other.mConfiguration)
+ && mDisplayId == other.mDisplayId;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + Objects.hashCode(mClientToken);
+ result = 31 * result + Objects.hashCode(mConfiguration);
+ result = 31 * result + mDisplayId;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "WindowContextConfigurationChangeItem{clientToken=" + mClientToken
+ + ", config=" + mConfiguration
+ + ", displayId=" + mDisplayId
+ + "}";
+ }
+}
diff --git a/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java b/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
new file mode 100644
index 000000000000..ed52a6496e95
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.os.Parcel;
+
+import java.util.Objects;
+
+/**
+ * {@link android.window.WindowContext} window removal message.
+ * @hide
+ */
+public class WindowContextWindowRemovalItem extends ClientTransactionItem {
+
+ @Nullable
+ private IBinder mClientToken;
+
+ @Override
+ public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+ @NonNull PendingTransactionActions pendingActions) {
+ client.handleWindowContextWindowRemoval(mClientToken);
+ }
+
+ // ObjectPoolItem implementation
+
+ private WindowContextWindowRemovalItem() {}
+
+ /** Obtains an instance initialized with provided params. */
+ public static WindowContextWindowRemovalItem obtain(@NonNull IBinder clientToken) {
+ WindowContextWindowRemovalItem instance =
+ ObjectPool.obtain(WindowContextWindowRemovalItem.class);
+ if (instance == null) {
+ instance = new WindowContextWindowRemovalItem();
+ }
+ instance.mClientToken = requireNonNull(clientToken);
+
+ return instance;
+ }
+
+ @Override
+ public void recycle() {
+ mClientToken = null;
+ ObjectPool.recycle(this);
+ }
+
+ // Parcelable implementation
+
+ /** Writes to Parcel. */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeStrongBinder(mClientToken);
+ }
+
+ /** Reads from Parcel. */
+ private WindowContextWindowRemovalItem(@NonNull Parcel in) {
+ mClientToken = in.readStrongBinder();
+ }
+
+ public static final @NonNull Creator<WindowContextWindowRemovalItem> CREATOR = new Creator<>() {
+ public WindowContextWindowRemovalItem createFromParcel(Parcel in) {
+ return new WindowContextWindowRemovalItem(in);
+ }
+
+ public WindowContextWindowRemovalItem[] newArray(int size) {
+ return new WindowContextWindowRemovalItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final WindowContextWindowRemovalItem other = (WindowContextWindowRemovalItem) o;
+ return Objects.equals(mClientToken, other.mClientToken);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + Objects.hashCode(mClientToken);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "WindowContextWindowRemovalItem{clientToken=" + mClientToken + "}";
+ }
+}
diff --git a/core/java/android/app/servertransaction/WindowTokenClientController.java b/core/java/android/app/servertransaction/WindowTokenClientController.java
new file mode 100644
index 000000000000..5d123a043835
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowTokenClientController.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.view.WindowManager.LayoutParams.WindowType;
+import static android.view.WindowManagerGlobal.getWindowManagerService;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.view.IWindowManager;
+import android.window.WindowContext;
+import android.window.WindowTokenClient;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Singleton controller to manage the attached {@link WindowTokenClient}s, and to dispatch
+ * corresponding window configuration change from server side.
+ * @hide
+ */
+public class WindowTokenClientController {
+
+ private static final String TAG = WindowTokenClientController.class.getSimpleName();
+ private static WindowTokenClientController sController;
+
+ private final Object mLock = new Object();
+
+ /** Mapping from a client defined token to the {@link WindowTokenClient} it represents. */
+ @GuardedBy("mLock")
+ private final ArrayMap<IBinder, WindowTokenClient> mWindowTokenClientMap = new ArrayMap<>();
+
+ /** Gets the singleton controller. */
+ public static WindowTokenClientController getInstance() {
+ synchronized (WindowTokenClientController.class) {
+ if (sController == null) {
+ sController = new WindowTokenClientController();
+ }
+ return sController;
+ }
+ }
+
+ /** Overrides the {@link #getInstance()} for test only. */
+ @VisibleForTesting
+ public static void overrideForTesting(@NonNull WindowTokenClientController controller) {
+ synchronized (WindowTokenClientController.class) {
+ sController = controller;
+ }
+ }
+
+ private WindowTokenClientController() {}
+
+ /**
+ * Attaches a {@link WindowTokenClient} to a {@link com.android.server.wm.DisplayArea}.
+ *
+ * @param client The {@link WindowTokenClient} to attach.
+ * @param type The window type of the {@link WindowContext}
+ * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
+ * @param options The window context launched option
+ * @return {@code true} if attaching successfully.
+ */
+ public boolean attachToDisplayArea(@NonNull WindowTokenClient client,
+ @WindowType int type, int displayId, @Nullable Bundle options) {
+ final Configuration configuration;
+ try {
+ configuration = getWindowManagerService()
+ .attachWindowContextToDisplayArea(client, type, displayId, options);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ if (configuration == null) {
+ return false;
+ }
+ onWindowContextTokenAttached(client, displayId, configuration);
+ return true;
+ }
+
+ /**
+ * Attaches a {@link WindowTokenClient} to a {@code DisplayContent}.
+ *
+ * @param client The {@link WindowTokenClient} to attach.
+ * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
+ * @return {@code true} if attaching successfully.
+ */
+ public boolean attachToDisplayContent(@NonNull WindowTokenClient client, int displayId) {
+ final IWindowManager wms = getWindowManagerService();
+ // #createSystemUiContext may call this method before WindowManagerService is initialized.
+ if (wms == null) {
+ return false;
+ }
+ final Configuration configuration;
+ try {
+ configuration = wms.attachToDisplayContent(client, displayId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ if (configuration == null) {
+ return false;
+ }
+ onWindowContextTokenAttached(client, displayId, configuration);
+ return true;
+ }
+
+ /**
+ * Attaches this {@link WindowTokenClient} to a {@code windowToken}.
+ *
+ * @param client The {@link WindowTokenClient} to attach.
+ * @param windowToken the window token to associated with
+ */
+ public void attachToWindowToken(@NonNull WindowTokenClient client,
+ @NonNull IBinder windowToken) {
+ try {
+ getWindowManagerService().attachWindowContextToWindowToken(client, windowToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ // We don't report configuration change for now.
+ synchronized (mLock) {
+ mWindowTokenClientMap.put(client.asBinder(), client);
+ }
+ }
+
+ /** Detaches a {@link WindowTokenClient} from associated WindowContainer if there's one. */
+ public void detachIfNeeded(@NonNull WindowTokenClient client) {
+ synchronized (mLock) {
+ if (mWindowTokenClientMap.remove(client.asBinder()) == null) {
+ return;
+ }
+ }
+ try {
+ getWindowManagerService().detachWindowContextFromWindowContainer(client);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private void onWindowContextTokenAttached(@NonNull WindowTokenClient client, int displayId,
+ @NonNull Configuration configuration) {
+ synchronized (mLock) {
+ mWindowTokenClientMap.put(client.asBinder(), client);
+ }
+ client.onConfigurationChanged(configuration, displayId,
+ false /* shouldReportConfigChange */);
+ }
+
+ /** Called when receives {@link WindowContextConfigurationChangeItem}. */
+ public void onWindowContextConfigurationChanged(@NonNull IBinder clientToken,
+ @NonNull Configuration configuration, int displayId) {
+ final WindowTokenClient windowTokenClient = getWindowTokenClient(clientToken);
+ if (windowTokenClient != null) {
+ windowTokenClient.onConfigurationChanged(configuration, displayId);
+ }
+ }
+
+ /** Called when receives {@link WindowContextWindowRemovalItem}. */
+ public void onWindowContextWindowRemoved(@NonNull IBinder clientToken) {
+ final WindowTokenClient windowTokenClient = getWindowTokenClient(clientToken);
+ if (windowTokenClient != null) {
+ windowTokenClient.onWindowTokenRemoved();
+ }
+ }
+
+ @Nullable
+ private WindowTokenClient getWindowTokenClient(@NonNull IBinder clientToken) {
+ final WindowTokenClient windowTokenClient;
+ synchronized (mLock) {
+ windowTokenClient = mWindowTokenClientMap.get(clientToken);
+ }
+ if (windowTokenClient == null) {
+ Log.w(TAG, "Can't find attached WindowTokenClient for " + clientToken);
+ }
+ return windowTokenClient;
+ }
+}
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index ceaf337b2122..2584f047295d 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -119,7 +119,7 @@ public class Handler {
* crashes (if a handler is sometimes created on a thread without a Looper active), or race
* conditions, where the thread a handler is associated with is not what the author
* anticipated. Instead, use an {@link java.util.concurrent.Executor} or specify the Looper
- * explicitly, using {@link Looper#getMainLooper}, {link android.view.View#getHandler}, or
+ * explicitly, using {@link Looper#getMainLooper}, {@link android.view.View#getHandler}, or
* similar. If the implicit thread local behavior is required for compatibility, use
* {@code new Handler(Looper.myLooper())} to make it clear to readers.
*
@@ -144,7 +144,7 @@ public class Handler {
* crashes (if a handler is sometimes created on a thread without a Looper active), or race
* conditions, where the thread a handler is associated with is not what the author
* anticipated. Instead, use an {@link java.util.concurrent.Executor} or specify the Looper
- * explicitly, using {@link Looper#getMainLooper}, {link android.view.View#getHandler}, or
+ * explicitly, using {@link Looper#getMainLooper}, {@link android.view.View#getHandler}, or
* similar. If the implicit thread local behavior is required for compatibility, use
* {@code new Handler(Looper.myLooper(), callback)} to make it clear to readers.
*/
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index f256210365ba..447c3bcdc186 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -27,6 +27,7 @@ import android.annotation.PluralsRes;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Typeface;
import android.icu.lang.UCharacter;
import android.icu.text.CaseMap;
import android.icu.text.Edits;
@@ -2482,12 +2483,28 @@ public class TextUtils {
if (ellipsizeDip == 0) {
return gettingCleaned.toString();
} else {
- // Truncate
- final TextPaint paint = new TextPaint();
- paint.setTextSize(42);
+ final float assumedFontSizePx = 42;
+ if (Typeface.getSystemFontMap().isEmpty()) {
+ // In the system server, the font files may not be loaded, so unable to perform
+ // ellipsize, so use the estimated char count for the ellipsize.
+
+ // The median of glyph widths of the Roboto is 0.57em, so use it as a reference
+ // of the glyph width.
+ final float assumedCharWidthInEm = 0.57f;
+ final float assumedCharWidthInPx = assumedFontSizePx * assumedCharWidthInEm;
+
+ // Even if the argument name is `ellipsizeDip`, the unit of this argument is pixels.
+ final int charCount = (int) ((ellipsizeDip + 0.5f) / assumedCharWidthInPx);
+ return TextUtils.trimToSize(gettingCleaned.toString(), charCount)
+ + getEllipsisString(TruncateAt.END);
+ } else {
+ // Truncate
+ final TextPaint paint = new TextPaint();
+ paint.setTextSize(assumedFontSizePx);
- return TextUtils.ellipsize(gettingCleaned.toString(), paint, ellipsizeDip,
- TextUtils.TruncateAt.END);
+ return TextUtils.ellipsize(gettingCleaned.toString(), paint, ellipsizeDip,
+ TextUtils.TruncateAt.END);
+ }
}
}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 99a4f6b41ef3..6aa85062562c 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -34,6 +34,7 @@ import android.util.ArraySet;
import android.util.Log;
import android.view.inputmethod.InputMethodManager;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastPrintWriter;
import java.io.FileDescriptor;
@@ -184,6 +185,15 @@ public final class WindowManagerGlobal {
}
}
+ /** Overrides the {@link #getWindowManagerService()} for test only. */
+ @VisibleForTesting
+ public static void overrideWindowManagerServiceForTesting(
+ @NonNull IWindowManager windowManager) {
+ synchronized (WindowManagerGlobal.class) {
+ sWindowManagerService = windowManager;
+ }
+ }
+
@UnsupportedAppUsage
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 8ba8b8cca5ed..0699bc1cb734 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -32,6 +32,7 @@ import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.TimeUtils;
import android.util.Xml;
+import android.view.Choreographer;
import android.view.InflateException;
import org.xmlpull.v1.XmlPullParser;
@@ -153,7 +154,13 @@ public class AnimationUtils {
*/
public static long getExpectedPresentationTimeNanos() {
AnimationState state = sAnimationState.get();
- return state.mExpectedPresentationTimeNanos;
+ if (state.animationClockLocked) {
+ return state.mExpectedPresentationTimeNanos;
+ }
+ // When this methoed is called outside of a Choreographer callback,
+ // we obtain the value of expectedPresentTimeNanos from the Choreographer.
+ // This helps avoid returning a time that could potentially be earlier than current time.
+ return Choreographer.getInstance().getLatestExpectedPresentTimeNanos();
}
/**
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index e9d7b9b25d91..a95748c6d425 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -43,7 +43,6 @@ import android.view.KeyEvent;
import android.view.View;
import android.view.ViewRootImpl;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.IRemoteInputConnection;
@@ -54,6 +53,7 @@ import java.lang.annotation.Retention;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -158,18 +158,13 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
boolean cancellable();
}
- @GuardedBy("mLock")
- @Nullable
- private InputConnection mInputConnection;
+ @NonNull
+ private final AtomicReference<InputConnection> mInputConnectionRef;
@NonNull
private final Looper mLooper;
private final Handler mH;
- private final Object mLock = new Object();
- @GuardedBy("mLock")
- private boolean mFinished = false;
-
private final InputMethodManager mParentInputMethodManager;
private final WeakReference<View> mServedView;
@@ -185,7 +180,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
RemoteInputConnectionImpl(@NonNull Looper looper,
@NonNull InputConnection inputConnection,
@NonNull InputMethodManager inputMethodManager, @Nullable View servedView) {
- mInputConnection = inputConnection;
+ mInputConnectionRef = new AtomicReference<>(inputConnection);
mLooper = looper;
mH = new Handler(mLooper);
mParentInputMethodManager = inputMethodManager;
@@ -197,9 +192,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
*/
@Nullable
public InputConnection getInputConnection() {
- synchronized (mLock) {
- return mInputConnection;
- }
+ return mInputConnectionRef.get();
}
/**
@@ -215,9 +208,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
* {@link InputConnection#closeConnection()} as a result of {@link #deactivate()}.
*/
private boolean isFinished() {
- synchronized (mLock) {
- return mFinished;
- }
+ return mInputConnectionRef.get() == null;
}
private boolean isActive() {
@@ -386,10 +377,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
// TODO(b/199934664): See if we can remove this by providing a default impl.
}
} finally {
- synchronized (mLock) {
- mInputConnection = null;
- mFinished = true;
- }
+ mInputConnectionRef.set(null);
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
}
@@ -441,7 +429,6 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
public String toString() {
return "RemoteInputConnectionImpl{"
+ "connection=" + getInputConnection()
- + " finished=" + isFinished()
+ " mParentInputMethodManager.isActive()=" + mParentInputMethodManager.isActive()
+ " mServedView=" + mServedView.get()
+ "}";
@@ -455,16 +442,14 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
* {@link DumpableInputConnection#dumpDebug(ProtoOutputStream, long)}.
*/
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
- synchronized (mLock) {
- // Check that the call is initiated in the target thread of the current InputConnection
- // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are
- // executed on this thread. Otherwise the messages are dispatched to the correct thread
- // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance
- // reasons.
- if ((mInputConnection instanceof DumpableInputConnection)
- && mLooper.isCurrentThread()) {
- ((DumpableInputConnection) mInputConnection).dumpDebug(proto, fieldId);
- }
+ final InputConnection ic = mInputConnectionRef.get();
+ // Check that the call is initiated in the target thread of the current InputConnection
+ // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are
+ // executed on this thread. Otherwise the messages are dispatched to the correct thread
+ // in IInputConnectionWrapper, but this is not wanted while dumping, for performance
+ // reasons.
+ if ((ic instanceof DumpableInputConnection) && mLooper.isCurrentThread()) {
+ ((DumpableInputConnection) ic).dumpDebug(proto, fieldId);
}
}
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index 4b9a957f541d..eb270e2fb2f0 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -21,6 +21,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.servertransaction.WindowTokenClientController;
import android.content.Context;
import android.os.Bundle;
import android.os.IBinder;
@@ -104,7 +105,8 @@ public class WindowContextController {
throw new IllegalStateException("A Window Context can be only attached to "
+ "a DisplayArea once.");
}
- mAttachedToDisplayArea = mToken.attachToDisplayArea(type, displayId, options)
+ mAttachedToDisplayArea = WindowTokenClientController.getInstance().attachToDisplayArea(
+ mToken, type, displayId, options)
? AttachStatus.STATUS_ATTACHED : AttachStatus.STATUS_FAILED;
if (mAttachedToDisplayArea == AttachStatus.STATUS_FAILED) {
Log.w(TAG, "attachToDisplayArea fail, type:" + type + ", displayId:"
@@ -140,13 +142,13 @@ public class WindowContextController {
throw new IllegalStateException("The Window Context should have been attached"
+ " to a DisplayArea. AttachToDisplayArea:" + mAttachedToDisplayArea);
}
- mToken.attachToWindowToken(windowToken);
+ WindowTokenClientController.getInstance().attachToWindowToken(mToken, windowToken);
}
/** Detaches the window context from the node it's currently associated with. */
public void detachIfNeeded() {
if (mAttachedToDisplayArea == AttachStatus.STATUS_ATTACHED) {
- mToken.detachFromWindowContainerIfNeeded();
+ WindowTokenClientController.getInstance().detachIfNeeded(mToken);
mAttachedToDisplayArea = AttachStatus.STATUS_DETACHED;
if (DEBUG_ATTACH) {
Log.d(TAG, "Detach Window Context.");
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index a208634abe78..47d3df870216 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -20,13 +20,12 @@ import static android.window.ConfigurationHelper.isDifferentDisplay;
import static android.window.ConfigurationHelper.shouldUpdateResources;
import android.annotation.AnyThread;
-import android.annotation.BinderThread;
import android.annotation.MainThread;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.IWindowToken;
import android.app.ResourcesManager;
+import android.app.servertransaction.WindowTokenClientController;
import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -36,14 +35,9 @@ import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
-import android.os.RemoteException;
import android.util.Log;
-import android.view.IWindowManager;
-import android.view.WindowManager.LayoutParams.WindowType;
-import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.function.pooled.PooledLambda;
import java.lang.ref.WeakReference;
@@ -70,15 +64,11 @@ public class WindowTokenClient extends IWindowToken.Stub {
private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
- private IWindowManager mWms;
-
@GuardedBy("itself")
private final Configuration mConfiguration = new Configuration();
private boolean mShouldDumpConfigForIme;
- private boolean mAttachToWindowContainer;
-
private final Handler mHandler = ActivityThread.currentActivityThread().getHandler();
/**
@@ -101,96 +91,15 @@ public class WindowTokenClient extends IWindowToken.Stub {
}
/**
- * Attaches this {@link WindowTokenClient} to a {@link com.android.server.wm.DisplayArea}.
- *
- * @param type The window type of the {@link WindowContext}
- * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
- * @param options The window context launched option
- * @return {@code true} if attaching successfully.
- */
- public boolean attachToDisplayArea(@WindowType int type, int displayId,
- @Nullable Bundle options) {
- try {
- final Configuration configuration = getWindowManagerService()
- .attachWindowContextToDisplayArea(this, type, displayId, options);
- if (configuration == null) {
- return false;
- }
- onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */);
- mAttachToWindowContainer = true;
- return true;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Attaches this {@link WindowTokenClient} to a {@code DisplayContent}.
- *
- * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
- * @return {@code true} if attaching successfully.
- */
- public boolean attachToDisplayContent(int displayId) {
- final IWindowManager wms = getWindowManagerService();
- // #createSystemUiContext may call this method before WindowManagerService is initialized.
- if (wms == null) {
- return false;
- }
- try {
- final Configuration configuration = wms.attachToDisplayContent(this, displayId);
- if (configuration == null) {
- return false;
- }
- onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */);
- mAttachToWindowContainer = true;
- return true;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Attaches this {@link WindowTokenClient} to a {@code windowToken}.
- *
- * @param windowToken the window token to associated with
- */
- public void attachToWindowToken(IBinder windowToken) {
- try {
- getWindowManagerService().attachWindowContextToWindowToken(this, windowToken);
- mAttachToWindowContainer = true;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /** Detaches this {@link WindowTokenClient} from associated WindowContainer if there's one. */
- public void detachFromWindowContainerIfNeeded() {
- if (!mAttachToWindowContainer) {
- return;
- }
- try {
- getWindowManagerService().detachWindowContextFromWindowContainer(this);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- private IWindowManager getWindowManagerService() {
- if (mWms == null) {
- mWms = WindowManagerGlobal.getWindowManagerService();
- }
- return mWms;
- }
-
- /**
* Called when {@link Configuration} updates from the server side receive.
*
* @param newConfig the updated {@link Configuration}
* @param newDisplayId the updated {@link android.view.Display} ID
*/
- @BinderThread
+ @AnyThread
@Override
public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
+ // TODO(b/290876897): No need to post on mHandler after migrating to ClientTransaction
mHandler.post(PooledLambda.obtainRunnable(this::onConfigurationChanged, newConfig,
newDisplayId, true /* shouldReportConfigChange */).recycleOnUse());
}
@@ -207,15 +116,14 @@ public class WindowTokenClient extends IWindowToken.Stub {
* {@code shouldReportConfigChange} is {@code true}, which is usually from
* {@link IWindowToken#onConfigurationChanged(Configuration, int)}
* directly, while this method could be run on any thread if it is used to initialize
- * Context's {@code Configuration} via {@link #attachToDisplayArea(int, int, Bundle)}
- * or {@link #attachToDisplayContent(int)}.
+ * Context's {@code Configuration} via {@link WindowTokenClientController#attachToDisplayArea}
+ * or {@link WindowTokenClientController#attachToDisplayContent}.
*
* @param shouldReportConfigChange {@code true} to indicate that the {@code Configuration}
* should be dispatched to listeners.
*
*/
@AnyThread
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public void onConfigurationChanged(Configuration newConfig, int newDisplayId,
boolean shouldReportConfigChange) {
final Context context = mContextRef.get();
@@ -280,9 +188,10 @@ public class WindowTokenClient extends IWindowToken.Stub {
}
}
- @BinderThread
+ @AnyThread
@Override
public void onWindowTokenRemoved() {
+ // TODO(b/290876897): No need to post on mHandler after migrating to ClientTransaction
mHandler.post(PooledLambda.obtainRunnable(
WindowTokenClient::onWindowTokenRemovedInner, this).recycleOnUse());
}
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index c2cfcd64c835..66b0158fbd67 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -1565,6 +1565,13 @@ public class LockPatternView extends View {
mInStealthMode = ss.isInStealthMode();
}
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ setSystemGestureExclusionRects(List.of(new Rect(left, top, right, bottom)));
+ }
+
/**
* The parecelable for saving and restoring a lock pattern view.
*/
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 48577416b3d0..c1b55cdfc6d7 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -21,6 +21,7 @@ import static android.content.Intent.ACTION_EDIT;
import static android.content.Intent.ACTION_VIEW;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
@@ -29,6 +30,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import android.annotation.Nullable;
import android.app.Activity;
@@ -46,6 +49,7 @@ import android.app.servertransaction.ConfigurationChangeItem;
import android.app.servertransaction.NewIntentItem;
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.StopActivityItem;
+import android.app.servertransaction.WindowTokenClientController;
import android.content.Context;
import android.content.Intent;
import android.content.res.CompatibilityInfo;
@@ -70,6 +74,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.content.ReferrerIntent;
import org.junit.After;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -83,7 +88,7 @@ import java.util.function.Consumer;
/**
* Test for verifying {@link android.app.ActivityThread} class.
* Build/Install/Run:
- * atest FrameworksCoreTests:android.app.activity.ActivityThreadTest
+ * atest FrameworksCoreTests:ActivityThreadTest
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
@@ -100,14 +105,24 @@ public class ActivityThreadTest {
new ActivityTestRule<>(TestActivity.class, true /* initialTouchMode */,
false /* launchActivity */);
+ private WindowTokenClientController mOriginalWindowTokenClientController;
+
private ArrayList<VirtualDisplay> mCreatedVirtualDisplays;
+ @Before
+ public void setup() {
+ // Keep track of the original controller, so that it can be used to restore in tearDown()
+ // when there is override in some test cases.
+ mOriginalWindowTokenClientController = WindowTokenClientController.getInstance();
+ }
+
@After
public void tearDown() {
if (mCreatedVirtualDisplays != null) {
mCreatedVirtualDisplays.forEach(VirtualDisplay::release);
mCreatedVirtualDisplays = null;
}
+ WindowTokenClientController.overrideForTesting(mOriginalWindowTokenClientController);
}
@Test
@@ -730,6 +745,39 @@ public class ActivityThreadTest {
assertFalse(activity.enterPipSkipped());
}
+ @Test
+ public void testHandleWindowContextConfigurationChanged() {
+ final Activity activity = mActivityTestRule.launchActivity(new Intent());
+ final ActivityThread activityThread = activity.getActivityThread();
+ final WindowTokenClientController windowTokenClientController =
+ mock(WindowTokenClientController.class);
+ WindowTokenClientController.overrideForTesting(windowTokenClientController);
+ final IBinder clientToken = mock(IBinder.class);
+ final Configuration configuration = new Configuration();
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> activityThread
+ .handleWindowContextConfigurationChanged(
+ clientToken, configuration, DEFAULT_DISPLAY));
+
+ verify(windowTokenClientController).onWindowContextConfigurationChanged(
+ clientToken, configuration, DEFAULT_DISPLAY);
+ }
+
+ @Test
+ public void testHandleWindowContextWindowRemoval() {
+ final Activity activity = mActivityTestRule.launchActivity(new Intent());
+ final ActivityThread activityThread = activity.getActivityThread();
+ final WindowTokenClientController windowTokenClientController =
+ mock(WindowTokenClientController.class);
+ WindowTokenClientController.overrideForTesting(windowTokenClientController);
+ final IBinder clientToken = mock(IBinder.class);
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> activityThread
+ .handleWindowContextWindowRemoval(clientToken));
+
+ verify(windowTokenClientController).onWindowContextWindowRemoved(clientToken);
+ }
+
/**
* Calls {@link ActivityThread#handleActivityConfigurationChanged(ActivityClientRecord,
* Configuration, int)} to try to push activity configuration to the activity for the given
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java
new file mode 100644
index 000000000000..7811e1a58c22
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.ClientTransactionHandler;
+import android.content.res.Configuration;
+import android.os.IBinder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowContextConfigurationChangeItem}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:WindowContextConfigurationChangeItemTest
+ */
+public class WindowContextConfigurationChangeItemTest {
+
+ @Mock
+ private ClientTransactionHandler mHandler;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private PendingTransactionActions mPendingActions;
+ @Mock
+ private IBinder mClientToken;
+ // Can't mock final class.
+ private final Configuration mConfiguration = new Configuration();
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testExecute() {
+ final WindowContextConfigurationChangeItem item = WindowContextConfigurationChangeItem
+ .obtain(mClientToken, mConfiguration, DEFAULT_DISPLAY);
+ item.execute(mHandler, mToken, mPendingActions);
+
+ verify(mHandler).handleWindowContextConfigurationChanged(mClientToken, mConfiguration,
+ DEFAULT_DISPLAY);
+ }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
new file mode 100644
index 000000000000..2c83c70e9ae2
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowContextWindowRemovalItem}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:WindowContextWindowRemovalItemTest
+ */
+public class WindowContextWindowRemovalItemTest {
+
+ @Mock
+ private ClientTransactionHandler mHandler;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private PendingTransactionActions mPendingActions;
+ @Mock
+ private IBinder mClientToken;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testExecute() {
+ final WindowContextWindowRemovalItem item = WindowContextWindowRemovalItem.obtain(
+ mClientToken);
+ item.execute(mHandler, mToken, mPendingActions);
+
+ verify(mHandler).handleWindowContextWindowRemoval(mClientToken);
+ }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowTokenClientControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowTokenClientControllerTest.java
new file mode 100644
index 000000000000..3b2fe583f605
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowTokenClientControllerTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+import android.window.WindowTokenClient;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowTokenClientController}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:WindowTokenClientControllerTest
+ */
+@SmallTest
+@Presubmit
+public class WindowTokenClientControllerTest {
+
+ @Mock
+ private IWindowManager mWindowManagerService;
+ @Mock
+ private WindowTokenClient mWindowTokenClient;
+ @Mock
+ private IBinder mClientToken;
+ @Mock
+ private IBinder mWindowToken;
+ // Can't mock final class.
+ private final Configuration mConfiguration = new Configuration();
+
+ private IWindowManager mOriginalWindowManagerService;
+
+ private WindowTokenClientController mController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mOriginalWindowManagerService = WindowManagerGlobal.getWindowManagerService();
+ WindowManagerGlobal.overrideWindowManagerServiceForTesting(mWindowManagerService);
+ doReturn(mClientToken).when(mWindowTokenClient).asBinder();
+ mController = spy(WindowTokenClientController.getInstance());
+ }
+
+ @After
+ public void tearDown() {
+ WindowManagerGlobal.overrideWindowManagerServiceForTesting(mOriginalWindowManagerService);
+ }
+
+ @Test
+ public void testAttachToDisplayArea() throws RemoteException {
+ doReturn(null).when(mWindowManagerService).attachWindowContextToDisplayArea(
+ any(), anyInt(), anyInt(), any());
+
+ assertFalse(mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+ DEFAULT_DISPLAY, null /* options */));
+ verify(mWindowManagerService).attachWindowContextToDisplayArea(mWindowTokenClient,
+ TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY, null /* options */);
+ verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
+
+ doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayArea(
+ any(), anyInt(), anyInt(), any());
+
+ assertTrue(mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+ DEFAULT_DISPLAY, null /* options */));
+ verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY,
+ false /* shouldReportConfigChange */);
+ }
+
+ @Test
+ public void testAttachToDisplayArea_detachIfNeeded() throws RemoteException {
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ doReturn(null).when(mWindowManagerService).attachWindowContextToDisplayArea(
+ any(), anyInt(), anyInt(), any());
+ mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+ DEFAULT_DISPLAY, null /* options */);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayArea(
+ any(), anyInt(), anyInt(), any());
+ mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+ DEFAULT_DISPLAY, null /* options */);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService).detachWindowContextFromWindowContainer(any());
+ }
+
+ @Test
+ public void testAttachToDisplayContent() throws RemoteException {
+ doReturn(null).when(mWindowManagerService).attachToDisplayContent(
+ any(), anyInt());
+
+ assertFalse(mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY));
+ verify(mWindowManagerService).attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY);
+ verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
+
+ doReturn(mConfiguration).when(mWindowManagerService).attachToDisplayContent(
+ any(), anyInt());
+
+ assertTrue(mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY));
+ verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY,
+ false /* shouldReportConfigChange */);
+ }
+
+ @Test
+ public void testAttachToDisplayContent_detachIfNeeded() throws RemoteException {
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ doReturn(null).when(mWindowManagerService).attachToDisplayContent(
+ any(), anyInt());
+ mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ doReturn(mConfiguration).when(mWindowManagerService).attachToDisplayContent(
+ any(), anyInt());
+ mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService).detachWindowContextFromWindowContainer(any());
+ }
+
+ @Test
+ public void testAttachToWindowToken() throws RemoteException {
+ mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+
+ verify(mWindowManagerService).attachWindowContextToWindowToken(mWindowTokenClient,
+ mWindowToken);
+ }
+
+ @Test
+ public void testAttachToWindowToken_detachIfNeeded() throws RemoteException {
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService).detachWindowContextFromWindowContainer(any());
+ }
+
+ @Test
+ public void testOnWindowContextConfigurationChanged() {
+ mController.onWindowContextConfigurationChanged(
+ mClientToken, mConfiguration, DEFAULT_DISPLAY);
+
+ verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt());
+
+ mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+
+ mController.onWindowContextConfigurationChanged(
+ mClientToken, mConfiguration, DEFAULT_DISPLAY);
+
+ verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY);
+ }
+
+ @Test
+ public void testOnWindowContextWindowRemoved() {
+ mController.onWindowContextWindowRemoved(mClientToken);
+
+ verify(mWindowTokenClient, never()).onWindowTokenRemoved();
+
+ mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+
+ mController.onWindowContextWindowRemoved(mClientToken);
+
+ verify(mWindowTokenClient).onWindowTokenRemoved();
+ }
+}
diff --git a/core/tests/coretests/src/android/content/TEST_MAPPING b/core/tests/coretests/src/android/content/TEST_MAPPING
new file mode 100644
index 000000000000..bbc2458f5d8b
--- /dev/null
+++ b/core/tests/coretests/src/android/content/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.content.ContentCaptureOptionsTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING b/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING
new file mode 100644
index 000000000000..f8beac2814db
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.view.contentcapture"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING b/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING
new file mode 100644
index 000000000000..3cd4e17d820b
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.view.contentprotection"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/tests/coretests/src/android/window/WindowContextControllerTest.java b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
index a52d2e88145f..5f2aecc40d16 100644
--- a/core/tests/coretests/src/android/window/WindowContextControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
@@ -24,17 +24,20 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.app.servertransaction.WindowTokenClientController;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,14 +59,26 @@ import org.mockito.MockitoAnnotations;
public class WindowContextControllerTest {
private WindowContextController mController;
@Mock
+ private WindowTokenClientController mWindowTokenClientController;
+ @Mock
private WindowTokenClient mMockToken;
+ private WindowTokenClientController mOriginalController;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mController = new WindowContextController(mMockToken);
doNothing().when(mMockToken).onConfigurationChanged(any(), anyInt(), anyBoolean());
- doReturn(true).when(mMockToken).attachToDisplayArea(anyInt(), anyInt(), any());
+ mOriginalController = WindowTokenClientController.getInstance();
+ WindowTokenClientController.overrideForTesting(mWindowTokenClientController);
+ doReturn(true).when(mWindowTokenClientController).attachToDisplayArea(
+ eq(mMockToken), anyInt(), anyInt(), any());
+ }
+
+ @After
+ public void tearDown() {
+ WindowTokenClientController.overrideForTesting(mOriginalController);
}
@Test(expected = IllegalStateException.class)
@@ -78,7 +93,7 @@ public class WindowContextControllerTest {
public void testDetachIfNeeded_NotAttachedYet_DoNothing() {
mController.detachIfNeeded();
- verify(mMockToken, never()).detachFromWindowContainerIfNeeded();
+ verify(mWindowTokenClientController, never()).detachIfNeeded(any());
}
@Test
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index a605e2bdd3d6..8bd500eac67b 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -166,7 +166,7 @@ android_library {
// *.kt sources are inside a filegroup.
"kotlin-annotations",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
manifest: "AndroidManifest.xml",
plugins: ["dagger2-compiler"],
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt
index d5d072a8d449..122dcbb3c2ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt
@@ -94,7 +94,6 @@ class FloatingContentCoordinator constructor() {
* non-overlapping.
* @return The new bounds for this content.
*/
- @JvmDefault
fun calculateNewBoundsOnOverlap(
overlappingContentBounds: Rect,
otherContentBounds: List<Rect>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 711df0d89936..c05af73e6765 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -368,7 +368,6 @@ class DesktopModeTaskRepository {
/**
* Called when the active tasks change in desktop mode.
*/
- @JvmDefault
fun onActiveTasksChanged(displayId: Int) {}
}
@@ -379,17 +378,15 @@ class DesktopModeTaskRepository {
/**
* Called when the desktop starts or stops showing freeform tasks.
*/
- @JvmDefault
fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) {}
/**
* Called when the desktop stashed status changes.
*/
- @JvmDefault
fun onStashedChanged(displayId: Int, stashed: Boolean) {}
}
}
private fun <T> Iterable<T>.toDumpString(): String {
return joinToString(separator = ", ", prefix = "[", postfix = "]")
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index af8ef174b168..7699b4bfd13a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -737,12 +737,23 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
Intent fillInIntent2 = null;
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
+ final ActivityOptions activityOptions1 = options1 != null
+ ? ActivityOptions.fromBundle(options1) : ActivityOptions.makeBasic();
+ final ActivityOptions activityOptions2 = options2 != null
+ ? ActivityOptions.fromBundle(options2) : ActivityOptions.makeBasic();
if (samePackage(packageName1, packageName2, userId1, userId2)) {
if (supportMultiInstancesSplit(packageName1)) {
fillInIntent1 = new Intent();
fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
fillInIntent2 = new Intent();
fillInIntent2.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+
+ if (shortcutInfo1 != null) {
+ activityOptions1.setApplyMultipleTaskFlagForShortcut(true);
+ }
+ if (shortcutInfo2 != null) {
+ activityOptions2.setApplyMultipleTaskFlagForShortcut(true);
+ }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
pendingIntent2 = null;
@@ -754,9 +765,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
Toast.LENGTH_SHORT).show();
}
}
- mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1, options1,
- pendingIntent2, fillInIntent2, shortcutInfo2, options2, splitPosition, splitRatio,
- remoteTransition, instanceId);
+ mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1,
+ activityOptions1.toBundle(), pendingIntent2, fillInIntent2, shortcutInfo2,
+ activityOptions2.toBundle(), splitPosition, splitRatio, remoteTransition,
+ instanceId);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index e52fd00e7df7..dc78c9b139f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -407,7 +407,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
change.getEndAbsBounds().width(), change.getEndAbsBounds().height());
}
// Rotation change of independent non display window container.
- if (change.getParent() == null
+ if (change.getParent() == null && !change.hasFlags(FLAG_IS_DISPLAY)
&& change.getStartRotation() != change.getEndRotation()) {
startRotationAnimation(startTransaction, change, info,
ROTATION_ANIMATION_ROTATE, animations, onAnimFinish);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index a242c72db8b3..c22cc6fbea8f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -186,9 +186,12 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
final RemoteTransition remoteTransition = mRequestedRemotes.get(mergeTarget);
- final IRemoteTransition remote = remoteTransition.getRemoteTransition();
+ if (remoteTransition == null) return;
+
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Merge into remote: %s",
remoteTransition);
+
+ final IRemoteTransition remote = remoteTransition.getRemoteTransition();
if (remote == null) return;
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index 2f7a25ea586d..97a717bd403a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
@@ -107,4 +108,10 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) :
Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
super.focusChanges()
}
+
+ @FlakyTest(bugId = 289943985)
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index ad4d97f6fe40..38e9f390835c 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -61,7 +61,7 @@ android_test {
"libstaticjvmtiagent",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
plugins: ["dagger2-compiler"],
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 7ef82a7ff6e5..ffc664c2e1bc 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -61,18 +61,10 @@ namespace uirenderer {
ADD_FAILURE() << "ClipState not a rect"; \
}
-#define INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, functionCall) \
- TEST(test_case_name, test_name##_##pipeline) { \
- RenderPipelineType oldType = Properties::getRenderPipelineType(); \
- Properties::overrideRenderPipelineType(RenderPipelineType::pipeline); \
- functionCall; \
- Properties::overrideRenderPipelineType(oldType); \
- };
-
-#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, pipeline) \
- INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, \
- TestUtils::runOnRenderThread( \
- test_case_name##_##test_name##_RenderThreadTest::doTheThing))
+#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name) \
+ TEST(test_case_name, test_name) { \
+ TestUtils::runOnRenderThread(test_case_name##_##test_name##_RenderThreadTest::doTheThing); \
+ }
/**
* Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope
@@ -83,21 +75,7 @@ namespace uirenderer {
public: \
static void doTheThing(renderthread::RenderThread& renderThread); \
}; \
- INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \
- /* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \
- /* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */ \
- void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \
- renderthread::RenderThread& renderThread)
-
-/**
- * Like RENDERTHREAD_TEST, but only runs with the Skia RenderPipelineTypes
- */
-#define RENDERTHREAD_SKIA_PIPELINE_TEST(test_case_name, test_name) \
- class test_case_name##_##test_name##_RenderThreadTest { \
- public: \
- static void doTheThing(renderthread::RenderThread& renderThread); \
- }; \
- INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \
+ INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name); \
/* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \
/* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */ \
void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \
diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp
index cc7d34b3994e..89d00d3c0dcb 100644
--- a/libs/hwui/tests/unit/CacheManagerTests.cpp
+++ b/libs/hwui/tests/unit/CacheManagerTests.cpp
@@ -35,7 +35,7 @@ static size_t getCacheUsage(GrDirectContext* grContext) {
}
// TOOD(258700630): fix this test and re-enable
-RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, DISABLED_trimMemory) {
+RENDERTHREAD_TEST(CacheManager, DISABLED_trimMemory) {
int32_t width = DeviceInfo::get()->getWidth();
int32_t height = DeviceInfo::get()->getHeight();
GrDirectContext* grContext = renderThread.getGrContext();
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 1e055c26afc5..073a8357e574 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -355,7 +355,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) {
EXPECT_EQ(3, canvas.getIndex());
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
+RENDERTHREAD_TEST(RenderNodeDrawable, emptyReceiver) {
class ProjectionTestCanvas : public SkCanvas {
public:
ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
@@ -419,7 +419,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
EXPECT_EQ(2, canvas.getDrawCounter());
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) {
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
/* R is backward projected on B and C is a layer.
A
/ \
@@ -1052,7 +1052,7 @@ TEST(RenderNodeDrawable, renderNode) {
}
// Verify that layers are composed with linear filtering.
-RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) {
+RENDERTHREAD_TEST(RenderNodeDrawable, layerComposeQuality) {
static const int CANVAS_WIDTH = 1;
static const int CANVAS_HEIGHT = 1;
static const int LAYER_WIDTH = 1;
@@ -1170,7 +1170,7 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) {
}
// Draw a vector drawable twice but with different bounds and verify correct bounds are used.
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
+RENDERTHREAD_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
static const int CANVAS_WIDTH = 100;
static const int CANVAS_HEIGHT = 200;
class VectorDrawableTestCanvas : public TestCanvasBase {
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 9aa2e1db4461..0f8bd1368f5a 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -370,9 +370,9 @@ TEST(ShaderCacheTest, testCacheValidation) {
}
using namespace android::uirenderer;
-RENDERTHREAD_SKIA_PIPELINE_TEST(ShaderCacheTest, testOnVkFrameFlushed) {
+RENDERTHREAD_TEST(ShaderCacheTest, testOnVkFrameFlushed) {
if (Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan) {
- // RENDERTHREAD_SKIA_PIPELINE_TEST declares both SkiaVK and SkiaGL variants.
+ // RENDERTHREAD_TEST declares both SkiaVK and SkiaGL variants.
GTEST_SKIP() << "This test is only applicable to RenderPipelineType::SkiaVulkan";
}
if (!folderExist(getExternalStorageFolder())) {
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 1a1ce1e9cf66..f6be7b20a9e2 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -136,7 +136,7 @@ public:
}
};
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) {
+RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren) {
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
@@ -195,7 +195,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) {
canvasContext->destroy();
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) {
+RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) {
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 6f180e7498cb..3ded540c3152 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -42,7 +42,7 @@ using namespace android::uirenderer;
using namespace android::uirenderer::renderthread;
using namespace android::uirenderer::skiapipeline;
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) {
+RENDERTHREAD_TEST(SkiaPipeline, renderFrame) {
auto redNode = TestUtils::createSkiaNode(
0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -62,7 +62,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) {
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) {
+RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckOpaque) {
auto halfGreenNode = TestUtils::createSkiaNode(
0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) {
Paint greenPaint;
@@ -89,7 +89,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) {
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
+RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
auto redNode = TestUtils::createSkiaNode(
0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -111,7 +111,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) {
+RENDERTHREAD_TEST(SkiaPipeline, renderLayer) {
auto redNode = TestUtils::createSkiaNode(
0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -154,7 +154,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) {
blueNode->setLayerSurface(sk_sp<SkSurface>());
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) {
+RENDERTHREAD_TEST(SkiaPipeline, renderOverdraw) {
ScopedProperty<bool> prop(Properties::debugOverdraw, true);
auto whiteNode = TestUtils::createSkiaNode(
@@ -227,7 +227,7 @@ public:
};
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) {
+RENDERTHREAD_TEST(SkiaPipeline, deferRenderNodeScene) {
class DeferTestCanvas : public SkCanvas {
public:
DeferTestCanvas() : SkCanvas(800, 600) {}
@@ -297,7 +297,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) {
EXPECT_EQ(4, surface->canvas()->mDrawCounter);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) {
+RENDERTHREAD_TEST(SkiaPipeline, clipped) {
static const int CANVAS_WIDTH = 200;
static const int CANVAS_HEIGHT = 200;
class ClippedTestCanvas : public SkCanvas {
@@ -330,7 +330,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) {
}
// Test renderFrame with a dirty clip and a pre-transform matrix.
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped_rotated) {
+RENDERTHREAD_TEST(SkiaPipeline, clipped_rotated) {
static const int CANVAS_WIDTH = 200;
static const int CANVAS_HEIGHT = 100;
static const SkMatrix rotateMatrix = SkMatrix::MakeAll(0, -1, CANVAS_HEIGHT, 1, 0, 0, 0, 0, 1);
@@ -366,7 +366,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped_rotated) {
EXPECT_EQ(1, surface->canvas()->mDrawCounter);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) {
+RENDERTHREAD_TEST(SkiaPipeline, clip_replace) {
static const int CANVAS_WIDTH = 50;
static const int CANVAS_HEIGHT = 50;
class ClipReplaceTestCanvas : public SkCanvas {
@@ -396,7 +396,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) {
EXPECT_EQ(1, surface->canvas()->mDrawCounter);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) {
+RENDERTHREAD_TEST(SkiaPipeline, context_lost) {
test::TestContext context;
auto surface = context.surface();
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
@@ -410,7 +410,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) {
EXPECT_TRUE(pipeline->isSurfaceReady());
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) {
+RENDERTHREAD_TEST(SkiaPipeline, pictureCallback) {
// create a pipeline and add a picture callback
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
int callbackCount = 0;
diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp
index 10c874ec3f12..76cbc8abc808 100644
--- a/libs/hwui/tests/unit/main.cpp
+++ b/libs/hwui/tests/unit/main.cpp
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
+#include <getopt.h>
+#include <signal.h>
#include "Properties.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
#include "hwui/Typeface.h"
#include "tests/common/LeakChecker.h"
-#include <signal.h>
-
using namespace std;
using namespace android;
using namespace android::uirenderer;
@@ -45,6 +45,57 @@ static void gtestSigHandler(int sig, siginfo_t* siginfo, void* context) {
raise(sig);
}
+// For options that only exist in long-form. Anything in the
+// 0-255 range is reserved for short options (which just use their ASCII value)
+namespace LongOpts {
+enum {
+ Reserved = 255,
+ Renderer,
+};
+}
+
+static const struct option LONG_OPTIONS[] = {
+ {"renderer", required_argument, nullptr, LongOpts::Renderer}, {0, 0, 0, 0}};
+
+static RenderPipelineType parseRenderer(const char* renderer) {
+ // Anything that's not skiavk is skiagl
+ if (!strcmp(renderer, "skiavk")) {
+ return RenderPipelineType::SkiaVulkan;
+ }
+ return RenderPipelineType::SkiaGL;
+}
+
+struct Options {
+ RenderPipelineType renderer = RenderPipelineType::SkiaGL;
+};
+
+Options parseOptions(int argc, char* argv[]) {
+ int c;
+ opterr = 0;
+ Options opts;
+
+ while (true) {
+ /* getopt_long stores the option index here. */
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "", LONG_OPTIONS, &option_index);
+
+ if (c == -1) break;
+
+ switch (c) {
+ case 0:
+ // Option set a flag, don't need to do anything
+ // (although none of the current LONG_OPTIONS do this...)
+ break;
+
+ case LongOpts::Renderer:
+ opts.renderer = parseRenderer(optarg);
+ break;
+ }
+ }
+ return opts;
+}
+
class TypefaceEnvironment : public testing::Environment {
public:
virtual void SetUp() { Typeface::setRobotoTypefaceForTest(); }
@@ -64,8 +115,9 @@ int main(int argc, char* argv[]) {
// Avoid talking to SF
Properties::isolatedProcess = true;
- // Default to GLES (Vulkan-aware tests will override this)
- Properties::overrideRenderPipelineType(RenderPipelineType::SkiaGL);
+
+ auto opts = parseOptions(argc, argv);
+ Properties::overrideRenderPipelineType(opts.renderer);
// Run the tests
testing::InitGoogleTest(&argc, argv);
diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS
index 6d6a9f8eb98c..bbe5e06bb282 100644
--- a/media/java/android/media/OWNERS
+++ b/media/java/android/media/OWNERS
@@ -10,5 +10,8 @@ include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
per-file *Image* = file:/graphics/java/android/graphics/OWNERS
+per-file ExifInterface.java,ExifInterfaceUtils.java,IMediaHTTPConnection.aidl,IMediaHTTPService.aidl,JetPlayer.java,MediaDataSource.java,MediaExtractor.java,MediaHTTPConnection.java,MediaHTTPService.java,MediaPlayer.java=set noparent
+per-file ExifInterface.java,ExifInterfaceUtils.java,IMediaHTTPConnection.aidl,IMediaHTTPService.aidl,JetPlayer.java,MediaDataSource.java,MediaExtractor.java,MediaHTTPConnection.java,MediaHTTPService.java,MediaPlayer.java=file:platform/frameworks/av:/media/janitors/media_solutions_OWNERS
+
# Haptics team also works on Ringtone
per-file *Ringtone* = file:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/media/jni/OWNERS b/media/jni/OWNERS
index 96894d15d8b1..e12d828733fa 100644
--- a/media/jni/OWNERS
+++ b/media/jni/OWNERS
@@ -3,3 +3,6 @@ per-file android_mtp_*.cpp=aprasath@google.com,anothermark@google.com,kumarashis
# extra for TV related files
per-file android_media_tv_*=hgchen@google.com,quxiangfang@google.com
+
+per-file android_media_JetPlayer.cpp,android_media_MediaDataSource.cpp,android_media_MediaDataSource.h,android_media_MediaPlayer.java=set noparent
+per-file android_media_JetPlayer.cpp,android_media_MediaDataSource.cpp,android_media_MediaDataSource.h,android_media_MediaPlayer.java=file:platform/frameworks/av:/media/janitors/media_solutions_OWNERS
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index dea7f03d369a..5ea98c0c6700 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -2615,7 +2615,7 @@ static void android_media_MediaCodec_native_queueLinearBlock(
return;
}
auto cryptoInfo =
- cryptoInfoObj ? NativeCryptoInfo{size} : NativeCryptoInfo{env, cryptoInfoObj};
+ cryptoInfoObj ? NativeCryptoInfo{env, cryptoInfoObj} : NativeCryptoInfo{size};
if (env->ExceptionCheck()) {
// Creation of cryptoInfo failed. Let the exception bubble up.
return;
diff --git a/packages/CredentialManager/Android.bp b/packages/CredentialManager/Android.bp
index 28b9bc04a249..fe26dc3d7feb 100644
--- a/packages/CredentialManager/Android.bp
+++ b/packages/CredentialManager/Android.bp
@@ -44,7 +44,7 @@ android_app {
platform_apis: true,
privileged: true,
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
optimize: {
proguard_compatibility: false,
diff --git a/packages/EasterEgg/Android.bp b/packages/EasterEgg/Android.bp
index 8699f59e4fba..0caf505c2177 100644
--- a/packages/EasterEgg/Android.bp
+++ b/packages/EasterEgg/Android.bp
@@ -70,5 +70,5 @@ android_app {
manifest: "AndroidManifest.xml",
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
index 1d433e767e5b..5bc271954b25 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
@@ -59,7 +59,7 @@ public abstract class AbstractWifiMacAddressPreferenceController
@Override
public boolean isAvailable() {
- return true;
+ return mWifiManager != null;
}
@Override
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 7be60431b91b..0f16d930dcf7 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -203,7 +203,7 @@ android_library {
manifest: "AndroidManifest.xml",
javacflags: ["-Adagger.fastInit=enabled"],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
plugins: ["dagger2-compiler"],
@@ -394,7 +394,7 @@ android_library {
"android.test.base",
"android.test.mock",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
aaptflags: [
"--extra-packages",
"com.android.systemui",
@@ -516,7 +516,7 @@ android_app {
certificate: "platform",
privileged: true,
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
dxflags: ["--multi-dex"],
optimize: {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 764a8556a54d..8306620b3de6 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -302,10 +302,9 @@ class ActivityLaunchAnimator(
interface Callback {
/** Whether we are currently on the keyguard or not. */
- @JvmDefault fun isOnKeyguard(): Boolean = false
+ fun isOnKeyguard(): Boolean = false
/** Hide the keyguard and animate using [runner]. */
- @JvmDefault
fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner) {
throw UnsupportedOperationException()
}
@@ -316,16 +315,16 @@ class ActivityLaunchAnimator(
interface Listener {
/** Called when an activity launch animation started. */
- @JvmDefault fun onLaunchAnimationStart() {}
+ fun onLaunchAnimationStart() {}
/**
* Called when an activity launch animation is finished. This will be called if and only if
* [onLaunchAnimationStart] was called earlier.
*/
- @JvmDefault fun onLaunchAnimationEnd() {}
+ fun onLaunchAnimationEnd() {}
/** Called when an activity launch animation made progress. */
- @JvmDefault fun onLaunchAnimationProgress(linearProgress: Float) {}
+ fun onLaunchAnimationProgress(linearProgress: Float) {}
}
/**
diff --git a/packages/SystemUI/compose/core/tests/Android.bp b/packages/SystemUI/compose/core/tests/Android.bp
index 6119e96e5bac..06d94ac5400e 100644
--- a/packages/SystemUI/compose/core/tests/Android.bp
+++ b/packages/SystemUI/compose/core/tests/Android.bp
@@ -44,5 +44,5 @@ android_test {
"androidx.compose.ui_ui-test-manifest",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/packages/SystemUI/compose/features/tests/Android.bp b/packages/SystemUI/compose/features/tests/Android.bp
index ff534bd01fd3..c7c9140b53ed 100644
--- a/packages/SystemUI/compose/features/tests/Android.bp
+++ b/packages/SystemUI/compose/features/tests/Android.bp
@@ -44,5 +44,5 @@ android_test {
"androidx.compose.ui_ui-test-manifest",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/packages/SystemUI/customization/Android.bp b/packages/SystemUI/customization/Android.bp
index dc450bb71dfe..fc37b355494f 100644
--- a/packages/SystemUI/customization/Android.bp
+++ b/packages/SystemUI/customization/Android.bp
@@ -48,5 +48,5 @@ android_library {
],
min_sdk_version: "current",
plugins: ["dagger2-compiler"],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 9c864abca9dd..a38c629b7741 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -366,10 +366,16 @@
<!-- Whether or not notifications that can be expanded will always be in their expanded state.
This value only affects notifications that are not a group of notifications from the same
- applications. If this value is false, then only the first notification will be expanded;
- the other notifications need to be manually expanded by the user. -->
+ applications. If this value is false, then only the first notification will be expanded
+ when config_autoExpandFirstNotification is true; the other notifications need to be
+ manually expanded by the user. -->
<bool name="config_alwaysExpandNonGroupedNotifications">false</bool>
+ <!-- Whether or not the first expandable notification will be expanded automatically by the
+ system. This value only affects notifications that are not a group of notifications from
+ the same applications and when config_alwaysExpandNonGroupedNotifications is false. -->
+ <bool name="config_autoExpandFirstNotification">true</bool>
+
<!-- Whether or not an expandable notification can be manually expanded or collapsed by the
user. Grouped notifications are still expandable even if this value is false. -->
<bool name="config_enableNonGroupedNotificationExpand">true</bool>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 28e786b71874..ca30e159a0ff 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -65,7 +65,7 @@ android_library {
],
min_sdk_version: "current",
plugins: ["dagger2-compiler"],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
}
java_library {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 35cf4a1ecf0a..35624770b712 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -43,8 +43,6 @@ import com.android.systemui.rotationlock.RotationLockModule;
import com.android.systemui.screenshot.ReferenceScreenshotModule;
import com.android.systemui.settings.dagger.MultiUserUtilsModule;
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shade.ShadeControllerImpl;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyboardShortcutsModule;
@@ -150,9 +148,6 @@ public abstract class ReferenceSystemUIModule {
@Binds
abstract DockManager bindDockManager(DockManagerImpl dockManager);
- @Binds
- abstract ShadeController provideShadeController(ShadeControllerImpl shadeController);
-
@SysUISingleton
@Provides
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 0670ec380861..79a1728470dc 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -254,7 +254,7 @@ object Flags {
/** Migrate the indication area to the new keyguard root view. */
// TODO(b/280067944): Tracking bug.
@JvmField
- val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area", teamfood = true)
+ val MIGRATE_INDICATION_AREA = releasedFlag(236, "migrate_indication_area")
/**
* Migrate the bottom area to the new keyguard root view.
@@ -294,6 +294,11 @@ object Flags {
@JvmField
val MIGRATE_NSSL = unreleasedFlag(242, "migrate_nssl")
+ /** Migrate the status view from the notification panel to keyguard root view. */
+ // TODO(b/291767565): Tracking bug.
+ @JvmField
+ val MIGRATE_KEYGUARD_STATUS_VIEW = unreleasedFlag(243, "migrate_keyguard_status_view")
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
diff --git a/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt b/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt
new file mode 100644
index 000000000000..eaecda52a5a2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.flags
+
+import android.util.Log
+import com.android.systemui.Dependency
+
+/**
+ * This class promotes best practices for flag guarding System UI view refactors.
+ * * [isEnabled] allows changing an implementation.
+ * * [assertDisabled] allows authors to flag code as being "dead" when the flag gets enabled and
+ * ensure that it is not being invoked accidentally in the post-flag refactor.
+ * * [expectEnabled] allows authors to guard new code with a "safe" alternative when invoked on
+ * flag-disabled builds, but with a check that should crash eng builds or tests when the
+ * expectation is violated.
+ *
+ * The constructors prefer that you provide a [FeatureFlags] instance, but does not require it,
+ * falling back to [Dependency.get]. This fallback should ONLY be used to flag-guard code changes
+ * inside views where injecting flag values after initialization can be error-prone.
+ */
+class ViewRefactorFlag
+private constructor(
+ private val injectedFlags: FeatureFlags?,
+ private val flag: BooleanFlag,
+ private val readFlagValue: (FeatureFlags) -> Boolean
+) {
+ @JvmOverloads
+ constructor(
+ flags: FeatureFlags? = null,
+ flag: UnreleasedFlag
+ ) : this(flags, flag, { it.isEnabled(flag) })
+
+ @JvmOverloads
+ constructor(
+ flags: FeatureFlags? = null,
+ flag: ReleasedFlag
+ ) : this(flags, flag, { it.isEnabled(flag) })
+
+ /** Whether the flag is enabled. Called to switch between an old behavior and a new behavior. */
+ val isEnabled by lazy {
+ @Suppress("DEPRECATION")
+ val featureFlags = injectedFlags ?: Dependency.get(FeatureFlags::class.java)
+ readFlagValue(featureFlags)
+ }
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ *
+ * Example usage:
+ * ```
+ * public void setController(NotificationShelfController notificationShelfController) {
+ * mShelfRefactor.assertDisabled();
+ * mController = notificationShelfController;
+ * }
+ * ````
+ */
+ fun assertDisabled() = check(!isEnabled) { "Code path not supported when $flag is enabled." }
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ *
+ * Example usage:
+ * ```
+ * public void setShelfIcons(NotificationIconContainer icons) {
+ * if (mShelfRefactor.expectEnabled()) {
+ * mShelfIcons = icons;
+ * }
+ * }
+ * ```
+ */
+ fun expectEnabled(): Boolean {
+ if (!isEnabled) {
+ val message = "Code path not supported when $flag is disabled."
+ Log.wtf(TAG, message, Exception(message))
+ }
+ return isEnabled
+ }
+
+ private companion object {
+ private const val TAG = "ViewRefactorFlag"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 364614421567..489d2ab4d342 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -171,7 +171,6 @@ class KeyguardUnlockAnimationController @Inject constructor(
* for the canned animation (if applicable) so interested parties can sync with it. If no
* canned animation is playing, these are both 0.
*/
- @JvmDefault
fun onUnlockAnimationStarted(
playingCannedAnimation: Boolean,
isWakeAndUnlockNotFromDream: Boolean,
@@ -184,7 +183,6 @@ class KeyguardUnlockAnimationController @Inject constructor(
* The keyguard is no longer visible in this state and the app/launcher behind the keyguard
* is now completely visible.
*/
- @JvmDefault
fun onUnlockAnimationFinished() {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index ddd4a2bf4453..e0834bb894b5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2982,6 +2982,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
}
private void onKeyguardExitFinished() {
+ if (DEBUG) Log.d(TAG, "onKeyguardExitFinished()");
// only play "unlock" noises if not on a call (since the incall UI
// disables the keyguard)
if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
@@ -3203,13 +3204,14 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
flags |= StatusBarManager.DISABLE_RECENT;
}
- if (mPowerGestureIntercepted) {
+ if (mPowerGestureIntercepted && mOccluded && isSecure()) {
flags |= StatusBarManager.DISABLE_RECENT;
}
if (DEBUG) {
Log.d(TAG, "adjustStatusBarLocked: mShowing=" + mShowing + " mOccluded=" + mOccluded
+ " isSecure=" + isSecure() + " force=" + forceHideHomeRecentsButtons
+ + " mPowerGestureIntercepted=" + mPowerGestureIntercepted
+ " --> flags=0x" + Integer.toHexString(flags));
}
@@ -3437,6 +3439,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
pw.print(" mPendingLock: "); pw.println(mPendingLock);
pw.print(" wakeAndUnlocking: "); pw.println(mWakeAndUnlocking);
pw.print(" mPendingPinLock: "); pw.println(mPendingPinLock);
+ pw.print(" mPowerGestureIntercepted: "); pw.println(mPowerGestureIntercepted);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
index d652889f0082..d949a2a0afe5 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
@@ -145,13 +145,10 @@ class PrivacyConfig @Inject constructor(
}
interface Callback {
- @JvmDefault
fun onFlagMicCameraChanged(flag: Boolean) {}
- @JvmDefault
fun onFlagLocationChanged(flag: Boolean) {}
- @JvmDefault
fun onFlagMediaProjectionChanged(flag: Boolean) {}
}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index a676150f44a2..eb8ef9bf3e50 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -216,7 +216,6 @@ class PrivacyItemController @Inject constructor(
interface Callback : PrivacyConfig.Callback {
fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>)
- @JvmDefault
fun onFlagAllChanged(flag: Boolean) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
index 995c6a476f0d..33c47cc082e1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
@@ -26,7 +26,7 @@ import java.util.concurrent.Executor
import javax.inject.Inject
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.shade.ShadeModule.Companion.SHADE_HEADER
+import com.android.systemui.shade.ShadeViewProviderModule.Companion.SHADE_HEADER
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import javax.inject.Named
diff --git a/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
index bb7f721ad61f..468a75d8276e 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
@@ -52,12 +52,12 @@ interface DisplayTracker {
interface Callback {
/** Notifies that a display has been added. */
- @JvmDefault fun onDisplayAdded(displayId: Int) {}
+ fun onDisplayAdded(displayId: Int) {}
/** Notifies that a display has been removed. */
- @JvmDefault fun onDisplayRemoved(displayId: Int) {}
+ fun onDisplayRemoved(displayId: Int) {}
/** Notifies a display has been changed */
- @JvmDefault fun onDisplayChanged(displayId: Int) {}
+ fun onDisplayChanged(displayId: Int) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index 33a3125d1c68..93a3e905f0e0 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -71,7 +71,6 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider {
* Same as {@link onUserChanging(Int, Context, CountDownLatch)} but the latch will be
* auto-decremented after the completion of this method.
*/
- @JvmDefault
fun onUserChanging(newUser: Int, userContext: Context) {}
/**
@@ -82,7 +81,6 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider {
* user switch duration. When overriding this method, countDown() MUST be called on the
* latch once execution is complete.
*/
- @JvmDefault
fun onUserChanging(newUser: Int, userContext: Context, latch: CountDownLatch) {
onUserChanging(newUser, userContext)
latch.countDown()
@@ -93,13 +91,11 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider {
* Override this method to run things after the screen is unfrozen for the user switch.
* Please see {@link #onUserChanging} if you need to hide jank.
*/
- @JvmDefault
fun onUserChanged(newUser: Int, userContext: Context) {}
/**
* Notifies that the current user's profiles have changed.
*/
- @JvmDefault
fun onProfilesChanged(profiles: List<@JvmSuppressWildcards UserInfo>) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
new file mode 100644
index 000000000000..7a803867d4a4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+
+/** Fulfills dependencies on the shade with empty implementations for variants with no shade. */
+@Module
+abstract class ShadeEmptyImplModule {
+ @Binds
+ @SysUISingleton
+ abstract fun bindsShadeViewController(svc: ShadeViewControllerEmptyImpl): ShadeViewController
+
+ @Binds
+ @SysUISingleton
+ abstract fun bindsShadeController(sc: ShadeControllerEmptyImpl): ShadeController
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index 8b89ff49f418..529f12e0658e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -54,7 +54,7 @@ import com.android.systemui.shade.ShadeHeaderController.Companion.LARGE_SCREEN_H
import com.android.systemui.shade.ShadeHeaderController.Companion.LARGE_SCREEN_HEADER_TRANSITION_ID
import com.android.systemui.shade.ShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT
import com.android.systemui.shade.ShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
-import com.android.systemui.shade.ShadeModule.Companion.SHADE_HEADER
+import com.android.systemui.shade.ShadeViewProviderModule.Companion.SHADE_HEADER
import com.android.systemui.shade.carrier.ShadeCarrierGroup
import com.android.systemui.shade.carrier.ShadeCarrierGroupController
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index be21df1139ad..89aaaafbcdf3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -16,293 +16,21 @@
package com.android.systemui.shade
-import android.annotation.SuppressLint
-import android.content.ContentResolver
-import android.os.Handler
-import android.view.LayoutInflater
-import android.view.ViewStub
-import androidx.constraintlayout.motion.widget.MotionLayout
-import com.android.keyguard.LockIconView
-import com.android.systemui.CoreStartable
-import com.android.systemui.R
-import com.android.systemui.battery.BatteryMeterView
-import com.android.systemui.battery.BatteryMeterViewController
-import com.android.systemui.biometrics.AuthRippleController
-import com.android.systemui.biometrics.AuthRippleView
-import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.android.systemui.privacy.OngoingPrivacyChip
-import com.android.systemui.scene.ui.view.WindowRootView
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.LightRevealScrim
-import com.android.systemui.statusbar.NotificationShelf
-import com.android.systemui.statusbar.NotificationShelfController
-import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent
-import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
-import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
-import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
-import com.android.systemui.statusbar.phone.StatusBarLocation
-import com.android.systemui.statusbar.phone.StatusIconContainer
-import com.android.systemui.statusbar.phone.TapAgainView
-import com.android.systemui.statusbar.policy.BatteryController
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.tuner.TunerService
+
import dagger.Binds
import dagger.Module
-import dagger.Provides
-import dagger.multibindings.ClassKey
-import dagger.multibindings.IntoMap
-import javax.inject.Named
-import javax.inject.Provider
/** Module for classes related to the notification shade. */
-@Module(includes = [StartShadeModule::class])
+@Module(includes = [StartShadeModule::class, ShadeViewProviderModule::class])
abstract class ShadeModule {
-
- @Binds
- @IntoMap
- @ClassKey(AuthRippleController::class)
- abstract fun bindAuthRippleController(controller: AuthRippleController): CoreStartable
-
@Binds
@SysUISingleton
abstract fun bindsShadeViewController(
notificationPanelViewController: NotificationPanelViewController
): ShadeViewController
- companion object {
- const val SHADE_HEADER = "large_screen_shade_header"
-
- @SuppressLint("InflateParams") // Root views don't have parents.
- @Provides
- @SysUISingleton
- fun providesWindowRootView(
- layoutInflater: LayoutInflater,
- featureFlags: FeatureFlags,
- ): WindowRootView {
- return if (
- featureFlags.isEnabled(Flags.SCENE_CONTAINER) && ComposeFacade.isComposeAvailable()
- ) {
- layoutInflater.inflate(R.layout.scene_window_root, null)
- } else {
- layoutInflater.inflate(R.layout.super_notification_shade, null)
- }
- as WindowRootView?
- ?: throw IllegalStateException("Window root view could not be properly inflated")
- }
-
- @Provides
- @SysUISingleton
- // TODO(b/277762009): Do something similar to
- // {@link StatusBarWindowModule.InternalWindowView} so that only
- // {@link NotificationShadeWindowViewController} can inject this view.
- fun providesNotificationShadeWindowView(
- root: WindowRootView,
- featureFlags: FeatureFlags,
- ): NotificationShadeWindowView {
- if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) {
- return root.findViewById(R.id.legacy_window_root)
- }
- return root as NotificationShadeWindowView?
- ?: throw IllegalStateException("root view not a NotificationShadeWindowView")
- }
-
- // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
- @Provides
- @SysUISingleton
- fun providesNotificationStackScrollLayout(
- notificationShadeWindowView: NotificationShadeWindowView,
- ): NotificationStackScrollLayout {
- return notificationShadeWindowView.findViewById(R.id.notification_stack_scroller)
- }
-
- @Provides
- @SysUISingleton
- fun providesNotificationShelfController(
- featureFlags: FeatureFlags,
- newImpl: Provider<NotificationShelfViewBinderWrapperControllerImpl>,
- notificationShelfComponentBuilder: NotificationShelfComponent.Builder,
- layoutInflater: LayoutInflater,
- notificationStackScrollLayout: NotificationStackScrollLayout,
- ): NotificationShelfController {
- return if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
- newImpl.get()
- } else {
- val shelfView =
- layoutInflater.inflate(
- R.layout.status_bar_notification_shelf,
- notificationStackScrollLayout,
- false
- ) as NotificationShelf
- val component =
- notificationShelfComponentBuilder.notificationShelf(shelfView).build()
- val notificationShelfController = component.notificationShelfController
- notificationShelfController.init()
- notificationShelfController
- }
- }
-
- // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
- @Provides
- @SysUISingleton
- fun providesNotificationPanelView(
- notificationShadeWindowView: NotificationShadeWindowView,
- ): NotificationPanelView {
- return notificationShadeWindowView.findViewById(R.id.notification_panel)
- }
-
- /**
- * Constructs a new, unattached [KeyguardBottomAreaView].
- *
- * Note that this is explicitly _not_ a singleton, as we want to be able to reinflate it
- */
- @Provides
- fun providesKeyguardBottomAreaView(
- npv: NotificationPanelView,
- layoutInflater: LayoutInflater,
- ): KeyguardBottomAreaView {
- return layoutInflater.inflate(R.layout.keyguard_bottom_area, npv, false)
- as KeyguardBottomAreaView
- }
-
- @Provides
- @SysUISingleton
- fun providesLightRevealScrim(
- notificationShadeWindowView: NotificationShadeWindowView,
- ): LightRevealScrim {
- return notificationShadeWindowView.findViewById(R.id.light_reveal_scrim)
- }
-
- @Provides
- @SysUISingleton
- fun providesKeyguardRootView(
- notificationShadeWindowView: NotificationShadeWindowView,
- ): KeyguardRootView {
- return notificationShadeWindowView.findViewById(R.id.keyguard_root_view)
- }
-
- @Provides
- @SysUISingleton
- fun providesSharedNotificationContainer(
- notificationShadeWindowView: NotificationShadeWindowView,
- ): SharedNotificationContainer {
- return notificationShadeWindowView.findViewById(R.id.shared_notification_container)
- }
-
- // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
- @Provides
- @SysUISingleton
- fun providesAuthRippleView(
- notificationShadeWindowView: NotificationShadeWindowView,
- ): AuthRippleView? {
- return notificationShadeWindowView.findViewById(R.id.auth_ripple)
- }
-
- // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
- @Provides
- @SysUISingleton
- fun providesLockIconView(
- keyguardRootView: KeyguardRootView,
- notificationPanelView: NotificationPanelView,
- featureFlags: FeatureFlags
- ): LockIconView {
- if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
- return keyguardRootView.findViewById(R.id.lock_icon_view)
- } else {
- return notificationPanelView.findViewById(R.id.lock_icon_view)
- }
- }
-
- // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
- @Provides
- @SysUISingleton
- fun providesTapAgainView(
- notificationPanelView: NotificationPanelView,
- ): TapAgainView {
- return notificationPanelView.findViewById(R.id.shade_falsing_tap_again)
- }
-
- // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
- @Provides
- @SysUISingleton
- fun providesNotificationsQuickSettingsContainer(
- notificationShadeWindowView: NotificationShadeWindowView,
- ): NotificationsQuickSettingsContainer {
- return notificationShadeWindowView.findViewById(R.id.notification_container_parent)
- }
-
- // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
- @Provides
- @SysUISingleton
- @Named(SHADE_HEADER)
- fun providesShadeHeaderView(
- notificationShadeWindowView: NotificationShadeWindowView,
- ): MotionLayout {
- val stub = notificationShadeWindowView.findViewById<ViewStub>(R.id.qs_header_stub)
- val layoutId = R.layout.combined_qs_header
- stub.layoutResource = layoutId
- return stub.inflate() as MotionLayout
- }
-
- @Provides
- @SysUISingleton
- fun providesCombinedShadeHeadersConstraintManager(): CombinedShadeHeadersConstraintManager {
- return CombinedShadeHeadersConstraintManagerImpl
- }
-
- // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
- @Provides
- @SysUISingleton
- @Named(SHADE_HEADER)
- fun providesBatteryMeterView(@Named(SHADE_HEADER) view: MotionLayout): BatteryMeterView {
- return view.findViewById(R.id.batteryRemainingIcon)
- }
-
- @Provides
- @SysUISingleton
- @Named(SHADE_HEADER)
- fun providesBatteryMeterViewController(
- @Named(SHADE_HEADER) batteryMeterView: BatteryMeterView,
- userTracker: UserTracker,
- configurationController: ConfigurationController,
- tunerService: TunerService,
- @Main mainHandler: Handler,
- contentResolver: ContentResolver,
- batteryController: BatteryController,
- ): BatteryMeterViewController {
- return BatteryMeterViewController(
- batteryMeterView,
- StatusBarLocation.QS,
- userTracker,
- configurationController,
- tunerService,
- mainHandler,
- contentResolver,
- batteryController,
- )
- }
-
- @Provides
- @SysUISingleton
- @Named(SHADE_HEADER)
- fun providesOngoingPrivacyChip(
- @Named(SHADE_HEADER) header: MotionLayout,
- ): OngoingPrivacyChip {
- return header.findViewById(R.id.privacy_chip)
- }
-
- @Provides
- @SysUISingleton
- @Named(SHADE_HEADER)
- fun providesStatusIconContainer(
- @Named(SHADE_HEADER) header: MotionLayout,
- ): StatusIconContainer {
- return header.findViewById(R.id.statusIcons)
- }
- }
+ @Binds
+ @SysUISingleton
+ abstract fun bindsShadeController(shadeControllerImpl: ShadeControllerImpl): ShadeController
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
index 56bb1a6020cf..5804040a8676 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
@@ -29,12 +29,12 @@ interface ShadeStateEvents {
interface ShadeStateEventsListener {
/** Invoked when the notification panel starts or stops collapsing. */
- @JvmDefault fun onPanelCollapsingChanged(isCollapsing: Boolean) {}
+ fun onPanelCollapsingChanged(isCollapsing: Boolean) {}
/**
* Invoked when the notification panel starts or stops launching an [android.app.Activity].
*/
- @JvmDefault fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) {}
+ fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) {}
/**
* Invoked when the "expand immediate" attribute changes.
@@ -45,6 +45,6 @@ interface ShadeStateEvents {
* Another example is when full QS is showing, and we swipe up from the bottom. Instead of
* going to QQS, the panel fully collapses.
*/
- @JvmDefault fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) {}
+ fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
new file mode 100644
index 000000000000..fc6479eb62a4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade
+
+import android.annotation.SuppressLint
+import android.content.ContentResolver
+import android.os.Handler
+import android.view.LayoutInflater
+import android.view.ViewStub
+import androidx.constraintlayout.motion.widget.MotionLayout
+import com.android.keyguard.LockIconView
+import com.android.systemui.R
+import com.android.systemui.battery.BatteryMeterView
+import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.biometrics.AuthRippleView
+import com.android.systemui.compose.ComposeFacade
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.shared.model.SceneContainerConfig
+import com.android.systemui.scene.shared.model.SceneContainerNames
+import com.android.systemui.scene.ui.view.SceneWindowRootView
+import com.android.systemui.scene.ui.view.WindowRootView
+import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.statusbar.NotificationShelf
+import com.android.systemui.statusbar.NotificationShelfController
+import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent
+import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
+import com.android.systemui.statusbar.phone.StatusBarLocation
+import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.statusbar.phone.TapAgainView
+import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.tuner.TunerService
+import dagger.Module
+import dagger.Provides
+import javax.inject.Named
+import javax.inject.Provider
+
+/** Module for providing views related to the shade. */
+@Module
+abstract class ShadeViewProviderModule {
+ companion object {
+ const val SHADE_HEADER = "large_screen_shade_header"
+
+ @SuppressLint("InflateParams") // Root views don't have parents.
+ @Provides
+ @SysUISingleton
+ fun providesWindowRootView(
+ layoutInflater: LayoutInflater,
+ featureFlags: FeatureFlags,
+ @Named(SceneContainerNames.SYSTEM_UI_DEFAULT)
+ viewModelProvider: Provider<SceneContainerViewModel>,
+ @Named(SceneContainerNames.SYSTEM_UI_DEFAULT)
+ containerConfigProvider: Provider<SceneContainerConfig>,
+ @Named(SceneContainerNames.SYSTEM_UI_DEFAULT)
+ scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>,
+ ): WindowRootView {
+ return if (
+ featureFlags.isEnabled(Flags.SCENE_CONTAINER) && ComposeFacade.isComposeAvailable()
+ ) {
+ val sceneWindowRootView =
+ layoutInflater.inflate(R.layout.scene_window_root, null) as SceneWindowRootView
+ sceneWindowRootView.init(
+ viewModel = viewModelProvider.get(),
+ containerConfig = containerConfigProvider.get(),
+ scenes = scenesProvider.get(),
+ )
+ sceneWindowRootView
+ } else {
+ layoutInflater.inflate(R.layout.super_notification_shade, null)
+ }
+ as WindowRootView?
+ ?: throw IllegalStateException("Window root view could not be properly inflated")
+ }
+
+ @Provides
+ @SysUISingleton
+ // TODO(b/277762009): Do something similar to
+ // {@link StatusBarWindowModule.InternalWindowView} so that only
+ // {@link NotificationShadeWindowViewController} can inject this view.
+ fun providesNotificationShadeWindowView(
+ root: WindowRootView,
+ featureFlags: FeatureFlags,
+ ): NotificationShadeWindowView {
+ if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) {
+ return root.findViewById(R.id.legacy_window_root)
+ }
+ return root as NotificationShadeWindowView?
+ ?: throw IllegalStateException("root view not a NotificationShadeWindowView")
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ fun providesNotificationStackScrollLayout(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): NotificationStackScrollLayout {
+ return notificationShadeWindowView.findViewById(R.id.notification_stack_scroller)
+ }
+
+ @Provides
+ @SysUISingleton
+ fun providesNotificationShelfController(
+ featureFlags: FeatureFlags,
+ newImpl: Provider<NotificationShelfViewBinderWrapperControllerImpl>,
+ notificationShelfComponentBuilder: NotificationShelfComponent.Builder,
+ layoutInflater: LayoutInflater,
+ notificationStackScrollLayout: NotificationStackScrollLayout,
+ ): NotificationShelfController {
+ return if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ newImpl.get()
+ } else {
+ val shelfView =
+ layoutInflater.inflate(
+ R.layout.status_bar_notification_shelf,
+ notificationStackScrollLayout,
+ false
+ ) as NotificationShelf
+ val component =
+ notificationShelfComponentBuilder.notificationShelf(shelfView).build()
+ val notificationShelfController = component.notificationShelfController
+ notificationShelfController.init()
+ notificationShelfController
+ }
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ fun providesNotificationPanelView(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): NotificationPanelView {
+ return notificationShadeWindowView.findViewById(R.id.notification_panel)
+ }
+
+ /**
+ * Constructs a new, unattached [KeyguardBottomAreaView].
+ *
+ * Note that this is explicitly _not_ a singleton, as we want to be able to reinflate it
+ */
+ @Provides
+ fun providesKeyguardBottomAreaView(
+ npv: NotificationPanelView,
+ layoutInflater: LayoutInflater,
+ ): KeyguardBottomAreaView {
+ return layoutInflater.inflate(R.layout.keyguard_bottom_area, npv, false)
+ as KeyguardBottomAreaView
+ }
+
+ @Provides
+ @SysUISingleton
+ fun providesLightRevealScrim(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): LightRevealScrim {
+ return notificationShadeWindowView.findViewById(R.id.light_reveal_scrim)
+ }
+
+ @Provides
+ @SysUISingleton
+ fun providesKeyguardRootView(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): KeyguardRootView {
+ return notificationShadeWindowView.findViewById(R.id.keyguard_root_view)
+ }
+
+ @Provides
+ @SysUISingleton
+ fun providesSharedNotificationContainer(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): SharedNotificationContainer {
+ return notificationShadeWindowView.findViewById(R.id.shared_notification_container)
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ fun providesAuthRippleView(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): AuthRippleView? {
+ return notificationShadeWindowView.findViewById(R.id.auth_ripple)
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ fun providesLockIconView(
+ keyguardRootView: KeyguardRootView,
+ notificationPanelView: NotificationPanelView,
+ featureFlags: FeatureFlags
+ ): LockIconView {
+ if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
+ return keyguardRootView.findViewById(R.id.lock_icon_view)
+ } else {
+ return notificationPanelView.findViewById(R.id.lock_icon_view)
+ }
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ fun providesTapAgainView(
+ notificationPanelView: NotificationPanelView,
+ ): TapAgainView {
+ return notificationPanelView.findViewById(R.id.shade_falsing_tap_again)
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ fun providesNotificationsQuickSettingsContainer(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): NotificationsQuickSettingsContainer {
+ return notificationShadeWindowView.findViewById(R.id.notification_container_parent)
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ @Named(SHADE_HEADER)
+ fun providesShadeHeaderView(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): MotionLayout {
+ val stub = notificationShadeWindowView.findViewById<ViewStub>(R.id.qs_header_stub)
+ val layoutId = R.layout.combined_qs_header
+ stub.layoutResource = layoutId
+ return stub.inflate() as MotionLayout
+ }
+
+ @Provides
+ @SysUISingleton
+ fun providesCombinedShadeHeadersConstraintManager(): CombinedShadeHeadersConstraintManager {
+ return CombinedShadeHeadersConstraintManagerImpl
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ @Named(SHADE_HEADER)
+ fun providesBatteryMeterView(@Named(SHADE_HEADER) view: MotionLayout): BatteryMeterView {
+ return view.findViewById(R.id.batteryRemainingIcon)
+ }
+
+ @Provides
+ @SysUISingleton
+ @Named(SHADE_HEADER)
+ fun providesBatteryMeterViewController(
+ @Named(SHADE_HEADER) batteryMeterView: BatteryMeterView,
+ userTracker: UserTracker,
+ configurationController: ConfigurationController,
+ tunerService: TunerService,
+ @Main mainHandler: Handler,
+ contentResolver: ContentResolver,
+ batteryController: BatteryController,
+ ): BatteryMeterViewController {
+ return BatteryMeterViewController(
+ batteryMeterView,
+ StatusBarLocation.QS,
+ userTracker,
+ configurationController,
+ tunerService,
+ mainHandler,
+ contentResolver,
+ batteryController,
+ )
+ }
+
+ @Provides
+ @SysUISingleton
+ @Named(SHADE_HEADER)
+ fun providesOngoingPrivacyChip(
+ @Named(SHADE_HEADER) header: MotionLayout,
+ ): OngoingPrivacyChip {
+ return header.findViewById(R.id.privacy_chip)
+ }
+
+ @Provides
+ @SysUISingleton
+ @Named(SHADE_HEADER)
+ fun providesStatusIconContainer(
+ @Named(SHADE_HEADER) header: MotionLayout,
+ ): StatusIconContainer {
+ return header.findViewById(R.id.statusIcons)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt
index c50693c30533..15ec18c528b6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt
@@ -17,6 +17,7 @@
package com.android.systemui.shade
import com.android.systemui.CoreStartable
+import com.android.systemui.biometrics.AuthRippleController
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
@@ -28,4 +29,9 @@ internal abstract class StartShadeModule {
@IntoMap
@ClassKey(ShadeController::class)
abstract fun bind(shadeController: ShadeController): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(AuthRippleController::class)
+ abstract fun bindAuthRippleController(controller: AuthRippleController): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
index 4ec5f46e7771..7a989cfe227a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
@@ -19,7 +19,6 @@ package com.android.systemui.statusbar;
import android.view.View;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -52,7 +51,6 @@ public class LegacyNotificationShelfControllerImpl implements NotificationShelfC
mActivatableNotificationViewController = activatableNotificationViewController;
mKeyguardBypassController = keyguardBypassController;
mStatusBarStateController = statusBarStateController;
- mView.setSensitiveRevealAnimEnabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index fb88a96c38c2..763400b307fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -27,10 +27,12 @@ import android.app.Notification;
import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.hardware.display.DisplayManager;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
@@ -41,6 +43,7 @@ import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.Log;
+import android.view.Display;
import android.view.View;
import android.widget.ImageView;
@@ -74,11 +77,15 @@ import com.android.systemui.util.concurrency.DelayableExecutor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashSet;
+import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.stream.Collectors;
import dagger.Lazy;
@@ -138,6 +145,14 @@ public class NotificationMediaManager implements Dumpable {
private BackDropView mBackdrop;
private ImageView mBackdropFront;
private ImageView mBackdropBack;
+ private final Point mTmpDisplaySize = new Point();
+
+ private final DisplayManager mDisplayManager;
+ @Nullable
+ private List<String> mSmallerInternalDisplayUids;
+ private Display mCurrentDisplay;
+
+ private LockscreenWallpaper.WallpaperDrawable mWallapperDrawable;
private final MediaController.Callback mMediaListener = new MediaController.Callback() {
@Override
@@ -184,7 +199,8 @@ public class NotificationMediaManager implements Dumpable {
SysuiColorExtractor colorExtractor,
KeyguardStateController keyguardStateController,
DumpManager dumpManager,
- WallpaperManager wallpaperManager) {
+ WallpaperManager wallpaperManager,
+ DisplayManager displayManager) {
mContext = context;
mMediaArtworkProcessor = mediaArtworkProcessor;
mKeyguardBypassController = keyguardBypassController;
@@ -200,6 +216,7 @@ public class NotificationMediaManager implements Dumpable {
mStatusBarStateController = statusBarStateController;
mColorExtractor = colorExtractor;
mKeyguardStateController = keyguardStateController;
+ mDisplayManager = displayManager;
mIsLockscreenLiveWallpaperEnabled = wallpaperManager.isLockscreenLiveWallpaperEnabled();
setupNotifPipeline();
@@ -477,6 +494,48 @@ public class NotificationMediaManager implements Dumpable {
}
/**
+ * Notify lockscreen wallpaper drawable the current internal display.
+ */
+ public void onDisplayUpdated(Display display) {
+ Trace.beginSection("NotificationMediaManager#onDisplayUpdated");
+ mCurrentDisplay = display;
+ if (mWallapperDrawable != null) {
+ mWallapperDrawable.onDisplayUpdated(isOnSmallerInternalDisplays());
+ }
+ Trace.endSection();
+ }
+
+ private boolean isOnSmallerInternalDisplays() {
+ if (mSmallerInternalDisplayUids == null) {
+ mSmallerInternalDisplayUids = findSmallerInternalDisplayUids();
+ }
+ return mSmallerInternalDisplayUids.contains(mCurrentDisplay.getUniqueId());
+ }
+
+ private List<String> findSmallerInternalDisplayUids() {
+ if (mSmallerInternalDisplayUids != null) {
+ return mSmallerInternalDisplayUids;
+ }
+ List<Display> internalDisplays = Arrays.stream(mDisplayManager.getDisplays(
+ DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED))
+ .filter(display -> display.getType() == Display.TYPE_INTERNAL)
+ .collect(Collectors.toList());
+ if (internalDisplays.isEmpty()) {
+ return List.of();
+ }
+ Display largestDisplay = internalDisplays.stream()
+ .max(Comparator.comparingInt(this::getRealDisplayArea))
+ .orElse(internalDisplays.get(0));
+ internalDisplays.remove(largestDisplay);
+ return internalDisplays.stream().map(Display::getUniqueId).collect(Collectors.toList());
+ }
+
+ private int getRealDisplayArea(Display display) {
+ display.getRealSize(mTmpDisplaySize);
+ return mTmpDisplaySize.x * mTmpDisplaySize.y;
+ }
+
+ /**
* Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
*/
public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
@@ -551,7 +610,7 @@ public class NotificationMediaManager implements Dumpable {
mLockscreenWallpaper != null ? mLockscreenWallpaper.getBitmap() : null;
if (lockWallpaper != null) {
artworkDrawable = new LockscreenWallpaper.WallpaperDrawable(
- mBackdropBack.getResources(), lockWallpaper);
+ mBackdropBack.getResources(), lockWallpaper, isOnSmallerInternalDisplays());
// We're in the SHADE mode on the SIM screen - yet we still need to show
// the lockscreen wallpaper in that mode.
allowWhenShade = mStatusBarStateController.getState() == KEYGUARD;
@@ -611,6 +670,10 @@ public class NotificationMediaManager implements Dumpable {
mBackdropBack.setBackgroundColor(0xFFFFFFFF);
mBackdropBack.setImageDrawable(new ColorDrawable(c));
} else {
+ if (artworkDrawable instanceof LockscreenWallpaper.WallpaperDrawable) {
+ mWallapperDrawable =
+ (LockscreenWallpaper.WallpaperDrawable) artworkDrawable;
+ }
mBackdropBack.setImageDrawable(artworkDrawable);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 0e20df6ecfba..b624115dc5e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -553,7 +553,6 @@ class NotificationShadeDepthController @Inject constructor(
*/
fun onWallpaperZoomOutChanged(zoomOut: Float)
- @JvmDefault
fun onBlurRadiusChanged(blurRadius: Int) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 25a1dc6322ba..3f37c60bee8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -24,7 +24,6 @@ import android.content.res.Resources;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.IndentingPrintWriter;
-import android.util.Log;
import android.util.MathUtils;
import android.view.View;
import android.view.ViewGroup;
@@ -40,6 +39,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.R;
import com.android.systemui.animation.ShadeInterpolation;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -95,8 +96,10 @@ public class NotificationShelf extends ActivatableNotificationView implements St
private float mCornerAnimationDistance;
private NotificationShelfController mController;
private float mActualWidth = -1;
- private boolean mSensitiveRevealAnimEnabled;
- private boolean mShelfRefactorFlagEnabled;
+ private final ViewRefactorFlag mSensitiveRevealAnim =
+ new ViewRefactorFlag(Flags.SENSITIVE_REVEAL_ANIM);
+ private final ViewRefactorFlag mShelfRefactor =
+ new ViewRefactorFlag(Flags.NOTIFICATION_SHELF_REFACTOR);
private boolean mCanModifyColorOfNotifications;
private boolean mCanInteract;
private NotificationStackScrollLayout mHostLayout;
@@ -130,7 +133,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
public void bind(AmbientState ambientState,
NotificationStackScrollLayoutController hostLayoutController) {
- assertRefactorFlagDisabled();
+ mShelfRefactor.assertDisabled();
mAmbientState = ambientState;
mHostLayoutController = hostLayoutController;
hostLayoutController.setOnNotificationRemovedListener((child, isTransferInProgress) -> {
@@ -140,7 +143,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout,
NotificationRoundnessManager roundnessManager) {
- if (!checkRefactorFlagEnabled()) return;
+ if (!mShelfRefactor.expectEnabled()) return;
mAmbientState = ambientState;
mHostLayout = hostLayout;
mRoundnessManager = roundnessManager;
@@ -268,7 +271,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight();
- if (mSensitiveRevealAnimEnabled && viewState.hidden) {
+ if (mSensitiveRevealAnim.isEnabled() && viewState.hidden) {
// if the shelf is hidden, position it at the end of the stack (plus the clip
// padding), such that when it appears animated, it will smoothly move in from the
// bottom, without jump cutting any notifications
@@ -279,7 +282,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
private int getSpeedBumpIndex() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.getSpeedBumpIndex();
} else {
return mHostLayoutController.getSpeedBumpIndex();
@@ -413,7 +416,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
expandingAnimated, isLastChild, shelfClipStart);
// TODO(b/172289889) scale mPaddingBetweenElements with expansion amount
- if ((!mSensitiveRevealAnimEnabled && ((isLastChild && !child.isInShelf())
+ if ((!mSensitiveRevealAnim.isEnabled() && ((isLastChild && !child.isInShelf())
|| backgroundForceHidden)) || aboveShelf) {
notificationClipEnd = shelfStart + getIntrinsicHeight();
} else {
@@ -462,7 +465,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
// if the shelf is visible, but if the shelf is hidden, it causes incorrect curling.
// notificationClipEnd handles the discrepancy between a visible and hidden shelf,
// so we use that when on the keyguard (and while animating away) to reduce curling.
- final float keyguardSafeShelfStart = !mSensitiveRevealAnimEnabled
+ final float keyguardSafeShelfStart = !mSensitiveRevealAnim.isEnabled()
&& mAmbientState.isOnKeyguard() ? notificationClipEnd : shelfStart;
updateCornerRoundnessOnScroll(anv, viewStart, keyguardSafeShelfStart);
}
@@ -504,7 +507,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
private ExpandableView getHostLayoutChildAt(int index) {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return (ExpandableView) mHostLayout.getChildAt(index);
} else {
return mHostLayoutController.getChildAt(index);
@@ -512,7 +515,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
private int getHostLayoutChildCount() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.getChildCount();
} else {
return mHostLayoutController.getChildCount();
@@ -520,7 +523,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
private boolean canModifyColorOfNotifications() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mCanModifyColorOfNotifications && mAmbientState.isShadeExpanded();
} else {
return mController.canModifyColorOfNotifications();
@@ -583,7 +586,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
private boolean isViewAffectedBySwipe(ExpandableView expandableView) {
- if (!mShelfRefactorFlagEnabled) {
+ if (!mShelfRefactor.isEnabled()) {
return mHostLayoutController.isViewAffectedBySwipe(expandableView);
} else {
return mRoundnessManager.isViewAffectedBySwipe(expandableView);
@@ -607,7 +610,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
private View getHostLayoutTransientView(int index) {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.getTransientView(index);
} else {
return mHostLayoutController.getTransientView(index);
@@ -615,7 +618,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
private int getHostLayoutTransientViewCount() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.getTransientViewCount();
} else {
return mHostLayoutController.getTransientViewCount();
@@ -961,7 +964,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
@Override
public void onStateChanged(int newState) {
- assertRefactorFlagDisabled();
+ mShelfRefactor.assertDisabled();
mStatusBarState = newState;
updateInteractiveness();
}
@@ -975,7 +978,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
private boolean canInteract() {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mCanInteract;
} else {
return mStatusBarState == StatusBarState.KEYGUARD;
@@ -1018,32 +1021,18 @@ public class NotificationShelf extends ActivatableNotificationView implements St
return false;
}
- private void assertRefactorFlagDisabled() {
- if (mShelfRefactorFlagEnabled) {
- NotificationShelfController.throwIllegalFlagStateError(false);
- }
- }
-
- private boolean checkRefactorFlagEnabled() {
- if (!mShelfRefactorFlagEnabled) {
- Log.wtf(TAG,
- "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is disabled.");
- }
- return mShelfRefactorFlagEnabled;
- }
-
public void setController(NotificationShelfController notificationShelfController) {
- assertRefactorFlagDisabled();
+ mShelfRefactor.assertDisabled();
mController = notificationShelfController;
}
public void setCanModifyColorOfNotifications(boolean canModifyColorOfNotifications) {
- if (!checkRefactorFlagEnabled()) return;
+ if (!mShelfRefactor.expectEnabled()) return;
mCanModifyColorOfNotifications = canModifyColorOfNotifications;
}
public void setCanInteract(boolean canInteract) {
- if (!checkRefactorFlagEnabled()) return;
+ if (!mShelfRefactor.expectEnabled()) return;
mCanInteract = canInteract;
updateInteractiveness();
}
@@ -1053,27 +1042,15 @@ public class NotificationShelf extends ActivatableNotificationView implements St
}
private int getIndexOfViewInHostLayout(ExpandableView child) {
- if (mShelfRefactorFlagEnabled) {
+ if (mShelfRefactor.isEnabled()) {
return mHostLayout.indexOfChild(child);
} else {
return mHostLayoutController.indexOfChild(child);
}
}
- /**
- * Set whether the sensitive reveal animation feature flag is enabled
- * @param enabled true if enabled
- */
- public void setSensitiveRevealAnimEnabled(boolean enabled) {
- mSensitiveRevealAnimEnabled = enabled;
- }
-
- public void setRefactorFlagEnabled(boolean enabled) {
- mShelfRefactorFlagEnabled = enabled;
- }
-
public void requestRoundnessResetFor(ExpandableView child) {
- if (!checkRefactorFlagEnabled()) return;
+ if (!mShelfRefactor.expectEnabled()) return;
child.requestRoundnessReset(SHELF_SCROLL);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
index 1619ddaac85c..8a3e21756c2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
@@ -16,11 +16,8 @@
package com.android.systemui.statusbar
-import android.util.Log
import android.view.View
import android.view.View.OnClickListener
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
@@ -49,29 +46,4 @@ interface NotificationShelfController {
/** @see View.setOnClickListener */
fun setOnClickListener(listener: OnClickListener)
-
- companion object {
- @JvmStatic
- fun assertRefactorFlagDisabled(featureFlags: FeatureFlags) {
- if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
- throwIllegalFlagStateError(expected = false)
- }
- }
-
- @JvmStatic
- fun checkRefactorFlagEnabled(featureFlags: FeatureFlags): Boolean =
- featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR).also { enabled ->
- if (!enabled) {
- Log.wtf("NotifShelf", getErrorMessage(expected = true))
- }
- }
-
- @JvmStatic
- fun throwIllegalFlagStateError(expected: Boolean): Nothing =
- error(getErrorMessage(expected))
-
- private fun getErrorMessage(expected: Boolean): String =
- "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is " +
- if (expected) "disabled" else "enabled"
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
index 599beecb0e00..6be407af581d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
@@ -29,7 +29,6 @@ interface SignalCallback {
*
* @param wifiIndicators a box type containing enough information to properly draw a Wi-Fi icon
*/
- @JvmDefault
fun setWifiIndicators(wifiIndicators: WifiIndicators) {}
/**
@@ -42,7 +41,6 @@ interface SignalCallback {
* NOTE: phones can have multiple subscriptions, so this [mobileDataIndicators] object should be
* indexed based on its [subId][MobileDataIndicators.subId]
*/
- @JvmDefault
fun setMobileDataIndicators(mobileDataIndicators: MobileDataIndicators) {}
/**
@@ -51,7 +49,6 @@ interface SignalCallback {
*
* @param subs a [SubscriptionInfo] for each subscription that we know about
*/
- @JvmDefault
fun setSubs(subs: List<@JvmSuppressWildcards SubscriptionInfo>) {}
/**
@@ -63,7 +60,6 @@ interface SignalCallback {
* @param show whether or not to show a "no sim" view
* @param simDetected whether any SIM is detected or not
*/
- @JvmDefault
fun setNoSims(show: Boolean, simDetected: Boolean) {}
/**
@@ -72,7 +68,6 @@ interface SignalCallback {
*
* @param icon an [IconState] for the current ethernet status
*/
- @JvmDefault
fun setEthernetIndicators(icon: IconState) {}
/**
@@ -80,7 +75,6 @@ interface SignalCallback {
*
* @param icon an [IconState] for the current airplane mode status
*/
- @JvmDefault
fun setIsAirplaneMode(icon: IconState) {}
/**
@@ -88,7 +82,6 @@ interface SignalCallback {
*
* @param enabled the current mobile data feature ennabled state
*/
- @JvmDefault
fun setMobileDataEnabled(enabled: Boolean) {}
/**
@@ -97,7 +90,6 @@ interface SignalCallback {
* @param noValidatedNetwork whether there is any validated network.
* @param noNetworksAvailable whether there is any WiFi networks available.
*/
- @JvmDefault
fun setConnectivityStatus(
noDefaultNetwork: Boolean,
noValidatedNetwork: Boolean,
@@ -109,7 +101,6 @@ interface SignalCallback {
* @param statusIcon the icon for the call indicator
* @param subId subscription ID for which to update the UI
*/
- @JvmDefault
fun setCallIndicator(statusIcon: IconState, subId: Int) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index e5ba3ce1fdae..1c7a1860dbb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.dagger;
import android.app.IActivityManager;
import android.app.WallpaperManager;
import android.content.Context;
+import android.hardware.display.DisplayManager;
import android.os.RemoteException;
import android.service.dreams.IDreamManager;
import android.util.Log;
@@ -146,7 +147,8 @@ public interface CentralSurfacesDependenciesModule {
SysuiColorExtractor colorExtractor,
KeyguardStateController keyguardStateController,
DumpManager dumpManager,
- WallpaperManager wallpaperManager) {
+ WallpaperManager wallpaperManager,
+ DisplayManager displayManager) {
return new NotificationMediaManager(
context,
centralSurfacesOptionalLazy,
@@ -162,7 +164,8 @@ public interface CentralSurfacesDependenciesModule {
colorExtractor,
keyguardStateController,
dumpManager,
- wallpaperManager);
+ wallpaperManager,
+ displayManager);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
index 2a18f1f51ace..ef9089099a86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -49,11 +49,10 @@ interface SystemStatusAnimationCallback {
fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator? { return null }
// Best method name, change my mind
- @JvmDefault
fun onSystemStatusAnimationTransitionToPersistentDot(contentDescription: String?): Animator? {
return null
}
- @JvmDefault fun onHidePersistentDot(): Animator? { return null }
+ fun onHidePersistentDot(): Animator? { return null }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 9ba219903272..8d1e8d0ab524 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -613,20 +613,20 @@ constructor(
interface WakeUpListener {
/** Called whenever the notifications are fully hidden or shown */
- @JvmDefault fun onFullyHiddenChanged(isFullyHidden: Boolean) {}
+ fun onFullyHiddenChanged(isFullyHidden: Boolean) {}
/**
* Called whenever the pulseExpansion changes
*
* @param expandingChanged if the user has started or stopped expanding
*/
- @JvmDefault fun onPulseExpansionChanged(expandingChanged: Boolean) {}
+ fun onPulseExpansionChanged(expandingChanged: Boolean) {}
/**
* Called when the animator started by [scheduleDelayedDozeAmountAnimation] begins running
* after the start delay, or after it ends/is cancelled.
*/
- @JvmDefault fun onDelayedDozeAmountAnimationRunning(running: Boolean) {}
+ fun onDelayedDozeAmountAnimationRunning(running: Boolean) {}
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
index 1cf9c1e1f299..8029f48152f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
@@ -3,10 +3,10 @@ package com.android.systemui.statusbar.notification
import android.util.FloatProperty
import android.view.View
import androidx.annotation.FloatRange
-import com.android.systemui.Dependency
import com.android.systemui.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.ViewRefactorFlag
import com.android.systemui.statusbar.notification.stack.AnimationProperties
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import kotlin.math.abs
@@ -27,37 +27,31 @@ interface Roundable {
/** Current top roundness */
@get:FloatRange(from = 0.0, to = 1.0)
- @JvmDefault
val topRoundness: Float
get() = roundableState.topRoundness
/** Current bottom roundness */
@get:FloatRange(from = 0.0, to = 1.0)
- @JvmDefault
val bottomRoundness: Float
get() = roundableState.bottomRoundness
/** Max radius in pixel */
- @JvmDefault
val maxRadius: Float
get() = roundableState.maxRadius
/** Current top corner in pixel, based on [topRoundness] and [maxRadius] */
- @JvmDefault
val topCornerRadius: Float
get() =
- if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.topCornerRadius
+ if (roundableState.newHeadsUpAnim.isEnabled) roundableState.topCornerRadius
else topRoundness * maxRadius
/** Current bottom corner in pixel, based on [bottomRoundness] and [maxRadius] */
- @JvmDefault
val bottomCornerRadius: Float
get() =
- if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.bottomCornerRadius
+ if (roundableState.newHeadsUpAnim.isEnabled) roundableState.bottomCornerRadius
else bottomRoundness * maxRadius
/** Get and update the current radii */
- @JvmDefault
val updatedRadii: FloatArray
get() =
roundableState.radiiBuffer.also { radii ->
@@ -80,7 +74,6 @@ interface Roundable {
* @param sourceType the source from which the request for roundness comes.
* @return Whether the roundness was changed.
*/
- @JvmDefault
fun requestTopRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float,
sourceType: SourceType,
@@ -125,7 +118,6 @@ interface Roundable {
* @param sourceType the source from which the request for roundness comes.
* @return Whether the roundness was changed.
*/
- @JvmDefault
fun requestTopRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float,
sourceType: SourceType,
@@ -149,7 +141,6 @@ interface Roundable {
* @param sourceType the source from which the request for roundness comes.
* @return Whether the roundness was changed.
*/
- @JvmDefault
fun requestBottomRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float,
sourceType: SourceType,
@@ -194,7 +185,6 @@ interface Roundable {
* @param sourceType the source from which the request for roundness comes.
* @return Whether the roundness was changed.
*/
- @JvmDefault
fun requestBottomRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float,
sourceType: SourceType,
@@ -219,7 +209,6 @@ interface Roundable {
* @param animate true if it should animate to that value.
* @return Whether the roundness was changed.
*/
- @JvmDefault
fun requestRoundness(
@FloatRange(from = 0.0, to = 1.0) top: Float,
@FloatRange(from = 0.0, to = 1.0) bottom: Float,
@@ -246,7 +235,6 @@ interface Roundable {
* @param sourceType the source from which the request for roundness comes.
* @return Whether the roundness was changed.
*/
- @JvmDefault
fun requestRoundness(
@FloatRange(from = 0.0, to = 1.0) top: Float,
@FloatRange(from = 0.0, to = 1.0) bottom: Float,
@@ -270,7 +258,6 @@ interface Roundable {
* @param sourceType the source from which the request for roundness comes.
* @param animate true if it should animate to that value.
*/
- @JvmDefault
fun requestRoundnessReset(sourceType: SourceType, animate: Boolean) {
requestRoundness(top = 0f, bottom = 0f, sourceType = sourceType, animate = animate)
}
@@ -284,19 +271,16 @@ interface Roundable {
*
* @param sourceType the source from which the request for roundness comes.
*/
- @JvmDefault
fun requestRoundnessReset(sourceType: SourceType) {
requestRoundnessReset(sourceType = sourceType, animate = roundableState.targetView.isShown)
}
/** Apply the roundness changes, usually means invalidate the [RoundableState.targetView]. */
- @JvmDefault
fun applyRoundnessAndInvalidate() {
roundableState.targetView.invalidate()
}
/** @return true if top or bottom roundness is not zero. */
- @JvmDefault
fun hasRoundedCorner(): Boolean {
return topRoundness != 0f || bottomRoundness != 0f
}
@@ -307,7 +291,6 @@ interface Roundable {
*
* This method reuses the previous [radii] for performance reasons.
*/
- @JvmDefault
fun updateRadii(
topCornerRadius: Float,
bottomCornerRadius: Float,
@@ -335,13 +318,12 @@ constructor(
internal val targetView: View,
private val roundable: Roundable,
maxRadius: Float,
- private val featureFlags: FeatureFlags = Dependency.get(FeatureFlags::class.java)
+ featureFlags: FeatureFlags? = null
) {
internal var maxRadius = maxRadius
private set
- internal val newHeadsUpAnimFlagEnabled
- get() = featureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS)
+ internal val newHeadsUpAnim = ViewRefactorFlag(featureFlags, Flags.IMPROVED_HUN_ANIMATIONS)
/** Animatable for top roundness */
private val topAnimatable = topAnimatable(roundable)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
index 1494574b26f0..93a34afc9fc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
@@ -48,6 +48,14 @@ class RowAppearanceCoordinator @Inject internal constructor(
private val mAlwaysExpandNonGroupedNotification =
context.resources.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications)
+ /**
+ * `true` if the first non-group expandable notification should be expanded automatically
+ * when possible. If `false`, then the first non-group expandable notification should not
+ * be expanded.
+ */
+ private val mAutoExpandFirstNotification =
+ context.resources.getBoolean(R.bool.config_autoExpandFirstNotification)
+
override fun attach(pipeline: NotifPipeline) {
pipeline.addOnBeforeRenderListListener(::onBeforeRenderList)
pipeline.addOnAfterRenderEntryListener(::onAfterRenderEntry)
@@ -61,8 +69,10 @@ class RowAppearanceCoordinator @Inject internal constructor(
private fun onAfterRenderEntry(entry: NotificationEntry, controller: NotifRowController) {
// If mAlwaysExpandNonGroupedNotification is false, then only expand the
- // very first notification and if it's not a child of grouped notifications.
- controller.setSystemExpanded(mAlwaysExpandNonGroupedNotification || entry == entryToExpand)
+ // very first notification if it's not a child of grouped notifications and when
+ // mAutoExpandFirstNotification is true.
+ controller.setSystemExpanded(mAlwaysExpandNonGroupedNotification ||
+ (mAutoExpandFirstNotification && entry == entryToExpand))
// Show/hide the feedback icon
controller.setFeedbackIcon(mAssistantFeedbackController.getFeedbackIcon(entry))
// Show the "alerted" bell icon
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 189608072ec7..9ecf50ee4f8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.collection.inflation;
-import static com.android.systemui.flags.Flags.NOTIFICATION_INLINE_REPLY_ANIMATION;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
@@ -176,8 +175,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
entry.setRow(row);
mNotifBindPipeline.manageRow(entry, row);
mPresenter.onBindRow(row);
- row.setInlineReplyAnimationFlagEnabled(
- mFeatureFlags.isEnabled(NOTIFICATION_INLINE_REPLY_ANIMATION));
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 908c11a1d076..36a8e9833d39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -566,7 +566,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
@Override
public float getTopCornerRadius() {
- if (isNewHeadsUpAnimFlagEnabled()) {
+ if (mImprovedHunAnimation.isEnabled()) {
return super.getTopCornerRadius();
}
@@ -576,7 +576,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
@Override
public float getBottomCornerRadius() {
- if (isNewHeadsUpAnimFlagEnabled()) {
+ if (mImprovedHunAnimation.isEnabled()) {
return super.getBottomCornerRadius();
}
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 b34c28163abb..42b99a1dc68c 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
@@ -77,6 +77,7 @@ import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -275,7 +276,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private OnExpandClickListener mOnExpandClickListener;
private View.OnClickListener mOnFeedbackClickListener;
private Path mExpandingClipPath;
- private boolean mIsInlineReplyAnimationFlagEnabled = false;
+ private final ViewRefactorFlag mInlineReplyAnimation =
+ new ViewRefactorFlag(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION);
// Listener will be called when receiving a long click event.
// Use #setLongPressPosition to optionally assign positional data with the long press.
@@ -3121,10 +3123,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return showingLayout != null && showingLayout.requireRowToHaveOverlappingRendering();
}
- public void setInlineReplyAnimationFlagEnabled(boolean isEnabled) {
- mIsInlineReplyAnimationFlagEnabled = isEnabled;
- }
-
@Override
public void setActualHeight(int height, boolean notifyListeners) {
boolean changed = height != getActualHeight();
@@ -3144,7 +3142,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
int contentHeight = Math.max(getMinHeight(), height);
for (NotificationContentView l : mLayouts) {
- if (mIsInlineReplyAnimationFlagEnabled) {
+ if (mInlineReplyAnimation.isEnabled()) {
l.setContentHeight(height);
} else {
l.setContentHeight(contentHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 7f23c1b89b51..c8f13a6302cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -28,10 +28,9 @@ import android.util.IndentingPrintWriter;
import android.view.View;
import android.view.ViewOutlineProvider;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.statusbar.notification.RoundableState;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import com.android.systemui.util.DumpUtilsKt;
@@ -50,7 +49,8 @@ public abstract class ExpandableOutlineView extends ExpandableView {
private float mOutlineAlpha = -1f;
private boolean mAlwaysRoundBothCorners;
private Path mTmpPath = new Path();
- private final FeatureFlags mFeatureFlags;
+ protected final ViewRefactorFlag mImprovedHunAnimation =
+ new ViewRefactorFlag(Flags.IMPROVED_HUN_ANIMATIONS);
/**
* {@code false} if the children views of the {@link ExpandableOutlineView} are translated when
@@ -126,7 +126,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
return EMPTY_PATH;
}
float bottomRadius = mAlwaysRoundBothCorners ? getMaxRadius() : getBottomCornerRadius();
- if (!isNewHeadsUpAnimFlagEnabled() && (topRadius + bottomRadius > height)) {
+ if (!mImprovedHunAnimation.isEnabled() && (topRadius + bottomRadius > height)) {
float overShoot = topRadius + bottomRadius - height;
float currentTopRoundness = getTopRoundness();
float currentBottomRoundness = getBottomRoundness();
@@ -167,7 +167,6 @@ public abstract class ExpandableOutlineView extends ExpandableView {
super(context, attrs);
setOutlineProvider(mProvider);
initDimens();
- mFeatureFlags = Dependency.get(FeatureFlags.class);
}
@Override
@@ -376,8 +375,4 @@ public abstract class ExpandableOutlineView extends ExpandableView {
});
}
- // TODO(b/290365128) replace with ViewRefactorFlag
- protected boolean isNewHeadsUpAnimFlagEnabled() {
- return mFeatureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index 23a58d252ba6..22a87a7c9432 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -21,7 +21,6 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl
@@ -66,7 +65,7 @@ class NotificationShelfViewBinderWrapperControllerImpl @Inject constructor() :
override fun setOnClickListener(listener: View.OnClickListener) = unsupported
private val unsupported: Nothing
- get() = NotificationShelfController.throwIllegalFlagStateError(expected = true)
+ get() = error("Code path not supported when NOTIFICATION_SHELF_REFACTOR is disabled")
}
/** Binds a [NotificationShelf] to its [view model][NotificationShelfViewModel]. */
@@ -80,8 +79,6 @@ object NotificationShelfViewBinder {
) {
ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager)
shelf.apply {
- setRefactorFlagEnabled(true)
- setSensitiveRevealAnimEnabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM))
// TODO(278765923): Replace with eventual NotificationIconContainerViewBinder#bind()
notificationIconAreaController.setShelfIcons(shelfIcons)
repeatWhenAttached {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index b0f3f598cb91..95e74f210c5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -29,7 +29,6 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
@@ -57,7 +56,6 @@ public class AmbientState implements Dumpable {
private final SectionProvider mSectionProvider;
private final BypassController mBypassController;
private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
- private final FeatureFlags mFeatureFlags;
/**
* Used to read bouncer states.
*/
@@ -261,13 +259,12 @@ public class AmbientState implements Dumpable {
@NonNull SectionProvider sectionProvider,
@NonNull BypassController bypassController,
@Nullable StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- @NonNull LargeScreenShadeInterpolator largeScreenShadeInterpolator,
- @NonNull FeatureFlags featureFlags) {
+ @NonNull LargeScreenShadeInterpolator largeScreenShadeInterpolator
+ ) {
mSectionProvider = sectionProvider;
mBypassController = bypassController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
- mFeatureFlags = featureFlags;
reload(context);
dumpManager.registerDumpable(this);
}
@@ -753,10 +750,6 @@ public class AmbientState implements Dumpable {
return mLargeScreenShadeInterpolator;
}
- public FeatureFlags getFeatureFlags() {
- return mFeatureFlags;
- }
-
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println("mTopPadding=" + mTopPadding);
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 c1ceb3ce5ab6..d71bc2fd6470 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
@@ -89,6 +89,7 @@ import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.shade.ShadeController;
@@ -198,7 +199,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private Set<Integer> mDebugTextUsedYPositions;
private final boolean mDebugRemoveAnimation;
private final boolean mSensitiveRevealAnimEndabled;
- private boolean mAnimatedInsets;
+ private final ViewRefactorFlag mAnimatedInsets;
+ private final ViewRefactorFlag mShelfRefactor;
private int mContentHeight;
private float mIntrinsicContentHeight;
@@ -621,7 +623,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
mSensitiveRevealAnimEndabled = featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
- setAnimatedInsetsEnabled(featureFlags.isEnabled(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS));
+ mAnimatedInsets =
+ new ViewRefactorFlag(featureFlags, Flags.ANIMATED_NOTIFICATION_SHADE_INSETS);
+ mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
mSectionsManager = Dependency.get(NotificationSectionsManager.class);
mScreenOffAnimationController =
Dependency.get(ScreenOffAnimationController.class);
@@ -660,7 +664,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mGroupMembershipManager = Dependency.get(GroupMembershipManager.class);
mGroupExpansionManager = Dependency.get(GroupExpansionManager.class);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- if (mAnimatedInsets) {
+ if (mAnimatedInsets.isEnabled()) {
setWindowInsetsAnimationCallback(mInsetsCallback);
}
}
@@ -730,11 +734,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
@VisibleForTesting
- void setAnimatedInsetsEnabled(boolean enabled) {
- mAnimatedInsets = enabled;
- }
-
- @VisibleForTesting
public void updateFooter() {
if (mFooterView == null) {
return;
@@ -1773,7 +1772,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return;
}
mForcedScroll = v;
- if (mAnimatedInsets) {
+ if (mAnimatedInsets.isEnabled()) {
updateForcedScroll();
} else {
scrollTo(v);
@@ -1822,7 +1821,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- if (!mAnimatedInsets) {
+ if (!mAnimatedInsets.isEnabled()) {
mBottomInset = insets.getInsets(WindowInsets.Type.ime()).bottom;
}
mWaterfallTopInset = 0;
@@ -1830,11 +1829,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (cutout != null) {
mWaterfallTopInset = cutout.getWaterfallInsets().top;
}
- if (mAnimatedInsets && !mIsInsetAnimationRunning) {
+ if (mAnimatedInsets.isEnabled() && !mIsInsetAnimationRunning) {
// update bottom inset e.g. after rotation
updateBottomInset(insets);
}
- if (!mAnimatedInsets) {
+ if (!mAnimatedInsets.isEnabled()) {
int range = getScrollRange();
if (mOwnScrollY > range) {
// HACK: We're repeatedly getting staggered insets here while the IME is
@@ -2714,7 +2713,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* @param listener callback for notification removed
*/
public void setOnNotificationRemovedListener(OnNotificationRemovedListener listener) {
- NotificationShelfController.assertRefactorFlagDisabled(mAmbientState.getFeatureFlags());
+ mShelfRefactor.assertDisabled();
mOnNotificationRemovedListener = listener;
}
@@ -2727,7 +2726,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (!mChildTransferInProgress) {
onViewRemovedInternal(expandableView, this);
}
- if (mAmbientState.getFeatureFlags().isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ if (mShelfRefactor.isEnabled()) {
mShelf.requestRoundnessResetFor(expandableView);
} else {
if (mOnNotificationRemovedListener != null) {
@@ -4943,18 +4942,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@Nullable
public ExpandableView getShelf() {
- if (NotificationShelfController.checkRefactorFlagEnabled(mAmbientState.getFeatureFlags())) {
- return mShelf;
- } else {
- return null;
- }
+ if (!mShelfRefactor.expectEnabled()) return null;
+ return mShelf;
}
public void setShelf(NotificationShelf shelf) {
- if (!NotificationShelfController.checkRefactorFlagEnabled(
- mAmbientState.getFeatureFlags())) {
- return;
- }
+ if (!mShelfRefactor.expectEnabled()) return;
int index = -1;
if (mShelf != null) {
index = indexOfChild(mShelf);
@@ -4968,7 +4961,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
public void setShelfController(NotificationShelfController notificationShelfController) {
- NotificationShelfController.assertRefactorFlagDisabled(mAmbientState.getFeatureFlags());
+ mShelfRefactor.assertDisabled();
int index = -1;
if (mShelf != null) {
index = indexOfChild(mShelf);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index ef7375aa690b..4668aa433533 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -65,6 +65,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.shared.model.KeyguardState;
@@ -205,6 +206,7 @@ public class NotificationStackScrollLayoutController {
private boolean mIsInTransitionToAod = false;
private final FeatureFlags mFeatureFlags;
+ private final ViewRefactorFlag mShelfRefactor;
private final NotificationTargetsHelper mNotificationTargetsHelper;
private final SecureSettings mSecureSettings;
private final NotificationDismissibilityProvider mDismissibilityProvider;
@@ -718,6 +720,7 @@ public class NotificationStackScrollLayoutController {
mShadeController = shadeController;
mNotifIconAreaController = notifIconAreaController;
mFeatureFlags = featureFlags;
+ mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
mNotificationTargetsHelper = notificationTargetsHelper;
mSecureSettings = secureSettings;
mDismissibilityProvider = dismissibilityProvider;
@@ -1432,7 +1435,7 @@ public class NotificationStackScrollLayoutController {
}
public void setShelfController(NotificationShelfController notificationShelfController) {
- NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags);
+ mShelfRefactor.assertDisabled();
mView.setShelfController(notificationShelfController);
}
@@ -1645,12 +1648,12 @@ public class NotificationStackScrollLayoutController {
}
public void setShelf(NotificationShelf shelf) {
- if (!NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) return;
+ if (!mShelfRefactor.expectEnabled()) return;
mView.setShelf(shelf);
}
public int getShelfHeight() {
- if (!NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) {
+ if (!mShelfRefactor.expectEnabled()) {
return 0;
}
ExpandableView shelf = mView.getShelf();
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 8361d6ba1801..c3c9a61df2ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -2061,6 +2061,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
void updateDisplaySize() {
mDisplay.getMetrics(mDisplayMetrics);
mDisplay.getSize(mCurrentDisplaySize);
+ mMediaManager.onDisplayUpdated(mDisplay);
if (DEBUG_GESTURES) {
mGestureRec.tag("display",
String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index b2c39f7e289f..92c786fb569f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -306,19 +306,25 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
/**
* Drawable that aligns left horizontally and center vertically (like ImageWallpaper).
+ *
+ * <p>Aligns to the center when showing on the smaller internal display of a multi display
+ * device.
*/
public static class WallpaperDrawable extends DrawableWrapper {
private final ConstantState mState;
private final Rect mTmpRect = new Rect();
+ private boolean mIsOnSmallerInternalDisplays;
- public WallpaperDrawable(Resources r, Bitmap b) {
- this(r, new ConstantState(b));
+ public WallpaperDrawable(Resources r, Bitmap b, boolean isOnSmallerInternalDisplays) {
+ this(r, new ConstantState(b), isOnSmallerInternalDisplays);
}
- private WallpaperDrawable(Resources r, ConstantState state) {
+ private WallpaperDrawable(Resources r, ConstantState state,
+ boolean isOnSmallerInternalDisplays) {
super(new BitmapDrawable(r, state.mBackground));
mState = state;
+ mIsOnSmallerInternalDisplays = isOnSmallerInternalDisplays;
}
@Override
@@ -357,10 +363,17 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
}
dy = (vheight - dheight * scale) * 0.5f;
+ int offsetX = 0;
+ // Offset to show the center area of the wallpaper on a smaller display for multi
+ // display device
+ if (mIsOnSmallerInternalDisplays) {
+ offsetX = bounds.centerX() - (Math.round(dwidth * scale) / 2);
+ }
+
mTmpRect.set(
- bounds.left,
+ bounds.left + offsetX,
bounds.top + Math.round(dy),
- bounds.left + Math.round(dwidth * scale),
+ bounds.left + Math.round(dwidth * scale) + offsetX,
bounds.top + Math.round(dheight * scale + dy));
super.onBoundsChange(mTmpRect);
@@ -371,6 +384,17 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
return mState;
}
+ /**
+ * Update bounds when the hosting display or the display size has changed.
+ *
+ * @param isOnSmallerInternalDisplays true if the drawable is on one of the internal
+ * displays with the smaller area.
+ */
+ public void onDisplayUpdated(boolean isOnSmallerInternalDisplays) {
+ mIsOnSmallerInternalDisplays = isOnSmallerInternalDisplays;
+ onBoundsChange(getBounds());
+ }
+
static class ConstantState extends Drawable.ConstantState {
private final Bitmap mBackground;
@@ -386,7 +410,7 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen
@Override
public Drawable newDrawable(@Nullable Resources res) {
- return new WallpaperDrawable(res, this);
+ return new WallpaperDrawable(res, this, /* isOnSmallerInternalDisplays= */ false);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index e18c9d86d74b..0bf0f4b504b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -24,6 +24,8 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -88,7 +90,7 @@ public class NotificationIconAreaController implements
private final ArrayList<Rect> mTintAreas = new ArrayList<>();
private final Context mContext;
- private final FeatureFlags mFeatureFlags;
+ private final ViewRefactorFlag mShelfRefactor;
private int mAodIconAppearTranslation;
@@ -120,12 +122,13 @@ public class NotificationIconAreaController implements
Optional<Bubbles> bubblesOptional,
DemoModeController demoModeController,
DarkIconDispatcher darkIconDispatcher,
- FeatureFlags featureFlags, StatusBarWindowController statusBarWindowController,
+ FeatureFlags featureFlags,
+ StatusBarWindowController statusBarWindowController,
ScreenOffAnimationController screenOffAnimationController) {
mContrastColorUtil = ContrastColorUtil.getInstance(context);
mContext = context;
mStatusBarStateController = statusBarStateController;
- mFeatureFlags = featureFlags;
+ mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
mStatusBarStateController.addCallback(this);
mMediaManager = notificationMediaManager;
mDozeParameters = dozeParameters;
@@ -179,12 +182,12 @@ public class NotificationIconAreaController implements
}
public void setupShelf(NotificationShelfController notificationShelfController) {
- NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags);
+ mShelfRefactor.assertDisabled();
mShelfIcons = notificationShelfController.getShelfIcons();
}
public void setShelfIcons(NotificationIconContainer icons) {
- if (NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) {
+ if (mShelfRefactor.expectEnabled()) {
mShelfIcons = icons;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 1d934d65df7e..79151fd987d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -548,7 +548,7 @@ public class PhoneStatusBarPolicy
final int iconResId = mUserManager.getUserStatusBarIconResId(userId);
// TODO(b/170249807, b/230779281): Handle non-managed-profile String
String accessibilityString = getManagedProfileAccessibilityString();
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
final boolean showIcon;
if (iconResId != Resources.ID_NULL && (!mKeyguardStateController.isShowing()
|| mKeyguardStateController.isOccluded())) {
@@ -629,6 +629,13 @@ public class PhoneStatusBarPolicy
}
@Override
+ public void appTransitionFinished(int displayId) {
+ if (mDisplayId == displayId) {
+ updateProfileIcon();
+ }
+ }
+
+ @Override
public void onKeyguardShowingChanged() {
updateProfileIcon();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index a9135ac91e44..ad8530df0523 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -79,7 +79,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
private final HeadsUpManagerPhone mHeadsUpManager;
private final AboveShelfObserver mAboveShelfObserver;
private final DozeScrimController mDozeScrimController;
- private final CentralSurfaces mCentralSurfaces;
private final NotificationsInteractor mNotificationsInteractor;
private final NotificationStackScrollLayoutController mNsslController;
private final LockscreenShadeTransitionController mShadeTransitionController;
@@ -107,7 +106,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
NotificationShadeWindowController notificationShadeWindowController,
DynamicPrivacyController dynamicPrivacyController,
KeyguardStateController keyguardStateController,
- CentralSurfaces centralSurfaces,
NotificationsInteractor notificationsInteractor,
LockscreenShadeTransitionController shadeTransitionController,
PowerInteractor powerInteractor,
@@ -128,8 +126,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
mQsController = quickSettingsController;
mHeadsUpManager = headsUp;
mDynamicPrivacyController = dynamicPrivacyController;
- // TODO: use KeyguardStateController#isOccluded to remove this dependency
- mCentralSurfaces = centralSurfaces;
mNotificationsInteractor = notificationsInteractor;
mNsslController = stackScrollerController;
mShadeTransitionController = shadeTransitionController;
@@ -283,7 +279,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu
@Override
public boolean suppressAwakeHeadsUp(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
- if (mCentralSurfaces.isOccluded()) {
+ if (mKeyguardStateController.isOccluded()) {
boolean devicePublic = mLockscreenUserManager
.isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
boolean userPublic = devicePublic
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 5d09e064604a..a501e87902a8 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -20,6 +20,11 @@ import android.content.Intent;
import com.android.systemui.Dependency;
+/**
+ * @deprecated Don't use this class to listen to Secure Settings. Use {@code SecureSettings} instead
+ * or {@code SettingsObserver} to be able to specify the handler.
+ */
+@Deprecated
public abstract class TunerService {
public static final String ACTION_CLEAR = "com.android.systemui.action.CLEAR_TUNER";
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 8cfe2eac3d33..ccc0a79d2cfe 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -56,7 +56,11 @@ import javax.inject.Inject;
/**
+ * @deprecated Don't use this class to listen to Secure Settings. Use {@code SecureSettings} instead
+ * or {@code SettingsObserver} to be able to specify the handler.
+ * This class will interact with SecureSettings using the main looper.
*/
+@Deprecated
@SysUISingleton
public class TunerServiceImpl extends TunerService {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index 8cf71a0b78c0..b0e8ec2f2df6 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -44,8 +44,7 @@ import com.android.systemui.recents.RecentsImplementation;
import com.android.systemui.screenshot.ReferenceScreenshotModule;
import com.android.systemui.settings.dagger.MultiUserUtilsModule;
import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shade.ShadeControllerEmptyImpl;
+import com.android.systemui.shade.ShadeEmptyImplModule;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyboardShortcutsModule;
@@ -95,6 +94,7 @@ import javax.inject.Named;
PowerModule.class,
QSModule.class,
ReferenceScreenshotModule.class,
+ ShadeEmptyImplModule.class,
StatusBarEventsModule.class,
VolumeModule.class,
KeyboardShortcutsModule.class
@@ -139,9 +139,6 @@ public abstract class TvSystemUIModule {
@Binds
abstract DockManager bindDockManager(DockManagerImpl dockManager);
- @Binds
- abstract ShadeController provideShadeController(ShadeControllerEmptyImpl shadeController);
-
@SysUISingleton
@Provides
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
diff --git a/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt b/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
index 693c2708b0f7..5582ced1bad6 100644
--- a/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
@@ -23,7 +23,6 @@ import android.os.UserHandle
* changes.
*/
interface UserAwareController {
- @JvmDefault
fun changeUser(newUser: UserHandle) {}
val currentUserId: Int
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
index 6decb88ee148..5867a40c78fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
@@ -30,6 +30,8 @@ import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -43,12 +45,14 @@ import org.junit.runner.RunWith;
@RunWithLooper
public class ExpandHelperTest extends SysuiTestCase {
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private ExpandableNotificationRow mRow;
private ExpandHelper mExpandHelper;
private ExpandHelper.Callback mCallback;
@Before
public void setUp() throws Exception {
+ mFeatureFlags.setDefault(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION);
mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
mDependency.injectMockDependency(NotificationMediaManager.class);
allowTestableLooperAsMainThread();
@@ -56,7 +60,8 @@ public class ExpandHelperTest extends SysuiTestCase {
NotificationTestHelper helper = new NotificationTestHelper(
mContext,
mDependency,
- TestableLooper.get(this));
+ TestableLooper.get(this),
+ mFeatureFlags);
mRow = helper.createRow();
mCallback = mock(ExpandHelper.Callback.class);
mExpandHelper = new ExpandHelper(context, mCallback, 10, 100);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 608778e05dad..1dc8453a90ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -27,6 +27,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
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.eq;
import static org.mockito.Mockito.mock;
@@ -87,6 +88,7 @@ import java.util.function.Consumer;
@RunWithLooper
public class ExpandableNotificationRowTest extends SysuiTestCase {
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private NotificationTestHelper mNotificationTestHelper;
@Rule public MockitoRule mockito = MockitoJUnit.rule();
@@ -96,12 +98,10 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
mNotificationTestHelper = new NotificationTestHelper(
mContext,
mDependency,
- TestableLooper.get(this));
+ TestableLooper.get(this),
+ mFeatureFlags);
mNotificationTestHelper.setDefaultInflationFlags(FLAG_CONTENT_VIEW_ALL);
-
- FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags();
- fakeFeatureFlags.set(Flags.SENSITIVE_REVEAL_ANIM, false);
- mNotificationTestHelper.setFeatureFlags(fakeFeatureFlags);
+ mFeatureFlags.setDefault(Flags.SENSITIVE_REVEAL_ANIM);
}
@Test
@@ -183,6 +183,14 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
}
@Test
+ public void testSetSensitiveOnNotifRowNotifiesOfHeightChange_withOtherFlagValue()
+ throws Exception {
+ FakeFeatureFlags flags = mFeatureFlags;
+ flags.set(Flags.SENSITIVE_REVEAL_ANIM, !flags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
+ testSetSensitiveOnNotifRowNotifiesOfHeightChange();
+ }
+
+ @Test
public void testSetSensitiveOnNotifRowNotifiesOfHeightChange() throws Exception {
// GIVEN a sensitive notification row that's currently redacted
ExpandableNotificationRow row = mNotificationTestHelper.createRow();
@@ -199,10 +207,19 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
// WHEN the row is set to no longer be sensitive
row.setSensitive(false, true);
+ boolean expectAnimation = mFeatureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
// VERIFY that the height change listener is invoked
assertThat(row.getShowingLayout()).isSameInstanceAs(row.getPrivateLayout());
assertThat(row.getIntrinsicHeight()).isGreaterThan(0);
- verify(listener).onHeightChanged(eq(row), eq(false));
+ verify(listener).onHeightChanged(eq(row), eq(expectAnimation));
+ }
+
+ @Test
+ public void testSetSensitiveOnGroupRowNotifiesOfHeightChange_withOtherFlagValue()
+ throws Exception {
+ FakeFeatureFlags flags = mFeatureFlags;
+ flags.set(Flags.SENSITIVE_REVEAL_ANIM, !flags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
+ testSetSensitiveOnGroupRowNotifiesOfHeightChange();
}
@Test
@@ -222,10 +239,19 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
// WHEN the row is set to no longer be sensitive
group.setSensitive(false, true);
+ boolean expectAnimation = mFeatureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
// VERIFY that the height change listener is invoked
assertThat(group.getShowingLayout()).isSameInstanceAs(group.getPrivateLayout());
assertThat(group.getIntrinsicHeight()).isGreaterThan(0);
- verify(listener).onHeightChanged(eq(group), eq(false));
+ verify(listener).onHeightChanged(eq(group), eq(expectAnimation));
+ }
+
+ @Test
+ public void testSetSensitiveOnPublicRowDoesNotNotifyOfHeightChange_withOtherFlagValue()
+ throws Exception {
+ FakeFeatureFlags flags = mFeatureFlags;
+ flags.set(Flags.SENSITIVE_REVEAL_ANIM, !flags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
+ testSetSensitiveOnPublicRowDoesNotNotifyOfHeightChange();
}
@Test
@@ -254,7 +280,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
assertThat(publicRow.getIntrinsicHeight()).isGreaterThan(0);
assertThat(publicRow.getPrivateLayout().getMinHeight())
.isEqualTo(publicRow.getPublicLayout().getMinHeight());
- verify(listener, never()).onHeightChanged(eq(publicRow), eq(false));
+ verify(listener, never()).onHeightChanged(eq(publicRow), anyBoolean());
}
private void measureAndLayout(ExpandableNotificationRow row) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 1a644d3540b0..d21029d33d5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -48,12 +48,16 @@ import android.text.TextUtils;
import android.view.LayoutInflater;
import android.widget.RemoteViews;
+import androidx.annotation.NonNull;
+
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.TestableDependency;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -90,6 +94,7 @@ import com.android.systemui.wmshell.BubblesTestActivity;
import org.mockito.ArgumentCaptor;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
@@ -130,14 +135,24 @@ public class NotificationTestHelper {
private final NotificationDismissibilityProvider mDismissibilityProvider;
public final Runnable mFutureDismissalRunnable;
private @InflationFlag int mDefaultInflationFlags;
- private FeatureFlags mFeatureFlags;
+ private final FakeFeatureFlags mFeatureFlags;
public NotificationTestHelper(
Context context,
TestableDependency dependency,
TestableLooper testLooper) {
+ this(context, dependency, testLooper, new FakeFeatureFlags());
+ }
+
+ public NotificationTestHelper(
+ Context context,
+ TestableDependency dependency,
+ TestableLooper testLooper,
+ @NonNull FakeFeatureFlags featureFlags) {
mContext = context;
mTestLooper = testLooper;
+ mFeatureFlags = Objects.requireNonNull(featureFlags);
+ dependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
dependency.injectMockDependency(NotificationMediaManager.class);
dependency.injectMockDependency(NotificationShadeWindowController.class);
dependency.injectMockDependency(MediaOutputDialogFactory.class);
@@ -183,17 +198,12 @@ public class NotificationTestHelper {
mFutureDismissalRunnable = mock(Runnable.class);
when(mOnUserInteractionCallback.registerFutureDismissal(any(), anyInt()))
.thenReturn(mFutureDismissalRunnable);
- mFeatureFlags = mock(FeatureFlags.class);
}
public void setDefaultInflationFlags(@InflationFlag int defaultInflationFlags) {
mDefaultInflationFlags = defaultInflationFlags;
}
- public void setFeatureFlags(FeatureFlags featureFlags) {
- mFeatureFlags = featureFlags;
- }
-
public ExpandableNotificationRowLogger getMockLogger() {
return mMockLogger;
}
@@ -527,6 +537,10 @@ public class NotificationTestHelper {
@InflationFlag int extraInflationFlags,
int importance)
throws Exception {
+ // NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be
+ // set, but we do not want to override an existing value that is needed by a specific test.
+ mFeatureFlags.setDefault(Flags.IMPROVED_HUN_ANIMATIONS);
+
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
mContext.LAYOUT_INFLATER_SERVICE);
mRow = (ExpandableNotificationRow) inflater.inflate(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
index 09382ec1945e..3d752880f423 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
@@ -20,7 +20,6 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
@@ -42,7 +41,6 @@ class AmbientStateTest : SysuiTestCase() {
private val bypassController = StackScrollAlgorithm.BypassController { false }
private val statusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>()
private val largeScreenShadeInterpolator = mock<LargeScreenShadeInterpolator>()
- private val featureFlags = mock<FeatureFlags>()
private lateinit var sut: AmbientState
@@ -55,8 +53,7 @@ class AmbientStateTest : SysuiTestCase() {
sectionProvider,
bypassController,
statusBarKeyguardViewManager,
- largeScreenShadeInterpolator,
- featureFlags
+ largeScreenShadeInterpolator
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index f38881c5b521..4b145d8b0dd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -30,7 +30,6 @@ import android.view.ViewGroup;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.controls.ui.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
@@ -65,7 +64,6 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
@Mock private SectionHeaderController mPeopleHeaderController;
@Mock private SectionHeaderController mAlertingHeaderController;
@Mock private SectionHeaderController mSilentHeaderController;
- @Mock private FeatureFlags mFeatureFlag;
private NotificationSectionsManager mSectionsManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 8d751e3b2808..1dc0ab07349b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -9,7 +9,9 @@ import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerPr
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation
+import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.StatusBarIconView
@@ -20,6 +22,7 @@ import com.android.systemui.util.mockito.mock
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
+import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -34,22 +37,32 @@ import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
-class NotificationShelfTest : SysuiTestCase() {
+open class NotificationShelfTest : SysuiTestCase() {
+
+ open val useShelfRefactor: Boolean = false
+ open val useSensitiveReveal: Boolean = false
+ private val flags = FakeFeatureFlags()
@Mock
private lateinit var largeScreenShadeInterpolator: LargeScreenShadeInterpolator
@Mock
- private lateinit var flags: FeatureFlags
- @Mock
private lateinit var ambientState: AmbientState
@Mock
private lateinit var hostLayoutController: NotificationStackScrollLayoutController
+ @Mock
+ private lateinit var hostLayout: NotificationStackScrollLayout
+ @Mock
+ private lateinit var roundnessManager: NotificationRoundnessManager
private lateinit var shelf: NotificationShelf
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ mDependency.injectTestDependency(FeatureFlags::class.java, flags)
+ flags.set(Flags.NOTIFICATION_SHELF_REFACTOR, useShelfRefactor)
+ flags.set(Flags.SENSITIVE_REVEAL_ANIM, useSensitiveReveal)
+ flags.setDefault(Flags.IMPROVED_HUN_ANIMATIONS)
val root = FrameLayout(context)
shelf = LayoutInflater.from(root.context)
.inflate(/* resource = */ R.layout.status_bar_notification_shelf,
@@ -57,10 +70,13 @@ class NotificationShelfTest : SysuiTestCase() {
/* attachToRoot = */false) as NotificationShelf
whenever(ambientState.largeScreenShadeInterpolator).thenReturn(largeScreenShadeInterpolator)
- whenever(ambientState.featureFlags).thenReturn(flags)
whenever(ambientState.isSmallScreen).thenReturn(true)
- shelf.bind(ambientState, /* hostLayoutController */ hostLayoutController)
+ if (useShelfRefactor) {
+ shelf.bind(ambientState, hostLayout, roundnessManager)
+ } else {
+ shelf.bind(ambientState, hostLayoutController)
+ }
shelf.layout(/* left */ 0, /* top */ 0, /* right */ 30, /* bottom */5)
}
@@ -345,7 +361,7 @@ class NotificationShelfTest : SysuiTestCase() {
@Test
fun updateState_withNullLastVisibleBackgroundChild_hideShelf() {
// GIVEN
- shelf.setSensitiveRevealAnimEnabled(true)
+ assumeTrue(useSensitiveReveal)
whenever(ambientState.stackY).thenReturn(100f)
whenever(ambientState.stackHeight).thenReturn(100f)
val paddingBetweenElements =
@@ -372,7 +388,7 @@ class NotificationShelfTest : SysuiTestCase() {
@Test
fun updateState_withNullFirstViewInShelf_hideShelf() {
// GIVEN
- shelf.setSensitiveRevealAnimEnabled(true)
+ assumeTrue(useSensitiveReveal)
whenever(ambientState.stackY).thenReturn(100f)
whenever(ambientState.stackHeight).thenReturn(100f)
val paddingBetweenElements =
@@ -399,7 +415,7 @@ class NotificationShelfTest : SysuiTestCase() {
@Test
fun updateState_withCollapsedShade_hideShelf() {
// GIVEN
- shelf.setSensitiveRevealAnimEnabled(true)
+ assumeTrue(useSensitiveReveal)
whenever(ambientState.stackY).thenReturn(100f)
whenever(ambientState.stackHeight).thenReturn(100f)
val paddingBetweenElements =
@@ -426,7 +442,7 @@ class NotificationShelfTest : SysuiTestCase() {
@Test
fun updateState_withHiddenSectionBeforeShelf_hideShelf() {
// GIVEN
- shelf.setSensitiveRevealAnimEnabled(true)
+ assumeTrue(useSensitiveReveal)
whenever(ambientState.stackY).thenReturn(100f)
whenever(ambientState.stackHeight).thenReturn(100f)
val paddingBetweenElements =
@@ -486,3 +502,25 @@ class NotificationShelfTest : SysuiTestCase() {
assertEquals(expectedAlpha, shelf.viewState.alpha)
}
}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationShelfWithRefactorTest : NotificationShelfTest() {
+ override val useShelfRefactor: Boolean = true
+}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationShelfWithSensitiveRevealTest : NotificationShelfTest() {
+ override val useSensitiveReveal: Boolean = true
+}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationShelfWithBothFlagsTest : NotificationShelfTest() {
+ override val useShelfRefactor: Boolean = true
+ override val useSensitiveReveal: Boolean = true
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index ee8325ec02b5..07eadf7c9bb4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -54,7 +54,6 @@ import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
@@ -119,6 +118,7 @@ import java.util.Optional;
@RunWith(AndroidTestingRunner.class)
public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Mock private NotificationGutsManager mNotificationGutsManager;
@Mock private NotificationsController mNotificationsController;
@Mock private NotificationVisibilityProvider mVisibilityProvider;
@@ -157,7 +157,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private StackStateLogger mStackLogger;
@Mock private NotificationStackScrollLogger mLogger;
@Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
- private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Mock private NotificationTargetsHelper mNotificationTargetsHelper;
@Mock private SecureSettings mSecureSettings;
@Mock private NotificationIconAreaController mIconAreaController;
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 8ad271bef2e4..72fcdec3c44c 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
@@ -68,7 +68,9 @@ import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.EmptyShadeView;
@@ -106,6 +108,7 @@ import java.util.ArrayList;
@TestableLooper.RunWithLooper
public class NotificationStackScrollLayoutTest extends SysuiTestCase {
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private NotificationStackScrollLayout mStackScroller; // Normally test this
private NotificationStackScrollLayout mStackScrollerInternal; // See explanation below
private AmbientState mAmbientState;
@@ -129,7 +132,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
- @Mock private FeatureFlags mFeatureFlags;
@Before
public void setUp() throws Exception {
@@ -143,11 +145,25 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mNotificationSectionsManager,
mBypassController,
mStatusBarKeyguardViewManager,
- mLargeScreenShadeInterpolator,
- mFeatureFlags
+ mLargeScreenShadeInterpolator
));
+ // Register the debug flags we use
+ assertFalse(Flags.NSSL_DEBUG_LINES.getDefault());
+ assertFalse(Flags.NSSL_DEBUG_REMOVE_ANIMATION.getDefault());
+ mFeatureFlags.set(Flags.NSSL_DEBUG_LINES, false);
+ mFeatureFlags.set(Flags.NSSL_DEBUG_REMOVE_ANIMATION, false);
+
+ // Register the feature flags we use
+ // TODO: Ideally we wouldn't need to set these unless a test actually reads them,
+ // and then we would test both configurations, but currently they are all read
+ // in the constructor.
+ mFeatureFlags.setDefault(Flags.SENSITIVE_REVEAL_ANIM);
+ mFeatureFlags.setDefault(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS);
+ mFeatureFlags.setDefault(Flags.NOTIFICATION_SHELF_REFACTOR);
+
// Inject dependencies before initializing the layout
+ mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
mDependency.injectTestDependency(SysuiStatusBarStateController.class, mBarState);
mDependency.injectMockDependency(ShadeController.class);
mDependency.injectTestDependency(
@@ -176,13 +192,18 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper,
mNotificationStackSizeCalculator);
mStackScroller = spy(mStackScrollerInternal);
- mStackScroller.setShelfController(notificationShelfController);
+ if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ mStackScroller.setShelfController(notificationShelfController);
+ }
mStackScroller.setNotificationsController(mNotificationsController);
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(true);
when(mStackScrollLayoutController.getNotificationRoundnessManager())
.thenReturn(mNotificationRoundnessManager);
mStackScroller.setController(mStackScrollLayoutController);
+ if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ mStackScroller.setShelf(mNotificationShelf);
+ }
doNothing().when(mGroupExpansionManager).collapseGroups();
doNothing().when(mExpandHelper).cancelImmediately();
@@ -899,7 +920,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
public void testWindowInsetAnimationProgress_updatesBottomInset() {
int bottomImeInset = 100;
- mStackScrollerInternal.setAnimatedInsetsEnabled(true);
WindowInsets windowInsets = new WindowInsets.Builder()
.setInsets(ime(), Insets.of(0, 0, 0, bottomImeInset)).build();
ArrayList<WindowInsetsAnimation> windowInsetsAnimations = new ArrayList<>();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index df65c09eb8a9..85a2bdd21073 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -47,6 +47,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
@@ -83,7 +84,7 @@ public class NotificationSwipeHelperTest extends SysuiTestCase {
private Handler mHandler;
private ExpandableNotificationRow mNotificationRow;
private Runnable mFalsingCheck;
- private FeatureFlags mFeatureFlags;
+ private final FeatureFlags mFeatureFlags = new FakeFeatureFlags();
private static final int FAKE_ROW_WIDTH = 20;
private static final int FAKE_ROW_HEIGHT = 20;
@@ -96,7 +97,6 @@ public class NotificationSwipeHelperTest extends SysuiTestCase {
mCallback = mock(NotificationSwipeHelper.NotificationCallback.class);
mListener = mock(NotificationMenuRowPlugin.OnMenuEventListener.class);
mNotificationRoundnessManager = mock(NotificationRoundnessManager.class);
- mFeatureFlags = mock(FeatureFlags.class);
mSwipeHelper = spy(new NotificationSwipeHelper(
mContext.getResources(),
ViewConfiguration.get(mContext),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
index 45725ced521c..e30947ce84bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
@@ -18,6 +18,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class NotificationTargetsHelperTest : SysuiTestCase() {
+ private val featureFlags = FakeFeatureFlags()
lateinit var notificationTestHelper: NotificationTestHelper
private val sectionsManager: NotificationSectionsManager = mock()
private val stackScrollLayout: NotificationStackScrollLayout = mock()
@@ -26,10 +27,10 @@ class NotificationTargetsHelperTest : SysuiTestCase() {
fun setUp() {
allowTestableLooperAsMainThread()
notificationTestHelper =
- NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
+ NotificationTestHelper(mContext, mDependency, TestableLooper.get(this), featureFlags)
}
- private fun notificationTargetsHelper() = NotificationTargetsHelper(FakeFeatureFlags())
+ private fun notificationTargetsHelper() = NotificationTargetsHelper(featureFlags)
@Test
fun targetsForFirstNotificationInGroup() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 4c97d20c5da8..987861d3f133 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -8,7 +8,6 @@ import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation.getContentAlpha
import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.EmptyShadeView
import com.android.systemui.statusbar.NotificationShelf
@@ -45,7 +44,6 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
private val dumpManager = mock<DumpManager>()
private val mStatusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>()
private val notificationShelf = mock<NotificationShelf>()
- private val featureFlags = mock<FeatureFlags>()
private val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply {
layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
}
@@ -56,7 +54,6 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
/* bypassController */ { false },
mStatusBarKeyguardViewManager,
largeScreenShadeInterpolator,
- featureFlags,
)
private val testableResources = mContext.getOrCreateTestableResources()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
index 85fbef0d7bb6..9795b9d3c169 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone
import android.app.AlarmManager
import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyResourcesManager
import android.content.SharedPreferences
import android.os.UserManager
import android.telecom.TelecomManager
@@ -49,6 +50,7 @@ import com.android.systemui.statusbar.policy.ZenModeController
import com.android.systemui.util.RingerModeTracker
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.kotlin.JavaAdapter
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.time.DateFormatUtil
import com.android.systemui.util.time.FakeSystemClock
@@ -67,6 +69,7 @@ import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyString
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.never
@@ -83,6 +86,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() {
companion object {
private const val ALARM_SLOT = "alarm"
private const val CONNECTED_DISPLAY_SLOT = "connected_display"
+ private const val MANAGED_PROFILE_SLOT = "managed_profile"
}
@Mock private lateinit var iconController: StatusBarIconController
@@ -104,6 +108,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() {
@Mock private lateinit var userManager: UserManager
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
+ @Mock private lateinit var devicePolicyManagerResources: DevicePolicyResourcesManager
@Mock private lateinit var recordingController: RecordingController
@Mock private lateinit var telecomManager: TelecomManager
@Mock private lateinit var sharedPreferences: SharedPreferences
@@ -132,6 +137,12 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() {
com.android.internal.R.string.status_bar_alarm_clock,
ALARM_SLOT
)
+ context.orCreateTestableResources.addOverride(
+ com.android.internal.R.string.status_bar_managed_profile,
+ MANAGED_PROFILE_SLOT
+ )
+ whenever(devicePolicyManager.resources).thenReturn(devicePolicyManagerResources)
+ whenever(devicePolicyManagerResources.getString(anyString(), any())).thenReturn("")
statusBarPolicy = createStatusBarPolicy()
}
@@ -182,6 +193,32 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() {
}
@Test
+ fun testAppTransitionFinished_doesNotShowManagedProfileIcon() {
+ whenever(userManager.getUserStatusBarIconResId(anyInt())).thenReturn(0 /* ID_NULL */)
+ whenever(keyguardStateController.isShowing).thenReturn(false)
+ statusBarPolicy.appTransitionFinished(0)
+ // The above call posts to bgExecutor and then back to mainExecutor
+ executor.advanceClockToLast()
+ executor.runAllReady()
+ executor.advanceClockToLast()
+ executor.runAllReady()
+ verify(iconController, never()).setIconVisibility(MANAGED_PROFILE_SLOT, true)
+ }
+
+ @Test
+ fun testAppTransitionFinished_showsManagedProfileIcon() {
+ whenever(userManager.getUserStatusBarIconResId(anyInt())).thenReturn(100)
+ whenever(keyguardStateController.isShowing).thenReturn(false)
+ statusBarPolicy.appTransitionFinished(0)
+ // The above call posts to bgExecutor and then back to mainExecutor
+ executor.advanceClockToLast()
+ executor.runAllReady()
+ executor.advanceClockToLast()
+ executor.runAllReady()
+ verify(iconController).setIconVisibility(MANAGED_PROFILE_SLOT, true)
+ }
+
+ @Test
fun connectedDisplay_connected_iconShown() =
testScope.runTest {
statusBarPolicy.init()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 9c7f6190de44..33144f233a71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -64,7 +64,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.classifier.FalsingCollectorFake;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
@@ -117,6 +117,7 @@ import java.util.Optional;
public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
private static final int DISPLAY_ID = 0;
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Mock
private AssistManager mAssistManager;
@@ -256,7 +257,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
notificationAnimationProvider,
mock(LaunchFullScreenIntentProvider.class),
mPowerInteractor,
- mock(FeatureFlags.class),
+ mFeatureFlags,
mUserTracker
);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index cd8aaa2685c2..9c52788dc2eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -79,7 +79,6 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
private CommandQueue mCommandQueue;
private FakeMetricsLogger mMetricsLogger;
private final ShadeController mShadeController = mock(ShadeController.class);
- private final CentralSurfaces mCentralSurfaces = mock(CentralSurfaces.class);
private final NotificationsInteractor mNotificationsInteractor =
mock(NotificationsInteractor.class);
private final KeyguardStateController mKeyguardStateController =
@@ -118,7 +117,6 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mock(NotificationShadeWindowController.class),
mock(DynamicPrivacyController.class),
mKeyguardStateController,
- mCentralSurfaces,
mNotificationsInteractor,
mock(LockscreenShadeTransitionController.class),
mock(PowerInteractor.class),
@@ -202,7 +200,6 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isOccluded()).thenReturn(false);
- when(mCentralSurfaces.isOccluded()).thenReturn(false);
assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 391c8ca4d286..7c285b8aa1a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -102,6 +102,8 @@ public class RemoteInputViewTest extends SysuiTestCase {
"com.android.sysuitest.dummynotificationsender";
private static final int DUMMY_MESSAGE_APP_ID = Process.LAST_APPLICATION_UID - 1;
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
+
@Mock private RemoteInputController mController;
@Mock private ShortcutManager mShortcutManager;
@Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
@@ -453,8 +455,7 @@ public class RemoteInputViewTest extends SysuiTestCase {
private RemoteInputViewController bindController(
RemoteInputView view,
NotificationEntry entry) {
- FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags();
- fakeFeatureFlags.set(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION, true);
+ mFeatureFlags.set(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION, true);
RemoteInputViewControllerImpl viewController = new RemoteInputViewControllerImpl(
view,
entry,
@@ -462,7 +463,7 @@ public class RemoteInputViewTest extends SysuiTestCase {
mController,
mShortcutManager,
mUiEventLoggerFake,
- fakeFeatureFlags
+ mFeatureFlags
);
viewController.bind();
return viewController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 4839eeba2124..17bb73b94ac2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -92,7 +92,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -305,6 +305,7 @@ public class BubblesTest extends SysuiTestCase {
private TestableLooper mTestableLooper;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
private UserHandle mUser0;
@@ -423,7 +424,7 @@ public class BubblesTest extends SysuiTestCase {
mCommonNotifCollection,
mNotifPipeline,
mSysUiState,
- mock(FeatureFlags.class),
+ mFeatureFlags,
mNotifPipelineFlags,
syncExecutor);
mBubblesManager.addNotifCallback(mNotifCallback);
@@ -432,7 +433,8 @@ public class BubblesTest extends SysuiTestCase {
mNotificationTestHelper = new NotificationTestHelper(
mContext,
mDependency,
- TestableLooper.get(this));
+ TestableLooper.get(this),
+ mFeatureFlags);
mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
mNonBubbleNotifRow = mNotificationTestHelper.createRow();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
index b94f816e1ca4..36fa7e65d8ec 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -62,6 +62,32 @@ class FakeFeatureFlags : FeatureFlags {
}
}
+ /**
+ * Set the given flag's default value if no other value has been set.
+ *
+ * REMINDER: You should always test your code with your flag in both configurations, so
+ * generally you should be setting a particular value. This method should be reserved for
+ * situations where the flag needs to be read (e.g. in the class constructor), but its
+ * value shouldn't affect the actual test cases. In those cases, it's mildly safer to use
+ * this method than to hard-code `false` or `true` because then at least if you're wrong,
+ * and the flag value *does* matter, you'll notice when the flag is flipped and tests
+ * start failing.
+ */
+ fun setDefault(flag: BooleanFlag) = booleanFlags.putIfAbsent(flag.id, flag.default)
+
+ /**
+ * Set the given flag's default value if no other value has been set.
+ *
+ * REMINDER: You should always test your code with your flag in both configurations, so
+ * generally you should be setting a particular value. This method should be reserved for
+ * situations where the flag needs to be read (e.g. in the class constructor), but its
+ * value shouldn't affect the actual test cases. In those cases, it's mildly safer to use
+ * this method than to hard-code `false` or `true` because then at least if you're wrong,
+ * and the flag value *does* matter, you'll notice when the flag is flipped and tests
+ * start failing.
+ */
+ fun setDefault(flag: SysPropBooleanFlag) = booleanFlags.putIfAbsent(flag.id, flag.default)
+
private fun notifyFlagChanged(flag: Flag<*>) {
flagListeners[flag.id]?.let { listeners ->
listeners.forEach { listener ->
diff --git a/packages/SystemUI/unfold/Android.bp b/packages/SystemUI/unfold/Android.bp
index 2e0a9462ffbe..1f0181f2e5bd 100644
--- a/packages/SystemUI/unfold/Android.bp
+++ b/packages/SystemUI/unfold/Android.bp
@@ -33,7 +33,7 @@ android_library {
"dagger2",
"jsr330",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
java_version: "1.8",
sdk_version: "current",
min_sdk_version: "current",
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
index fee485d97afa..896444d4df5a 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
@@ -35,14 +35,12 @@ interface UnfoldTransitionProgressProvider : CallbackController<TransitionProgre
interface TransitionProgressListener {
/** Called when transition is started */
- @JvmDefault
fun onTransitionStarted() {}
/**
* Called whenever transition progress is updated, [progress] is a value of the animation
* where 0 is fully folded, 1 is fully unfolded
*/
- @JvmDefault
fun onTransitionProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float) {}
/**
@@ -51,11 +49,9 @@ interface UnfoldTransitionProgressProvider : CallbackController<TransitionProgre
* For example, in [PhysicsBasedUnfoldTransitionProgressProvider] this could happen when the
* animation is not tied to the hinge angle anymore and it is about to run fixed animation.
*/
- @JvmDefault
fun onTransitionFinishing() {}
/** Called when transition is completely finished */
- @JvmDefault
fun onTransitionFinished() {}
}
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
index 0af372f9da24..bce7e8849903 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
@@ -31,9 +31,9 @@ interface FoldStateProvider : CallbackController<FoldUpdatesListener> {
val isFinishedOpening: Boolean
interface FoldUpdatesListener {
- @JvmDefault fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float) {}
- @JvmDefault fun onFoldUpdate(@FoldUpdate update: Int) {}
- @JvmDefault fun onUnfoldedScreenAvailable() {}
+ fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float) {}
+ fun onFoldUpdate(@FoldUpdate update: Int) {}
+ fun onUnfoldedScreenAvailable() {}
}
@IntDef(
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 1482d078fa8c..3f3fa3419117 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -246,6 +246,31 @@ public class FullScreenMagnificationController implements
}
@GuardedBy("mLock")
+ boolean isAtEdge() {
+ return isAtLeftEdge() || isAtRightEdge() || isAtTopEdge() || isAtBottomEdge();
+ }
+
+ @GuardedBy("mLock")
+ boolean isAtLeftEdge() {
+ return getOffsetX() == getMaxOffsetXLocked();
+ }
+
+ @GuardedBy("mLock")
+ boolean isAtRightEdge() {
+ return getOffsetX() == getMinOffsetXLocked();
+ }
+
+ @GuardedBy("mLock")
+ boolean isAtTopEdge() {
+ return getOffsetY() == getMaxOffsetYLocked();
+ }
+
+ @GuardedBy("mLock")
+ boolean isAtBottomEdge() {
+ return getOffsetY() == getMinOffsetYLocked();
+ }
+
+ @GuardedBy("mLock")
float getCenterX() {
return (mMagnificationBounds.width() / 2.0f
+ mMagnificationBounds.left - getOffsetX()) / getScale();
@@ -1086,6 +1111,87 @@ public class FullScreenMagnificationController implements
}
/**
+ * Returns whether the user is at one of the edges (left, right, top, bottom)
+ * of the magnification viewport
+ *
+ * @param displayId
+ * @return if user is at the edge of the view
+ */
+ public boolean isAtEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtEdge();
+ }
+ }
+
+ /**
+ * Returns whether the user is at the left edge of the viewport
+ *
+ * @param displayId
+ * @return if user is at left edge of view
+ */
+ public boolean isAtLeftEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtLeftEdge();
+ }
+ }
+
+ /**
+ * Returns whether the user is at the right edge of the viewport
+ *
+ * @param displayId
+ * @return if user is at right edge of view
+ */
+ public boolean isAtRightEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtRightEdge();
+ }
+ }
+
+ /**
+ * Returns whether the user is at the top edge of the viewport
+ *
+ * @param displayId
+ * @return if user is at top edge of view
+ */
+ public boolean isAtTopEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtTopEdge();
+ }
+ }
+
+ /**
+ * Returns whether the user is at the bottom edge of the viewport
+ *
+ * @param displayId
+ * @return if user is at bottom edge of view
+ */
+ public boolean isAtBottomEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtBottomEdge();
+ }
+ }
+
+ /**
* Returns the Y offset of the magnification viewport. If an animation
* is in progress, this reflects the end state of the animation.
*
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 038847e2a759..4aebbf11c7af 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -137,6 +137,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
@VisibleForTesting final DetectingState mDetectingState;
@VisibleForTesting final PanningScalingState mPanningScalingState;
@VisibleForTesting final ViewportDraggingState mViewportDraggingState;
+ @VisibleForTesting final SinglePanningState mSinglePanningState;
private final ScreenStateReceiver mScreenStateReceiver;
private final WindowMagnificationPromptController mPromptController;
@@ -146,7 +147,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
private PointerCoords[] mTempPointerCoords;
private PointerProperties[] mTempPointerProperties;
-
+ @VisibleForTesting boolean mIsSinglePanningEnabled;
public FullScreenMagnificationGestureHandler(@UiContext Context context,
FullScreenMagnificationController fullScreenMagnificationController,
AccessibilityTraceManager trace,
@@ -202,6 +203,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
mDetectingState = new DetectingState(context);
mViewportDraggingState = new ViewportDraggingState();
mPanningScalingState = new PanningScalingState(context);
+ mSinglePanningState = new SinglePanningState(context);
+ setSinglePanningEnabled(false);
if (mDetectShortcutTrigger) {
mScreenStateReceiver = new ScreenStateReceiver(context, this);
@@ -213,6 +216,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
transitionTo(mDetectingState);
}
+ @VisibleForTesting
+ void setSinglePanningEnabled(boolean isEnabled) {
+ mIsSinglePanningEnabled = isEnabled;
+ }
+
@Override
void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
handleEventWith(mCurrentState, event, rawEvent, policyFlags);
@@ -223,6 +231,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
// To keep InputEventConsistencyVerifiers within GestureDetectors happy
mPanningScalingState.mScrollGestureDetector.onTouchEvent(event);
mPanningScalingState.mScaleGestureDetector.onTouchEvent(event);
+ mSinglePanningState.mScrollGestureDetector.onTouchEvent(event);
try {
stateHandler.onMotionEvent(event, rawEvent, policyFlags);
@@ -669,7 +678,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
-
// Ensures that the state at the end of delegation is consistent with the last delegated
// UP/DOWN event in queue: still delegating if pointer is down, detecting otherwise
switch (event.getActionMasked()) {
@@ -726,6 +734,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
@VisibleForTesting Handler mHandler = new Handler(Looper.getMainLooper(), this);
+ private PointF mFirstPointerDownLocation = new PointF(Float.NaN, Float.NaN);
+
DetectingState(Context context) {
mLongTapMinDelay = ViewConfiguration.getLongPressTimeout();
mMultiTapMaxDelay = ViewConfiguration.getDoubleTapTimeout()
@@ -765,10 +775,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
cacheDelayedMotionEvent(event, rawEvent, policyFlags);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
-
mLastDetectingDownEventTime = event.getDownTime();
mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+ mFirstPointerDownLocation.set(event.getX(), event.getY());
+
if (!mFullScreenMagnificationController.magnificationRegionContains(
mDisplayId, event.getX(), event.getY())) {
@@ -800,7 +811,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
break;
case ACTION_POINTER_DOWN: {
if (isActivated() && event.getPointerCount() == 2) {
- storeSecondPointerDownLocation(event);
+ storePointerDownLocation(mSecondPointerDownLocation, event);
mHandler.sendEmptyMessageDelayed(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
ViewConfiguration.getTapTimeout());
} else {
@@ -815,7 +826,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
case ACTION_MOVE: {
if (isFingerDown()
&& distance(mLastDown, /* move */ event) > mSwipeMinDistance) {
-
// Swipe detected - transition immediately
// For convenience, viewport dragging takes precedence
@@ -826,10 +836,15 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
} else if (isActivated() && event.getPointerCount() == 2) {
//Primary pointer is swiping, so transit to PanningScalingState
transitToPanningScalingStateAndClear();
+ } else if (mIsSinglePanningEnabled
+ && isActivated()
+ && event.getPointerCount() == 1
+ && !isOverscroll(event)) {
+ transitToSinglePanningStateAndClear();
} else {
transitionToDelegatingStateAndClear();
}
- } else if (isActivated() && secondPointerDownValid()
+ } else if (isActivated() && pointerDownValid(mSecondPointerDownLocation)
&& distanceClosestPointerToPoint(
mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance) {
//Second pointer is swiping, so transit to PanningScalingState
@@ -843,11 +858,9 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
if (!mFullScreenMagnificationController.magnificationRegionContains(
mDisplayId, event.getX(), event.getY())) {
-
transitionToDelegatingStateAndClear();
} else if (isMultiTapTriggered(3 /* taps */)) {
-
onTripleTap(/* up */ event);
} else if (
@@ -856,7 +869,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
//TODO long tap should never happen here
&& ((timeBetween(mLastDown, mLastUp) >= mLongTapMinDelay)
|| (distance(mLastDown, mLastUp) >= mSwipeMinDistance))) {
-
transitionToDelegatingStateAndClear();
}
@@ -865,14 +877,28 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
}
}
- private void storeSecondPointerDownLocation(MotionEvent event) {
+ private boolean isOverscroll(MotionEvent event) {
+ if (!pointerDownValid(mFirstPointerDownLocation)) {
+ return false;
+ }
+ float dX = event.getX() - mFirstPointerDownLocation.x;
+ float dY = event.getY() - mFirstPointerDownLocation.y;
+ boolean didOverscroll =
+ mFullScreenMagnificationController.isAtLeftEdge(mDisplayId) && dX > 0
+ || mFullScreenMagnificationController.isAtRightEdge(mDisplayId) && dX < 0
+ || mFullScreenMagnificationController.isAtTopEdge(mDisplayId) && dY > 0
+ || mFullScreenMagnificationController.isAtBottomEdge(mDisplayId) && dY < 0;
+ return didOverscroll;
+ }
+
+ private void storePointerDownLocation(PointF pointerDownLocation, MotionEvent event) {
final int index = event.getActionIndex();
- mSecondPointerDownLocation.set(event.getX(index), event.getY(index));
+ pointerDownLocation.set(event.getX(index), event.getY(index));
}
- private boolean secondPointerDownValid() {
- return !(Float.isNaN(mSecondPointerDownLocation.x) && Float.isNaN(
- mSecondPointerDownLocation.y));
+ private boolean pointerDownValid(PointF pointerDownLocation) {
+ return !(Float.isNaN(pointerDownLocation.x) && Float.isNaN(
+ pointerDownLocation.y));
}
private void transitToPanningScalingStateAndClear() {
@@ -880,6 +906,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
clear();
}
+ private void transitToSinglePanningStateAndClear() {
+ transitionTo(mSinglePanningState);
+ clear();
+ }
+
public boolean isMultiTapTriggered(int numTaps) {
// Shortcut acts as the 2 initial taps
@@ -947,6 +978,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
setShortcutTriggered(false);
removePendingDelayedMessages();
clearDelayedMotionEvents();
+ mFirstPointerDownLocation.set(Float.NaN, Float.NaN);
mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
}
@@ -1165,12 +1197,14 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
+ ", mDelegatingState=" + mDelegatingState
+ ", mMagnifiedInteractionState=" + mPanningScalingState
+ ", mViewportDraggingState=" + mViewportDraggingState
+ + ", mSinglePanningState=" + mSinglePanningState
+ ", mDetectTripleTap=" + mDetectTripleTap
+ ", mDetectShortcutTrigger=" + mDetectShortcutTrigger
+ ", mCurrentState=" + State.nameOf(mCurrentState)
+ ", mPreviousState=" + State.nameOf(mPreviousState)
+ ", mMagnificationController=" + mFullScreenMagnificationController
+ ", mDisplayId=" + mDisplayId
+ + ", mIsSinglePanningEnabled=" + mIsSinglePanningEnabled
+ '}';
}
@@ -1285,8 +1319,67 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
* Indicates an error with a gesture handler or state.
*/
private static class GestureException extends Exception {
+
GestureException(String message) {
super(message);
}
}
+
+ final class SinglePanningState extends SimpleOnGestureListener implements State {
+ private final GestureDetector mScrollGestureDetector;
+ private MotionEventInfo mEvent;
+
+ SinglePanningState(Context context) {
+ mScrollGestureDetector = new GestureDetector(context, this, Handler.getMain());
+ }
+
+ @Override
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ int action = event.getActionMasked();
+ switch (action) {
+ case ACTION_UP:
+ case ACTION_CANCEL:
+ clear();
+ transitionTo(mDetectingState);
+ break;
+ }
+ }
+
+ @Override
+ public boolean onScroll(
+ MotionEvent first, MotionEvent second, float distanceX, float distanceY) {
+ if (mCurrentState != mSinglePanningState) {
+ return true;
+ }
+ mFullScreenMagnificationController.offsetMagnifiedRegion(
+ mDisplayId,
+ distanceX,
+ distanceY,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ if (DEBUG_PANNING_SCALING) {
+ Slog.i(
+ mLogTag,
+ "SinglePanningState Panned content by scrollX: "
+ + distanceX
+ + " scrollY: "
+ + distanceY
+ + " isAtEdge: "
+ + mFullScreenMagnificationController.isAtEdge(mDisplayId));
+ }
+ // TODO: b/280812104 Dispatch events before Delegation
+ if (mFullScreenMagnificationController.isAtEdge(mDisplayId)) {
+ clear();
+ transitionTo(mDelegatingState);
+ }
+ return /* event consumed: */ true;
+ }
+
+ @Override
+ public String toString() {
+ return "SinglePanningState{"
+ + "isEdgeOfView="
+ + mFullScreenMagnificationController.isAtEdge(mDisplayId)
+ + "}";
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c9769b3f9932..26bcb9805baa 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5083,6 +5083,10 @@ public class ActivityManagerService extends IActivityManager.Stub
// Tell anyone interested that we are done booting!
SystemProperties.set("sys.boot_completed", "1");
SystemProperties.set("dev.bootcomplete", "1");
+
+ // Start PSI monitoring in LMKD if it was skipped earlier.
+ ProcessList.startPsiMonitoringAfterBoot();
+
mUserController.onBootComplete(
new IIntentReceiver.Stub() {
@Override
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index acd9771c7750..e484a6cf251b 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -357,6 +357,7 @@ public final class ProcessList {
static final byte LMK_UPDATE_PROPS = 7;
static final byte LMK_KILL_OCCURRED = 8; // Msg to subscribed clients on kill occurred event
static final byte LMK_STATE_CHANGED = 9; // Msg to subscribed clients on state changed
+ static final byte LMK_START_MONITORING = 9; // Start monitoring if delayed earlier
// Low Memory Killer Daemon command codes.
// These must be kept in sync with async_event_type definitions in lmkd.h
@@ -1568,6 +1569,15 @@ public final class ProcessList {
return true;
}
+ /**
+ * {@hide}
+ */
+ public static void startPsiMonitoringAfterBoot() {
+ ByteBuffer buf = ByteBuffer.allocate(4);
+ buf.putInt(LMK_START_MONITORING);
+ writeLmkd(buf, null);
+ }
+
private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) {
if (!sLmkdConnection.isConnected()) {
// try to connect immediately and then keep retrying
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2db724f51918..8e1ad6529bc0 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2491,16 +2491,6 @@ public class NotificationManagerService extends SystemService {
getContext().registerReceiver(mReviewNotificationPermissionsReceiver,
ReviewNotificationPermissionsReceiver.getFilter(),
Context.RECEIVER_NOT_EXPORTED);
-
- mAppOps.startWatchingMode(AppOpsManager.OP_POST_NOTIFICATION, null,
- new AppOpsManager.OnOpChangedInternalListener() {
- @Override
- public void onOpChanged(@NonNull String op, @NonNull String packageName,
- int userId) {
- mHandler.post(
- () -> handleNotificationPermissionChange(packageName, userId));
- }
- });
}
/**
@@ -3560,16 +3550,20 @@ public class NotificationManagerService extends SystemService {
}
mPermissionHelper.setNotificationPermission(
pkg, UserHandle.getUserId(uid), enabled, true);
+ sendAppBlockStateChangedBroadcast(pkg, uid, !enabled);
mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_BAN_APP_NOTES)
.setType(MetricsEvent.TYPE_ACTION)
.setPackageName(pkg)
.setSubtype(enabled ? 1 : 0));
mNotificationChannelLogger.logAppNotificationsAllowed(uid, pkg, enabled);
+ // Now, cancel any outstanding notifications that are part of a just-disabled app
+ if (!enabled) {
+ cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0,
+ UserHandle.getUserId(uid), REASON_PACKAGE_BANNED);
+ }
- // Outstanding notifications from this package will be cancelled, and the package will
- // be sent the ACTION_APP_BLOCK_STATE_CHANGED broadcast, as soon as we get the
- // callback from AppOpsManager.
+ handleSavePolicyFile();
}
/**
@@ -5889,21 +5883,6 @@ public class NotificationManagerService extends SystemService {
}
};
- private void handleNotificationPermissionChange(String pkg, @UserIdInt int userId) {
- int uid = mPackageManagerInternal.getPackageUid(pkg, 0, userId);
- if (uid == INVALID_UID) {
- Log.e(TAG, String.format("No uid found for %s, %s!", pkg, userId));
- return;
- }
- boolean hasPermission = mPermissionHelper.hasPermission(uid);
- sendAppBlockStateChangedBroadcast(pkg, uid, !hasPermission);
- if (!hasPermission) {
- cancelAllNotificationsInt(MY_UID, MY_PID, pkg, /* channelId= */ null,
- /* mustHaveFlags= */ 0, /* mustNotHaveFlags= */ 0, userId,
- REASON_PACKAGE_BANNED);
- }
- }
-
protected void checkNotificationListenerAccess() {
if (!isCallerSystemOrPhone()) {
getContext().enforceCallingPermission(
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 36a0b0c0d8e9..1f5bd3e0cc60 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -75,6 +75,7 @@ import android.util.StatsEvent;
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import com.android.internal.logging.MetricsLogger;
@@ -108,30 +109,34 @@ public class ZenModeHelper {
static final int RULE_LIMIT_PER_PACKAGE = 100;
// pkg|userId => uid
- protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
+ @VisibleForTesting protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
private final Context mContext;
private final H mHandler;
private final SettingsObserver mSettingsObserver;
private final AppOpsManager mAppOps;
- @VisibleForTesting protected final NotificationManager mNotificationManager;
+ private final NotificationManager mNotificationManager;
private final SysUiStatsEvent.BuilderFactory mStatsEventBuilderFactory;
- @VisibleForTesting protected ZenModeConfig mDefaultConfig;
+ private ZenModeConfig mDefaultConfig;
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final ZenModeFiltering mFiltering;
- protected final RingerModeDelegate mRingerModeDelegate = new
+ private final RingerModeDelegate mRingerModeDelegate = new
RingerModeDelegate();
@VisibleForTesting protected final ZenModeConditions mConditions;
- Object mConfigsLock = new Object();
+ private final Object mConfigsArrayLock = new Object();
+ @GuardedBy("mConfigsArrayLock")
@VisibleForTesting final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>();
private final Metrics mMetrics = new Metrics();
private final ConditionProviders.Config mServiceConfig;
- private SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
- @VisibleForTesting protected ZenModeEventLogger mZenModeEventLogger;
+ private final SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
+ private final ZenModeEventLogger mZenModeEventLogger;
@VisibleForTesting protected int mZenMode;
@VisibleForTesting protected NotificationManager.Policy mConsolidatedPolicy;
private int mUser = UserHandle.USER_SYSTEM;
+
+ private final Object mConfigLock = new Object();
+ @GuardedBy("mConfigLock")
@VisibleForTesting protected ZenModeConfig mConfig;
@VisibleForTesting protected AudioManagerInternal mAudioManager;
protected PackageManager mPm;
@@ -159,7 +164,7 @@ public class ZenModeHelper {
mDefaultConfig = readDefaultConfig(mContext.getResources());
updateDefaultAutomaticRuleNames();
mConfig = mDefaultConfig.copy();
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.put(UserHandle.USER_SYSTEM, mConfig);
}
mConsolidatedPolicy = mConfig.toNotificationPolicy();
@@ -186,7 +191,7 @@ public class ZenModeHelper {
public boolean matchesCallFilter(UserHandle userHandle, Bundle extras,
ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity,
int callingUid) {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConsolidatedPolicy,
userHandle, extras, validator, contactsTimeoutMs, timeoutAffinity,
callingUid);
@@ -206,7 +211,7 @@ public class ZenModeHelper {
}
public boolean shouldIntercept(NotificationRecord record) {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
return mFiltering.shouldIntercept(mZenMode, mConsolidatedPolicy, record);
}
}
@@ -221,7 +226,7 @@ public class ZenModeHelper {
public void initZenMode() {
if (DEBUG) Log.d(TAG, "initZenMode");
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
// "update" config to itself, which will have no effect in the case where a config
// was read in via XML, but will initialize zen mode if nothing was read in and the
// config remains the default.
@@ -250,7 +255,7 @@ public class ZenModeHelper {
public void onUserRemoved(int user) {
if (user < UserHandle.USER_SYSTEM) return;
if (DEBUG) Log.d(TAG, "onUserRemoved u=" + user);
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.remove(user);
}
}
@@ -268,7 +273,7 @@ public class ZenModeHelper {
mUser = user;
if (DEBUG) Log.d(TAG, reason + " u=" + user);
ZenModeConfig config = null;
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
if (mConfigs.get(user) != null) {
config = mConfigs.get(user).copy();
}
@@ -278,7 +283,7 @@ public class ZenModeHelper {
config = mDefaultConfig.copy();
config.user = user;
}
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
setConfigLocked(config, null, reason, Process.SYSTEM_UID, true);
}
cleanUpZenRules();
@@ -314,7 +319,7 @@ public class ZenModeHelper {
public List<ZenRule> getZenRules() {
List<ZenRule> rules = new ArrayList<>();
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return rules;
for (ZenRule rule : mConfig.automaticRules.values()) {
if (canManageAutomaticZenRule(rule)) {
@@ -327,7 +332,7 @@ public class ZenModeHelper {
public AutomaticZenRule getAutomaticZenRule(String id) {
ZenRule rule;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return null;
rule = mConfig.automaticRules.get(id);
}
@@ -364,7 +369,7 @@ public class ZenModeHelper {
}
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) {
throw new AndroidRuntimeException("Could not create rule");
}
@@ -387,7 +392,7 @@ public class ZenModeHelper {
public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZenRule,
String reason, int callingUid, boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return false;
if (DEBUG) {
Log.d(TAG, "updateAutomaticZenRule zenRule=" + automaticZenRule
@@ -419,7 +424,7 @@ public class ZenModeHelper {
public boolean removeAutomaticZenRule(String id, String reason, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return false;
newConfig = mConfig.copy();
ZenRule ruleToRemove = newConfig.automaticRules.get(id);
@@ -450,7 +455,7 @@ public class ZenModeHelper {
public boolean removeAutomaticZenRules(String packageName, String reason, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return false;
newConfig = mConfig.copy();
for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
@@ -467,7 +472,7 @@ public class ZenModeHelper {
public void setAutomaticZenRuleState(String id, Condition condition, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return;
newConfig = mConfig.copy();
@@ -481,7 +486,7 @@ public class ZenModeHelper {
public void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return;
newConfig = mConfig.copy();
@@ -491,6 +496,7 @@ public class ZenModeHelper {
}
}
+ @GuardedBy("mConfigLock")
private void setAutomaticZenRuleStateLocked(ZenModeConfig config, List<ZenRule> rules,
Condition condition, int callingUid, boolean fromSystemOrSystemUi) {
if (rules == null || rules.isEmpty()) return;
@@ -538,7 +544,7 @@ public class ZenModeHelper {
return 0;
}
int count = 0;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
for (ZenRule rule : mConfig.automaticRules.values()) {
if (cn.equals(rule.component) || cn.equals(rule.configurationActivity)) {
count++;
@@ -555,7 +561,7 @@ public class ZenModeHelper {
return 0;
}
int count = 0;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
for (ZenRule rule : mConfig.automaticRules.values()) {
if (pkg.equals(rule.getPkg())) {
count++;
@@ -588,19 +594,23 @@ public class ZenModeHelper {
protected void updateDefaultZenRules(int callingUid, boolean fromSystemOrSystemUi) {
updateDefaultAutomaticRuleNames();
- for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
- ZenRule currRule = mConfig.automaticRules.get(defaultRule.id);
- // if default rule wasn't user-modified nor enabled, use localized name
- // instead of previous system name
- if (currRule != null && !currRule.modified && !currRule.enabled
- && !defaultRule.name.equals(currRule.name)) {
- if (canManageAutomaticZenRule(currRule)) {
- if (DEBUG) Slog.d(TAG, "Locale change - updating default zen rule name "
- + "from " + currRule.name + " to " + defaultRule.name);
- // update default rule (if locale changed, name of rule will change)
- currRule.name = defaultRule.name;
- updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule),
- "locale changed", callingUid, fromSystemOrSystemUi);
+ synchronized (mConfigLock) {
+ for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
+ ZenRule currRule = mConfig.automaticRules.get(defaultRule.id);
+ // if default rule wasn't user-modified nor enabled, use localized name
+ // instead of previous system name
+ if (currRule != null && !currRule.modified && !currRule.enabled
+ && !defaultRule.name.equals(currRule.name)) {
+ if (canManageAutomaticZenRule(currRule)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Locale change - updating default zen rule name "
+ + "from " + currRule.name + " to " + defaultRule.name);
+ }
+ // update default rule (if locale changed, name of rule will change)
+ currRule.name = defaultRule.name;
+ updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule),
+ "locale changed", callingUid, fromSystemOrSystemUi);
+ }
}
}
}
@@ -686,7 +696,7 @@ public class ZenModeHelper {
private void setManualZenMode(int zenMode, Uri conditionId, String reason, String caller,
boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return;
if (!Global.isValidZenMode(zenMode)) return;
if (DEBUG) Log.d(TAG, "setManualZenMode " + Global.zenModeToString(zenMode)
@@ -715,7 +725,7 @@ public class ZenModeHelper {
void dump(ProtoOutputStream proto) {
proto.write(ZenModeProto.ZEN_MODE, mZenMode);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig.manualRule != null) {
mConfig.manualRule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS);
}
@@ -737,14 +747,14 @@ public class ZenModeHelper {
pw.println(Global.zenModeToString(mZenMode));
pw.print(prefix);
pw.println("mConsolidatedPolicy=" + mConsolidatedPolicy.toString());
- synchronized(mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
final int N = mConfigs.size();
for (int i = 0; i < N; i++) {
dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i));
}
}
pw.print(prefix); pw.print("mUser="); pw.println(mUser);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
dump(pw, prefix, "mConfig", mConfig);
}
@@ -833,7 +843,7 @@ public class ZenModeHelper {
Settings.Secure.ZEN_SETTINGS_UPDATED, 1, userId);
}
if (DEBUG) Log.d(TAG, reason);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
setConfigLocked(config, null, reason, Process.SYSTEM_UID, true);
}
}
@@ -841,7 +851,7 @@ public class ZenModeHelper {
public void writeXml(TypedXmlSerializer out, boolean forBackup, Integer version, int userId)
throws IOException {
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
final int n = mConfigs.size();
for (int i = 0; i < n; i++) {
if (forBackup && mConfigs.keyAt(i) != userId) {
@@ -856,7 +866,9 @@ public class ZenModeHelper {
* @return user-specified default notification policy for priority only do not disturb
*/
public Policy getNotificationPolicy() {
- return getNotificationPolicy(mConfig);
+ synchronized (mConfigLock) {
+ return getNotificationPolicy(mConfig);
+ }
}
private static Policy getNotificationPolicy(ZenModeConfig config) {
@@ -867,8 +879,8 @@ public class ZenModeHelper {
* Sets the global notification policy used for priority only do not disturb
*/
public void setNotificationPolicy(Policy policy, int callingUid, boolean fromSystemOrSystemUi) {
- if (policy == null || mConfig == null) return;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
+ if (policy == null || mConfig == null) return;
final ZenModeConfig newConfig = mConfig.copy();
newConfig.applyNotificationPolicy(policy);
setConfigLocked(newConfig, null, "setNotificationPolicy", callingUid,
@@ -881,7 +893,7 @@ public class ZenModeHelper {
*/
private void cleanUpZenRules() {
long currentTime = System.currentTimeMillis();
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
final ZenModeConfig newConfig = mConfig.copy();
if (newConfig.automaticRules != null) {
for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
@@ -906,7 +918,7 @@ public class ZenModeHelper {
* @return a copy of the zen mode configuration
*/
public ZenModeConfig getConfig() {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
return mConfig.copy();
}
}
@@ -918,7 +930,8 @@ public class ZenModeHelper {
return mConsolidatedPolicy.copy();
}
- public boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
+ @GuardedBy("mConfigLock")
+ private boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
String reason, int callingUid, boolean fromSystemOrSystemUi) {
return setConfigLocked(config, reason, triggeringComponent, true /*setRingerMode*/,
callingUid, fromSystemOrSystemUi);
@@ -926,11 +939,12 @@ public class ZenModeHelper {
public void setConfig(ZenModeConfig config, ComponentName triggeringComponent, String reason,
int callingUid, boolean fromSystemOrSystemUi) {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
setConfigLocked(config, triggeringComponent, reason, callingUid, fromSystemOrSystemUi);
}
}
+ @GuardedBy("mConfigLock")
private boolean setConfigLocked(ZenModeConfig config, String reason,
ComponentName triggeringComponent, boolean setRingerMode, int callingUid,
boolean fromSystemOrSystemUi) {
@@ -942,7 +956,7 @@ public class ZenModeHelper {
}
if (config.user != mUser) {
// simply store away for background users
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.put(config.user, config);
}
if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
@@ -951,7 +965,7 @@ public class ZenModeHelper {
// handle CPS backed conditions - danger! may modify config
mConditions.evaluateConfig(config, null, false /*processSubscriptions*/);
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.put(config.user, config);
}
if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
@@ -979,6 +993,7 @@ public class ZenModeHelper {
* Carries out a config update (if needed) and (re-)evaluates the zen mode value afterwards.
* If logging is enabled, will also request logging of the outcome of this change if needed.
*/
+ @GuardedBy("mConfigLock")
private void updateConfigAndZenModeLocked(ZenModeConfig config, String reason,
boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) {
final boolean logZenModeEvents = mFlagResolver.isEnabled(
@@ -993,7 +1008,7 @@ public class ZenModeHelper {
}
final String val = Integer.toString(config.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
- evaluateZenMode(reason, setRingerMode);
+ evaluateZenModeLocked(reason, setRingerMode);
// After all changes have occurred, log if requested
if (logZenModeEvents) {
ZenModeEventLogger.ZenModeInfo newInfo = new ZenModeEventLogger.ZenModeInfo(
@@ -1025,7 +1040,8 @@ public class ZenModeHelper {
}
@VisibleForTesting
- protected void evaluateZenMode(String reason, boolean setRingerMode) {
+ @GuardedBy("mConfigLock")
+ protected void evaluateZenModeLocked(String reason, boolean setRingerMode) {
if (DEBUG) Log.d(TAG, "evaluateZenMode");
if (mConfig == null) return;
final int policyHashBefore = mConsolidatedPolicy == null ? 0
@@ -1056,8 +1072,8 @@ public class ZenModeHelper {
}
private int computeZenMode() {
- if (mConfig == null) return Global.ZEN_MODE_OFF;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
+ if (mConfig == null) return Global.ZEN_MODE_OFF;
if (mConfig.manualRule != null) return mConfig.manualRule.zenMode;
int zen = Global.ZEN_MODE_OFF;
for (ZenRule automaticRule : mConfig.automaticRules.values()) {
@@ -1094,8 +1110,8 @@ public class ZenModeHelper {
}
private void updateConsolidatedPolicy(String reason) {
- if (mConfig == null) return;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
+ if (mConfig == null) return;
ZenPolicy policy = new ZenPolicy();
if (mConfig.manualRule != null) {
applyCustomPolicy(policy, mConfig.manualRule);
@@ -1293,7 +1309,7 @@ public class ZenModeHelper {
* Generate pulled atoms about do not disturb configurations.
*/
public void pullRules(List<StatsEvent> events) {
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
final int numConfigs = mConfigs.size();
for (int i = 0; i < numConfigs; i++) {
final int user = mConfigs.keyAt(i);
@@ -1319,6 +1335,7 @@ public class ZenModeHelper {
}
}
+ @GuardedBy("mConfigsArrayLock")
private void ruleToProtoLocked(int user, ZenRule rule, boolean isManualRule,
List<StatsEvent> events) {
// Make the ID safe.
@@ -1389,7 +1406,7 @@ public class ZenModeHelper {
if (mZenMode == Global.ZEN_MODE_OFF
|| (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- && !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(mConfig))) {
+ && !areAllPriorityOnlyRingerSoundsMuted())) {
// in priority only with ringer not muted, save ringer mode changes
// in dnd off, save ringer mode changes
setPreviousRingerModeSetting(ringerModeNew);
@@ -1410,8 +1427,7 @@ public class ZenModeHelper {
&& (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
|| mZenMode == Global.ZEN_MODE_ALARMS
|| (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- && ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(
- mConfig)))) {
+ && areAllPriorityOnlyRingerSoundsMuted()))) {
newZen = Global.ZEN_MODE_OFF;
} else if (mZenMode != Global.ZEN_MODE_OFF) {
ringerModeExternalOut = AudioManager.RINGER_MODE_SILENT;
@@ -1430,6 +1446,12 @@ public class ZenModeHelper {
return ringerModeExternalOut;
}
+ private boolean areAllPriorityOnlyRingerSoundsMuted() {
+ synchronized (mConfigLock) {
+ return ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(mConfig);
+ }
+ }
+
@Override
public int onSetRingerModeExternal(int ringerModeOld, int ringerModeNew, String caller,
int ringerModeInternal, VolumePolicy policy) {
@@ -1633,7 +1655,7 @@ public class ZenModeHelper {
private void emitRules() {
final long now = SystemClock.elapsedRealtime();
final long since = (now - mRuleCountLogTime);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
int numZenRules = mConfig.automaticRules.size();
if (mNumZenRules != numZenRules
|| since > MINIMUM_LOG_PERIOD_MS) {
@@ -1651,7 +1673,7 @@ public class ZenModeHelper {
private void emitDndType() {
final long now = SystemClock.elapsedRealtime();
final long since = (now - mTypeLogTimeMs);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
boolean dndOn = mZenMode != Global.ZEN_MODE_OFF;
int zenType = !dndOn ? DND_OFF
: (mConfig.manualRule != null) ? DND_ON_MANUAL : DND_ON_AUTOMATIC;
diff --git a/services/core/java/com/android/server/pm/ArchiveManager.java b/services/core/java/com/android/server/pm/ArchiveManager.java
index 99479f088577..54352060cd38 100644
--- a/services/core/java/com/android/server/pm/ArchiveManager.java
+++ b/services/core/java/com/android/server/pm/ArchiveManager.java
@@ -77,9 +77,8 @@ final class ArchiveManager {
snapshot.enforceCrossUserPermission(callingUid, userId, true, true,
"archiveApp");
verifyCaller(callerPackageName, callingPackageName);
-
PackageStateInternal ps = getPackageState(packageName, snapshot, callingUid, user);
- verifyInstallOwnership(packageName, callingPackageName, ps.getInstallSource());
+ verifyInstaller(packageName, ps.getInstallSource());
List<LauncherActivityInfo> mainActivities = getLauncherApps().getActivityList(
ps.getPackageName(),
@@ -125,7 +124,7 @@ final class ArchiveManager {
Path.of("/TODO"), null);
activityInfos.add(activityInfo);
}
- // TODO(b/278553670) Adapt installer check verifyInstallOwnership and check for null there
+
InstallSource installSource = ps.getInstallSource();
String installerPackageName = installSource.mUpdateOwnerPackageName != null
? installSource.mUpdateOwnerPackageName : installSource.mInstallerPackageName;
@@ -159,19 +158,13 @@ final class ArchiveManager {
}
}
- private static void verifyInstallOwnership(String packageName, String callingPackageName,
- InstallSource installSource) {
- if (!TextUtils.equals(installSource.mInstallerPackageName,
- callingPackageName)) {
+ private static void verifyInstaller(String packageName, InstallSource installSource) {
+ // TODO(b/291060290) Verify installer supports unarchiving
+ if (installSource.mUpdateOwnerPackageName == null
+ && installSource.mInstallerPackageName == null) {
throw new SecurityException(
- TextUtils.formatSimple("Caller is not the installer of record for %s.",
+ TextUtils.formatSimple("No installer found to archive app %s.",
packageName));
}
- String updateOwnerPackageName = installSource.mUpdateOwnerPackageName;
- if (updateOwnerPackageName != null
- && !TextUtils.equals(updateOwnerPackageName, callingPackageName)) {
- throw new SecurityException(
- TextUtils.formatSimple("Caller is not the update owner for %s.", packageName));
- }
}
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index ead26cc0cf05..134b041cb242 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -4311,7 +4311,8 @@ final class InstallPackageHelper {
// - It's an APEX or overlay package since stopped state does not affect them.
// - It is enumerated with a <initial-package-state> tag having the stopped attribute
// set to false
- // - It doesn't have a launcher entry which means the user doesn't have a way to unstop it
+ // - It doesn't have an enabled and exported launcher activity, which means the user
+ // wouldn't have a way to un-stop it
final boolean isApexPkg = (scanFlags & SCAN_AS_APEX) != 0;
if (mPm.mShouldStopSystemPackagesByDefault
&& scanSystemPartition
@@ -4337,7 +4338,11 @@ final class InstallPackageHelper {
categories.add(Intent.CATEGORY_LAUNCHER);
final List<ParsedActivity> activities = parsedPackage.getActivities();
for (int indexActivity = 0; indexActivity < activities.size(); indexActivity++) {
- final List<ParsedIntentInfo> intents = activities.get(indexActivity).getIntents();
+ final ParsedActivity activity = activities.get(indexActivity);
+ if (!activity.isEnabled() || !activity.isExported()) {
+ continue;
+ }
+ final List<ParsedIntentInfo> intents = activity.getIntents();
for (int indexIntent = 0; indexIntent < intents.size(); indexIntent++) {
final IntentFilter intentFilter = intents.get(indexIntent).getIntentFilter();
if (intentFilter != null && intentFilter.matchCategories(categories) == null) {
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 3c846da9757f..5e23765df346 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -18,7 +18,12 @@
"name": "CtsCompilationTestCases"
},
{
- "name": "CtsAppEnumerationTestCases"
+ "name": "CtsAppEnumerationTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
},
{
"name": "CtsMatchFlagTestCases"
@@ -129,6 +134,9 @@
},
{
"name": "PackageManagerServiceHostTests"
+ },
+ {
+ "name": "CtsAppEnumerationTestCases"
}
],
"imports": [
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7bbe8781e434..5e01c4939d24 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -208,7 +208,6 @@ import com.android.internal.policy.LogDecelerateInterpolator;
import com.android.internal.policy.PhoneWindow;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.AccessibilityManagerInternal;
@@ -228,6 +227,7 @@ import com.android.server.policy.keyguard.KeyguardServiceDelegate;
import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener;
import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.vibrator.HapticFeedbackVibrationProvider;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -371,12 +371,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private static final String TALKBACK_LABEL = "TalkBack";
private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800;
- private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
- VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
- private static final VibrationAttributes PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES =
- VibrationAttributes.createForUsage(VibrationAttributes.USAGE_PHYSICAL_EMULATION);
- private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
- VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
/**
* Keyguard stuff
@@ -450,6 +444,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
PackageManager mPackageManager;
SideFpsEventHandler mSideFpsEventHandler;
LockPatternUtils mLockPatternUtils;
+ private HapticFeedbackVibrationProvider mHapticFeedbackVibrationProvider;
private boolean mHasFeatureAuto;
private boolean mHasFeatureWatch;
private boolean mHasFeatureLeanback;
@@ -458,9 +453,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// Assigned on main thread, accessed on UI thread
volatile VrManagerInternal mVrManagerInternal;
- // Vibrator pattern for haptic feedback during boot when safe mode is enabled.
- long[] mSafeModeEnabledVibePattern;
-
/** If true, hitting shift & menu will broadcast Intent.ACTION_BUG_REPORT */
boolean mEnableShiftMenuBugReports = false;
@@ -558,7 +550,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int mPowerVolUpBehavior;
boolean mStylusButtonsEnabled = true;
boolean mHasSoftInput = false;
- boolean mHapticTextHandleEnabled;
boolean mUseTvRouting;
boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
MetricsLogger mLogger;
@@ -2251,9 +2242,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup);
- mHapticTextHandleEnabled = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enableHapticTextHandle);
-
mUseTvRouting = AudioSystem.getPlatformType(mContext) == AudioSystem.PLATFORM_TELEVISION;
mHandleVolumeKeysInWM = mContext.getResources().getBoolean(
@@ -2294,8 +2282,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mContext.registerReceiver(mMultiuserReceiver, filter);
mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
- mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(),
- com.android.internal.R.array.config_safeModeEnabledVibePattern);
+ mHapticFeedbackVibrationProvider =
+ new HapticFeedbackVibrationProvider(mContext.getResources(), mVibrator);
mGlobalKeyManager = new GlobalKeyManager(mContext);
@@ -5499,10 +5487,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- static long[] getLongIntArray(Resources r, int resid) {
- return ArrayUtils.convertToLongArray(r.getIntArray(resid));
- }
-
private void bindKeyguard() {
synchronized (mLock) {
if (mKeyguardBound) {
@@ -5989,138 +5973,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (!mVibrator.hasVibrator()) {
return false;
}
- VibrationEffect effect = getVibrationEffect(effectId);
+ VibrationEffect effect =
+ mHapticFeedbackVibrationProvider.getVibrationForHapticFeedback(effectId);
if (effect == null) {
return false;
}
- VibrationAttributes attrs = getVibrationAttributes(effectId);
- if (always) {
- attrs = new VibrationAttributes.Builder(attrs)
- .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
- .build();
- }
+ VibrationAttributes attrs =
+ mHapticFeedbackVibrationProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ always);
mVibrator.vibrate(uid, packageName, effect, reason, attrs);
return true;
}
- private VibrationEffect getVibrationEffect(int effectId) {
- long[] pattern;
- switch (effectId) {
- case HapticFeedbackConstants.CONTEXT_CLICK:
- case HapticFeedbackConstants.GESTURE_END:
- case HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE:
- case HapticFeedbackConstants.SCROLL_TICK:
- case HapticFeedbackConstants.SEGMENT_TICK:
- return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
-
- case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
- if (!mHapticTextHandleEnabled) {
- return null;
- }
- // fallthrough
- case HapticFeedbackConstants.CLOCK_TICK:
- case HapticFeedbackConstants.SEGMENT_FREQUENT_TICK:
- return VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
-
- case HapticFeedbackConstants.KEYBOARD_RELEASE:
- case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
- case HapticFeedbackConstants.ENTRY_BUMP:
- case HapticFeedbackConstants.DRAG_CROSSING:
- return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
-
- case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
- case HapticFeedbackConstants.VIRTUAL_KEY:
- case HapticFeedbackConstants.EDGE_RELEASE:
- case HapticFeedbackConstants.CALENDAR_DATE:
- case HapticFeedbackConstants.CONFIRM:
- case HapticFeedbackConstants.GESTURE_START:
- case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
- case HapticFeedbackConstants.SCROLL_LIMIT:
- return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
-
- case HapticFeedbackConstants.LONG_PRESS:
- case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
- case HapticFeedbackConstants.DRAG_START:
- case HapticFeedbackConstants.EDGE_SQUEEZE:
- return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
-
- case HapticFeedbackConstants.REJECT:
- return VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
-
- case HapticFeedbackConstants.SAFE_MODE_ENABLED:
- pattern = mSafeModeEnabledVibePattern;
- break;
-
- case HapticFeedbackConstants.ASSISTANT_BUTTON:
- if (mVibrator.areAllPrimitivesSupported(
- VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
- VibrationEffect.Composition.PRIMITIVE_TICK)) {
- // quiet ramp, short pause, then sharp tick
- return VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50)
- .compose();
- }
- // fallback for devices without composition support
- return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
-
- case HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE:
- return getScaledPrimitiveOrElseEffect(
- VibrationEffect.Composition.PRIMITIVE_TICK, 0.4f,
- VibrationEffect.EFFECT_TEXTURE_TICK);
-
- case HapticFeedbackConstants.TOGGLE_ON:
- return getScaledPrimitiveOrElseEffect(
- VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f,
- VibrationEffect.EFFECT_TICK);
-
- case HapticFeedbackConstants.TOGGLE_OFF:
- return getScaledPrimitiveOrElseEffect(
- VibrationEffect.Composition.PRIMITIVE_LOW_TICK, 0.2f,
- VibrationEffect.EFFECT_TEXTURE_TICK);
-
- case HapticFeedbackConstants.NO_HAPTICS:
- default:
- return null;
- }
- if (pattern.length == 0) {
- // No vibration
- return null;
- } else if (pattern.length == 1) {
- // One-shot vibration
- return VibrationEffect.createOneShot(pattern[0], VibrationEffect.DEFAULT_AMPLITUDE);
- } else {
- // Pattern vibration
- return VibrationEffect.createWaveform(pattern, -1);
- }
- }
-
- private VibrationEffect getScaledPrimitiveOrElseEffect(int primitiveId, float scale,
- int elseEffectId) {
- if (mVibrator.areAllPrimitivesSupported(primitiveId)) {
- return VibrationEffect.startComposition()
- .addPrimitive(primitiveId, scale)
- .compose();
- } else {
- return VibrationEffect.get(elseEffectId);
- }
- }
-
- private VibrationAttributes getVibrationAttributes(int effectId) {
- switch (effectId) {
- case HapticFeedbackConstants.EDGE_SQUEEZE:
- case HapticFeedbackConstants.EDGE_RELEASE:
- return PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES;
- case HapticFeedbackConstants.ASSISTANT_BUTTON:
- case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
- case HapticFeedbackConstants.SCROLL_TICK:
- case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
- case HapticFeedbackConstants.SCROLL_LIMIT:
- return HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES;
- default:
- return TOUCH_VIBRATION_ATTRIBUTES;
- }
- }
@Override
public void keepScreenOnStartedLw() {
@@ -6258,8 +6122,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pw.print("mAllowStartActivityForLongPressOnPowerDuringSetup=");
pw.println(mAllowStartActivityForLongPressOnPowerDuringSetup);
pw.print(prefix);
- pw.print("mHasSoftInput="); pw.print(mHasSoftInput);
- pw.print(" mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled);
+ pw.print("mHasSoftInput="); pw.println(mHasSoftInput);
pw.print(prefix);
pw.print("mDismissImeOnBackKeyPressed="); pw.print(mDismissImeOnBackKeyPressed);
pw.print(" mIncallPowerBehavior=");
@@ -6284,6 +6147,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout);
pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
+ mHapticFeedbackVibrationProvider.dump(prefix, pw);
mGlobalKeyManager.dump(prefix, pw);
mKeyCombinationManager.dump(prefix, pw);
mSingleKeyGestureDetector.dump(prefix, pw);
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
new file mode 100644
index 000000000000..308ce4f9cb68
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.os.VibrationAttributes;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.view.HapticFeedbackConstants;
+
+import java.io.PrintWriter;
+
+/**
+ * Provides the {@link VibrationEffect} and {@link VibrationAttributes} for haptic feedback.
+ *
+ * @hide
+ */
+public final class HapticFeedbackVibrationProvider {
+ private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
+ private static final VibrationAttributes PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_PHYSICAL_EMULATION);
+ private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
+
+ private final Vibrator mVibrator;
+ private final boolean mHapticTextHandleEnabled;
+ // Vibrator effect for haptic feedback during boot when safe mode is enabled.
+ private final VibrationEffect mSafeModeEnabledVibrationEffect;
+
+ /** @hide */
+ public HapticFeedbackVibrationProvider(Resources res, Vibrator vibrator) {
+ mVibrator = vibrator;
+ mHapticTextHandleEnabled = res.getBoolean(
+ com.android.internal.R.bool.config_enableHapticTextHandle);
+ mSafeModeEnabledVibrationEffect =
+ VibrationSettings.createEffectFromResource(
+ res, com.android.internal.R.array.config_safeModeEnabledVibePattern);
+ }
+
+ /**
+ * Provides the {@link VibrationEffect} for a given haptic feedback effect ID (provided in
+ * {@link HapticFeedbackConstants}).
+ *
+ * @param effectId the haptic feedback effect ID whose respective vibration we want to get.
+ * @return a {@link VibrationEffect} for the given haptic feedback effect ID, or {@code null} if
+ * the provided effect ID is not supported.
+ */
+ @Nullable public VibrationEffect getVibrationForHapticFeedback(int effectId) {
+ switch (effectId) {
+ case HapticFeedbackConstants.CONTEXT_CLICK:
+ case HapticFeedbackConstants.GESTURE_END:
+ case HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE:
+ case HapticFeedbackConstants.SCROLL_TICK:
+ case HapticFeedbackConstants.SEGMENT_TICK:
+ return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+
+ case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
+ if (!mHapticTextHandleEnabled) {
+ return null;
+ }
+ // fallthrough
+ case HapticFeedbackConstants.CLOCK_TICK:
+ case HapticFeedbackConstants.SEGMENT_FREQUENT_TICK:
+ return VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
+
+ case HapticFeedbackConstants.KEYBOARD_RELEASE:
+ case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
+ case HapticFeedbackConstants.ENTRY_BUMP:
+ case HapticFeedbackConstants.DRAG_CROSSING:
+ return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
+
+ case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
+ case HapticFeedbackConstants.VIRTUAL_KEY:
+ case HapticFeedbackConstants.EDGE_RELEASE:
+ case HapticFeedbackConstants.CALENDAR_DATE:
+ case HapticFeedbackConstants.CONFIRM:
+ case HapticFeedbackConstants.GESTURE_START:
+ case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
+ case HapticFeedbackConstants.SCROLL_LIMIT:
+ return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+
+ case HapticFeedbackConstants.LONG_PRESS:
+ case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
+ case HapticFeedbackConstants.DRAG_START:
+ case HapticFeedbackConstants.EDGE_SQUEEZE:
+ return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
+
+ case HapticFeedbackConstants.REJECT:
+ return VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
+
+ case HapticFeedbackConstants.SAFE_MODE_ENABLED:
+ return mSafeModeEnabledVibrationEffect;
+
+ case HapticFeedbackConstants.ASSISTANT_BUTTON:
+ if (mVibrator.areAllPrimitivesSupported(
+ VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
+ VibrationEffect.Composition.PRIMITIVE_TICK)) {
+ // quiet ramp, short pause, then sharp tick
+ return VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50)
+ .compose();
+ }
+ // fallback for devices without composition support
+ return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
+
+ case HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE:
+ return getScaledPrimitiveOrElseEffect(
+ VibrationEffect.Composition.PRIMITIVE_TICK, 0.4f,
+ VibrationEffect.EFFECT_TEXTURE_TICK);
+
+ case HapticFeedbackConstants.TOGGLE_ON:
+ return getScaledPrimitiveOrElseEffect(
+ VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f,
+ VibrationEffect.EFFECT_TICK);
+
+ case HapticFeedbackConstants.TOGGLE_OFF:
+ return getScaledPrimitiveOrElseEffect(
+ VibrationEffect.Composition.PRIMITIVE_LOW_TICK, 0.2f,
+ VibrationEffect.EFFECT_TEXTURE_TICK);
+
+ case HapticFeedbackConstants.NO_HAPTICS:
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Provides the {@link VibrationAttributes} that should be used for a haptic feedback.
+ *
+ * @param effectId the haptic feedback effect ID whose respective vibration attributes we want
+ * to get.
+ * @param bypassVibrationIntensitySetting {@code true} if the returned attribute should bypass
+ * vibration intensity settings. {@code false} otherwise.
+ * @return the {@link VibrationAttributes} that should be used for the provided haptic feedback.
+ */
+ public VibrationAttributes getVibrationAttributesForHapticFeedback(
+ int effectId, boolean bypassVibrationIntensitySetting) {
+ VibrationAttributes attrs;
+ switch (effectId) {
+ case HapticFeedbackConstants.EDGE_SQUEEZE:
+ case HapticFeedbackConstants.EDGE_RELEASE:
+ attrs = PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES;
+ break;
+ case HapticFeedbackConstants.ASSISTANT_BUTTON:
+ case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
+ case HapticFeedbackConstants.SCROLL_TICK:
+ case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
+ case HapticFeedbackConstants.SCROLL_LIMIT:
+ attrs = HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES;
+ break;
+ default:
+ attrs = TOUCH_VIBRATION_ATTRIBUTES;
+ }
+ if (bypassVibrationIntensitySetting) {
+ attrs = new VibrationAttributes.Builder(attrs)
+ .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
+ .build();
+ }
+ return attrs;
+ }
+
+ /** Dumps relevant state. */
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print("mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled);
+ }
+
+ private VibrationEffect getScaledPrimitiveOrElseEffect(
+ int primitiveId, float scale, int elseEffectId) {
+ if (mVibrator.areAllPrimitivesSupported(primitiveId)) {
+ return VibrationEffect.startComposition()
+ .addPrimitive(primitiveId, scale)
+ .compose();
+ } else {
+ return VibrationEffect.get(elseEffectId);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 4ae7c77c104e..dbd6bf461e85 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -698,7 +698,23 @@ final class VibrationSettings {
@Nullable
private VibrationEffect createEffectFromResource(int resId) {
- long[] timings = getLongIntArray(mContext.getResources(), resId);
+ return createEffectFromResource(mContext.getResources(), resId);
+ }
+
+ /**
+ * Provides a {@link VibrationEffect} from a timings-array provided as an int-array resource..
+ *
+ * <p>If the timings array is {@code null} or empty, it returns {@code null}.
+ *
+ * <p>If the timings array has a size of one, it returns a one-shot vibration with duration that
+ * is equal to the single value in the array.
+ *
+ * <p>If the timings array has more than one values, it returns a non-repeating wave-form
+ * vibration with off-on timings as per the provided timings array.
+ */
+ @Nullable
+ static VibrationEffect createEffectFromResource(Resources res, int resId) {
+ long[] timings = getLongIntArray(res, resId);
return createEffectFromTimings(timings);
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 799643444b3c..b55af76e3799 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1016,8 +1016,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
&& mWallpaper.userId == mCurrentUserId) {
Slog.w(TAG, "Wallpaper reconnect timed out for " + mWallpaper.wallpaperComponent
+ ", reverting to built-in wallpaper!");
- clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId,
- null);
+ clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null);
}
}
};
@@ -1197,7 +1196,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
} else {
// Timeout
Slog.w(TAG, "Reverting to built-in wallpaper!");
- clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
+ clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null);
final String flattened = wpService.flattenToString();
EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
flattened.substring(0, Math.min(flattened.length(),
@@ -1236,8 +1235,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
} else {
if (mLmkLimitRebindRetries <= 0) {
Slog.w(TAG, "Reverting to built-in wallpaper due to lmk!");
- clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId,
- null);
+ clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null);
mLmkLimitRebindRetries = LMK_RECONNECT_REBIND_RETRIES;
return;
}
@@ -1256,7 +1254,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
&& mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME
> SystemClock.uptimeMillis()) {
Slog.w(TAG, "Reverting to built-in wallpaper!");
- clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
+ clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null);
} else {
mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
tryToRebind();
@@ -1497,7 +1495,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
wallpaper, null)) {
Slog.w(TAG, "Wallpaper " + wpService
+ " no longer available; reverting to default");
- clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
+ clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null);
}
}
}
@@ -1585,7 +1583,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (doit) {
Slog.w(TAG, "Wallpaper uninstalled, removing: "
+ wallpaper.wallpaperComponent);
- clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
+ clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null);
}
}
}
@@ -1606,7 +1604,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
} catch (NameNotFoundException e) {
Slog.w(TAG, "Wallpaper component gone, removing: "
+ wallpaper.wallpaperComponent);
- clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
+ clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null);
}
}
if (wallpaper.nextWallpaperComponent != null
@@ -1638,7 +1636,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
mShuttingDown = false;
mImageWallpaper = ComponentName.unflattenFromString(
context.getResources().getString(R.string.image_wallpaper_component));
- mDefaultWallpaperComponent = WallpaperManager.getCmfDefaultWallpaperComponent(context);
+ ComponentName defaultComponent = WallpaperManager.getCmfDefaultWallpaperComponent(context);
+ mDefaultWallpaperComponent = defaultComponent == null ? mImageWallpaper : defaultComponent;
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mIPackageManager = AppGlobals.getPackageManager();
@@ -1721,7 +1720,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (DEBUG) {
Slog.i(TAG, "Unable to regenerate crop; resetting");
}
- clearWallpaperLocked(false, FLAG_SYSTEM, UserHandle.USER_SYSTEM, null);
+ clearWallpaperLocked(FLAG_SYSTEM, UserHandle.USER_SYSTEM, null);
}
} else {
if (DEBUG) {
@@ -1978,7 +1977,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
if (si == null) {
- clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply);
+ clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, reply);
} else {
Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked");
// We might end up persisting the current wallpaper data
@@ -2002,10 +2001,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (serviceInfo == null) {
if (wallpaper.mWhich == (FLAG_LOCK | FLAG_SYSTEM)) {
- clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
- clearWallpaperLocked(false, FLAG_LOCK, wallpaper.userId, reply);
+ clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null);
+ clearWallpaperLocked(FLAG_LOCK, wallpaper.userId, reply);
} else {
- clearWallpaperLocked(false, wallpaper.mWhich, wallpaper.userId, reply);
+ clearWallpaperLocked(wallpaper.mWhich, wallpaper.userId, reply);
}
return;
}
@@ -2038,9 +2037,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
WallpaperData data = null;
synchronized (mLock) {
if (mIsLockscreenLiveWallpaperEnabled) {
- clearWallpaperLocked(callingPackage, false, which, userId);
+ clearWallpaperLocked(callingPackage, which, userId);
} else {
- clearWallpaperLocked(false, which, userId, null);
+ clearWallpaperLocked(which, userId, null);
}
if (which == FLAG_LOCK) {
@@ -2058,8 +2057,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
- private void clearWallpaperLocked(String callingPackage, boolean defaultFailed,
- int which, int userId) {
+ private void clearWallpaperLocked(String callingPackage, int which, int userId) {
// Might need to bring it in the first time to establish our rewrite
if (!mWallpaperMap.contains(userId)) {
@@ -2094,7 +2092,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
component = wallpaper.wallpaperComponent;
finalWhich = FLAG_LOCK | FLAG_SYSTEM;
} else {
- component = defaultFailed ? mImageWallpaper : null;
+ component = null;
finalWhich = which;
}
@@ -2114,8 +2112,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
// TODO(b/266818039) remove this version of the method
- private void clearWallpaperLocked(boolean defaultFailed, int which, int userId,
- IRemoteCallback reply) {
+ private void clearWallpaperLocked(int which, int userId, IRemoteCallback reply) {
if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to clear");
}
@@ -2168,9 +2165,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
wallpaper.primaryColors = null;
wallpaper.imageWallpaperPending = false;
if (userId != mCurrentUserId) return;
- if (bindWallpaperComponentLocked(defaultFailed
- ? mImageWallpaper
- : null, true, false, wallpaper, reply)) {
+ if (bindWallpaperComponentLocked(null, true, false, wallpaper, reply)) {
return;
}
} catch (IllegalArgumentException e1) {
@@ -3523,11 +3518,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
try {
if (componentName == null) {
componentName = mDefaultWallpaperComponent;
- if (componentName == null) {
- // Fall back to static image wallpaper
- componentName = mImageWallpaper;
- if (DEBUG_LIVE) Slog.v(TAG, "No default component; using image wallpaper");
- }
}
int serviceUserId = wallpaper.userId;
ServiceInfo si = mIPackageManager.getServiceInfo(componentName,
@@ -3997,7 +3987,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
mFallbackWallpaper = new WallpaperData(systemUserId, FLAG_SYSTEM);
mFallbackWallpaper.allowBackup = false;
mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked();
- bindWallpaperComponentLocked(mImageWallpaper, true, false, mFallbackWallpaper, null);
+ bindWallpaperComponentLocked(mDefaultWallpaperComponent, true, false,
+ mFallbackWallpaper, null);
}
}
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index 56edde09f747..271d71ed0ec0 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -24,6 +24,7 @@ import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityThread;
@@ -40,7 +41,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -48,7 +48,6 @@ import android.util.DisplayMetrics;
import android.util.Slog;
import android.view.Display;
import android.view.Gravity;
-import android.view.IWindowManager;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -56,7 +55,6 @@ import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.WindowInsets.Type;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.Button;
@@ -95,6 +93,10 @@ public class ImmersiveModeConfirmation {
*/
@Nullable
private Context mWindowContext;
+ /**
+ * The root display area feature id that the {@link #mWindowContext} is attaching to.
+ */
+ private int mWindowContextRootDisplayAreaId = FEATURE_UNDEFINED;
// Local copy of vr mode enabled state, to avoid calling into VrManager with
// the lock held.
private boolean mVrModeEnabled;
@@ -206,12 +208,15 @@ public class ImmersiveModeConfirmation {
private void handleHide() {
if (mClingWindow != null) {
if (DEBUG) Slog.d(TAG, "Hiding immersive mode confirmation");
- // We don't care which root display area the window manager is specifying for removal.
- try {
- getWindowManager(FEATURE_UNDEFINED).removeView(mClingWindow);
- } catch (WindowManager.InvalidDisplayException e) {
- Slog.w(TAG, "Fail to hide the immersive confirmation window because of " + e);
- return;
+ if (mWindowManager != null) {
+ try {
+ mWindowManager.removeView(mClingWindow);
+ } catch (WindowManager.InvalidDisplayException e) {
+ Slog.w(TAG, "Fail to hide the immersive confirmation window because of "
+ + e);
+ }
+ mWindowManager = null;
+ mWindowContext = null;
}
mClingWindow = null;
}
@@ -394,26 +399,18 @@ public class ImmersiveModeConfirmation {
* @return the WindowManager specifying with the {@code rootDisplayAreaId} to attach the
* confirmation window.
*/
- private WindowManager getWindowManager(int rootDisplayAreaId) {
- if (mWindowManager == null || mWindowContext == null) {
- // Create window context to specify the RootDisplayArea
- final Bundle options = getOptionsForWindowContext(rootDisplayAreaId);
- mWindowContext = mContext.createWindowContext(
- IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, options);
- mWindowManager = mWindowContext.getSystemService(WindowManager.class);
- return mWindowManager;
+ @NonNull
+ private WindowManager createWindowManager(int rootDisplayAreaId) {
+ if (mWindowManager != null) {
+ throw new IllegalStateException(
+ "Must not create a new WindowManager while there is an existing one");
}
-
- // Update the window context and window manager to specify the RootDisplayArea
+ // Create window context to specify the RootDisplayArea
final Bundle options = getOptionsForWindowContext(rootDisplayAreaId);
- final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
- try {
- wms.attachWindowContextToDisplayArea(mWindowContext.getWindowContextToken(),
- IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, mContext.getDisplayId(), options);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
-
+ mWindowContextRootDisplayAreaId = rootDisplayAreaId;
+ mWindowContext = mContext.createWindowContext(
+ IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, options);
+ mWindowManager = mWindowContext.getSystemService(WindowManager.class);
return mWindowManager;
}
@@ -434,14 +431,23 @@ public class ImmersiveModeConfirmation {
}
private void handleShow(int rootDisplayAreaId) {
- if (DEBUG) Slog.d(TAG, "Showing immersive mode confirmation");
+ if (mClingWindow != null) {
+ if (rootDisplayAreaId == mWindowContextRootDisplayAreaId) {
+ if (DEBUG) Slog.d(TAG, "Immersive mode confirmation has already been shown");
+ return;
+ } else {
+ // Hide the existing confirmation before show a new one in the new root.
+ if (DEBUG) Slog.d(TAG, "Immersive mode confirmation was shown in a different root");
+ handleHide();
+ }
+ }
+ if (DEBUG) Slog.d(TAG, "Showing immersive mode confirmation");
mClingWindow = new ClingWindowView(mContext, mConfirm);
-
// show the confirmation
- WindowManager.LayoutParams lp = getClingWindowLayoutParams();
+ final WindowManager.LayoutParams lp = getClingWindowLayoutParams();
try {
- getWindowManager(rootDisplayAreaId).addView(mClingWindow, lp);
+ createWindowManager(rootDisplayAreaId).addView(mClingWindow, lp);
} catch (WindowManager.InvalidDisplayException e) {
Slog.w(TAG, "Fail to show the immersive confirmation window because of " + e);
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 0c1f33ccedbc..92c0987d5636 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1020,7 +1020,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
final WindowContainer<?> parent = getParent();
final Task thisTask = asTask();
if (thisTask != null && parent.asTask() == null
- && mTransitionController.isTransientHide(thisTask)) {
+ && mTransitionController.isTransientVisible(thisTask)) {
// Keep transient-hide root tasks visible. Non-root tasks still follow standard rule.
return TASK_FRAGMENT_VISIBILITY_VISIBLE;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index a14354041b91..eaea53d555e2 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -407,6 +407,36 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
return false;
}
+ /** Returns {@code true} if the task should keep visible if this is a transient transition. */
+ boolean isTransientVisible(@NonNull Task task) {
+ if (mTransientLaunches == null) return false;
+ int occludedCount = 0;
+ final int numTransient = mTransientLaunches.size();
+ for (int i = numTransient - 1; i >= 0; --i) {
+ final Task transientRoot = mTransientLaunches.keyAt(i).getRootTask();
+ if (transientRoot == null) continue;
+ final WindowContainer<?> rootParent = transientRoot.getParent();
+ if (rootParent == null || rootParent.getTopChild() == transientRoot) continue;
+ final ActivityRecord topOpaque = mController.mAtm.mTaskSupervisor
+ .mOpaqueActivityHelper.getOpaqueActivity(rootParent);
+ if (transientRoot.compareTo(topOpaque.getRootTask()) < 0) {
+ occludedCount++;
+ }
+ }
+ if (occludedCount == numTransient) {
+ for (int i = mTransientLaunches.size() - 1; i >= 0; --i) {
+ if (mTransientLaunches.keyAt(i).isDescendantOf(task)) {
+ // Keep transient activity visible until transition finished, so it won't pause
+ // with transient-hide tasks that may delay resuming the next top.
+ return true;
+ }
+ }
+ // Let transient-hide activities pause before transition is finished.
+ return false;
+ }
+ return isInTransientHide(task);
+ }
+
boolean canApplyDim(@NonNull Task task) {
if (mTransientLaunches == null) return true;
final Dimmer dimmer = task.getDimmer();
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index bfaf6fcd3110..dfaa17494855 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -477,15 +477,22 @@ class TransitionController {
if (mCollectingTransition != null && mCollectingTransition.isInTransientHide(task)) {
return true;
}
- for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) {
- if (mWaitingTransitions.get(i).isInTransientHide(task)) return true;
- }
for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
if (mPlayingTransitions.get(i).isInTransientHide(task)) return true;
}
return false;
}
+ boolean isTransientVisible(@NonNull Task task) {
+ if (mCollectingTransition != null && mCollectingTransition.isTransientVisible(task)) {
+ return true;
+ }
+ for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
+ if (mPlayingTransitions.get(i).isTransientVisible(task)) return true;
+ }
+ return false;
+ }
+
boolean canApplyDim(@Nullable Task task) {
if (task == null) {
// Always allow non-activity window.
diff --git a/services/incremental/TEST_MAPPING b/services/incremental/TEST_MAPPING
index be7feb5c9bf7..648b904787ce 100644
--- a/services/incremental/TEST_MAPPING
+++ b/services/incremental/TEST_MAPPING
@@ -9,6 +9,9 @@
]
},
{
+ "name": "CtsPackageManagerIncrementalStatsHostTestCases"
+ },
+ {
"name": "CtsIncrementalInstallHostTestCases"
},
{
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
index 7b1654549841..a8b0a7b5633d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
@@ -40,6 +40,7 @@ import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.text.TextUtils;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -169,14 +170,14 @@ public class ArchiveManagerTest {
}
@Test
- public void archiveApp_callerNotInstallerOfRecord() {
+ public void archiveApp_noInstallerFound() {
InstallSource otherInstallSource =
InstallSource.create(
CALLER_PACKAGE,
CALLER_PACKAGE,
- /* installerPackageName= */ "different",
+ /* installerPackageName= */ null,
Binder.getCallingUid(),
- CALLER_PACKAGE,
+ /* updateOwnerPackageName= */ null,
/* installerAttributionTag= */ null,
/* packageSource= */ 0);
when(mPackageState.getInstallSource()).thenReturn(otherInstallSource);
@@ -187,29 +188,8 @@ public class ArchiveManagerTest {
mIntentSender)
);
assertThat(e).hasMessageThat().isEqualTo(
- String.format("Caller is not the installer of record for %s.", PACKAGE));
- }
-
- @Test
- public void archiveApp_callerNotUpdateOwner() {
- InstallSource otherInstallSource =
- InstallSource.create(
- CALLER_PACKAGE,
- CALLER_PACKAGE,
- CALLER_PACKAGE,
- Binder.getCallingUid(),
- /* updateOwnerPackageName= */ "different",
- /* installerAttributionTag= */ null,
- /* packageSource= */ 0);
- when(mPackageState.getInstallSource()).thenReturn(otherInstallSource);
-
- Exception e = assertThrows(
- SecurityException.class,
- () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT,
- mIntentSender)
- );
- assertThat(e).hasMessageThat().isEqualTo(
- String.format("Caller is not the update owner for %s.", PACKAGE));
+ TextUtils.formatSimple("No installer found to archive app %s.",
+ PACKAGE));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 32d0c98d4481..989aee06a1df 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -33,6 +33,7 @@ import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -42,6 +43,8 @@ import static org.mockito.Mockito.when;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
import android.os.Handler;
import android.os.Message;
import android.os.UserHandle;
@@ -67,11 +70,13 @@ import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.List;
@@ -123,9 +128,10 @@ public class FullScreenMagnificationGestureHandlerTest {
public static final int STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP = 8;
public static final int STATE_PANNING = 9;
public static final int STATE_SCALING_AND_PANNING = 10;
+ public static final int STATE_SINGLE_PANNING = 11;
public static final int FIRST_STATE = STATE_IDLE;
- public static final int LAST_STATE = STATE_SCALING_AND_PANNING;
+ public static final int LAST_STATE = STATE_SINGLE_PANNING;
// Co-prime x and y, to potentially catch x-y-swapped errors
public static final float DEFAULT_X = 301;
@@ -155,6 +161,10 @@ public class FullScreenMagnificationGestureHandlerTest {
private float mOriginalMagnificationPersistedScale;
+ static final Rect INITIAL_MAGNIFICATION_BOUNDS = new Rect(0, 0, 800, 800);
+
+ static final Region INITIAL_MAGNIFICATION_REGION = new Region(INITIAL_MAGNIFICATION_BOUNDS);
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -182,11 +192,19 @@ public class FullScreenMagnificationGestureHandlerTest {
new MagnificationScaleProvider(mContext),
() -> null,
ConcurrentUtils.DIRECT_EXECUTOR) {
- @Override
- public boolean magnificationRegionContains(int displayId, float x, float y) {
- return true;
- }
+ @Override
+ public boolean magnificationRegionContains(int displayId, float x, float y) {
+ return true;
+ }
};
+
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ Object[] args = invocationOnMock.getArguments();
+ Region regionArg = (Region) args[1];
+ regionArg.set(new Rect(INITIAL_MAGNIFICATION_BOUNDS));
+ return null;
+ }).when(mockWindowManager).getMagnificationRegion(anyInt(), any(Region.class));
+
mFullScreenMagnificationController.register(DISPLAY_0);
mFullScreenMagnificationController.setAlwaysOnMagnificationEnabled(true);
mClock = new OffsettableClock.Stopped();
@@ -214,6 +232,7 @@ public class FullScreenMagnificationGestureHandlerTest {
mContext, mFullScreenMagnificationController, mMockTraceManager, mMockCallback,
detectTripleTap, detectShortcutTrigger,
mWindowMagnificationPromptController, DISPLAY_0);
+ h.setSinglePanningEnabled(true);
mHandler = new TestHandler(h.mDetectingState, mClock) {
@Override
protected String messageToString(Message m) {
@@ -239,6 +258,7 @@ public class FullScreenMagnificationGestureHandlerTest {
* {@link #returnToNormalFrom} (for navigating back to {@link #STATE_IDLE})
*/
@Test
+ @Ignore("b/291925580")
public void testEachState_isReachableAndRecoverable() {
forEachState(state -> {
goFromStateIdleTo(state);
@@ -526,6 +546,75 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
+ public void testActionUpNotAtEdge_singlePanningState_detectingState() {
+ goFromStateIdleTo(STATE_SINGLE_PANNING);
+
+ send(upEvent());
+
+ check(mMgh.mCurrentState == mMgh.mDetectingState, STATE_IDLE);
+ assertTrue(isZoomed());
+ }
+
+ @Test
+ public void testScroll_SinglePanningDisabled_delegatingState() {
+ mMgh.setSinglePanningEnabled(false);
+
+ goFromStateIdleTo(STATE_ACTIVATED);
+ allowEventDelegation();
+ swipeAndHold();
+
+ assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+ }
+
+ @Test
+ public void testScroll_zoomedStateAndAtEdge_delegatingState() {
+ goFromStateIdleTo(STATE_ACTIVATED);
+ mFullScreenMagnificationController.setCenter(
+ DISPLAY_0,
+ INITIAL_MAGNIFICATION_BOUNDS.left,
+ INITIAL_MAGNIFICATION_BOUNDS.top / 2,
+ false,
+ 1);
+ final float swipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
+ PointF initCoords =
+ new PointF(
+ mFullScreenMagnificationController.getCenterX(DISPLAY_0),
+ mFullScreenMagnificationController.getCenterY(DISPLAY_0));
+ PointF endCoords = new PointF(initCoords.x, initCoords.y);
+ endCoords.offset(swipeMinDistance, 0);
+ allowEventDelegation();
+
+ swipeAndHold(initCoords, endCoords);
+
+ assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+ assertTrue(isZoomed());
+ }
+
+ @Test
+ public void testScroll_singlePanningAndAtEdge_delegatingState() {
+ goFromStateIdleTo(STATE_SINGLE_PANNING);
+ mFullScreenMagnificationController.setCenter(
+ DISPLAY_0,
+ INITIAL_MAGNIFICATION_BOUNDS.left,
+ INITIAL_MAGNIFICATION_BOUNDS.top / 2,
+ false,
+ 1);
+ final float swipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
+ PointF initCoords =
+ new PointF(
+ mFullScreenMagnificationController.getCenterX(DISPLAY_0),
+ mFullScreenMagnificationController.getCenterY(DISPLAY_0));
+ PointF endCoords = new PointF(initCoords.x, initCoords.y);
+ endCoords.offset(swipeMinDistance, 0);
+ allowEventDelegation();
+
+ swipeAndHold(initCoords, endCoords);
+
+ assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+ assertTrue(isZoomed());
+ }
+
+ @Test
public void testShortcutTriggered_invokeShowWindowPromptAction() {
goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
@@ -740,6 +829,10 @@ public class FullScreenMagnificationGestureHandlerTest {
state);
check(mMgh.mPanningScalingState.mScaling, state);
} break;
+ case STATE_SINGLE_PANNING: {
+ check(isZoomed(), state);
+ check(mMgh.mCurrentState == mMgh.mSinglePanningState, state);
+ } break;
default: throw new IllegalArgumentException("Illegal state: " + state);
}
}
@@ -803,6 +896,10 @@ public class FullScreenMagnificationGestureHandlerTest {
send(pointerEvent(ACTION_MOVE, DEFAULT_X * 2, DEFAULT_Y * 4));
send(pointerEvent(ACTION_MOVE, DEFAULT_X * 2, DEFAULT_Y * 5));
} break;
+ case STATE_SINGLE_PANNING: {
+ goFromStateIdleTo(STATE_ACTIVATED);
+ swipeAndHold();
+ } break;
default:
throw new IllegalArgumentException("Illegal state: " + state);
}
@@ -859,6 +956,10 @@ public class FullScreenMagnificationGestureHandlerTest {
case STATE_SCALING_AND_PANNING: {
returnToNormalFrom(STATE_PANNING);
} break;
+ case STATE_SINGLE_PANNING: {
+ send(upEvent());
+ returnToNormalFrom(STATE_ACTIVATED);
+ } break;
default: throw new IllegalArgumentException("Illegal state: " + state);
}
}
@@ -906,6 +1007,11 @@ public class FullScreenMagnificationGestureHandlerTest {
send(moveEvent(DEFAULT_X * 2, DEFAULT_Y * 2));
}
+ private void swipeAndHold(PointF start, PointF end) {
+ send(downEvent(start.x, start.y));
+ send(moveEvent(end.x, end.y));
+ }
+
private void longTap() {
send(downEvent());
fastForward(2000);
diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING
new file mode 100644
index 000000000000..0ffa891ce3e1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.contentcapture"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING
new file mode 100644
index 000000000000..419508ca5e17
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.contentprotection"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index a109d5cddd21..f552ab2dab60 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -406,7 +406,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
UriGrantsManagerInternal mUgmInternal;
@Mock
AppOpsManager mAppOpsManager;
- private AppOpsManager.OnOpChangedListener mOnPermissionChangeListener;
@Mock
private TestableNotificationManagerService.NotificationAssistantAccessGrantedCallback
mNotificationAssistantAccessGrantedCallback;
@@ -606,12 +605,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
tr.addOverride(com.android.internal.R.string.config_defaultSearchSelectorPackageName,
SEARCH_SELECTOR_PKG);
- doAnswer(invocation -> {
- mOnPermissionChangeListener = invocation.getArgument(2);
- return null;
- }).when(mAppOpsManager).startWatchingMode(eq(AppOpsManager.OP_POST_NOTIFICATION), any(),
- any());
-
mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper()));
mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient,
mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr,
@@ -3224,6 +3217,48 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testUpdateAppNotifyCreatorBlock() throws Exception {
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
+
+ mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
+ Thread.sleep(500);
+ waitForIdle();
+
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
+
+ assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
+ captor.getValue().getAction());
+ assertEquals(PKG, captor.getValue().getPackage());
+ assertTrue(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
+ }
+
+ @Test
+ public void testUpdateAppNotifyCreatorBlock_notIfMatchesExistingSetting() throws Exception {
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+
+ mBinderService.setNotificationsEnabledForPackage(PKG, 0, false);
+ verify(mContext, never()).sendBroadcastAsUser(any(), any(), eq(null));
+ }
+
+ @Test
+ public void testUpdateAppNotifyCreatorUnblock() throws Exception {
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+
+ mBinderService.setNotificationsEnabledForPackage(PKG, mUid, true);
+ Thread.sleep(500);
+ waitForIdle();
+
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
+
+ assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
+ captor.getValue().getAction());
+ assertEquals(PKG, captor.getValue().getPackage());
+ assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
+ }
+
+ @Test
public void testUpdateChannelNotifyCreatorBlock() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
@@ -12139,134 +12174,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
any(), eq(FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER | FLAG_SERVICE_SENDER));
}
- @Test
- public void onOpChanged_permissionRevoked_cancelsAllNotificationsFromPackage()
- throws RemoteException {
- // Have preexisting posted notifications from revoked package and other packages.
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("revoked", 1001, 1, 0), mTestNotificationChannel));
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 2, 0), mTestNotificationChannel));
- // Have preexisting enqueued notifications from revoked package and other packages.
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("revoked", 1001, 3, 0), mTestNotificationChannel));
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 4, 0), mTestNotificationChannel));
- assertThat(mService.mNotificationList).hasSize(2);
- assertThat(mService.mEnqueuedNotifications).hasSize(2);
-
- when(mPackageManagerInternal.getPackageUid("revoked", 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(false);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, "revoked", 0);
- waitForIdle();
-
- assertThat(mService.mNotificationList).hasSize(1);
- assertThat(mService.mNotificationList.get(0).getSbn().getPackageName()).isEqualTo("other");
- assertThat(mService.mEnqueuedNotifications).hasSize(1);
- assertThat(mService.mEnqueuedNotifications.get(0).getSbn().getPackageName()).isEqualTo(
- "other");
- }
-
- @Test
- public void onOpChanged_permissionStillGranted_notificationsAreNotAffected()
- throws RemoteException {
- // NOTE: This combination (receiving the onOpChanged broadcast for a package, the permission
- // being now granted, AND having previously posted notifications from said package) should
- // never happen (if we trust the broadcasts are correct). So this test is for a what-if
- // scenario, to verify we still handle it reasonably.
-
- // Have preexisting posted notifications from specific package and other packages.
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("granted", 1001, 1, 0), mTestNotificationChannel));
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 2, 0), mTestNotificationChannel));
- // Have preexisting enqueued notifications from specific package and other packages.
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("granted", 1001, 3, 0), mTestNotificationChannel));
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 4, 0), mTestNotificationChannel));
- assertThat(mService.mNotificationList).hasSize(2);
- assertThat(mService.mEnqueuedNotifications).hasSize(2);
-
- when(mPackageManagerInternal.getPackageUid("granted", 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(true);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, "granted", 0);
- waitForIdle();
-
- assertThat(mService.mNotificationList).hasSize(2);
- assertThat(mService.mEnqueuedNotifications).hasSize(2);
- }
-
- @Test
- public void onOpChanged_permissionGranted_notifiesAppUnblocked() throws Exception {
- when(mPackageManagerInternal.getPackageUid(PKG, 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(true);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, PKG, 0);
- waitForIdle();
- mTestableLooper.moveTimeForward(500);
- waitForIdle();
-
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext).sendBroadcastAsUser(captor.capture(), any(), eq(null));
- assertThat(captor.getValue().getAction()).isEqualTo(
- NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED);
- assertThat(captor.getValue().getPackage()).isEqualTo(PKG);
- assertThat(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true)).isFalse();
- }
-
- @Test
- public void onOpChanged_permissionRevoked_notifiesAppBlocked() throws Exception {
- when(mPackageManagerInternal.getPackageUid(PKG, 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(false);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, PKG, 0);
- waitForIdle();
- mTestableLooper.moveTimeForward(500);
- waitForIdle();
-
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext).sendBroadcastAsUser(captor.capture(), any(), eq(null));
- assertThat(captor.getValue().getAction()).isEqualTo(
- NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED);
- assertThat(captor.getValue().getPackage()).isEqualTo(PKG);
- assertThat(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, false)).isTrue();
- }
-
- @Test
- public void setNotificationsEnabledForPackage_disabling_clearsNotifications() throws Exception {
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("package", 1001, 1, 0), mTestNotificationChannel));
- assertThat(mService.mNotificationList).hasSize(1);
- when(mPackageManagerInternal.getPackageUid("package", 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasRequestedPermission(any(), eq("package"), anyInt())).thenReturn(
- true);
-
- // Start with granted permission and simulate effect of revoking it.
- when(mPermissionHelper.hasPermission(1001)).thenReturn(true);
- doAnswer(invocation -> {
- when(mPermissionHelper.hasPermission(1001)).thenReturn(false);
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, "package", 0);
- return null;
- }).when(mPermissionHelper).setNotificationPermission("package", 0, false, true);
-
- mBinderService.setNotificationsEnabledForPackage("package", 1001, false);
- waitForIdle();
-
- assertThat(mService.mNotificationList).hasSize(0);
-
- mTestableLooper.moveTimeForward(500);
- waitForIdle();
- verify(mContext).sendBroadcastAsUser(any(), eq(UserHandle.of(0)), eq(null));
- }
-
private static <T extends Parcelable> T parcelAndUnparcel(T source,
Parcelable.Creator<T> creator) {
Parcel parcel = Parcel.obtain();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index dedb8f170ee0..3ee75de23fdb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -771,7 +771,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.mConfig = null; // will evaluate config to zen mode off
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -798,7 +798,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -825,7 +825,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -2269,7 +2269,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
// Artificially turn zen mode "on". Re-evaluating zen mode should cause it to turn back off
// given that we don't have any zen rules active.
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
// Check that the change actually took: zen mode should be off now
assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index abf21a57dd40..7eab06ac8b95 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -656,7 +656,7 @@ public class RootTaskTests extends WindowTestsBase {
topSplitPrimary.getVisibility(null /* starting */));
// Make primary split root transient-hide.
spyOn(splitPrimary.mTransitionController);
- doReturn(true).when(splitPrimary.mTransitionController).isTransientHide(
+ doReturn(true).when(splitPrimary.mTransitionController).isTransientVisible(
organizer.mPrimary);
// The split root and its top become visible.
assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
index b181213df003..fb957485d9eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
@@ -91,7 +91,7 @@ public class ScreenshotTests {
}
@Test
- public void testScreenshotSecureLayers() {
+ public void testScreenshotSecureLayers() throws InterruptedException {
SurfaceControl secureSC = new SurfaceControl.Builder()
.setName("SecureChildSurfaceControl")
.setBLASTLayer()
@@ -197,6 +197,8 @@ public class ScreenshotTests {
private static final long WAIT_TIMEOUT_S = 5;
private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final CountDownLatch mAttachedLatch = new CountDownLatch(1);
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -204,7 +206,16 @@ public class ScreenshotTests {
PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL));
}
- SurfaceControl.Transaction addChildSc(SurfaceControl surfaceControl) {
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mAttachedLatch.countDown();
+ }
+
+ SurfaceControl.Transaction addChildSc(SurfaceControl surfaceControl)
+ throws InterruptedException {
+ assertTrue("Failed to wait for onAttachedToWindow",
+ mAttachedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
CountDownLatch countDownLatch = new CountDownLatch(1);
mHandler.post(() -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index ffecafb7b4ed..5154d17f2e6b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1488,6 +1488,47 @@ public class TransitionTests extends WindowTestsBase {
}
@Test
+ public void testIsTransientVisible() {
+ final ActivityRecord appB = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setVisible(false).build();
+ final ActivityRecord recent = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setVisible(false).build();
+ final ActivityRecord appA = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final Task taskA = appA.getTask();
+ final Task taskB = appB.getTask();
+ final Task taskRecent = recent.getTask();
+ registerTestTransitionPlayer();
+ final TransitionController controller = mRootWindowContainer.mTransitionController;
+ final Transition transition = createTestTransition(TRANSIT_OPEN, controller);
+ controller.moveToCollecting(transition);
+ transition.collect(recent);
+ transition.collect(taskA);
+ transition.setTransientLaunch(recent, taskA);
+ taskRecent.moveToFront("move-recent-to-front");
+
+ // During collecting and playing, the recent is on top so it is visible naturally.
+ // While B needs isTransientVisible to keep visibility because it is occluded by recents.
+ assertFalse(controller.isTransientVisible(taskB));
+ assertTrue(controller.isTransientVisible(taskA));
+ assertFalse(controller.isTransientVisible(taskRecent));
+ // Switch to playing state.
+ transition.onTransactionReady(transition.getSyncId(), mMockT);
+ assertTrue(controller.isTransientVisible(taskA));
+
+ // Switch to another task. For example, use gesture navigation to switch tasks.
+ taskB.moveToFront("move-b-to-front");
+ // The previous app (taskA) should be paused first so it loses transient visible. Because
+ // visually it is taskA -> taskB, the pause -> resume order should be the same.
+ assertFalse(controller.isTransientVisible(taskA));
+ // Keep the recent visible so there won't be 2 activities pausing at the same time. It is
+ // to avoid the latency to resume the current top, i.e. appB.
+ assertTrue(controller.isTransientVisible(taskRecent));
+ // The recent is paused after the transient transition is finished.
+ controller.finishTransition(transition);
+ assertFalse(controller.isTransientVisible(taskRecent));
+ }
+
+ @Test
public void testNotReadyPushPop() {
final TransitionController controller = new TestTransitionController(mAtm);
controller.setSyncEngine(mWm.mSyncEngine);
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
index 0f406fda28b6..845e64949230 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
@@ -70,6 +70,9 @@ class MainActivityStartsSecondaryWithAlwaysExpandTest(flicker: LegacyFlickerTest
@Ignore("Not applicable to this CUJ.") override fun navBarWindowIsVisibleAtStartAndEnd() {}
+ @FlakyTest(bugId = 291575593)
+ override fun entireScreenCovered() {}
+
@Ignore("Not applicable to this CUJ.") override fun statusBarWindowIsAlwaysVisible() {}
@Ignore("Not applicable to this CUJ.") override fun statusBarLayerPositionAtStartAndEnd() {}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index 7a16060e3370..94b090f42c9b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
@@ -200,7 +200,7 @@ open class OpenAppFromLockscreenViaIntentTest(flicker: LegacyFlickerTest) :
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
- @FlakyTest(bugId = 251217585)
+ @FlakyTest(bugId = 285980483)
@Test
override fun focusChanges() {
super.focusChanges()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index 4164c0d440c0..df9780ef8b98 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -21,6 +21,7 @@ import android.app.WallpaperManager
import android.content.res.Resources
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.flicker.subject.layers.LayersTraceSubject.Companion.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.ComponentNameMatcher.Companion.SPLASH_SCREEN
import android.tools.common.traces.component.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER
@@ -190,6 +191,16 @@ class TaskTransitionTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
}
}
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + listOf(launchNewTaskApp)
+ )
+ }
+ }
+
companion object {
private fun getWallpaperPackage(instrumentation: Instrumentation): IComponentMatcher {
val wallpaperManager = WallpaperManager.getInstance(instrumentation.targetContext)