Merge "Make ATMService#mUiContext mockable" into tm-qpr-dev
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 877e7d3..6f4bb45 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -145,7 +145,10 @@
* that it is preserved through activty destroy and restore.
*/
private ArrayList<String> getPendingExitNames() {
- if (mPendingExitNames == null && mEnterTransitionCoordinator != null) {
+ if (mPendingExitNames == null
+ && mEnterTransitionCoordinator != null
+ && !mEnterTransitionCoordinator.isReturning()
+ ) {
mPendingExitNames = mEnterTransitionCoordinator.getPendingExitSharedElementNames();
}
return mPendingExitNames;
@@ -202,6 +205,7 @@
restoreExitedViews();
activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
}
+ getPendingExitNames(); // Set mPendingExitNames before resetting mEnterTransitionCoordinator
mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning(),
mEnterActivityOptions.isCrossTask());
@@ -250,6 +254,7 @@
public void onStop(Activity activity) {
restoreExitedViews();
if (mEnterTransitionCoordinator != null) {
+ getPendingExitNames(); // Set mPendingExitNames before clearing
mEnterTransitionCoordinator.stop();
mEnterTransitionCoordinator = null;
}
@@ -275,6 +280,7 @@
restoreReenteringViews();
} else if (mEnterTransitionCoordinator.isReturning()) {
mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> {
+ getPendingExitNames(); // Set mPendingExitNames before clearing
mEnterTransitionCoordinator = null;
});
}
@@ -374,6 +380,7 @@
}
public void startExitOutTransition(Activity activity, Bundle options) {
+ getPendingExitNames(); // Set mPendingExitNames before clearing mEnterTransitionCoordinator
mEnterTransitionCoordinator = null;
if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) ||
mExitTransitionCoordinators == null) {
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 559313a..41345e0 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -141,7 +141,9 @@
*
* Without this flag, the message passed to {@code grantTrust} is only used for debugging
* purposes. With the flag, it may be displayed to the user as the reason why the device is
- * unlocked.
+ * unlocked. If this flag isn't set OR the message is set to null, the device will display
+ * its own default message for trust granted. If the TrustAgent intentionally doesn't want to
+ * show any message, then it can set this flag AND set the message to an empty string.
*/
public static final int FLAG_GRANT_TRUST_DISPLAY_MESSAGE = 1 << 3;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ba6ba63..b0d4657 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -12709,7 +12709,6 @@
if (mViewTranslationCallback != null) {
mViewTranslationCallback.onClearTranslation(this);
}
- clearViewTranslationCallback();
clearViewTranslationResponse();
if (hasTranslationTransientState()) {
setHasTransientState(false);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 2195b83..218ca58 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -285,12 +285,18 @@
int TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21;
/**
- * Keyguard is being occluded.
+ * Keyguard is being occluded by non-Dream.
* @hide
*/
int TRANSIT_OLD_KEYGUARD_OCCLUDE = 22;
/**
+ * Keyguard is being occluded by Dream.
+ * @hide
+ */
+ int TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM = 33;
+
+ /**
* Keyguard is being unoccluded.
* @hide
*/
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 4706aff..5f8acff 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -111,6 +111,7 @@
private Icon mLargeIcon;
private View mExpandButtonContainer;
private ViewGroup mExpandButtonAndContentContainer;
+ private ViewGroup mExpandButtonContainerA11yContainer;
private NotificationExpandButton mExpandButton;
private MessagingLinearLayout mImageMessageContainer;
private int mBadgeProtrusion;
@@ -234,6 +235,8 @@
});
mConversationText = findViewById(R.id.conversation_text);
mExpandButtonContainer = findViewById(R.id.expand_button_container);
+ mExpandButtonContainerA11yContainer =
+ findViewById(R.id.expand_button_a11y_container);
mConversationHeader = findViewById(R.id.conversation_header);
mContentContainer = findViewById(R.id.notification_action_list_margin_target);
mExpandButtonAndContentContainer = findViewById(R.id.expand_button_and_content_container);
@@ -1091,7 +1094,7 @@
newContainer = mExpandButtonAndContentContainer;
} else {
buttonGravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
- newContainer = this;
+ newContainer = mExpandButtonContainerA11yContainer;
}
mExpandButton.setExpanded(!mIsCollapsed);
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index 42fb4a2..ce8a904 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -89,45 +89,62 @@
<include layout="@layout/notification_material_action_list" />
</com.android.internal.widget.RemeasuringLinearLayout>
- <!--This is dynamically placed between here and at the end of the layout. It starts here since
- only FrameLayout layout params have gravity-->
+ <!--expand_button_a11y_container ensures talkback focus order is correct when view is expanded.
+ The -1px of marginTop and 1px of paddingTop make sure expand_button_a11y_container is prior to
+ its sibling view in accessibility focus order.
+ {see android.view.ViewGroup.addChildrenForAccessibility()}
+ expand_button_container will be moved under expand_button_and_content_container when collapsed,
+ this dynamic movement ensures message can flow under expand button when expanded-->
<FrameLayout
- android:id="@+id/expand_button_container"
- android:layout_width="wrap_content"
+ android:id="@+id/expand_button_a11y_container"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="end|top"
android:clipChildren="false"
- android:clipToPadding="false">
- <!--This layout makes sure that we can nicely center the expand content in the
- collapsed layout while the parent makes sure that we're never laid out bigger
- than the messaging content.-->
- <LinearLayout
- android:id="@+id/expand_button_touch_container"
+ android:clipToPadding="false"
+ android:layout_marginTop="-1px"
+ android:paddingTop="1px"
+ >
+ <!--expand_button_container is dynamically placed between here and at the end of the
+ layout. It starts here since only FrameLayout layout params have gravity-->
+ <FrameLayout
+ android:id="@+id/expand_button_container"
android:layout_width="wrap_content"
- android:layout_height="@dimen/conversation_expand_button_height"
- android:orientation="horizontal"
+ android:layout_height="match_parent"
android:layout_gravity="end|top"
- android:paddingEnd="0dp"
- android:clipToPadding="false"
android:clipChildren="false"
- >
- <!-- Images -->
- <com.android.internal.widget.MessagingLinearLayout
- android:id="@+id/conversation_image_message_container"
- android:forceHasOverlappingRendering="false"
- android:layout_width="40dp"
- android:layout_height="40dp"
- android:layout_marginStart="@dimen/conversation_image_start_margin"
- android:spacing="0dp"
- android:layout_gravity="center"
+ android:clipToPadding="false">
+ <!--expand_button_touch_container makes sure that we can nicely center the expand
+ content in the collapsed layout while the parent makes sure that we're never laid out
+ bigger than the messaging content.-->
+ <LinearLayout
+ android:id="@+id/expand_button_touch_container"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/conversation_expand_button_height"
+ android:orientation="horizontal"
+ android:layout_gravity="end|top"
+ android:paddingEnd="0dp"
android:clipToPadding="false"
android:clipChildren="false"
- />
- <include layout="@layout/notification_expand_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- />
- </LinearLayout>
+ >
+ <!-- Images -->
+ <com.android.internal.widget.MessagingLinearLayout
+ android:id="@+id/conversation_image_message_container"
+ android:forceHasOverlappingRendering="false"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:layout_marginStart="@dimen/conversation_image_start_margin"
+ android:spacing="0dp"
+ android:layout_gravity="center"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ />
+ <include layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ />
+ </LinearLayout>
+ </FrameLayout>
</FrameLayout>
</com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e773a9c..a6174e1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4279,6 +4279,7 @@
<java-symbol type="id" name="conversation_icon_badge_ring" />
<java-symbol type="id" name="conversation_icon_badge_bg" />
<java-symbol type="id" name="expand_button_container" />
+ <java-symbol type="id" name="expand_button_a11y_container" />
<java-symbol type="id" name="expand_button_touch_container" />
<java-symbol type="id" name="messaging_group_content_container" />
<java-symbol type="id" name="expand_button_and_content_container" />
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 7c50982..347904e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -82,7 +82,7 @@
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.SplitscreenPipMixedHandler;
+import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
import com.android.wm.shell.unfold.UnfoldAnimationController;
@@ -484,13 +484,13 @@
@WMSingleton
@Provides
- static SplitscreenPipMixedHandler provideSplitscreenPipMixedHandler(
+ static DefaultMixedHandler provideDefaultMixedHandler(
ShellInit shellInit,
Optional<SplitScreenController> splitScreenOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
Transitions transitions) {
- return new SplitscreenPipMixedHandler(shellInit, splitScreenOptional,
- pipTouchHandlerOptional, transitions);
+ return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
+ pipTouchHandlerOptional);
}
//
@@ -619,7 +619,7 @@
@ShellCreateTriggerOverride
@Provides
static Object provideIndependentShellComponentsToCreate(
- SplitscreenPipMixedHandler splitscreenPipMixedHandler,
+ DefaultMixedHandler defaultMixedHandler,
Optional<DesktopModeController> desktopModeController) {
return new Object();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 4697a01..b59fe18 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -258,12 +258,12 @@
break;
case ACTION_DRAG_ENTERED:
pd.dragLayout.show();
- pd.dragLayout.update(event);
break;
case ACTION_DRAG_LOCATION:
pd.dragLayout.update(event);
break;
case ACTION_DROP: {
+ pd.dragLayout.update(event);
return handleDrop(event, pd);
}
case ACTION_DRAG_EXITED: {
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 8729161..9206afb 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
@@ -147,7 +147,6 @@
private final DragAndDropController mDragAndDropController;
private final Transitions mTransitions;
private final TransactionPool mTransactionPool;
- private final SplitscreenEventLogger mLogger;
private final IconProvider mIconProvider;
private final Optional<RecentTasksController> mRecentTasksOptional;
private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
@@ -186,7 +185,6 @@
mDragAndDropController = dragAndDropController;
mTransitions = transitions;
mTransactionPool = transactionPool;
- mLogger = new SplitscreenEventLogger();
mIconProvider = iconProvider;
mRecentTasksOptional = recentTasks;
mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
@@ -221,7 +219,7 @@
protected StageCoordinator createStageCoordinator() {
return new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
+ mDisplayInsetsController, mTransitions, mTransactionPool,
mIconProvider, mMainExecutor, mRecentTasksOptional);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index f8b40be..9c9841f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -250,14 +250,14 @@
ShellTaskOrganizer taskOrganizer, DisplayController displayController,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
- TransactionPool transactionPool, SplitscreenEventLogger logger,
+ TransactionPool transactionPool,
IconProvider iconProvider, ShellExecutor mainExecutor,
Optional<RecentTasksController> recentTasks) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
mTaskOrganizer = taskOrganizer;
- mLogger = logger;
+ mLogger = new SplitscreenEventLogger();
mMainExecutor = mainExecutor;
mRecentTasks = recentTasks;
@@ -301,7 +301,7 @@
DisplayController displayController, DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
- SplitscreenEventLogger logger, ShellExecutor mainExecutor,
+ ShellExecutor mainExecutor,
Optional<RecentTasksController> recentTasks) {
mContext = context;
mDisplayId = displayId;
@@ -316,7 +316,7 @@
mSplitLayout = splitLayout;
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
this::onTransitionAnimationComplete, this);
- mLogger = logger;
+ mLogger = new SplitscreenEventLogger();
mMainExecutor = mainExecutor;
mRecentTasks = recentTasks;
mDisplayController.addDisplayWindowListener(this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index e26c259..bcf4fbd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -35,10 +35,14 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.splitscreen.StageCoordinator;
+import com.android.wm.shell.sysui.ShellInit;
import java.util.ArrayList;
+import java.util.Optional;
/**
* A handler for dealing with transitions involving multiple other handlers. For example: an
@@ -47,8 +51,8 @@
public class DefaultMixedHandler implements Transitions.TransitionHandler {
private final Transitions mPlayer;
- private final PipTransitionController mPipHandler;
- private final StageCoordinator mSplitHandler;
+ private PipTransitionController mPipHandler;
+ private StageCoordinator mSplitHandler;
private static class MixedTransition {
static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
@@ -77,13 +81,22 @@
mTransition = transition;
}
}
+
private final ArrayList<MixedTransition> mActiveTransitions = new ArrayList<>();
- public DefaultMixedHandler(@NonNull Transitions player,
- @NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler) {
+ public DefaultMixedHandler(@NonNull ShellInit shellInit, @NonNull Transitions player,
+ Optional<SplitScreenController> splitScreenControllerOptional,
+ Optional<PipTouchHandler> pipTouchHandlerOptional) {
mPlayer = player;
- mPipHandler = pipHandler;
- mSplitHandler = splitHandler;
+ if (Transitions.ENABLE_SHELL_TRANSITIONS && pipTouchHandlerOptional.isPresent()
+ && splitScreenControllerOptional.isPresent()) {
+ // Add after dependencies because it is higher priority
+ shellInit.addInitCallback(() -> {
+ mPipHandler = pipTouchHandlerOptional.get().getTransitionHandler();
+ mSplitHandler = splitScreenControllerOptional.get().getTransitionHandler();
+ mPlayer.addHandler(this);
+ }, this);
+ }
}
@Nullable
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SplitscreenPipMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SplitscreenPipMixedHandler.java
deleted file mode 100644
index 678e91f..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SplitscreenPipMixedHandler.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2022 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.wm.shell.transition;
-
-import com.android.wm.shell.pip.phone.PipTouchHandler;
-import com.android.wm.shell.splitscreen.SplitScreenController;
-import com.android.wm.shell.sysui.ShellInit;
-
-import java.util.Optional;
-
-/**
- * Handles transitions between the Splitscreen and PIP components.
- */
-public class SplitscreenPipMixedHandler {
-
- private final Optional<SplitScreenController> mSplitScreenOptional;
- private final Optional<PipTouchHandler> mPipTouchHandlerOptional;
- private final Transitions mTransitions;
-
- public SplitscreenPipMixedHandler(ShellInit shellInit,
- Optional<SplitScreenController> splitScreenControllerOptional,
- Optional<PipTouchHandler> pipTouchHandlerOptional,
- Transitions transitions) {
- mSplitScreenOptional = splitScreenControllerOptional;
- mPipTouchHandlerOptional = pipTouchHandlerOptional;
- mTransitions = transitions;
- if (Transitions.ENABLE_SHELL_TRANSITIONS
- && mSplitScreenOptional.isPresent() && mPipTouchHandlerOptional.isPresent()) {
- shellInit.addInitCallback(this::onInit, this);
- }
- }
-
- private void onInit() {
- // Special handling for initializing based on multiple components
- final DefaultMixedHandler mixedHandler = new DefaultMixedHandler(mTransitions,
- mPipTouchHandlerOptional.get().getTransitionHandler(),
- mSplitScreenOptional.get().getTransitionHandler());
- // Added at end so that it has highest priority.
- mTransitions.addHandler(mixedHandler);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
index eab75b9..c045ceb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
@@ -114,7 +114,8 @@
/**
* Get all {@link ActivityManager.RecentTaskInfo}s grouped together.
*/
- public List<ActivityManager.RecentTaskInfo> getAllTaskInfos() {
+ @NonNull
+ public List<ActivityManager.RecentTaskInfo> getTaskInfoList() {
return Arrays.asList(mTasks);
}
@@ -148,7 +149,7 @@
if (mSplitBounds != null) {
taskString.append(", SplitBounds: ").append(mSplitBounds);
}
- taskString.append(", Type=").append(mType);
+ taskString.append(", Type=");
switch (mType) {
case TYPE_SINGLE:
taskString.append("TYPE_SINGLE");
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
new file mode 100644
index 0000000..baa06f2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 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.wm.shell.recents
+
+import android.app.ActivityManager
+import android.graphics.Rect
+import android.os.Parcel
+import android.testing.AndroidTestingRunner
+import android.window.IWindowContainerToken
+import android.window.WindowContainerToken
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.util.GroupedRecentTaskInfo
+import com.android.wm.shell.util.GroupedRecentTaskInfo.CREATOR
+import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM
+import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_SINGLE
+import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_SPLIT
+import com.android.wm.shell.util.SplitBounds
+import com.google.common.truth.Correspondence
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+/**
+ * Tests for [GroupedRecentTaskInfo]
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class GroupedRecentTaskInfoTest : ShellTestCase() {
+
+ @Test
+ fun testSingleTask_hasCorrectType() {
+ assertThat(singleTaskGroupInfo().type).isEqualTo(TYPE_SINGLE)
+ }
+
+ @Test
+ fun testSingleTask_task1Set_task2Null() {
+ val group = singleTaskGroupInfo()
+ assertThat(group.taskInfo1.taskId).isEqualTo(1)
+ assertThat(group.taskInfo2).isNull()
+ }
+
+ @Test
+ fun testSingleTask_taskInfoList_hasOneTask() {
+ val list = singleTaskGroupInfo().taskInfoList
+ assertThat(list).hasSize(1)
+ assertThat(list[0].taskId).isEqualTo(1)
+ }
+
+ @Test
+ fun testSplitTasks_hasCorrectType() {
+ assertThat(splitTasksGroupInfo().type).isEqualTo(TYPE_SPLIT)
+ }
+
+ @Test
+ fun testSplitTasks_task1Set_task2Set_boundsSet() {
+ val group = splitTasksGroupInfo()
+ assertThat(group.taskInfo1.taskId).isEqualTo(1)
+ assertThat(group.taskInfo2?.taskId).isEqualTo(2)
+ assertThat(group.splitBounds).isNotNull()
+ }
+
+ @Test
+ fun testSplitTasks_taskInfoList_hasTwoTasks() {
+ val list = splitTasksGroupInfo().taskInfoList
+ assertThat(list).hasSize(2)
+ assertThat(list[0].taskId).isEqualTo(1)
+ assertThat(list[1].taskId).isEqualTo(2)
+ }
+
+ @Test
+ fun testFreeformTasks_hasCorrectType() {
+ assertThat(freeformTasksGroupInfo().type).isEqualTo(TYPE_FREEFORM)
+ }
+
+ @Test
+ fun testSplitTasks_taskInfoList_hasThreeTasks() {
+ val list = freeformTasksGroupInfo().taskInfoList
+ assertThat(list).hasSize(3)
+ assertThat(list[0].taskId).isEqualTo(1)
+ assertThat(list[1].taskId).isEqualTo(2)
+ assertThat(list[2].taskId).isEqualTo(3)
+ }
+
+ @Test
+ fun testParcelling_singleTask() {
+ val recentTaskInfo = singleTaskGroupInfo()
+ val parcel = Parcel.obtain()
+ recentTaskInfo.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+ // Read the object back from the parcel
+ val recentTaskInfoParcel = CREATOR.createFromParcel(parcel)
+ assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_SINGLE)
+ assertThat(recentTaskInfoParcel.taskInfo1.taskId).isEqualTo(1)
+ assertThat(recentTaskInfoParcel.taskInfo2).isNull()
+ }
+
+ @Test
+ fun testParcelling_splitTasks() {
+ val recentTaskInfo = splitTasksGroupInfo()
+ val parcel = Parcel.obtain()
+ recentTaskInfo.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+ // Read the object back from the parcel
+ val recentTaskInfoParcel = CREATOR.createFromParcel(parcel)
+ assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_SPLIT)
+ assertThat(recentTaskInfoParcel.taskInfo1.taskId).isEqualTo(1)
+ assertThat(recentTaskInfoParcel.taskInfo2).isNotNull()
+ assertThat(recentTaskInfoParcel.taskInfo2!!.taskId).isEqualTo(2)
+ assertThat(recentTaskInfoParcel.splitBounds).isNotNull()
+ }
+
+ @Test
+ fun testParcelling_freeformTasks() {
+ val recentTaskInfo = freeformTasksGroupInfo()
+ val parcel = Parcel.obtain()
+ recentTaskInfo.writeToParcel(parcel, 0)
+ parcel.setDataPosition(0)
+ // Read the object back from the parcel
+ val recentTaskInfoParcel = CREATOR.createFromParcel(parcel)
+ assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_FREEFORM)
+ assertThat(recentTaskInfoParcel.taskInfoList).hasSize(3)
+ // Only compare task ids
+ val taskIdComparator = Correspondence.transforming<ActivityManager.RecentTaskInfo, Int>(
+ { it?.taskId }, "has taskId of"
+ )
+ assertThat(recentTaskInfoParcel.taskInfoList).comparingElementsUsing(taskIdComparator)
+ .containsExactly(1, 2, 3)
+ }
+
+ private fun createTaskInfo(id: Int) = ActivityManager.RecentTaskInfo().apply {
+ taskId = id
+ token = WindowContainerToken(mock(IWindowContainerToken::class.java))
+ }
+
+ private fun singleTaskGroupInfo(): GroupedRecentTaskInfo {
+ val task = createTaskInfo(id = 1)
+ return GroupedRecentTaskInfo.forSingleTask(task)
+ }
+
+ private fun splitTasksGroupInfo(): GroupedRecentTaskInfo {
+ val task1 = createTaskInfo(id = 1)
+ val task2 = createTaskInfo(id = 2)
+ val splitBounds = SplitBounds(Rect(), Rect(), 1, 2)
+ return GroupedRecentTaskInfo.forSplitTasks(task1, task2, splitBounds)
+ }
+
+ private fun freeformTasksGroupInfo(): GroupedRecentTaskInfo {
+ val task1 = createTaskInfo(id = 1)
+ val task2 = createTaskInfo(id = 2)
+ val task3 = createTaskInfo(id = 3)
+ return GroupedRecentTaskInfo.forFreeformTasks(task1, task2, task3)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 9e755dc..e9a1e25 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -213,8 +213,8 @@
assertEquals(GroupedRecentTaskInfo.TYPE_SINGLE, singleGroup2.getType());
// Check freeform group entries
- assertEquals(t1, freeformGroup.getAllTaskInfos().get(0));
- assertEquals(t3, freeformGroup.getAllTaskInfos().get(1));
+ assertEquals(t1, freeformGroup.getTaskInfoList().get(0));
+ assertEquals(t3, freeformGroup.getTaskInfoList().get(1));
// Check single entries
assertEquals(t2, singleGroup1.getTaskInfo1());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index a67853c..ae69b3d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -71,11 +71,11 @@
DisplayController displayController, DisplayImeController imeController,
DisplayInsetsController insetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
- SplitscreenEventLogger logger, ShellExecutor mainExecutor,
+ ShellExecutor mainExecutor,
Optional<RecentTasksController> recentTasks) {
super(context, displayId, syncQueue, taskOrganizer, mainStage,
sideStage, displayController, imeController, insetsController, splitLayout,
- transitions, transactionPool, logger, mainExecutor, recentTasks);
+ transitions, transactionPool, mainExecutor, recentTasks);
// Prepare root task for testing.
mRootTask = new TestRunningTaskInfoBuilder().build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 1d038f4..ea0033b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -95,7 +95,6 @@
@Mock private TransactionPool mTransactionPool;
@Mock private Transitions mTransitions;
@Mock private SurfaceSession mSurfaceSession;
- @Mock private SplitscreenEventLogger mLogger;
@Mock private IconProvider mIconProvider;
@Mock private ShellExecutor mMainExecutor;
private SplitLayout mSplitLayout;
@@ -127,7 +126,7 @@
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController,
mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
- mTransactionPool, mLogger, mMainExecutor, Optional.empty());
+ mTransactionPool, mMainExecutor, Optional.empty());
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
.when(mTransitions).startTransition(anyInt(), any(), any());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 4b68870..ea9390e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -97,8 +97,6 @@
@Mock
private TransactionPool mTransactionPool;
@Mock
- private SplitscreenEventLogger mLogger;
- @Mock
private ShellExecutor mMainExecutor;
private final Rect mBounds1 = new Rect(10, 20, 30, 40);
@@ -115,7 +113,7 @@
MockitoAnnotations.initMocks(this);
mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool, mLogger,
+ mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool,
mMainExecutor, Optional.empty()));
doNothing().when(mStageCoordinator).updateActivityOptions(any(), anyInt());
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
index ead5174..0de7be0 100644
--- a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
+++ b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
@@ -43,6 +43,8 @@
<color name="settingslib_color_grey400">#bdc1c6</color>
<color name="settingslib_color_grey300">#dadce0</color>
<color name="settingslib_color_grey200">#e8eaed</color>
+ <color name="settingslib_color_grey100">#f1f3f4</color>
+ <color name="settingslib_color_grey50">#f8f9fa</color>
<color name="settingslib_color_orange600">#e8710a</color>
<color name="settingslib_color_orange400">#fa903e</color>
<color name="settingslib_color_orange300">#fcad70</color>
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
new file mode 100644
index 0000000..93b6acc
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2022 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.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+
+import com.airbnb.lottie.LottieAnimationView;
+import com.airbnb.lottie.LottieProperty;
+import com.airbnb.lottie.model.KeyPath;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Util class which dynamically changes the color of tags in a lottie json file between Dark Theme
+ * (DT) and Light Theme (LT). This class assumes the json file is for Dark Theme.
+ */
+public class LottieColorUtils {
+ private static final Map<String, Integer> DARK_TO_LIGHT_THEME_COLOR_MAP;
+
+ static {
+ HashMap<String, Integer> map = new HashMap<>();
+ map.put(
+ ".grey600",
+ R.color.settingslib_color_grey300);
+ map.put(
+ ".grey800",
+ R.color.settingslib_color_grey200);
+ map.put(
+ ".grey900",
+ R.color.settingslib_color_grey50);
+ map.put(
+ ".red400",
+ R.color.settingslib_color_red600);
+ map.put(
+ ".black",
+ android.R.color.white);
+ map.put(
+ ".blue400",
+ R.color.settingslib_color_blue600);
+ map.put(
+ ".green400",
+ R.color.settingslib_color_green600);
+ DARK_TO_LIGHT_THEME_COLOR_MAP = Collections.unmodifiableMap(map);
+ }
+
+ private LottieColorUtils() {
+ }
+
+ private static boolean isDarkMode(Context context) {
+ return (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
+ == Configuration.UI_MODE_NIGHT_YES;
+ }
+
+ /** Applies dynamic colors based on DT vs. LT. The LottieAnimationView should be Dark Theme. */
+ public static void applyDynamicColors(Context context,
+ LottieAnimationView lottieAnimationView) {
+ // Assume the default for the lottie is dark mode
+ if (isDarkMode(context)) {
+ return;
+ }
+ for (String key : DARK_TO_LIGHT_THEME_COLOR_MAP.keySet()) {
+ final int color = context.getColor(DARK_TO_LIGHT_THEME_COLOR_MAP.get(key));
+ lottieAnimationView.addValueCallback(
+ new KeyPath("**", key, "**"),
+ LottieProperty.COLOR_FILTER,
+ frameInfo -> new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
+ }
+ }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt
new file mode 100644
index 0000000..b006615
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2022 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.internal.systemui.lint
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UCallExpression
+
+/**
+ * Checks for slow calls to ActivityManager.getCurrentUser() or UserManager.getUserInfo() and
+ * suggests using UserTracker instead. For more info, see: http://go/multi-user-in-systemui-slides.
+ */
+@Suppress("UnstableApiUsage")
+class SlowUserQueryDetector : Detector(), SourceCodeScanner {
+
+ override fun getApplicableMethodNames(): List<String> {
+ return listOf("getCurrentUser", "getUserInfo")
+ }
+
+ override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+ val evaluator = context.evaluator
+ if (
+ evaluator.isStatic(method) &&
+ method.name == "getCurrentUser" &&
+ method.containingClass?.qualifiedName == "android.app.ActivityManager"
+ ) {
+ context.report(
+ ISSUE_SLOW_USER_ID_QUERY,
+ method,
+ context.getNameLocation(node),
+ "ActivityManager.getCurrentUser() is slow. " +
+ "Use UserTracker.getUserId() instead."
+ )
+ }
+ if (
+ !evaluator.isStatic(method) &&
+ method.name == "getUserInfo" &&
+ method.containingClass?.qualifiedName == "android.os.UserManager"
+ ) {
+ context.report(
+ ISSUE_SLOW_USER_INFO_QUERY,
+ method,
+ context.getNameLocation(node),
+ "UserManager.getUserInfo() is slow. " + "Use UserTracker.getUserInfo() instead."
+ )
+ }
+ }
+
+ companion object {
+ @JvmField
+ val ISSUE_SLOW_USER_ID_QUERY: Issue =
+ Issue.create(
+ id = "SlowUserIdQuery",
+ briefDescription = "User ID queried using ActivityManager instead of UserTracker.",
+ explanation =
+ "ActivityManager.getCurrentUser() makes a binder call and is slow. " +
+ "Instead, inject a UserTracker and call UserTracker.getUserId(). For " +
+ "more info, see: http://go/multi-user-in-systemui-slides",
+ category = Category.PERFORMANCE,
+ priority = 8,
+ severity = Severity.WARNING,
+ implementation =
+ Implementation(SlowUserQueryDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
+
+ @JvmField
+ val ISSUE_SLOW_USER_INFO_QUERY: Issue =
+ Issue.create(
+ id = "SlowUserInfoQuery",
+ briefDescription = "User info queried using UserManager instead of UserTracker.",
+ explanation =
+ "UserManager.getUserInfo() makes a binder call and is slow. " +
+ "Instead, inject a UserTracker and call UserTracker.getUserInfo(). For " +
+ "more info, see: http://go/multi-user-in-systemui-slides",
+ category = Category.PERFORMANCE,
+ priority = 8,
+ severity = Severity.WARNING,
+ implementation =
+ Implementation(SlowUserQueryDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
+ }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
index c7c73d3..4879883 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -30,6 +30,8 @@
get() = listOf(
BindServiceViaContextDetector.ISSUE,
BroadcastSentViaContextDetector.ISSUE,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY,
GetMainLooperViaContextDetector.ISSUE,
RegisterReceiverViaContextDetector.ISSUE,
SoftwareBitmapDetector.ISSUE,
diff --git a/packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt
new file mode 100644
index 0000000..2738f04
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt
@@ -0,0 +1,194 @@
+package com.android.internal.systemui.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+class SlowUserQueryDetectorTest : LintDetectorTest() {
+
+ override fun getDetector(): Detector = SlowUserQueryDetector()
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+ override fun getIssues(): List<Issue> =
+ listOf(
+ SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+ )
+
+ @Test
+ fun testGetCurrentUser() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.app.ActivityManager;
+
+ public class TestClass1 {
+ public void slewlyGetCurrentUser() {
+ ActivityManager.getCurrentUser();
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(
+ SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+ )
+ .run()
+ .expectWarningCount(1)
+ .expectContains(
+ "ActivityManager.getCurrentUser() is slow. " +
+ "Use UserTracker.getUserId() instead."
+ )
+ }
+
+ @Test
+ fun testGetUserInfo() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import android.os.UserManager;
+
+ public class TestClass2 {
+ public void slewlyGetUserInfo(UserManager userManager) {
+ userManager.getUserInfo();
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(
+ SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+ )
+ .run()
+ .expectWarningCount(1)
+ .expectContains(
+ "UserManager.getUserInfo() is slow. " + "Use UserTracker.getUserInfo() instead."
+ )
+ }
+
+ @Test
+ fun testUserTrackerGetUserId() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import com.android.systemui.settings.UserTracker;
+
+ public class TestClass3 {
+ public void quicklyGetUserId(UserTracker userTracker) {
+ userTracker.getUserId();
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(
+ SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+ )
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun testUserTrackerGetUserInfo() {
+ lint()
+ .files(
+ TestFiles.java(
+ """
+ package test.pkg;
+ import com.android.systemui.settings.UserTracker;
+
+ public class TestClass4 {
+ public void quicklyGetUserId(UserTracker userTracker) {
+ userTracker.getUserInfo();
+ }
+ }
+ """
+ )
+ .indented(),
+ *stubs
+ )
+ .issues(
+ SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+ SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+ )
+ .run()
+ .expectClean()
+ }
+
+ private val activityManagerStub: TestFile =
+ java(
+ """
+ package android.app;
+
+ public class ActivityManager {
+ public static int getCurrentUser() {};
+ }
+ """
+ )
+
+ private val userManagerStub: TestFile =
+ java(
+ """
+ package android.os;
+ import android.content.pm.UserInfo;
+ import android.annotation.UserIdInt;
+
+ public class UserManager {
+ public UserInfo getUserInfo(@UserIdInt int userId) {};
+ }
+ """
+ )
+
+ private val userIdIntStub: TestFile =
+ java(
+ """
+ package android.annotation;
+
+ public @interface UserIdInt {}
+ """
+ )
+
+ private val userInfoStub: TestFile =
+ java(
+ """
+ package android.content.pm;
+
+ public class UserInfo {}
+ """
+ )
+
+ private val userTrackerStub: TestFile =
+ java(
+ """
+ package com.android.systemui.settings;
+ import android.content.pm.UserInfo;
+
+ public interface UserTracker {
+ public int getUserId();
+ public UserInfo getUserInfo();
+ }
+ """
+ )
+
+ private val stubs =
+ arrayOf(activityManagerStub, userManagerStub, userIdIntStub, userInfoStub, userTrackerStub)
+}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt
new file mode 100644
index 0000000..02d76f4
--- /dev/null
+++ b/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2022 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.user
+
+import android.content.Context
+import androidx.appcompat.content.res.AppCompatResources
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.compose.gallery.R
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.user.shared.model.UserModel
+import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
+import com.android.systemui.util.mockito.mock
+
+object Fakes {
+ private val USER_TINT_COLORS =
+ arrayOf(
+ 0x000000,
+ 0x0000ff,
+ 0x00ff00,
+ 0x00ffff,
+ 0xff0000,
+ 0xff00ff,
+ 0xffff00,
+ 0xffffff,
+ )
+
+ fun fakeUserSwitcherViewModel(
+ context: Context,
+ userCount: Int,
+ ): UserSwitcherViewModel {
+ return UserSwitcherViewModel.Factory(
+ userInteractor =
+ UserInteractor(
+ repository =
+ FakeUserRepository().apply {
+ setUsers(
+ (0 until userCount).map { index ->
+ UserModel(
+ id = index,
+ name = Text.Loaded("user_$index"),
+ image =
+ checkNotNull(
+ AppCompatResources.getDrawable(
+ context,
+ R.drawable.ic_avatar_guest_user
+ )
+ ),
+ isSelected = index == 0,
+ isSelectable = true,
+ )
+ }
+ )
+ setActions(
+ UserActionModel.values().mapNotNull {
+ if (it == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT) {
+ null
+ } else {
+ it
+ }
+ }
+ )
+ },
+ controller = mock(),
+ activityStarter = mock(),
+ keyguardInteractor =
+ KeyguardInteractor(
+ repository =
+ FakeKeyguardRepository().apply { setKeyguardShowing(false) },
+ ),
+ ),
+ powerInteractor =
+ PowerInteractor(
+ repository = FakePowerRepository(),
+ )
+ )
+ .create(UserSwitcherViewModel::class.java)
+ }
+}
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 51cc195..689938a 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -240,8 +240,6 @@
-packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesManager.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
-packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -529,6 +527,8 @@
-packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
-packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt
-packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
+-packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
+-packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
-packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt
-packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
-packages/SystemUI/src/com/android/systemui/tv/TVSystemUICoreStartableModule.kt
@@ -677,7 +677,6 @@
-packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
-packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -834,6 +833,7 @@
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
-packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml
new file mode 100644
index 0000000..57b3acd
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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
+ -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <com.android.keyguard.BouncerKeyguardMessageArea
+ android:id="@+id/bouncer_message_area"
+ style="@style/Keyguard.TextView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/keyguard_lock_padding"
+ android:ellipsize="marquee"
+ android:focusable="true"
+ android:gravity="center"
+ android:singleLine="true" />
+</merge>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
index e77e084..5486adb 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -28,6 +28,7 @@
android:layout_gravity="center_horizontal|bottom"
android:gravity="bottom"
>
+ <include layout="@layout/keyguard_bouncer_message_area"/>
<Space
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
index 231ead8..2b7bdc2 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -31,6 +31,7 @@
android:layout_gravity="center_horizontal|bottom"
android:clipChildren="false"
android:clipToPadding="false">
+ <include layout="@layout/keyguard_bouncer_message_area"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/pattern_container"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index 5936ead..64ece47 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -27,6 +27,7 @@
android:clipToPadding="false"
android:orientation="vertical"
androidprv:layout_maxWidth="@dimen/keyguard_security_width">
+<include layout="@layout/keyguard_bouncer_message_area"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/pin_container"
@@ -189,8 +190,6 @@
</androidx.constraintlayout.widget.ConstraintLayout>
-
-
<include layout="@layout/keyguard_eca"
android:id="@+id/keyguard_selector_fade_container"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
index dae2e56..f2fe520 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
@@ -26,20 +26,17 @@
android:layout_height="match_parent"
androidprv:layout_maxWidth="@dimen/keyguard_security_width"
android:layout_gravity="center_horizontal|bottom">
-
- <Space
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- />
-
+ <include layout="@layout/keyguard_bouncer_message_area" />
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
<ImageView
android:id="@+id/keyguard_sim"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tint="@color/background_protected"
android:src="@drawable/ic_lockscreen_sim"/>
-
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -52,14 +49,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/eca_overlap" />
-
<RelativeLayout
android:id="@+id/row0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="4dp"
>
-
<com.android.keyguard.PasswordTextView
android:id="@+id/simPinEntry"
style="@style/Widget.TextView.Password"
@@ -195,7 +190,6 @@
/>
</LinearLayout>
</LinearLayout>
-
<include layout="@layout/keyguard_eca"
android:id="@+id/keyguard_selector_fade_container"
android:layout_width="match_parent"
@@ -205,5 +199,4 @@
android:layout_marginTop="@dimen/keyguard_eca_top_margin"
android:layout_marginBottom="2dp"
android:gravity="center_horizontal"/>
-
</com.android.keyguard.KeyguardSimPinView>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
index 74f7820..a21ec29 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
@@ -27,12 +27,12 @@
android:layout_height="match_parent"
androidprv:layout_maxWidth="@dimen/keyguard_security_width"
android:layout_gravity="center_horizontal|bottom">
+ <include layout="@layout/keyguard_bouncer_message_area"/>
- <Space
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- />
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
<ImageView
android:id="@+id/keyguard_sim"
diff --git a/packages/SystemUI/res/drawable/media_seekbar_thumb.xml b/packages/SystemUI/res/drawable/media_seekbar_thumb.xml
new file mode 100644
index 0000000..5eb2bfd
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_seekbar_thumb.xml
@@ -0,0 +1,50 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="16dp"
+ android:width="4dp"
+ android:viewportHeight="16"
+ android:viewportWidth="4">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_0_G"
+ android:translateX="2"
+ android:translateY="8">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M2 -6 C2,-6 2,6 2,6 C2,7.1 1.1,8 0,8 C0,8 0,8 0,8 C-1.1,8 -2,7.1 -2,6 C-2,6 -2,-6 -2,-6 C-2,-7.1 -1.1,-8 0,-8 C0,-8 0,-8 0,-8 C1.1,-8 2,-7.1 2,-6c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="1017"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 86f8ce2..0c57b934 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -88,7 +88,7 @@
android:layout_marginTop="@dimen/status_bar_height"
android:layout_gravity="top|center_horizontal"
android:gravity="center_horizontal">
- <com.android.keyguard.KeyguardMessageArea
+ <com.android.keyguard.AuthKeyguardMessageArea
android:id="@+id/keyguard_message_area"
style="@style/Keyguard.TextView"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/values-sw720dp-port/dimens.xml b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
index a0bf072..3d8da8a 100644
--- a/packages/SystemUI/res/values-sw720dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
@@ -23,7 +23,7 @@
<dimen name="status_view_margin_horizontal">124dp</dimen>
<dimen name="keyguard_clock_top_margin">80dp</dimen>
<dimen name="keyguard_status_view_bottom_margin">80dp</dimen>
- <dimen name="bouncer_user_switcher_y_trans">90dp</dimen>
+ <dimen name="bouncer_user_switcher_y_trans">200dp</dimen>
<dimen name="large_screen_shade_header_left_padding">24dp</dimen>
<dimen name="qqs_layout_padding_bottom">40dp</dimen>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index ae9ebba..ad8113a 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -630,11 +630,15 @@
<!-- This value is used when calculating whether the device is in ambient light mode. It is
light mode when the light sensor sample value exceeds above this value. -->
- <integer name="config_ambientLightModeThreshold">10</integer>
+ <item name="config_ambientLightModeThreshold" translatable="false" format="float" type="dimen">
+ 0.8
+ </item>
<!-- This value is used when calculating whether the device is in ambient dark mode. It is
dark mode when the light sensor sample value drops below this value. -->
- <integer name="config_ambientDarkModeThreshold">5</integer>
+ <item name="config_ambientDarkModeThreshold" translatable="false" format="float" type="dimen">
+ 0.4
+ </item>
<!-- This value is used when calculating whether the device is in ambient light mode. Each
sample contains light sensor events from this span of time duration. -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index a36a518..ac3eb7e 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -573,6 +573,7 @@
</style>
<style name="MediaPlayer.ProgressBar" parent="@android:style/Widget.ProgressBar.Horizontal">
+ <item name="android:thumb">@drawable/media_seekbar_thumb</item>
<item name="android:thumbTint">?android:attr/textColorPrimary</item>
<item name="android:progressDrawable">@drawable/media_squiggly_progress</item>
<item name="android:progressTint">?android:attr/textColorPrimary</item>
diff --git a/packages/SystemUI/src/com/android/keyguard/AuthKeyguardMessageArea.kt b/packages/SystemUI/src/com/android/keyguard/AuthKeyguardMessageArea.kt
new file mode 100644
index 0000000..82ce1ca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/AuthKeyguardMessageArea.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 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.keyguard
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.graphics.Color
+import android.util.AttributeSet
+
+/**
+ * Displays security messages for auth outside of the security method (pin, password, pattern), like
+ * biometric auth.
+ */
+class AuthKeyguardMessageArea(context: Context?, attrs: AttributeSet?) :
+ KeyguardMessageArea(context, attrs) {
+ override fun updateTextColor() {
+ setTextColor(ColorStateList.valueOf(Color.WHITE))
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
new file mode 100644
index 0000000..0075ddd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 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.keyguard
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.content.res.TypedArray
+import android.graphics.Color
+import android.util.AttributeSet
+import com.android.settingslib.Utils
+
+/** Displays security messages for the keyguard bouncer. */
+class BouncerKeyguardMessageArea(context: Context?, attrs: AttributeSet?) :
+ KeyguardMessageArea(context, attrs) {
+ private val DEFAULT_COLOR = -1
+ private var mDefaultColorState: ColorStateList? = null
+ private var mNextMessageColorState: ColorStateList? = ColorStateList.valueOf(DEFAULT_COLOR)
+
+ override fun updateTextColor() {
+ var colorState = mDefaultColorState
+ mNextMessageColorState?.defaultColor?.let { color ->
+ if (color != DEFAULT_COLOR) {
+ colorState = mNextMessageColorState
+ mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR)
+ }
+ }
+ setTextColor(colorState)
+ }
+
+ override fun setNextMessageColor(colorState: ColorStateList?) {
+ mNextMessageColorState = colorState
+ }
+
+ override fun onThemeChanged() {
+ val array: TypedArray =
+ mContext.obtainStyledAttributes(intArrayOf(android.R.attr.textColorPrimary))
+ val newTextColors: ColorStateList = ColorStateList.valueOf(array.getColor(0, Color.RED))
+ array.recycle()
+ mDefaultColorState = newTextColors
+ super.onThemeChanged()
+ }
+
+ override fun reloadColor() {
+ mDefaultColorState = Utils.getColorAttr(context, android.R.attr.textColorPrimary)
+ super.reloadColor()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index b8fcb10..92ba619 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -50,7 +50,6 @@
private final FalsingCollector mFalsingCollector;
private final EmergencyButtonController mEmergencyButtonController;
private CountDownTimer mCountdownTimer;
- protected KeyguardMessageAreaController mMessageAreaController;
private boolean mDismissing;
protected AsyncTask<?, ?, ?> mPendingLockCheck;
protected boolean mResumed;
@@ -80,14 +79,13 @@
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, FalsingCollector falsingCollector,
EmergencyButtonController emergencyButtonController) {
- super(view, securityMode, keyguardSecurityCallback, emergencyButtonController);
+ super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
+ messageAreaControllerFactory);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
mFalsingCollector = falsingCollector;
mEmergencyButtonController = emergencyButtonController;
- KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
- mMessageAreaController = messageAreaControllerFactory.create(kma);
}
abstract void resetState();
@@ -95,7 +93,6 @@
@Override
public void onInit() {
super.onInit();
- mMessageAreaController.init();
}
@Override
@@ -134,6 +131,10 @@
@Override
public void showMessage(CharSequence message, ColorStateList colorState) {
+ if (mMessageAreaController == null) {
+ return;
+ }
+
if (colorState != null) {
mMessageAreaController.setNextMessageColor(colorState);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 50c9193..f26b905 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -17,9 +17,11 @@
package com.android.keyguard;
import android.annotation.CallSuper;
+import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.telephony.TelephonyManager;
+import android.util.Log;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.util.LatencyTracker;
@@ -44,6 +46,7 @@
private final EmergencyButton mEmergencyButton;
private final EmergencyButtonController mEmergencyButtonController;
private boolean mPaused;
+ protected KeyguardMessageAreaController<BouncerKeyguardMessageArea> mMessageAreaController;
// The following is used to ignore callbacks from SecurityViews that are no longer current
// (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
@@ -71,12 +74,24 @@
protected KeyguardInputViewController(T view, SecurityMode securityMode,
KeyguardSecurityCallback keyguardSecurityCallback,
- EmergencyButtonController emergencyButtonController) {
+ EmergencyButtonController emergencyButtonController,
+ @Nullable KeyguardMessageAreaController.Factory messageAreaControllerFactory) {
super(view);
mSecurityMode = securityMode;
mKeyguardSecurityCallback = keyguardSecurityCallback;
mEmergencyButton = view == null ? null : view.findViewById(R.id.emergency_call_button);
mEmergencyButtonController = emergencyButtonController;
+ if (messageAreaControllerFactory != null) {
+ try {
+ BouncerKeyguardMessageArea kma = view.requireViewById(R.id.bouncer_message_area);
+ mMessageAreaController = messageAreaControllerFactory.create(kma);
+ mMessageAreaController.init();
+ mMessageAreaController.setIsVisible(true);
+ } catch (IllegalArgumentException exception) {
+ Log.e("KeyguardInputViewController",
+ "Ensure that a BouncerKeyguardMessageArea is included in the layout");
+ }
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index 5ab2fd0..c79fc2c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -17,9 +17,7 @@
package com.android.keyguard;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.TypedArray;
-import android.graphics.Color;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
@@ -33,7 +31,6 @@
import androidx.annotation.Nullable;
import com.android.internal.policy.SystemBarUtils;
-import com.android.settingslib.Utils;
import com.android.systemui.R;
import java.lang.ref.WeakReference;
@@ -41,7 +38,7 @@
/***
* Manages a number of views inside of the given layout. See below for a list of widgets.
*/
-public class KeyguardMessageArea extends TextView implements SecurityMessageDisplay {
+public abstract class KeyguardMessageArea extends TextView implements SecurityMessageDisplay {
/** Handler token posted with accessibility announcement runnables. */
private static final Object ANNOUNCE_TOKEN = new Object();
@@ -50,15 +47,11 @@
* lift-to-type from interrupting itself.
*/
private static final long ANNOUNCEMENT_DELAY = 250;
- private static final int DEFAULT_COLOR = -1;
private final Handler mHandler;
- private ColorStateList mDefaultColorState;
private CharSequence mMessage;
- private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
- private boolean mBouncerShowing;
- private boolean mAltBouncerShowing;
+ private boolean mIsVisible;
/**
* Container that wraps the KeyguardMessageArea - may be null if current view hierarchy doesn't
* contain {@link R.id.keyguard_message_area_container}.
@@ -96,23 +89,11 @@
mContainer.setLayoutParams(lp);
}
- @Override
- public void setNextMessageColor(ColorStateList colorState) {
- mNextMessageColorState = colorState;
- }
-
- void onThemeChanged() {
- TypedArray array = mContext.obtainStyledAttributes(new int[] {
- android.R.attr.textColorPrimary
- });
- ColorStateList newTextColors = ColorStateList.valueOf(array.getColor(0, Color.RED));
- array.recycle();
- mDefaultColorState = newTextColors;
+ protected void onThemeChanged() {
update();
}
- void reloadColor() {
- mDefaultColorState = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary);
+ protected void reloadColor() {
update();
}
@@ -151,17 +132,6 @@
setMessage(message);
}
- public static KeyguardMessageArea findSecurityMessageDisplay(View v) {
- KeyguardMessageArea messageArea = v.findViewById(R.id.keyguard_message_area);
- if (messageArea == null) {
- messageArea = v.getRootView().findViewById(R.id.keyguard_message_area);
- }
- if (messageArea == null) {
- throw new RuntimeException("Can't find keyguard_message_area in " + v.getClass());
- }
- return messageArea;
- }
-
private void securityMessageChanged(CharSequence message) {
mMessage = message;
update();
@@ -177,40 +147,23 @@
void update() {
CharSequence status = mMessage;
- setVisibility(TextUtils.isEmpty(status) || (!mBouncerShowing && !mAltBouncerShowing)
- ? INVISIBLE : VISIBLE);
+ setVisibility(TextUtils.isEmpty(status) || (!mIsVisible) ? INVISIBLE : VISIBLE);
setText(status);
- ColorStateList colorState = mDefaultColorState;
- if (mNextMessageColorState.getDefaultColor() != DEFAULT_COLOR) {
- colorState = mNextMessageColorState;
- mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
- }
- if (mAltBouncerShowing) {
- // alt bouncer has a black scrim, so always show the text in white
- colorState = ColorStateList.valueOf(Color.WHITE);
- }
- setTextColor(colorState);
+ updateTextColor();
}
/**
* Set whether the bouncer is fully showing
*/
- public void setBouncerShowing(boolean bouncerShowing) {
- if (mBouncerShowing != bouncerShowing) {
- mBouncerShowing = bouncerShowing;
+ public void setIsVisible(boolean isVisible) {
+ if (mIsVisible != isVisible) {
+ mIsVisible = isVisible;
update();
}
}
- /**
- * Set whether the alt bouncer is showing
- */
- void setAltBouncerShowing(boolean showing) {
- if (mAltBouncerShowing != showing) {
- mAltBouncerShowing = showing;
- update();
- }
- }
+ /** Set the text color */
+ protected abstract void updateTextColor();
/**
* Runnable used to delay accessibility announcements.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index 3ec8ce9..c2802f7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -26,11 +26,14 @@
import javax.inject.Inject;
-/** Controller for a {@link KeyguardMessageAreaController}. */
-public class KeyguardMessageAreaController extends ViewController<KeyguardMessageArea> {
+/**
+ * Controller for a {@link KeyguardMessageAreaController}.
+ * @param <T> A subclass of KeyguardMessageArea.
+ */
+public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
+ extends ViewController<T> {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ConfigurationController mConfigurationController;
- private boolean mAltBouncerShowing;
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
public void onFinishedGoingToSleep(int why) {
@@ -59,7 +62,7 @@
}
};
- private KeyguardMessageAreaController(KeyguardMessageArea view,
+ protected KeyguardMessageAreaController(T view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
ConfigurationController configurationController) {
super(view);
@@ -83,17 +86,10 @@
}
/**
- * Set whether alt bouncer is showing
+ * Indicate that view is visible and can display messages.
*/
- public void setAltBouncerShowing(boolean showing) {
- mView.setAltBouncerShowing(showing);
- }
-
- /**
- * Set bouncer is fully showing
- */
- public void setBouncerShowing(boolean showing) {
- mView.setBouncerShowing(showing);
+ public void setIsVisible(boolean isVisible) {
+ mView.setIsVisible(isVisible);
}
public void setMessage(CharSequence s) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 1862fc7..afc2590 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -71,7 +71,7 @@
*/
private long mLastPokeTime = -UNLOCK_PATTERN_WAKE_INTERVAL_MS;
- KeyguardMessageArea mSecurityMessageDisplay;
+ BouncerKeyguardMessageArea mSecurityMessageDisplay;
private View mEcaView;
private ConstraintLayout mContainer;
@@ -120,7 +120,7 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mSecurityMessageDisplay = KeyguardMessageArea.findSecurityMessageDisplay(this);
+ mSecurityMessageDisplay = findViewById(R.id.bouncer_message_area);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 9aa6f03..9871645 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -59,12 +59,9 @@
private final LatencyTracker mLatencyTracker;
private final FalsingCollector mFalsingCollector;
private final EmergencyButtonController mEmergencyButtonController;
- private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory;
private final DevicePostureController mPostureController;
private final DevicePostureController.Callback mPostureCallback =
posture -> mView.onDevicePostureChanged(posture);
-
- private KeyguardMessageAreaController mMessageAreaController;
private LockPatternView mLockPatternView;
private CountDownTimer mCountdownTimer;
private AsyncTask<?, ?, ?> mPendingLockCheck;
@@ -201,15 +198,13 @@
EmergencyButtonController emergencyButtonController,
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
DevicePostureController postureController) {
- super(view, securityMode, keyguardSecurityCallback, emergencyButtonController);
+ super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
+ messageAreaControllerFactory);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
mFalsingCollector = falsingCollector;
mEmergencyButtonController = emergencyButtonController;
- mMessageAreaControllerFactory = messageAreaControllerFactory;
- KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
- mMessageAreaController = mMessageAreaControllerFactory.create(kma);
mLockPatternView = mView.findViewById(R.id.lockPatternView);
mPostureController = postureController;
}
@@ -217,7 +212,6 @@
@Override
public void onInit() {
super.onInit();
- mMessageAreaController.init();
}
@Override
@@ -346,6 +340,9 @@
@Override
public void showMessage(CharSequence message, ColorStateList colorState) {
+ if (mMessageAreaController == null) {
+ return;
+ }
if (colorState != null) {
mMessageAreaController.setNextMessageColor(colorState);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index d0baf3d..f73c98e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -665,7 +665,8 @@
// When using EXACTLY spec, measure will use the layout width if > 0. Set before
// measuring the child
lp.width = MeasureSpec.getSize(updatedWidthMeasureSpec);
- measureChildWithMargins(view, updatedWidthMeasureSpec, 0, heightMeasureSpec, 0);
+ measureChildWithMargins(view, updatedWidthMeasureSpec, 0,
+ heightMeasureSpec, 0);
maxWidth = Math.max(maxWidth,
view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
@@ -1306,7 +1307,6 @@
int yTrans = mResources.getDimensionPixelSize(R.dimen.bouncer_user_switcher_y_trans);
if (mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
mUserSwitcherViewGroup.setTranslationY(yTrans);
- mViewFlipper.setTranslationY(-yTrans);
} else {
// Attempt to reposition a bit higher to make up for this frame being a bit lower
// on the device
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
index 51b68b7..bddf4b0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
@@ -146,7 +146,8 @@
protected NullKeyguardInputViewController(SecurityMode securityMode,
KeyguardSecurityCallback keyguardSecurityCallback,
EmergencyButtonController emergencyButtonController) {
- super(null, securityMode, keyguardSecurityCallback, emergencyButtonController);
+ super(null, securityMode, keyguardSecurityCallback, emergencyButtonController,
+ null);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 4984300..4283832 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -466,6 +466,7 @@
FACE_AUTH_TRIGGERED_TRUST_DISABLED);
}
+ mLogger.logTrustChanged(wasTrusted, enabled, userId);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -481,12 +482,16 @@
final boolean userHasTrust = getUserHasTrust(userId);
if (userHasTrust && trustGrantedMessages != null) {
for (String msg : trustGrantedMessages) {
- if (!TextUtils.isEmpty(msg)) {
- message = msg;
+ message = msg;
+ if (!TextUtils.isEmpty(message)) {
break;
}
}
}
+
+ if (message != null) {
+ mLogger.logShowTrustGrantedMessage(message.toString());
+ }
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -743,6 +748,7 @@
mFingerprintCancelSignal = null;
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_UPDATED_FP_AUTHENTICATED);
+ mLogger.d("onFingerprintAuthenticated");
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -986,6 +992,7 @@
mFaceCancelSignal = null;
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
FACE_AUTH_UPDATED_ON_FACE_AUTHENTICATED);
+ mLogger.d("onFaceAuthenticated");
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -3445,6 +3452,7 @@
mUserFaceAuthenticated.clear();
mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FINGERPRINT, unlockedUser);
mTrustManager.clearAllBiometricRecognized(BiometricSourceType.FACE, unlockedUser);
+ mLogger.d("clearBiometricRecognized");
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -3694,6 +3702,9 @@
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println("KeyguardUpdateMonitor state:");
+ pw.println(" getUserHasTrust()=" + getUserHasTrust(getCurrentUser()));
+ pw.println(" getUserUnlockedWithBiometric()="
+ + getUserUnlockedWithBiometric(getCurrentUser()));
pw.println(" SIM States:");
for (SimData data : mSimDatas.values()) {
pw.println(" " + data.toString());
diff --git a/packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java b/packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java
index 7c86a1d..777bd19 100644
--- a/packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java
+++ b/packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java
@@ -20,7 +20,8 @@
public interface SecurityMessageDisplay {
- void setNextMessageColor(ColorStateList colorState);
+ /** Set text color for the next security message. */
+ default void setNextMessageColor(ColorStateList colorState) {}
void setMessage(CharSequence msg);
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 2bc98f1..7a00cd9 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -340,4 +340,40 @@
bool1 = dismissKeyguard
}, { "reportUserRequestedUnlock origin=$str1 reason=$str2 dismissKeyguard=$bool1" })
}
+
+ fun logShowTrustGrantedMessage(
+ message: String
+ ) {
+ logBuffer.log(TAG, DEBUG, {
+ str1 = message
+ }, { "showTrustGrantedMessage message$str1" })
+ }
+
+ fun logTrustChanged(
+ wasTrusted: Boolean,
+ isNowTrusted: Boolean,
+ userId: Int
+ ) {
+ logBuffer.log(TAG, DEBUG, {
+ bool1 = wasTrusted
+ bool2 = isNowTrusted
+ int1 = userId
+ }, { "onTrustChanged[user=$int1] wasTrusted=$bool1 isNowTrusted=$bool2" })
+ }
+
+ fun logKeyguardStateUpdate(
+ secure: Boolean,
+ canDismissLockScreen: Boolean,
+ trusted: Boolean,
+ trustManaged: Boolean
+
+ ) {
+ logBuffer.log("KeyguardState", DEBUG, {
+ bool1 = secure
+ bool2 = canDismissLockScreen
+ bool3 = trusted
+ bool4 = trustManaged
+ }, { "#update secure=$bool1 canDismissKeyguard=$bool2" +
+ " trusted=$bool3 trustManaged=$bool4" })
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
index 65c4e13..b40b356 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
@@ -23,6 +23,7 @@
import android.view.Surface
import android.view.View
import com.airbnb.lottie.LottieAnimationView
+import com.android.settingslib.widget.LottieColorUtils
import com.android.systemui.R
import com.android.systemui.biometrics.AuthBiometricView.BiometricState
import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATED
@@ -100,6 +101,8 @@
iconView.playAnimation()
iconViewOverlay.playAnimation()
}
+ LottieColorUtils.applyDynamicColors(context, iconView)
+ LottieColorUtils.applyDynamicColors(context, iconViewOverlay)
}
private fun updateIconNormal(@BiometricState lastState: Int, @BiometricState newState: Int) {
@@ -118,6 +121,7 @@
if (shouldAnimateForTransition(lastState, newState)) {
iconView.playAnimation()
}
+ LottieColorUtils.applyDynamicColors(context, iconView)
}
override fun updateIcon(@BiometricState lastState: Int, @BiometricState newState: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index d89c0be..b598554 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -101,6 +101,11 @@
* Called when the always on suppression state changes. See {@link #isAlwaysOnSuppressed()}.
*/
default void onAlwaysOnSuppressedChanged(boolean suppressed) {}
+
+ /**
+ * Called when the dozing state may have been updated.
+ */
+ default void onDozingChanged(boolean isDozing) {}
}
interface PulseCallback {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 6dfbd42..2da9232 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -29,6 +29,7 @@
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
@@ -189,6 +190,9 @@
return apps.length == 0 ? TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER
: TRANSIT_OLD_KEYGUARD_GOING_AWAY;
} else if (type == TRANSIT_KEYGUARD_OCCLUDE) {
+ boolean isOccludeByDream = apps.length > 0 && apps[0].taskInfo.topActivityType
+ == WindowConfiguration.ACTIVITY_TYPE_DREAM;
+ if (isOccludeByDream) return TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM;
return TRANSIT_OLD_KEYGUARD_OCCLUDE;
} else if (type == TRANSIT_KEYGUARD_UNOCCLUDE) {
return TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
@@ -303,6 +307,12 @@
definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE,
occludeAnimationAdapter);
+ final RemoteAnimationAdapter occludeByDreamAnimationAdapter =
+ new RemoteAnimationAdapter(
+ mKeyguardViewMediator.getOccludeByDreamAnimationRunner(), 0, 0);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM,
+ occludeByDreamAnimationAdapter);
+
final RemoteAnimationAdapter unoccludeAnimationAdapter =
new RemoteAnimationAdapter(
mKeyguardViewMediator.getUnoccludeAnimationRunner(), 0, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index d4ef467..d4e0f061 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -887,6 +887,86 @@
private IRemoteAnimationRunner mOccludeAnimationRunner =
new OccludeActivityLaunchRemoteAnimationRunner(mOccludeAnimationController);
+ private final IRemoteAnimationRunner mOccludeByDreamAnimationRunner =
+ new IRemoteAnimationRunner.Stub() {
+ @Nullable private ValueAnimator mOccludeByDreamAnimator;
+
+ @Override
+ public void onAnimationCancelled(boolean isKeyguardOccluded) {
+ if (mOccludeByDreamAnimator != null) {
+ mOccludeByDreamAnimator.cancel();
+ }
+ setOccluded(isKeyguardOccluded /* isOccluded */, false /* animate */);
+ if (DEBUG) {
+ Log.d(TAG, "Occlude by Dream animation cancelled. Occluded state is now: "
+ + mOccluded);
+ }
+ }
+
+ @Override
+ public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
+ setOccluded(true /* isOccluded */, true /* animate */);
+
+ if (apps == null || apps.length == 0 || apps[0] == null) {
+ if (DEBUG) {
+ Log.d(TAG, "No apps provided to the OccludeByDream runner; "
+ + "skipping occluding animation.");
+ }
+ finishedCallback.onAnimationFinished();
+ return;
+ }
+
+ final RemoteAnimationTarget primary = apps[0];
+ final boolean isDream = (apps[0].taskInfo.topActivityType
+ == WindowConfiguration.ACTIVITY_TYPE_DREAM);
+ if (!isDream) {
+ Log.w(TAG, "The occluding app isn't Dream; "
+ + "finishing up. Please check that the config is correct.");
+ finishedCallback.onAnimationFinished();
+ return;
+ }
+
+ final SyncRtSurfaceTransactionApplier applier =
+ new SyncRtSurfaceTransactionApplier(
+ mKeyguardViewControllerLazy.get().getViewRootImpl().getView());
+
+ mContext.getMainExecutor().execute(() -> {
+ if (mOccludeByDreamAnimator != null) {
+ mOccludeByDreamAnimator.cancel();
+ }
+
+ mOccludeByDreamAnimator = ValueAnimator.ofFloat(0f, 1f);
+ // Use the same duration as for the UNOCCLUDE.
+ mOccludeByDreamAnimator.setDuration(UNOCCLUDE_ANIMATION_DURATION);
+ mOccludeByDreamAnimator.setInterpolator(Interpolators.LINEAR);
+ mOccludeByDreamAnimator.addUpdateListener(
+ animation -> {
+ SyncRtSurfaceTransactionApplier.SurfaceParams.Builder
+ paramsBuilder =
+ new SyncRtSurfaceTransactionApplier.SurfaceParams
+ .Builder(primary.leash)
+ .withAlpha(animation.getAnimatedFraction());
+ applier.scheduleApply(paramsBuilder.build());
+ });
+ mOccludeByDreamAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ try {
+ finishedCallback.onAnimationFinished();
+ mOccludeByDreamAnimator = null;
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+
+ mOccludeByDreamAnimator.start();
+ });
+ }
+ };
+
/**
* Animation controller for activities that unocclude the keyguard. This does not use the
* ActivityLaunchAnimator since we're just translating down, rather than emerging from a view
@@ -1682,6 +1762,10 @@
return mOccludeAnimationRunner;
}
+ public IRemoteAnimationRunner getOccludeByDreamAnimationRunner() {
+ return mOccludeByDreamAnimationRunner;
+ }
+
public IRemoteAnimationRunner getUnoccludeAnimationRunner() {
return mUnoccludeAnimationRunner;
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index e52d9ee..840a4b2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -20,6 +20,7 @@
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.Position
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.doze.DozeHost
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
@@ -28,6 +29,7 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
/** Defines interface for classes that encapsulate application state for the keyguard. */
interface KeyguardRepository {
@@ -102,6 +104,7 @@
constructor(
statusBarStateController: StatusBarStateController,
keyguardStateController: KeyguardStateController,
+ dozeHost: DozeHost,
) : KeyguardRepository {
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
override val animateBottomAreaDozingTransitions =
@@ -136,19 +139,21 @@
awaitClose { keyguardStateController.removeCallback(callback) }
}
- override val isDozing: Flow<Boolean> = conflatedCallbackFlow {
- val callback =
- object : StatusBarStateController.StateListener {
- override fun onDozingChanged(isDozing: Boolean) {
- trySendWithFailureLogging(isDozing, TAG, "updated isDozing")
- }
+ override val isDozing: Flow<Boolean> =
+ conflatedCallbackFlow {
+ val callback =
+ object : DozeHost.Callback {
+ override fun onDozingChanged(isDozing: Boolean) {
+ trySendWithFailureLogging(isDozing, TAG, "updated isDozing")
+ }
+ }
+ dozeHost.addCallback(callback)
+ trySendWithFailureLogging(false, TAG, "initial isDozing: false")
+
+ awaitClose { dozeHost.removeCallback(callback) }
}
+ .distinctUntilChanged()
- statusBarStateController.addCallback(callback)
- trySendWithFailureLogging(statusBarStateController.isDozing, TAG, "initial isDozing")
-
- awaitClose { statusBarStateController.removeCallback(callback) }
- }
override val dozeAmount: Flow<Float> = conflatedCallbackFlow {
val callback =
object : StatusBarStateController.StateListener {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
index 8f32ff9..ac2c9b1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
@@ -94,6 +94,7 @@
hasFavorites = favorites?.isNotEmpty() == true,
hasServiceInfos = serviceInfos.isNotEmpty(),
iconResourceId = component.getTileImageId(),
+ visibility = component.getVisibility(),
),
TAG,
)
@@ -110,9 +111,16 @@
isFeatureEnabled: Boolean,
hasFavorites: Boolean,
hasServiceInfos: Boolean,
+ visibility: ControlsComponent.Visibility,
@DrawableRes iconResourceId: Int?,
): KeyguardQuickAffordanceConfig.State {
- return if (isFeatureEnabled && hasFavorites && hasServiceInfos && iconResourceId != null) {
+ return if (
+ isFeatureEnabled &&
+ hasFavorites &&
+ hasServiceInfos &&
+ iconResourceId != null &&
+ visibility == ControlsComponent.Visibility.AVAILABLE
+ ) {
KeyguardQuickAffordanceConfig.State.Visible(
icon = ContainedDrawable.WithResource(iconResourceId),
contentDescriptionResourceId = component.getTileTitleId(),
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index ae4c7c7..6baf6e1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -41,6 +41,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.media.dream.MediaDreamComplication
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.NotifPanelEvents
import com.android.systemui.statusbar.CrossFadeHelper
@@ -401,7 +402,7 @@
}
/**
- * Is the doze animation currently Running
+ * Is the dream overlay currently active
*/
private var dreamOverlayActive: Boolean = false
private set(value) {
@@ -412,6 +413,17 @@
}
/**
+ * Is the dream media complication currently active
+ */
+ private var dreamMediaComplicationActive: Boolean = false
+ private set(value) {
+ if (field != value) {
+ field = value
+ updateDesiredLocation(forceNoAnimation = true)
+ }
+ }
+
+ /**
* The current cross fade progress. 0.5f means it's just switching
* between the start and the end location and the content is fully faded, while 0.75f means
* that we're halfway faded in again in the target state.
@@ -500,6 +512,12 @@
})
dreamOverlayStateController.addCallback(object : DreamOverlayStateController.Callback {
+ override fun onComplicationsChanged() {
+ dreamMediaComplicationActive = dreamOverlayStateController.complications.any {
+ it is MediaDreamComplication
+ }
+ }
+
override fun onStateChanged() {
dreamOverlayStateController.isOverlayActive.also { dreamOverlayActive = it }
}
@@ -1068,7 +1086,7 @@
val onLockscreen = (!bypassController.bypassEnabled &&
(statusbarState == StatusBarState.KEYGUARD))
val location = when {
- dreamOverlayActive -> LOCATION_DREAM_OVERLAY
+ dreamOverlayActive && dreamMediaComplicationActive -> LOCATION_DREAM_OVERLAY
(qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
!hasActiveMedia -> LOCATION_QS
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 35a6c74..5d6d683 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -34,15 +34,14 @@
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.taptotransfer.common.ChipInfoCommon
-import com.android.systemui.media.taptotransfer.common.DEFAULT_TIMEOUT_MILLIS
-import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS
+import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
+import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.android.systemui.util.animation.AnimationUtil.Companion.frames
import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.view.ViewUtil
import javax.inject.Inject
/**
@@ -56,18 +55,16 @@
context: Context,
@MediaTttReceiverLogger logger: MediaTttLogger,
windowManager: WindowManager,
- viewUtil: ViewUtil,
mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
powerManager: PowerManager,
@Main private val mainHandler: Handler,
private val uiEventLogger: MediaTttReceiverUiEventLogger,
-) : MediaTttChipControllerCommon<ChipReceiverInfo>(
+) : TemporaryViewDisplayController<ChipReceiverInfo>(
context,
logger,
windowManager,
- viewUtil,
mainExecutor,
accessibilityManager,
configurationController,
@@ -119,18 +116,18 @@
uiEventLogger.logReceiverStateChange(chipState)
if (chipState == ChipStateReceiver.FAR_FROM_SENDER) {
- removeChip(removalReason = ChipStateReceiver.FAR_FROM_SENDER::class.simpleName!!)
+ removeView(removalReason = ChipStateReceiver.FAR_FROM_SENDER::class.simpleName!!)
return
}
if (appIcon == null) {
- displayChip(ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appName))
+ displayView(ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appName))
return
}
appIcon.loadDrawableAsync(
context,
Icon.OnDrawableLoadedListener { drawable ->
- displayChip(ChipReceiverInfo(routeInfo, drawable, appName))
+ displayView(ChipReceiverInfo(routeInfo, drawable, appName))
},
// Notify the listener on the main handler since the listener will update
// the UI.
@@ -138,19 +135,19 @@
)
}
- override fun updateChipView(newChipInfo: ChipReceiverInfo, currentChipView: ViewGroup) {
- super.updateChipView(newChipInfo, currentChipView)
+ override fun updateView(newInfo: ChipReceiverInfo, currentView: ViewGroup) {
+ super.updateView(newInfo, currentView)
val iconName = setIcon(
- currentChipView,
- newChipInfo.routeInfo.clientPackageName,
- newChipInfo.appIconDrawableOverride,
- newChipInfo.appNameOverride
+ currentView,
+ newInfo.routeInfo.clientPackageName,
+ newInfo.appIconDrawableOverride,
+ newInfo.appNameOverride
)
- currentChipView.contentDescription = iconName
+ currentView.contentDescription = iconName
}
- override fun animateChipIn(chipView: ViewGroup) {
- val appIconView = chipView.requireViewById<View>(R.id.app_icon)
+ override fun animateViewIn(view: ViewGroup) {
+ val appIconView = view.requireViewById<View>(R.id.app_icon)
appIconView.animate()
.translationYBy(-1 * getTranslationAmount().toFloat())
.setDuration(30.frames)
@@ -160,8 +157,8 @@
.setDuration(5.frames)
.start()
// Using withEndAction{} doesn't apply a11y focus when screen is unlocked.
- appIconView.postOnAnimation { chipView.requestAccessibilityFocus() }
- startRipple(chipView.requireViewById(R.id.ripple))
+ appIconView.postOnAnimation { view.requestAccessibilityFocus() }
+ startRipple(view.requireViewById(R.id.ripple))
}
override fun getIconSize(isAppIcon: Boolean): Int? =
@@ -216,7 +213,7 @@
val routeInfo: MediaRoute2Info,
val appIconDrawableOverride: Drawable?,
val appNameOverride: CharSequence?
-) : ChipInfoCommon {
+) : TemporaryViewInfo {
override fun getTimeoutMs() = DEFAULT_TIMEOUT_MILLIS
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index a153cb6..bde588c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -25,7 +25,7 @@
import com.android.internal.logging.UiEventLogger
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R
-import com.android.systemui.media.taptotransfer.common.DEFAULT_TIMEOUT_MILLIS
+import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS
/**
* A class enumerating all the possible states of the media tap-to-transfer chip on the sender
@@ -120,7 +120,7 @@
// state, but that may take too long to go through the binder and the user may be
// confused ast o why the UI hasn't changed yet. So, we immediately change the UI
// here.
- controllerSender.displayChip(
+ controllerSender.displayView(
ChipSenderInfo(
TRANSFER_TO_THIS_DEVICE_TRIGGERED, routeInfo, undoCallback
)
@@ -155,7 +155,7 @@
// state, but that may take too long to go through the binder and the user may be
// confused as to why the UI hasn't changed yet. So, we immediately change the UI
// here.
- controllerSender.displayChip(
+ controllerSender.displayView(
ChipSenderInfo(
TRANSFER_TO_RECEIVER_TRIGGERED, routeInfo, undoCallback
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index 9335489..0c1ebd7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -33,14 +33,13 @@
import com.android.systemui.animation.ViewHierarchyAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.taptotransfer.common.ChipInfoCommon
-import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
-import com.android.systemui.media.taptotransfer.common.MediaTttRemovalReason
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.temporarydisplay.TemporaryDisplayRemovalReason
+import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
+import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.view.ViewUtil
import javax.inject.Inject
/**
@@ -53,17 +52,15 @@
context: Context,
@MediaTttSenderLogger logger: MediaTttLogger,
windowManager: WindowManager,
- viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
powerManager: PowerManager,
private val uiEventLogger: MediaTttSenderUiEventLogger
-) : MediaTttChipControllerCommon<ChipSenderInfo>(
+) : TemporaryViewDisplayController<ChipSenderInfo>(
context,
logger,
windowManager,
- viewUtil,
mainExecutor,
accessibilityManager,
configurationController,
@@ -106,53 +103,52 @@
uiEventLogger.logSenderStateChange(chipState)
if (chipState == ChipStateSender.FAR_FROM_RECEIVER) {
- removeChip(removalReason = ChipStateSender.FAR_FROM_RECEIVER::class.simpleName!!)
+ removeView(removalReason = ChipStateSender.FAR_FROM_RECEIVER::class.simpleName!!)
} else {
- displayChip(ChipSenderInfo(chipState, routeInfo, undoCallback))
+ displayView(ChipSenderInfo(chipState, routeInfo, undoCallback))
}
}
- /** Displays the chip view for the given state. */
- override fun updateChipView(
- newChipInfo: ChipSenderInfo,
- currentChipView: ViewGroup
+ override fun updateView(
+ newInfo: ChipSenderInfo,
+ currentView: ViewGroup
) {
- super.updateChipView(newChipInfo, currentChipView)
+ super.updateView(newInfo, currentView)
- val chipState = newChipInfo.state
+ val chipState = newInfo.state
// App icon
- val iconName = setIcon(currentChipView, newChipInfo.routeInfo.clientPackageName)
+ val iconName = setIcon(currentView, newInfo.routeInfo.clientPackageName)
// Text
- val otherDeviceName = newChipInfo.routeInfo.name.toString()
+ val otherDeviceName = newInfo.routeInfo.name.toString()
val chipText = chipState.getChipTextString(context, otherDeviceName)
- currentChipView.requireViewById<TextView>(R.id.text).text = chipText
+ currentView.requireViewById<TextView>(R.id.text).text = chipText
// Loading
- currentChipView.requireViewById<View>(R.id.loading).visibility =
+ currentView.requireViewById<View>(R.id.loading).visibility =
chipState.isMidTransfer.visibleIfTrue()
// Undo
- val undoView = currentChipView.requireViewById<View>(R.id.undo)
+ val undoView = currentView.requireViewById<View>(R.id.undo)
val undoClickListener = chipState.undoClickListener(
- this, newChipInfo.routeInfo, newChipInfo.undoCallback, uiEventLogger
+ this, newInfo.routeInfo, newInfo.undoCallback, uiEventLogger
)
undoView.setOnClickListener(undoClickListener)
undoView.visibility = (undoClickListener != null).visibleIfTrue()
// Failure
- currentChipView.requireViewById<View>(R.id.failure_icon).visibility =
+ currentView.requireViewById<View>(R.id.failure_icon).visibility =
chipState.isTransferFailure.visibleIfTrue()
// For accessibility
- currentChipView.requireViewById<ViewGroup>(
+ currentView.requireViewById<ViewGroup>(
R.id.media_ttt_sender_chip_inner
).contentDescription = "$iconName $chipText"
}
- override fun animateChipIn(chipView: ViewGroup) {
- val chipInnerView = chipView.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner)
+ override fun animateViewIn(view: ViewGroup) {
+ val chipInnerView = view.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner)
ViewHierarchyAnimator.animateAddition(
chipInnerView,
ViewHierarchyAnimator.Hotspot.TOP,
@@ -165,14 +161,14 @@
)
}
- override fun removeChip(removalReason: String) {
+ override fun removeView(removalReason: String) {
// Don't remove the chip if we're mid-transfer since the user should still be able to
// see the status of the transfer. (But do remove it if it's finally timed out.)
- if (chipInfo?.state?.isMidTransfer == true &&
- removalReason != MediaTttRemovalReason.REASON_TIMEOUT) {
+ if (info?.state?.isMidTransfer == true &&
+ removalReason != TemporaryDisplayRemovalReason.REASON_TIMEOUT) {
return
}
- super.removeChip(removalReason)
+ super.removeView(removalReason)
}
private fun Boolean.visibleIfTrue(): Int {
@@ -188,7 +184,7 @@
val state: ChipStateSender,
val routeInfo: MediaRoute2Info,
val undoCallback: IUndoMediaTransferCallback? = null
-) : ChipInfoCommon {
+) : TemporaryViewInfo {
override fun getTimeoutMs() = state.timeout
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index e93f605..abafecc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -29,6 +29,7 @@
import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.LockIconViewController;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
@@ -138,6 +139,13 @@
return mView.findViewById(R.id.keyguard_bouncer_container);
}
+ /**
+ * @return Location where to place the KeyguardMessageArea
+ */
+ public AuthKeyguardMessageArea getKeyguardMessageArea() {
+ return mView.findViewById(R.id.keyguard_message_area);
+ }
+
/** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */
public void setupExpandedStatusBar() {
mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
index 621a609..9b3fe92 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
@@ -26,6 +26,7 @@
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
import com.android.systemui.tuner.TunerService
@@ -49,6 +50,7 @@
private val dockManager: DockManager,
private val centralSurfaces: CentralSurfaces,
private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
+ private val statusBarStateController: StatusBarStateController,
tunerService: TunerService,
dumpManager: DumpManager
) : GestureDetector.SimpleOnGestureListener(), Dumpable {
@@ -74,7 +76,8 @@
}
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
- if (singleTapEnabled &&
+ if (statusBarStateController.isPulsing &&
+ singleTapEnabled &&
!dockManager.isDocked &&
!falsingManager.isProximityNear &&
!falsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY)
@@ -89,7 +92,8 @@
}
override fun onDoubleTap(e: MotionEvent): Boolean {
- if ((doubleTapEnabled || singleTapEnabled) &&
+ if (statusBarStateController.isPulsing &&
+ (doubleTapEnabled || singleTapEnabled) &&
!falsingManager.isProximityNear &&
!falsingManager.isFalseDoubleTap
) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 2251ab5..408c61f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -692,11 +692,11 @@
/**
* Returns the indication text indicating that trust has been granted.
*
- * @return {@code null} or an empty string if a trust indication text should not be shown.
+ * @return an empty string if a trust indication text should not be shown.
*/
@VisibleForTesting
String getTrustGrantedIndication() {
- return TextUtils.isEmpty(mTrustGrantedIndication)
+ return mTrustGrantedIndication == null
? mContext.getString(R.string.keyguard_indication_trust_unlocked)
: mTrustGrantedIndication.toString();
}
@@ -932,7 +932,7 @@
return; // udfps affordance is highlighted, no need to show action to unlock
} else if (mKeyguardUpdateMonitor.isFaceEnrolled()) {
String message = mContext.getString(R.string.keyguard_retry);
- mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
+ mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState);
}
} else {
final boolean canSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer(
@@ -1080,7 +1080,7 @@
}
return;
} else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
+ mStatusBarKeyguardViewManager.setKeyguardMessage(helpString,
mInitialTextColorState);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
if (isCoExFaceAcquisitionMessage && msgId == FACE_ACQUIRED_TOO_DARK) {
@@ -1155,7 +1155,7 @@
showActionToUnlock();
}
} else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mStatusBarKeyguardViewManager.showBouncerMessage(errString, mInitialTextColorState);
+ mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
showBiometricMessage(errString);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index f6c4a31..cb13fcf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -81,7 +81,6 @@
}
lateinit var root: View
- private var blurRoot: View? = null
private var keyguardAnimator: Animator? = null
private var notificationAnimator: Animator? = null
private var updateScheduled: Boolean = false
@@ -235,7 +234,7 @@
val opaque = scrimsVisible && !blursDisabledForAppLaunch
Trace.traceCounter(Trace.TRACE_TAG_APP, "shade_blur_radius", blur)
- blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur, opaque)
+ blurUtils.applyBlur(root.viewRootImpl, blur, opaque)
lastAppliedBlur = blur
wallpaperController.setNotificationShadeZoom(zoomOut)
listeners.forEach {
@@ -271,7 +270,6 @@
override fun onAnimationEnd(animation: Animator?) {
keyguardAnimator = null
wakeAndUnlockBlurRadius = 0f
- scheduleUpdate()
}
})
start()
@@ -302,7 +300,6 @@
override fun onDozeAmountChanged(linear: Float, eased: Float) {
wakeAndUnlockBlurRadius = blurUtils.blurRadiusOfRatio(eased)
- scheduleUpdate()
}
}
@@ -439,12 +436,11 @@
shadeAnimation.animateTo(blurUtils.blurRadiusOfRatio(targetBlurNormalized).toInt())
}
- private fun scheduleUpdate(viewToBlur: View? = null) {
+ private fun scheduleUpdate() {
if (updateScheduled) {
return
}
updateScheduled = true
- blurRoot = viewToBlur
choreographer.postFrameCallback(updateBlurCallback)
}
@@ -495,16 +491,11 @@
*/
private var pendingRadius = -1
- /**
- * View on {@link Surface} that wants depth.
- */
- private var view: View? = null
-
private var springAnimation = SpringAnimation(this, object :
FloatPropertyCompat<DepthAnimation>("blurRadius") {
override fun setValue(rect: DepthAnimation?, value: Float) {
radius = value
- scheduleUpdate(view)
+ scheduleUpdate()
}
override fun getValue(rect: DepthAnimation?): Float {
@@ -519,11 +510,10 @@
springAnimation.addEndListener { _, _, _, _ -> pendingRadius = -1 }
}
- fun animateTo(newRadius: Int, viewToBlur: View? = null) {
- if (pendingRadius == newRadius && view == viewToBlur) {
+ fun animateTo(newRadius: Int) {
+ if (pendingRadius == newRadius) {
return
}
- view = viewToBlur
pendingRadius = newRadius
springAnimation.animateToFinalPosition(newRadius.toFloat())
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index cea3deb..41c0367 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress;
+
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -90,6 +92,12 @@
super(context, attrs);
}
+ @VisibleForTesting
+ public NotificationShelf(Context context, AttributeSet attrs, boolean showNotificationShelf) {
+ super(context, attrs);
+ mShowNotificationShelf = showNotificationShelf;
+ }
+
@Override
@VisibleForTesting
public void onFinishInflate() {
@@ -175,7 +183,11 @@
if (ambientState.isExpansionChanging() && !ambientState.isOnKeyguard()) {
float expansion = ambientState.getExpansionFraction();
- viewState.alpha = ShadeInterpolation.getContentAlpha(expansion);
+ if (ambientState.isBouncerInTransit()) {
+ viewState.alpha = aboutToShowBouncerProgress(expansion);
+ } else {
+ viewState.alpha = ShadeInterpolation.getContentAlpha(expansion);
+ }
} else {
viewState.alpha = 1f - ambientState.getHideAmount();
}
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 ea28452..ce465bc 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
@@ -23,6 +23,8 @@
import android.content.Context;
import android.util.MathUtils;
+import androidx.annotation.VisibleForTesting;
+
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -137,7 +139,17 @@
* True right after we swipe up on lockscreen and have not finished the fling down that follows.
* False when we stop flinging or leave lockscreen.
*/
- private boolean mNeedFlingAfterLockscreenSwipeUp = false;
+ private boolean mIsFlingRequiredAfterLockScreenSwipeUp = false;
+
+ @VisibleForTesting
+ public boolean isFlingRequiredAfterLockScreenSwipeUp() {
+ return mIsFlingRequiredAfterLockScreenSwipeUp;
+ }
+
+ @VisibleForTesting
+ public void setFlingRequiredAfterLockScreenSwipeUp(boolean value) {
+ mIsFlingRequiredAfterLockScreenSwipeUp = value;
+ }
/**
* @return Height of the notifications panel without top padding when expansion completes.
@@ -181,7 +193,7 @@
public void setSwipingUp(boolean isSwipingUp) {
if (!isSwipingUp && mIsSwipingUp) {
// Just stopped swiping up.
- mNeedFlingAfterLockscreenSwipeUp = true;
+ mIsFlingRequiredAfterLockScreenSwipeUp = true;
}
mIsSwipingUp = isSwipingUp;
}
@@ -196,10 +208,10 @@
/**
* @param isFlinging Whether we are flinging the shade open or closed.
*/
- public void setIsFlinging(boolean isFlinging) {
+ public void setFlinging(boolean isFlinging) {
if (isOnKeyguard() && !isFlinging && mIsFlinging) {
// Just stopped flinging.
- mNeedFlingAfterLockscreenSwipeUp = false;
+ mIsFlingRequiredAfterLockScreenSwipeUp = false;
}
mIsFlinging = isFlinging;
}
@@ -508,7 +520,7 @@
public void setStatusBarState(int statusBarState) {
if (mStatusBarState != StatusBarState.KEYGUARD) {
- mNeedFlingAfterLockscreenSwipeUp = false;
+ mIsFlingRequiredAfterLockScreenSwipeUp = false;
}
mStatusBarState = statusBarState;
}
@@ -576,7 +588,7 @@
* @return Whether we need to do a fling down after swiping up on lockscreen.
*/
public boolean isFlingingAfterSwipeUpOnLockscreen() {
- return mIsFlinging && mNeedFlingAfterLockscreenSwipeUp;
+ return mIsFlinging && mIsFlingRequiredAfterLockScreenSwipeUp;
}
/**
@@ -744,7 +756,8 @@
pw.println("mIsSwipingUp=" + mIsSwipingUp);
pw.println("mPanelTracking=" + mPanelTracking);
pw.println("mIsFlinging=" + mIsFlinging);
- pw.println("mNeedFlingAfterLockscreenSwipeUp=" + mNeedFlingAfterLockscreenSwipeUp);
+ pw.println("mIsFlingRequiredAfterLockScreenSwipeUp="
+ + mIsFlingRequiredAfterLockScreenSwipeUp);
pw.println("mZDistanceBetweenElements=" + mZDistanceBetweenElements);
pw.println("mBaseZHeight=" + mBaseZHeight);
}
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 8b322fc..5fbaa51 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
@@ -5021,7 +5021,7 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setPanelFlinging(boolean flinging) {
- mAmbientState.setIsFlinging(flinging);
+ mAmbientState.setFlinging(flinging);
if (!flinging) {
// re-calculate the stack height which was frozen while flinging
updateStackPosition();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 20b6d0b..f37243a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -40,6 +40,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.RegisterStatusBarResult;
+import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.FaceAuthApiRequestReason;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -220,6 +221,9 @@
ViewGroup getBouncerContainer();
+ /** Get the Keyguard Message Area that displays auth messages. */
+ AuthKeyguardMessageArea getKeyguardMessageArea();
+
int getStatusBarHeight();
void updateQsExpansionEnabled();
@@ -390,8 +394,6 @@
void fadeKeyguardAfterLaunchTransition(Runnable beforeFading,
Runnable endRunnable, Runnable cancelRunnable);
- void fadeKeyguardWhilePulsing();
-
void animateKeyguardUnoccluding();
void startLaunchTransitionTimeout();
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 6426aef..cebb4b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -120,6 +120,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.RegisterStatusBarResult;
+import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.FaceAuthApiRequestReason;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -1582,6 +1583,11 @@
}
@Override
+ public AuthKeyguardMessageArea getKeyguardMessageArea() {
+ return mNotificationShadeWindowViewController.getKeyguardMessageArea();
+ }
+
+ @Override
public int getStatusBarHeight() {
return mStatusBarWindowController.getStatusBarHeight();
}
@@ -3019,19 +3025,6 @@
}
/**
- * Fades the content of the Keyguard while we are dozing and makes it invisible when finished
- * fading.
- */
- @Override
- public void fadeKeyguardWhilePulsing() {
- mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING,
- ()-> {
- hideKeyguard();
- mStatusBarKeyguardViewManager.onKeyguardFadedAway();
- }).start();
- }
-
- /**
* Plays the animation when an activity that was occluding Keyguard goes away.
*/
@Override
@@ -3320,14 +3313,12 @@
// show the bouncer/lockscreen.
if (!mKeyguardViewMediator.isHiding()
&& !mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) {
- if (mState == StatusBarState.SHADE_LOCKED
- && mKeyguardUpdateMonitor.isUdfpsEnrolled()) {
+ if (mState == StatusBarState.SHADE_LOCKED) {
// shade is showing while locked on the keyguard, so go back to showing the
// lock screen where users can use the UDFPS affordance to enter the device
mStatusBarKeyguardViewManager.reset(true);
- } else if ((mState == StatusBarState.KEYGUARD
- && !mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing())
- || mState == StatusBarState.SHADE_LOCKED) {
+ } else if (mState == StatusBarState.KEYGUARD
+ && !mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()) {
mStatusBarKeyguardViewManager.showGenericBouncer(true /* scrimmed */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index ddff7d8..24ce5e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -28,8 +28,6 @@
import android.view.MotionEvent;
import android.view.View;
-import androidx.annotation.Nullable;
-
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.assist.AssistManager;
@@ -50,11 +48,9 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.unfold.FoldAodAnimationController;
-import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.util.Assert;
import java.util.ArrayList;
-import java.util.Optional;
import javax.inject.Inject;
@@ -80,8 +76,6 @@
private final WakefulnessLifecycle mWakefulnessLifecycle;
private final SysuiStatusBarStateController mStatusBarStateController;
private final DeviceProvisionedController mDeviceProvisionedController;
- @Nullable
- private final FoldAodAnimationController mFoldAodAnimationController;
private final HeadsUpManagerPhone mHeadsUpManagerPhone;
private final BatteryController mBatteryController;
private final ScrimController mScrimController;
@@ -114,7 +108,6 @@
Lazy<AssistManager> assistManagerLazy,
DozeScrimController dozeScrimController, KeyguardUpdateMonitor keyguardUpdateMonitor,
PulseExpansionHandler pulseExpansionHandler,
- Optional<SysUIUnfoldComponent> sysUIUnfoldComponent,
NotificationShadeWindowController notificationShadeWindowController,
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
AuthController authController,
@@ -138,8 +131,6 @@
mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
mAuthController = authController;
mNotificationIconAreaController = notificationIconAreaController;
- mFoldAodAnimationController = sysUIUnfoldComponent
- .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
}
// TODO: we should try to not pass status bar in here if we can avoid it.
@@ -167,6 +158,7 @@
}
void firePowerSaveChanged(boolean active) {
+ Assert.isMainThread();
for (Callback callback : mCallbacks) {
callback.onPowerSaveChanged(active);
}
@@ -177,6 +169,7 @@
entry.setPulseSuppressed(true);
mNotificationIconAreaController.updateAodNotificationIcons();
};
+ Assert.isMainThread();
for (Callback callback : mCallbacks) {
callback.onNotificationAlerted(pulseSuppressedListener);
}
@@ -193,11 +186,13 @@
@Override
public void addCallback(@NonNull Callback callback) {
+ Assert.isMainThread();
mCallbacks.add(callback);
}
@Override
public void removeCallback(@NonNull Callback callback) {
+ Assert.isMainThread();
mCallbacks.remove(callback);
}
@@ -212,12 +207,10 @@
}
void updateDozing() {
- // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
- boolean
- dozing =
- mDozingRequested && mStatusBarStateController.getState() == StatusBarState.KEYGUARD
- || mBiometricUnlockControllerLazy.get().getMode()
- == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+ Assert.isMainThread();
+
+ boolean dozing =
+ mDozingRequested && mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
// When in wake-and-unlock we may not have received a change to StatusBarState
// but we still should not be dozing, manually set to false.
if (mBiometricUnlockControllerLazy.get().getMode()
@@ -225,10 +218,10 @@
dozing = false;
}
- mStatusBarStateController.setIsDozing(dozing);
- if (mFoldAodAnimationController != null) {
- mFoldAodAnimationController.setIsDozing(dozing);
+ for (Callback callback : mCallbacks) {
+ callback.onDozingChanged(dozing);
}
+ mStatusBarStateController.setIsDozing(dozing);
}
@Override
@@ -452,6 +445,7 @@
return;
}
mAlwaysOnSuppressed = suppressed;
+ Assert.isMainThread();
for (Callback callback : mCallbacks) {
callback.onAlwaysOnSuppressedChanged(suppressed);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index f106e54..4c5c23c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -44,7 +44,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardMessageArea;
+import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.KeyguardMessageAreaController;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -122,7 +122,7 @@
private final DreamOverlayStateController mDreamOverlayStateController;
@Nullable
private final FoldAodAnimationController mFoldAodAnimationController;
- private KeyguardMessageAreaController mKeyguardMessageAreaController;
+ private KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController;
private final Lazy<com.android.systemui.shade.ShadeController> mShadeController;
private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
@@ -311,7 +311,7 @@
mBypassController = bypassController;
mNotificationContainer = notificationContainer;
mKeyguardMessageAreaController = mKeyguardMessageAreaFactory.create(
- KeyguardMessageArea.findSecurityMessageDisplay(container));
+ centralSurfaces.getKeyguardMessageArea());
registerListeners();
}
@@ -378,11 +378,9 @@
return;
} else if (mNotificationPanelViewController.isUnlockHintRunning()) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
- } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
- && mKeyguardUpdateManager.isUdfpsEnrolled()) {
+ } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) {
// Don't expand to the bouncer. Instead transition back to the lock screen (see
- // CentralSurfaces#showBouncerOrLockScreenIfKeyguard) where the user can use the UDFPS
- // affordance to enter the device (or swipe up to the input bouncer)
+ // CentralSurfaces#showBouncerOrLockScreenIfKeyguard)
return;
} else if (bouncerNeedsScrimming()) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
@@ -616,7 +614,8 @@
private void updateAlternateAuthShowing(boolean updateScrim) {
final boolean isShowingAltAuth = isShowingAlternateAuth();
if (mKeyguardMessageAreaController != null) {
- mKeyguardMessageAreaController.setAltBouncerShowing(isShowingAltAuth);
+ mKeyguardMessageAreaController.setIsVisible(isShowingAltAuth);
+ mKeyguardMessageAreaController.setMessage("");
}
mBypassController.setAltBouncerShowing(isShowingAltAuth);
mKeyguardUpdateManager.setUdfpsBouncerShowing(isShowingAltAuth);
@@ -837,30 +836,24 @@
});
} else {
executeAfterKeyguardGoneAction();
- boolean wakeUnlockPulsing =
- mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING;
mCentralSurfaces.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
mBiometricUnlockController.startKeyguardFadingAway();
hideBouncer(true /* destroyView */);
- if (wakeUnlockPulsing) {
- mCentralSurfaces.fadeKeyguardWhilePulsing();
+
+ boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
+ if (!staying) {
+ mNotificationShadeWindowController.setKeyguardFadingAway(true);
+ mCentralSurfaces.hideKeyguard();
+ // hide() will happen asynchronously and might arrive after the scrims
+ // were already hidden, this means that the transition callback won't
+ // be triggered anymore and StatusBarWindowController will be forever in
+ // the fadingAway state.
+ mCentralSurfaces.updateScrimController();
wakeAndUnlockDejank();
} else {
- boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
- if (!staying) {
- mNotificationShadeWindowController.setKeyguardFadingAway(true);
- mCentralSurfaces.hideKeyguard();
- // hide() will happen asynchronously and might arrive after the scrims
- // were already hidden, this means that the transition callback won't
- // be triggered anymore and StatusBarWindowController will be forever in
- // the fadingAway state.
- mCentralSurfaces.updateScrimController();
- wakeAndUnlockDejank();
- } else {
- mCentralSurfaces.hideKeyguard();
- mCentralSurfaces.finishKeyguardFadingAway();
- mBiometricUnlockController.finishKeyguardFadingAway();
- }
+ mCentralSurfaces.hideKeyguard();
+ mCentralSurfaces.finishKeyguardFadingAway();
+ mBiometricUnlockController.finishKeyguardFadingAway();
}
updateStates();
@@ -1043,7 +1036,6 @@
if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
mNotificationShadeWindowController.setBouncerShowing(bouncerShowing);
mCentralSurfaces.setBouncerShowing(bouncerShowing);
- mKeyguardMessageAreaController.setBouncerShowing(bouncerShowing);
}
if (occluded != mLastOccluded || mFirstUpdate) {
@@ -1192,7 +1184,8 @@
}
}
- public void showBouncerMessage(String message, ColorStateList colorState) {
+ /** Display security message to relevant KeyguardMessageArea. */
+ public void setKeyguardMessage(String message, ColorStateList colorState) {
if (isShowingAlternateAuth()) {
if (mKeyguardMessageAreaController != null) {
mKeyguardMessageAreaController.setMessage(message);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
index 3eff0bd..3c243ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
@@ -16,8 +16,15 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
+import android.content.Context
import android.graphics.Color
import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
+import androidx.annotation.VisibleForTesting
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
+import com.android.systemui.R
+import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
@@ -41,6 +48,7 @@
class WifiViewModel @Inject constructor(
statusBarPipelineFlags: StatusBarPipelineFlags,
private val constants: WifiConstants,
+ private val context: Context,
private val logger: ConnectivityPipelineLogger,
private val interactor: WifiInteractor,
) {
@@ -61,19 +69,43 @@
}
}
+ /** The content description for the wifi icon. */
+ private val contentDescription: Flow<ContentDescription?> = interactor.wifiNetwork.map {
+ when (it) {
+ is WifiNetworkModel.CarrierMerged -> null
+ is WifiNetworkModel.Inactive ->
+ ContentDescription.Loaded(
+ "${context.getString(WIFI_NO_CONNECTION)},${context.getString(NO_INTERNET)}"
+ )
+ is WifiNetworkModel.Active ->
+ when (it.level) {
+ null -> null
+ else -> {
+ val levelDesc = context.getString(WIFI_CONNECTION_STRENGTH[it.level])
+ when {
+ it.isValidated -> ContentDescription.Loaded(levelDesc)
+ else -> ContentDescription.Loaded(
+ "$levelDesc,${context.getString(NO_INTERNET)}"
+ )
+ }
+ }
+ }
+ }
+ }
+
/**
* The wifi icon that should be displayed. Null if we shouldn't display any icon.
*/
val wifiIcon: Flow<Icon?> = combine(
interactor.isForceHidden,
- iconResId
- ) { isForceHidden, iconResId ->
+ iconResId,
+ contentDescription,
+ ) { isForceHidden, iconResId, contentDescription ->
when {
isForceHidden ||
iconResId == null ||
iconResId <= 0 -> null
- // TODO(b/238425913): Implement the content description.
- else -> Icon.Resource(iconResId, /* contentDescription= */ null)
+ else -> Icon.Resource(iconResId, contentDescription)
}
}
@@ -95,4 +127,10 @@
} else {
flowOf(Color.CYAN)
}
+
+ companion object {
+ @StringRes
+ @VisibleForTesting
+ internal val NO_INTERNET = R.string.data_connection_no_internet
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index bdac888..f4d08e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -31,6 +31,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -60,6 +61,7 @@
private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
new UpdateMonitorCallback();
private final Lazy<KeyguardUnlockAnimationController> mUnlockAnimationControllerLazy;
+ private final KeyguardUpdateMonitorLogger mLogger;
private boolean mCanDismissLockScreen;
private boolean mShowing;
@@ -107,8 +109,10 @@
KeyguardUpdateMonitor keyguardUpdateMonitor,
LockPatternUtils lockPatternUtils,
Lazy<KeyguardUnlockAnimationController> keyguardUnlockAnimationController,
+ KeyguardUpdateMonitorLogger logger,
DumpManager dumpManager) {
mContext = context;
+ mLogger = logger;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
@@ -245,6 +249,8 @@
mTrusted = trusted;
mTrustManaged = trustManaged;
mFaceAuthEnabled = faceAuthEnabled;
+ mLogger.logKeyguardStateUpdate(
+ mSecure, mCanDismissLockScreen, mTrusted, mTrustManaged);
notifyUnlockedChanged();
}
Trace.endSection();
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
similarity index 64%
rename from packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
rename to packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index 3a0ac1b..734eeec 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.taptotransfer.common
+package com.android.systemui.temporarydisplay
import android.annotation.LayoutRes
import android.annotation.SuppressLint
@@ -37,30 +37,30 @@
import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.view.ViewUtil
/**
- * A superclass controller that provides common functionality for showing chips on the sender device
- * and the receiver device.
+ * A generic controller that can temporarily display a new view in a new window.
*
- * Subclasses need to override and implement [updateChipView], which is where they can control what
+ * Subclasses need to override and implement [updateView], which is where they can control what
* gets displayed to the user.
*
* The generic type T is expected to contain all the information necessary for the subclasses to
- * display the chip in a certain state, since they receive <T> in [updateChipView].
+ * display the view in a certain state, since they receive <T> in [updateView].
+ *
+ * TODO(b/245610654): Remove all the media-specific logic from this class.
*/
-abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
- internal val context: Context,
- internal val logger: MediaTttLogger,
- internal val windowManager: WindowManager,
- private val viewUtil: ViewUtil,
- @Main private val mainExecutor: DelayableExecutor,
- private val accessibilityManager: AccessibilityManager,
- private val configurationController: ConfigurationController,
- private val powerManager: PowerManager,
- @LayoutRes private val chipLayoutRes: Int,
+abstract class TemporaryViewDisplayController<T : TemporaryViewInfo>(
+ internal val context: Context,
+ internal val logger: MediaTttLogger,
+ internal val windowManager: WindowManager,
+ @Main private val mainExecutor: DelayableExecutor,
+ private val accessibilityManager: AccessibilityManager,
+ private val configurationController: ConfigurationController,
+ private val powerManager: PowerManager,
+ @LayoutRes private val viewLayoutRes: Int,
) {
/**
* Window layout params that will be used as a starting point for the [windowLayoutParams] of
@@ -85,31 +85,31 @@
*/
internal abstract val windowLayoutParams: WindowManager.LayoutParams
- /** The chip view currently being displayed. Null if the chip is not being displayed. */
- private var chipView: ViewGroup? = null
+ /** The view currently being displayed. Null if the view is not being displayed. */
+ private var view: ViewGroup? = null
- /** The chip info currently being displayed. Null if the chip is not being displayed. */
- internal var chipInfo: T? = null
+ /** The info currently being displayed. Null if the view is not being displayed. */
+ internal var info: T? = null
- /** A [Runnable] that, when run, will cancel the pending timeout of the chip. */
- private var cancelChipViewTimeout: Runnable? = null
+ /** A [Runnable] that, when run, will cancel the pending timeout of the view. */
+ private var cancelViewTimeout: Runnable? = null
/**
- * Displays the chip with the provided [newChipInfo].
+ * Displays the view with the provided [newInfo].
*
- * This method handles inflating and attaching the view, then delegates to [updateChipView] to
- * display the correct information in the chip.
+ * This method handles inflating and attaching the view, then delegates to [updateView] to
+ * display the correct information in the view.
*/
- fun displayChip(newChipInfo: T) {
- val currentChipView = chipView
+ fun displayView(newInfo: T) {
+ val currentView = view
- if (currentChipView != null) {
- updateChipView(newChipInfo, currentChipView)
+ if (currentView != null) {
+ updateView(newInfo, currentView)
} else {
- // The chip is new, so set up all our callbacks and inflate the view
+ // The view is new, so set up all our callbacks and inflate the view
configurationController.addCallback(displayScaleListener)
- // Wake the screen if necessary so the user will see the chip. (Per b/239426653, we want
- // the chip to show over the dream state, so we should only wake up if the screen is
+ // Wake the screen if necessary so the user will see the view. (Per b/239426653, we want
+ // the view to show over the dream state, so we should only wake up if the screen is
// completely off.)
if (!powerManager.isScreenOn) {
powerManager.wakeUp(
@@ -119,79 +119,79 @@
)
}
- inflateAndUpdateChip(newChipInfo)
+ inflateAndUpdateView(newInfo)
}
- // Cancel and re-set the chip timeout each time we get a new state.
+ // Cancel and re-set the view timeout each time we get a new state.
val timeout = accessibilityManager.getRecommendedTimeoutMillis(
- newChipInfo.getTimeoutMs().toInt(),
- // Not all chips have controls so FLAG_CONTENT_CONTROLS might be superfluous, but
+ newInfo.getTimeoutMs().toInt(),
+ // Not all views have controls so FLAG_CONTENT_CONTROLS might be superfluous, but
// include it just to be safe.
FLAG_CONTENT_ICONS or FLAG_CONTENT_TEXT or FLAG_CONTENT_CONTROLS
)
- cancelChipViewTimeout?.run()
- cancelChipViewTimeout = mainExecutor.executeDelayed(
- { removeChip(MediaTttRemovalReason.REASON_TIMEOUT) },
+ cancelViewTimeout?.run()
+ cancelViewTimeout = mainExecutor.executeDelayed(
+ { removeView(TemporaryDisplayRemovalReason.REASON_TIMEOUT) },
timeout.toLong()
)
}
- /** Inflates a new chip view, updates it with [newChipInfo], and adds the view to the window. */
- private fun inflateAndUpdateChip(newChipInfo: T) {
- val newChipView = LayoutInflater
+ /** Inflates a new view, updates it with [newInfo], and adds the view to the window. */
+ private fun inflateAndUpdateView(newInfo: T) {
+ val newView = LayoutInflater
.from(context)
- .inflate(chipLayoutRes, null) as ViewGroup
- chipView = newChipView
- updateChipView(newChipInfo, newChipView)
- windowManager.addView(newChipView, windowLayoutParams)
- animateChipIn(newChipView)
+ .inflate(viewLayoutRes, null) as ViewGroup
+ view = newView
+ updateView(newInfo, newView)
+ windowManager.addView(newView, windowLayoutParams)
+ animateViewIn(newView)
}
- /** Removes then re-inflates the chip. */
- private fun reinflateChip() {
- val currentChipInfo = chipInfo
- if (chipView == null || currentChipInfo == null) { return }
+ /** Removes then re-inflates the view. */
+ private fun reinflateView() {
+ val currentInfo = info
+ if (view == null || currentInfo == null) { return }
- windowManager.removeView(chipView)
- inflateAndUpdateChip(currentChipInfo)
+ windowManager.removeView(view)
+ inflateAndUpdateView(currentInfo)
}
private val displayScaleListener = object : ConfigurationController.ConfigurationListener {
override fun onDensityOrFontScaleChanged() {
- reinflateChip()
+ reinflateView()
}
}
/**
- * Hides the chip.
+ * Hides the view.
*
- * @param removalReason a short string describing why the chip was removed (timeout, state
+ * @param removalReason a short string describing why the view was removed (timeout, state
* change, etc.)
*/
- open fun removeChip(removalReason: String) {
- if (chipView == null) { return }
+ open fun removeView(removalReason: String) {
+ if (view == null) { return }
logger.logChipRemoval(removalReason)
configurationController.removeCallback(displayScaleListener)
- windowManager.removeView(chipView)
- chipView = null
- chipInfo = null
- // No need to time the chip out since it's already gone
- cancelChipViewTimeout?.run()
+ windowManager.removeView(view)
+ view = null
+ info = null
+ // No need to time the view out since it's already gone
+ cancelViewTimeout?.run()
}
/**
- * A method implemented by subclasses to update [currentChipView] based on [newChipInfo].
+ * A method implemented by subclasses to update [currentView] based on [newInfo].
*/
@CallSuper
- open fun updateChipView(newChipInfo: T, currentChipView: ViewGroup) {
- chipInfo = newChipInfo
+ open fun updateView(newInfo: T, currentView: ViewGroup) {
+ info = newInfo
}
/**
- * A method that can be implemented by subclcasses to do custom animations for when the chip
+ * A method that can be implemented by subclasses to do custom animations for when the view
* appears.
*/
- open fun animateChipIn(chipView: ViewGroup) {}
+ open fun animateViewIn(view: ViewGroup) {}
/**
* Returns the size that the icon should be, or null if no size override is needed.
@@ -209,12 +209,12 @@
* @return the content description of the icon.
*/
internal fun setIcon(
- currentChipView: ViewGroup,
+ currentView: ViewGroup,
appPackageName: String?,
appIconDrawableOverride: Drawable? = null,
appNameOverride: CharSequence? = null,
): CharSequence {
- val appIconView = currentChipView.requireViewById<CachingIconView>(R.id.app_icon)
+ val appIconView = currentView.requireViewById<CachingIconView>(R.id.app_icon)
val iconInfo = getIconInfo(appPackageName)
getIconSize(iconInfo.isAppIcon)?.let { size ->
@@ -264,9 +264,9 @@
// Used in CTS tests UpdateMediaTapToTransferSenderDisplayTest and
// UpdateMediaTapToTransferReceiverDisplayTest
private const val WINDOW_TITLE = "Media Transfer Chip View"
-private val TAG = MediaTttChipControllerCommon::class.simpleName!!
+private val TAG = TemporaryViewDisplayController::class.simpleName!!
-object MediaTttRemovalReason {
+object TemporaryDisplayRemovalReason {
const val REASON_TIMEOUT = "TIMEOUT"
const val REASON_SCREEN_TAP = "SCREEN_TAP"
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
similarity index 73%
rename from packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt
rename to packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
index a29c588..4fe753a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/ChipInfoCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.media.taptotransfer.common
+package com.android.systemui.temporarydisplay
/**
- * A superclass chip state that will be subclassed by the sender chip and receiver chip.
+ * A superclass view state used with [TemporaryViewDisplayController].
*/
-interface ChipInfoCommon {
+interface TemporaryViewInfo {
/**
- * Returns the amount of time the given chip state should display on the screen before it times
+ * Returns the amount of time the given view state should display on the screen before it times
* out and disappears.
*/
- fun getTimeoutMs(): Long
+ fun getTimeoutMs(): Long = DEFAULT_TIMEOUT_MILLIS
}
const val DEFAULT_TIMEOUT_MILLIS = 10000L
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index 8f127fd..0f06144 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -18,22 +18,31 @@
import android.content.Context
import android.hardware.devicestate.DeviceStateManager
-import android.os.Handler
import android.os.PowerManager
import android.provider.Settings
+import androidx.annotation.VisibleForTesting
import androidx.core.view.OneShotPreDrawListener
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
import com.android.internal.util.LatencyTracker
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.ScreenOffAnimation
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus
+import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.settings.GlobalSettings
-import java.util.concurrent.Executor
+import dagger.Lazy
import java.util.function.Consumer
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
/**
* Controls folding to AOD animation: when AOD is enabled and foldable device is folded we play a
@@ -43,16 +52,16 @@
class FoldAodAnimationController
@Inject
constructor(
- @Main private val handler: Handler,
- @Main private val executor: Executor,
+ @Main private val executor: DelayableExecutor,
private val context: Context,
private val deviceStateManager: DeviceStateManager,
private val wakefulnessLifecycle: WakefulnessLifecycle,
private val globalSettings: GlobalSettings,
private val latencyTracker: LatencyTracker,
+ private val keyguardInteractor: Lazy<KeyguardInteractor>,
) : CallbackController<FoldAodAnimationStatus>, ScreenOffAnimation, WakefulnessLifecycle.Observer {
- private lateinit var mCentralSurfaces: CentralSurfaces
+ private lateinit var centralSurfaces: CentralSurfaces
private var isFolded = false
private var isFoldHandled = true
@@ -64,12 +73,13 @@
private var shouldPlayAnimation = false
private var isAnimationPlaying = false
+ private var cancelAnimation: Runnable? = null
private val statusListeners = arrayListOf<FoldAodAnimationStatus>()
private val foldToAodLatencyTracker = FoldToAodLatencyTracker()
private val startAnimationRunnable = Runnable {
- mCentralSurfaces.notificationPanelViewController.startFoldToAodAnimation(
+ centralSurfaces.notificationPanelViewController.startFoldToAodAnimation(
/* startAction= */ { foldToAodLatencyTracker.onAnimationStarted() },
/* endAction= */ { setAnimationState(playing = false) },
/* cancelAction= */ { setAnimationState(playing = false) },
@@ -77,10 +87,14 @@
}
override fun initialize(centralSurfaces: CentralSurfaces, lightRevealScrim: LightRevealScrim) {
- this.mCentralSurfaces = centralSurfaces
+ this.centralSurfaces = centralSurfaces
deviceStateManager.registerCallback(executor, FoldListener())
wakefulnessLifecycle.addObserver(this)
+
+ centralSurfaces.notificationPanelViewController.view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) { listenForDozing(this) }
+ }
}
/** Returns true if we should run fold to AOD animation */
@@ -94,7 +108,7 @@
override fun startAnimation(): Boolean =
if (shouldStartAnimation()) {
setAnimationState(playing = true)
- mCentralSurfaces.notificationPanelViewController.prepareFoldToAodAnimation()
+ centralSurfaces.notificationPanelViewController.prepareFoldToAodAnimation()
true
} else {
setAnimationState(playing = false)
@@ -104,8 +118,8 @@
override fun onStartedWakingUp() {
if (isAnimationPlaying) {
foldToAodLatencyTracker.cancel()
- handler.removeCallbacks(startAnimationRunnable)
- mCentralSurfaces.notificationPanelViewController.cancelFoldToAodAnimation()
+ cancelAnimation?.run()
+ centralSurfaces.notificationPanelViewController.cancelFoldToAodAnimation()
}
setAnimationState(playing = false)
@@ -138,13 +152,13 @@
// We should play the folding to AOD animation
setAnimationState(playing = true)
- mCentralSurfaces.notificationPanelViewController.prepareFoldToAodAnimation()
+ centralSurfaces.notificationPanelViewController.prepareFoldToAodAnimation()
// We don't need to wait for the scrim as it is already displayed
// but we should wait for the initial animation preparations to be drawn
// (setting initial alpha/translation)
OneShotPreDrawListener.add(
- mCentralSurfaces.notificationPanelViewController.view,
+ centralSurfaces.notificationPanelViewController.view,
onReady
)
} else {
@@ -165,18 +179,14 @@
fun onScreenTurnedOn() {
if (shouldPlayAnimation) {
- handler.removeCallbacks(startAnimationRunnable)
+ cancelAnimation?.run()
// Post starting the animation to the next frame to avoid junk due to inset changes
- handler.post(startAnimationRunnable)
+ cancelAnimation = executor.executeDelayed(startAnimationRunnable, /* delayMillis= */ 0)
shouldPlayAnimation = false
}
}
- fun setIsDozing(dozing: Boolean) {
- isDozing = dozing
- }
-
override fun isAnimationPlaying(): Boolean = isAnimationPlaying
override fun isKeyguardHideDelayed(): Boolean = isAnimationPlaying()
@@ -204,6 +214,11 @@
statusListeners.remove(listener)
}
+ @VisibleForTesting
+ internal suspend fun listenForDozing(scope: CoroutineScope): Job {
+ return scope.launch { keyguardInteractor.get().isDozing.collect { isDozing = it } }
+ }
+
interface FoldAodAnimationStatus {
fun onFoldToAodAnimationChanged()
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
index 84305cc..f71d596 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -16,11 +16,15 @@
package com.android.systemui.util.kotlin
+import java.util.concurrent.atomic.AtomicReference
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.launch
/**
* Returns a new [Flow] that combines the two most recent emissions from [this] using [transform].
@@ -116,3 +120,35 @@
/** Elements that are present in the second [Set] but not in the first. */
val added: Set<T>,
)
+
+/**
+ * Returns a new [Flow] that emits at the same rate as [this], but combines the emitted value with
+ * the most recent emission from [other] using [transform].
+ *
+ * Note that the returned Flow will not emit anything until [other] has emitted at least one value.
+ */
+fun <A, B, C> Flow<A>.sample(other: Flow<B>, transform: suspend (A, B) -> C): Flow<C> = flow {
+ coroutineScope {
+ val noVal = Any()
+ val sampledRef = AtomicReference(noVal)
+ val job = launch(Dispatchers.Unconfined) {
+ other.collect { sampledRef.set(it) }
+ }
+ collect {
+ val sampled = sampledRef.get()
+ if (sampled != noVal) {
+ @Suppress("UNCHECKED_CAST")
+ emit(transform(it, sampled as B))
+ }
+ }
+ job.cancel()
+ }
+}
+
+/**
+ * Returns a new [Flow] that emits at the same rate as [this], but emits the most recently emitted
+ * value from [other] instead.
+ *
+ * Note that the returned Flow will not emit anything until [other] has emitted at least one value.
+ */
+fun <A> Flow<*>.sample(other: Flow<A>): Flow<A> = sample(other) { _, a -> a }
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Suspend.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Suspend.kt
new file mode 100644
index 0000000..2e551f1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Suspend.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 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.util.kotlin
+
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * Runs the given [blocks] in parallel, returning the result of the first one to complete, and
+ * cancelling all others.
+ */
+suspend fun <R> race(vararg blocks: suspend () -> R): R = coroutineScope {
+ val completion = CompletableDeferred<R>()
+ val raceJob = launch {
+ for (block in blocks) {
+ launch { completion.complete(block()) }
+ }
+ }
+ completion.await().also { raceJob.cancel() }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java
similarity index 90%
rename from packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
rename to packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java
index 013c298..0a9c745 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java
@@ -33,14 +33,14 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
-public class KeyguardMessageAreaTest extends SysuiTestCase {
+public class AuthKeyguardMessageAreaTest extends SysuiTestCase {
private KeyguardMessageArea mKeyguardMessageArea;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mKeyguardMessageArea = new KeyguardMessageArea(mContext, null);
- mKeyguardMessageArea.setBouncerShowing(true);
+ mKeyguardMessageArea = new AuthKeyguardMessageArea(mContext, null);
+ mKeyguardMessageArea.setIsVisible(true);
}
@Test
@@ -53,7 +53,7 @@
@Test
public void testHiddenWhenBouncerHidden() {
- mKeyguardMessageArea.setBouncerShowing(false);
+ mKeyguardMessageArea.setIsVisible(false);
mKeyguardMessageArea.setVisibility(View.INVISIBLE);
mKeyguardMessageArea.setMessage("oobleck");
assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.INVISIBLE);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index 90f7fda..8bbaf3d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -56,7 +56,7 @@
@Mock
private PasswordTextView mPasswordEntry;
@Mock
- private KeyguardMessageArea mKeyguardMessageArea;
+ private BouncerKeyguardMessageArea mKeyguardMessageArea;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
@@ -85,7 +85,7 @@
when(mAbsKeyInputView.getPasswordTextViewId()).thenReturn(1);
when(mAbsKeyInputView.findViewById(1)).thenReturn(mPasswordEntry);
when(mAbsKeyInputView.isAttachedToWindow()).thenReturn(true);
- when(mAbsKeyInputView.findViewById(R.id.keyguard_message_area))
+ when(mAbsKeyInputView.requireViewById(R.id.bouncer_message_area))
.thenReturn(mKeyguardMessageArea);
mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
index 8293cc2..69524e5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
@@ -89,8 +89,8 @@
@Test
public void testSetBouncerVisible() {
- mMessageAreaController.setBouncerShowing(true);
- verify(mKeyguardMessageArea).setBouncerShowing(true);
+ mMessageAreaController.setIsVisible(true);
+ verify(mKeyguardMessageArea).setIsVisible(true);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index ec85603..b89dbd9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -64,9 +64,10 @@
@Mock
lateinit var keyguardViewController: KeyguardViewController
@Mock
- private lateinit var mKeyguardMessageArea: KeyguardMessageArea
+ private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea
@Mock
- private lateinit var mKeyguardMessageAreaController: KeyguardMessageAreaController
+ private lateinit var mKeyguardMessageAreaController:
+ KeyguardMessageAreaController<BouncerKeyguardMessageArea>
private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController
@@ -74,7 +75,8 @@
fun setup() {
MockitoAnnotations.initMocks(this)
Mockito.`when`(
- keyguardPasswordView.findViewById<KeyguardMessageArea>(R.id.keyguard_message_area)
+ keyguardPasswordView
+ .requireViewById<BouncerKeyguardMessageArea>(R.id.bouncer_message_area)
).thenReturn(mKeyguardMessageArea)
Mockito.`when`(messageAreaControllerFactory.create(mKeyguardMessageArea))
.thenReturn(mKeyguardMessageAreaController)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 616a105..3262a77 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -66,10 +66,11 @@
var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory
@Mock
- private lateinit var mKeyguardMessageArea: KeyguardMessageArea
+ private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea
@Mock
- private lateinit var mKeyguardMessageAreaController: KeyguardMessageAreaController
+ private lateinit var mKeyguardMessageAreaController:
+ KeyguardMessageAreaController<BouncerKeyguardMessageArea>
@Mock
private lateinit var mLockPatternView: LockPatternView
@@ -83,7 +84,8 @@
fun setup() {
MockitoAnnotations.initMocks(this)
`when`(mKeyguardPatternView.isAttachedToWindow).thenReturn(true)
- `when`(mKeyguardPatternView.findViewById<KeyguardMessageArea>(R.id.keyguard_message_area))
+ `when`(mKeyguardPatternView
+ .requireViewById<BouncerKeyguardMessageArea>(R.id.bouncer_message_area))
.thenReturn(mKeyguardMessageArea)
`when`(mKeyguardPatternView.findViewById<LockPatternView>(R.id.lockPatternView))
.thenReturn(mLockPatternView)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index 7bc8e8a..97d556b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -51,7 +51,7 @@
@Mock
private PasswordTextView mPasswordEntry;
@Mock
- private KeyguardMessageArea mKeyguardMessageArea;
+ private BouncerKeyguardMessageArea mKeyguardMessageArea;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
@@ -90,7 +90,7 @@
when(mPinBasedInputView.findViewById(1)).thenReturn(mPasswordEntry);
when(mPinBasedInputView.isAttachedToWindow()).thenReturn(true);
when(mPinBasedInputView.getButtons()).thenReturn(mButtons);
- when(mPinBasedInputView.findViewById(R.id.keyguard_message_area))
+ when(mPinBasedInputView.requireViewById(R.id.bouncer_message_area))
.thenReturn(mKeyguardMessageArea);
when(mPinBasedInputView.findViewById(R.id.delete_button))
.thenReturn(mDeleteButton);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index d68e8bd..c6ebaa8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -41,6 +41,7 @@
import android.hardware.biometrics.BiometricSourceType;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowInsetsController;
@@ -117,7 +118,7 @@
@Mock
private KeyguardMessageAreaController mKeyguardMessageAreaController;
@Mock
- private KeyguardMessageArea mKeyguardMessageArea;
+ private BouncerKeyguardMessageArea mKeyguardMessageArea;
@Mock
private ConfigurationController mConfigurationController;
@Mock
@@ -163,9 +164,10 @@
when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
.thenReturn(mAdminSecondaryLockScreenController);
when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
- mKeyguardPasswordView = spy(new KeyguardPasswordView(getContext()));
+ mKeyguardPasswordView = spy((KeyguardPasswordView) LayoutInflater.from(mContext).inflate(
+ R.layout.keyguard_password_view, null));
when(mKeyguardPasswordView.getRootView()).thenReturn(mSecurityViewFlipper);
- when(mKeyguardPasswordView.findViewById(R.id.keyguard_message_area))
+ when(mKeyguardPasswordView.requireViewById(R.id.bouncer_message_area))
.thenReturn(mKeyguardMessageArea);
when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class)))
.thenReturn(mKeyguardMessageAreaController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 3aa2266..ba1e168 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -19,6 +19,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Position
+import com.android.systemui.doze.DozeHost
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.argumentCaptor
@@ -40,6 +41,7 @@
class KeyguardRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var dozeHost: DozeHost
@Mock private lateinit var keyguardStateController: KeyguardStateController
private lateinit var underTest: KeyguardRepositoryImpl
@@ -48,7 +50,12 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- underTest = KeyguardRepositoryImpl(statusBarStateController, keyguardStateController)
+ underTest =
+ KeyguardRepositoryImpl(
+ statusBarStateController,
+ keyguardStateController,
+ dozeHost,
+ )
}
@Test
@@ -129,8 +136,8 @@
var latest: Boolean? = null
val job = underTest.isDozing.onEach { latest = it }.launchIn(this)
- val captor = argumentCaptor<StatusBarStateController.StateListener>()
- verify(statusBarStateController).addCallback(captor.capture())
+ val captor = argumentCaptor<DozeHost.Callback>()
+ verify(dozeHost).addCallback(captor.capture())
captor.value.onDozingChanged(true)
assertThat(latest).isTrue()
@@ -139,7 +146,7 @@
assertThat(latest).isFalse()
job.cancel()
- verify(statusBarStateController).removeCallback(captor.value)
+ verify(dozeHost).removeCallback(captor.value)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
index 9acd21c..9a91ea91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
@@ -51,18 +51,19 @@
@Parameters(
name =
"feature enabled = {0}, has favorites = {1}, has service infos = {2}, can show" +
- " while locked = {3} - expected visible = {4}"
+ " while locked = {3}, visibility is AVAILABLE {4} - expected visible = {5}"
)
@JvmStatic
fun data() =
- (0 until 16)
+ (0 until 32)
.map { combination ->
arrayOf(
- /* isFeatureEnabled= */ combination and 0b1000 != 0,
- /* hasFavorites= */ combination and 0b0100 != 0,
- /* hasServiceInfos= */ combination and 0b0010 != 0,
- /* canShowWhileLocked= */ combination and 0b0001 != 0,
- /* isVisible= */ combination == 0b1111,
+ /* isFeatureEnabled= */ combination and 0b10000 != 0,
+ /* hasFavorites= */ combination and 0b01000 != 0,
+ /* hasServiceInfos= */ combination and 0b00100 != 0,
+ /* canShowWhileLocked= */ combination and 0b00010 != 0,
+ /* visibilityAvailable= */ combination and 0b00001 != 0,
+ /* isVisible= */ combination == 0b11111,
)
}
.toList()
@@ -81,7 +82,8 @@
@JvmField @Parameter(1) var hasFavorites: Boolean = false
@JvmField @Parameter(2) var hasServiceInfos: Boolean = false
@JvmField @Parameter(3) var canShowWhileLocked: Boolean = false
- @JvmField @Parameter(4) var isVisible: Boolean = false
+ @JvmField @Parameter(4) var isVisibilityAvailable: Boolean = false
+ @JvmField @Parameter(5) var isVisibleExpected: Boolean = false
@Before
fun setUp() {
@@ -93,6 +95,14 @@
.thenReturn(Optional.of(controlsListingController))
whenever(component.canShowWhileLockedSetting)
.thenReturn(MutableStateFlow(canShowWhileLocked))
+ whenever(component.getVisibility())
+ .thenReturn(
+ if (isVisibilityAvailable) {
+ ControlsComponent.Visibility.AVAILABLE
+ } else {
+ ControlsComponent.Visibility.UNAVAILABLE
+ }
+ )
underTest =
HomeControlsKeyguardQuickAffordanceConfig(
@@ -128,7 +138,7 @@
assertThat(values.last())
.isInstanceOf(
- if (isVisible) {
+ if (isVisibleExpected) {
KeyguardQuickAffordanceConfig.State.Visible::class.java
} else {
KeyguardQuickAffordanceConfig.State.Hidden::class.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
index 059487d..dede4ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
@@ -69,6 +69,7 @@
val controlsController = mock<ControlsController>()
whenever(component.getControlsController()).thenReturn(Optional.of(controlsController))
whenever(component.getControlsListingController()).thenReturn(Optional.empty())
+ whenever(component.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
whenever(controlsController.getFavorites()).thenReturn(listOf(mock()))
val values = mutableListOf<KeyguardQuickAffordanceConfig.State>()
@@ -87,6 +88,7 @@
val controlsController = mock<ControlsController>()
whenever(component.getControlsController()).thenReturn(Optional.of(controlsController))
whenever(component.getControlsListingController()).thenReturn(Optional.empty())
+ whenever(component.getVisibility()).thenReturn(ControlsComponent.Visibility.AVAILABLE)
whenever(controlsController.getFavorites()).thenReturn(listOf(mock()))
val values = mutableListOf<KeyguardQuickAffordanceConfig.State>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index 18bfd04..954b438 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -29,6 +29,7 @@
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.media.dream.MediaDreamComplication
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.testing.FakeNotifPanelEvents
import com.android.systemui.statusbar.StatusBarState
@@ -38,6 +39,8 @@
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.animation.UniqueObjectHostView
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.utils.os.FakeHandler
import com.google.common.truth.Truth.assertThat
@@ -79,6 +82,9 @@
private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
@Captor
private lateinit var statusBarCallback: ArgumentCaptor<(StatusBarStateController.StateListener)>
+ @Captor
+ private lateinit var dreamOverlayCallback:
+ ArgumentCaptor<(DreamOverlayStateController.Callback)>
@JvmField
@Rule
val mockito = MockitoJUnit.rule()
@@ -113,6 +119,7 @@
fakeHandler,)
verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
verify(statusBarStateController).addCallback(statusBarCallback.capture())
+ verify(dreamOverlayStateController).addCallback(dreamOverlayCallback.capture())
setupHost(lockHost, MediaHierarchyManager.LOCATION_LOCKSCREEN, LOCKSCREEN_TOP)
setupHost(qsHost, MediaHierarchyManager.LOCATION_QS, QS_TOP)
setupHost(qqsHost, MediaHierarchyManager.LOCATION_QQS, QQS_TOP)
@@ -332,6 +339,27 @@
assertThat(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).isFalse()
}
+ @Test
+ fun testDream() {
+ goToDream()
+ setMediaDreamComplicationEnabled(true)
+ verify(mediaCarouselController).onDesiredLocationChanged(
+ eq(MediaHierarchyManager.LOCATION_DREAM_OVERLAY),
+ nullable(),
+ eq(false),
+ anyLong(),
+ anyLong())
+ clearInvocations(mediaCarouselController)
+
+ setMediaDreamComplicationEnabled(false)
+ verify(mediaCarouselController).onDesiredLocationChanged(
+ eq(MediaHierarchyManager.LOCATION_QQS),
+ any(MediaHostState::class.java),
+ eq(false),
+ anyLong(),
+ anyLong())
+ }
+
private fun enableSplitShade() {
context.getOrCreateTestableResources().addOverride(
R.bool.config_use_split_notification_shade, true
@@ -343,6 +371,8 @@
whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
settings.putInt(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, 1)
statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
+ whenever(dreamOverlayStateController.isOverlayActive).thenReturn(false)
+ dreamOverlayCallback.value.onStateChanged()
clearInvocations(mediaCarouselController)
}
@@ -354,6 +384,17 @@
)
}
+ private fun goToDream() {
+ whenever(dreamOverlayStateController.isOverlayActive).thenReturn(true)
+ dreamOverlayCallback.value.onStateChanged()
+ }
+
+ private fun setMediaDreamComplicationEnabled(enabled: Boolean) {
+ val complications = if (enabled) listOf(mock<MediaDreamComplication>()) else emptyList()
+ whenever(dreamOverlayStateController.complications).thenReturn(complications)
+ dreamOverlayCallback.value.onComplicationsChanged()
+ }
+
private fun expandQS() {
mediaHierarchyManager.qsExpansion = 1.0f
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 171d893..e7b4593 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -41,7 +41,6 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
-import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -74,8 +73,6 @@
@Mock
private lateinit var windowManager: WindowManager
@Mock
- private lateinit var viewUtil: ViewUtil
- @Mock
private lateinit var commandQueue: CommandQueue
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
@@ -102,7 +99,6 @@
context,
logger,
windowManager,
- viewUtil,
FakeExecutor(FakeSystemClock()),
accessibilityManager,
configurationController,
@@ -182,7 +178,7 @@
@Test
fun setIcon_isAppIcon_usesAppIconSize() {
- controllerReceiver.displayChip(getChipReceiverInfo())
+ controllerReceiver.displayView(getChipReceiverInfo())
val chipView = getChipView()
controllerReceiver.setIcon(chipView, PACKAGE_NAME)
@@ -198,7 +194,7 @@
@Test
fun setIcon_notAppIcon_usesGenericIconSize() {
- controllerReceiver.displayChip(getChipReceiverInfo())
+ controllerReceiver.displayView(getChipReceiverInfo())
val chipView = getChipView()
controllerReceiver.setIcon(chipView, appPackageName = null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index 1061e3c..52b6eed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -42,7 +42,6 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
-import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -51,8 +50,8 @@
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -75,8 +74,6 @@
@Mock
private lateinit var windowManager: WindowManager
@Mock
- private lateinit var viewUtil: ViewUtil
- @Mock
private lateinit var commandQueue: CommandQueue
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
@@ -110,7 +107,6 @@
context,
logger,
windowManager,
- viewUtil,
fakeExecutor,
accessibilityManager,
configurationController,
@@ -309,7 +305,7 @@
@Test
fun almostCloseToStartCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
val state = almostCloseToStartCast()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -325,7 +321,7 @@
@Test
fun almostCloseToEndCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
val state = almostCloseToEndCast()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -341,7 +337,7 @@
@Test
fun transferToReceiverTriggered_appIcon_loadingIcon_noUndo_noFailureIcon() {
val state = transferToReceiverTriggered()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -357,7 +353,7 @@
@Test
fun transferToThisDeviceTriggered_appIcon_loadingIcon_noUndo_noFailureIcon() {
val state = transferToThisDeviceTriggered()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -373,7 +369,7 @@
@Test
fun transferToReceiverSucceeded_appIcon_deviceName_noLoadingIcon_noFailureIcon() {
val state = transferToReceiverSucceeded()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -387,7 +383,7 @@
@Test
fun transferToReceiverSucceeded_nullUndoRunnable_noUndo() {
- controllerSender.displayChip(transferToReceiverSucceeded(undoCallback = null))
+ controllerSender.displayView(transferToReceiverSucceeded(undoCallback = null))
val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -398,7 +394,7 @@
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
- controllerSender.displayChip(transferToReceiverSucceeded(undoCallback))
+ controllerSender.displayView(transferToReceiverSucceeded(undoCallback))
val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
@@ -414,7 +410,7 @@
}
}
- controllerSender.displayChip(transferToReceiverSucceeded(undoCallback))
+ controllerSender.displayView(transferToReceiverSucceeded(undoCallback))
getChipView().getUndoButton().performClick()
assertThat(undoCallbackCalled).isTrue()
@@ -425,7 +421,7 @@
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
- controllerSender.displayChip(transferToReceiverSucceeded(undoCallback))
+ controllerSender.displayView(transferToReceiverSucceeded(undoCallback))
getChipView().getUndoButton().performClick()
@@ -440,7 +436,7 @@
@Test
fun transferToThisDeviceSucceeded_appIcon_deviceName_noLoadingIcon_noFailureIcon() {
val state = transferToThisDeviceSucceeded()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -454,7 +450,7 @@
@Test
fun transferToThisDeviceSucceeded_nullUndoRunnable_noUndo() {
- controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback = null))
+ controllerSender.displayView(transferToThisDeviceSucceeded(undoCallback = null))
val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -465,7 +461,7 @@
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
- controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback))
+ controllerSender.displayView(transferToThisDeviceSucceeded(undoCallback))
val chipView = getChipView()
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
@@ -481,7 +477,7 @@
}
}
- controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback))
+ controllerSender.displayView(transferToThisDeviceSucceeded(undoCallback))
getChipView().getUndoButton().performClick()
assertThat(undoCallbackCalled).isTrue()
@@ -492,7 +488,7 @@
val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
- controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback))
+ controllerSender.displayView(transferToThisDeviceSucceeded(undoCallback))
getChipView().getUndoButton().performClick()
@@ -507,7 +503,7 @@
@Test
fun transferToReceiverFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() {
val state = transferToReceiverFailed()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -523,7 +519,7 @@
@Test
fun transferToThisDeviceFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() {
val state = transferToThisDeviceFailed()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
@@ -538,24 +534,24 @@
@Test
fun changeFromAlmostCloseToStartToTransferTriggered_loadingIconAppears() {
- controllerSender.displayChip(almostCloseToStartCast())
- controllerSender.displayChip(transferToReceiverTriggered())
+ controllerSender.displayView(almostCloseToStartCast())
+ controllerSender.displayView(transferToReceiverTriggered())
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
}
@Test
fun changeFromTransferTriggeredToTransferSucceeded_loadingIconDisappears() {
- controllerSender.displayChip(transferToReceiverTriggered())
- controllerSender.displayChip(transferToReceiverSucceeded())
+ controllerSender.displayView(transferToReceiverTriggered())
+ controllerSender.displayView(transferToReceiverSucceeded())
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.GONE)
}
@Test
fun changeFromTransferTriggeredToTransferSucceeded_undoButtonAppears() {
- controllerSender.displayChip(transferToReceiverTriggered())
- controllerSender.displayChip(
+ controllerSender.displayView(transferToReceiverTriggered())
+ controllerSender.displayView(
transferToReceiverSucceeded(
object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
@@ -568,26 +564,26 @@
@Test
fun changeFromTransferSucceededToAlmostCloseToStart_undoButtonDisappears() {
- controllerSender.displayChip(transferToReceiverSucceeded())
- controllerSender.displayChip(almostCloseToStartCast())
+ controllerSender.displayView(transferToReceiverSucceeded())
+ controllerSender.displayView(almostCloseToStartCast())
assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE)
}
@Test
fun changeFromTransferTriggeredToTransferFailed_failureIconAppears() {
- controllerSender.displayChip(transferToReceiverTriggered())
- controllerSender.displayChip(transferToReceiverFailed())
+ controllerSender.displayView(transferToReceiverTriggered())
+ controllerSender.displayView(transferToReceiverFailed())
assertThat(getChipView().getFailureIcon().visibility).isEqualTo(View.VISIBLE)
}
@Test
- fun transferToReceiverTriggeredThenRemoveChip_chipStillDisplayed() {
- controllerSender.displayChip(transferToReceiverTriggered())
+ fun transferToReceiverTriggeredThenRemoveView_viewStillDisplayed() {
+ controllerSender.displayView(transferToReceiverTriggered())
fakeClock.advanceTime(1000L)
- controllerSender.removeChip("fakeRemovalReason")
+ controllerSender.removeView("fakeRemovalReason")
fakeExecutor.runAllReady()
verify(windowManager, never()).removeView(any())
@@ -596,9 +592,9 @@
@Test
fun transferToReceiverTriggeredThenFarFromReceiver_eventuallyTimesOut() {
val state = transferToReceiverTriggered()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
fakeClock.advanceTime(1000L)
- controllerSender.removeChip("fakeRemovalReason")
+ controllerSender.removeView("fakeRemovalReason")
fakeClock.advanceTime(TIMEOUT + 1L)
@@ -606,11 +602,11 @@
}
@Test
- fun transferToThisDeviceTriggeredThenRemoveChip_chipStillDisplayed() {
- controllerSender.displayChip(transferToThisDeviceTriggered())
+ fun transferToThisDeviceTriggeredThenRemoveView_viewStillDisplayed() {
+ controllerSender.displayView(transferToThisDeviceTriggered())
fakeClock.advanceTime(1000L)
- controllerSender.removeChip("fakeRemovalReason")
+ controllerSender.removeView("fakeRemovalReason")
fakeExecutor.runAllReady()
verify(windowManager, never()).removeView(any())
@@ -619,9 +615,9 @@
@Test
fun transferToThisDeviceTriggeredThenFarFromReceiver_eventuallyTimesOut() {
val state = transferToThisDeviceTriggered()
- controllerSender.displayChip(state)
+ controllerSender.displayView(state)
fakeClock.advanceTime(1000L)
- controllerSender.removeChip("fakeRemovalReason")
+ controllerSender.removeView("fakeRemovalReason")
fakeClock.advanceTime(TIMEOUT + 1L)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 43fc8983..2adc389 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -19,8 +19,10 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.MotionEvent
+import android.view.ViewGroup
import androidx.test.filters.SmallTest
import com.android.keyguard.LockIconViewController
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
@@ -246,6 +248,18 @@
verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
assertThat(returnVal).isTrue()
}
+
+ @Test
+ fun testGetBouncerContainer() {
+ underTest.bouncerContainer
+ verify(view).findViewById<ViewGroup>(R.id.keyguard_bouncer_container)
+ }
+
+ @Test
+ fun testGetKeyguardMessageArea() {
+ underTest.keyguardMessageArea
+ verify(view).findViewById<ViewGroup>(R.id.keyguard_message_area)
+ }
}
private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
index d2970a6..97c0bb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
@@ -27,6 +27,7 @@
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.tuner.TunerService
import com.android.systemui.tuner.TunerService.Tunable
@@ -63,6 +64,8 @@
private lateinit var tunerService: TunerService
@Mock
private lateinit var dumpManager: DumpManager
+ @Mock
+ private lateinit var statusBarStateController: StatusBarStateController
private lateinit var tunableCaptor: ArgumentCaptor<Tunable>
private lateinit var underTest: PulsingGestureListener
@@ -77,6 +80,7 @@
dockManager,
centralSurfaces,
ambientDisplayConfiguration,
+ statusBarStateController,
tunerService,
dumpManager
)
@@ -85,6 +89,8 @@
@Test
fun testGestureDetector_singleTapEnabled() {
+ whenever(statusBarStateController.isPulsing).thenReturn(true)
+
// GIVEN tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
@@ -102,6 +108,8 @@
@Test
fun testGestureDetector_doubleTapEnabled() {
+ whenever(statusBarStateController.isPulsing).thenReturn(true)
+
// GIVEN double tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
@@ -119,6 +127,8 @@
@Test
fun testGestureDetector_singleTapEnabled_falsing() {
+ whenever(statusBarStateController.isPulsing).thenReturn(true)
+
// GIVEN tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
@@ -135,7 +145,23 @@
}
@Test
+ fun testGestureDetector_notPulsing_noFalsingCheck() {
+ whenever(statusBarStateController.isPulsing).thenReturn(false)
+
+ // GIVEN tap is enabled, prox not covered
+ whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
+ // WHEN there's a tap
+ underTest.onSingleTapConfirmed(downEv)
+
+ // THEN the falsing manager never gets a call (because the device wasn't pulsing
+ // during the tap)
+ verify(falsingManager, never()).isFalseTap(anyInt())
+ }
+
+ @Test
fun testGestureDetector_doubleTapEnabled_falsing() {
+ whenever(statusBarStateController.isPulsing).thenReturn(true)
+
// GIVEN double tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
@@ -153,6 +179,8 @@
@Test
fun testGestureDetector_singleTapEnabled_proxCovered() {
+ whenever(statusBarStateController.isPulsing).thenReturn(true)
+
// GIVEN tap is enabled, not a false tap based on classifiers
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
@@ -170,6 +198,8 @@
@Test
fun testGestureDetector_doubleTapEnabled_proxCovered() {
+ whenever(statusBarStateController.isPulsing).thenReturn(true)
+
// GIVEN double tap is enabled, not a false tap based on classifiers
whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
updateSettings()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index d73d07c..ac8874b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -639,7 +639,7 @@
mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT,
"A message", BiometricSourceType.FACE);
- verify(mStatusBarKeyguardViewManager).showBouncerMessage(eq(message), any());
+ verify(mStatusBarKeyguardViewManager).setKeyguardMessage(eq(message), any());
}
@Test
@@ -1050,14 +1050,14 @@
}
@Test
- public void onTrustGrantedMessageDoesShowsOnTrustGranted() {
+ public void onTrustGrantedMessageShowsOnTrustGranted() {
createController();
mController.setVisible(true);
// GIVEN trust is granted
when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
- // WHEN the showTrustGranted message is called
+ // WHEN the showTrustGranted method is called
final String trustGrantedMsg = "testing trust granted message";
mController.getKeyguardCallback().showTrustGrantedMessage(trustGrantedMsg);
@@ -1068,6 +1068,38 @@
}
@Test
+ public void onTrustGrantedMessage_nullMessage_showsDefaultMessage() {
+ createController();
+ mController.setVisible(true);
+
+ // GIVEN trust is granted
+ when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
+
+ // WHEN the showTrustGranted method is called with a null message
+ mController.getKeyguardCallback().showTrustGrantedMessage(null);
+
+ // THEN verify the default trust granted message shows
+ verifyIndicationMessage(
+ INDICATION_TYPE_TRUST,
+ getContext().getString(R.string.keyguard_indication_trust_unlocked));
+ }
+
+ @Test
+ public void onTrustGrantedMessage_emptyString_showsNoMessage() {
+ createController();
+ mController.setVisible(true);
+
+ // GIVEN trust is granted
+ when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
+
+ // WHEN the showTrustGranted method is called with an EMPTY string
+ mController.getKeyguardCallback().showTrustGrantedMessage("");
+
+ // THEN verify NO trust message is shown
+ verifyNoMessage(INDICATION_TYPE_TRUST);
+ }
+
+ @Test
public void coEx_faceSuccess_showsPressToOpen() {
// GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, no a11y enabled
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index b166b73..6446fb5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -48,7 +48,6 @@
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.anyString
@@ -56,6 +55,7 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
@RunWith(AndroidTestingRunner::class)
@@ -139,7 +139,7 @@
notificationShadeDepthController.onPanelExpansionChanged(
PanelExpansionChangeEvent(
fraction = 1f, expanded = true, tracking = false, dragDownPxAmount = 0f))
- verify(shadeAnimation).animateTo(eq(maxBlur), any())
+ verify(shadeAnimation).animateTo(eq(maxBlur))
}
@Test
@@ -147,7 +147,7 @@
notificationShadeDepthController.onPanelExpansionChanged(
PanelExpansionChangeEvent(
fraction = 0.01f, expanded = false, tracking = false, dragDownPxAmount = 0f))
- verify(shadeAnimation).animateTo(eq(maxBlur), any())
+ verify(shadeAnimation).animateTo(eq(maxBlur))
}
@Test
@@ -157,7 +157,7 @@
notificationShadeDepthController.onPanelExpansionChanged(
PanelExpansionChangeEvent(
fraction = 0f, expanded = false, tracking = false, dragDownPxAmount = 0f))
- verify(shadeAnimation).animateTo(eq(0), any())
+ verify(shadeAnimation).animateTo(eq(0))
}
@Test
@@ -168,15 +168,15 @@
onPanelExpansionChanged_apliesBlur_ifShade()
clearInvocations(shadeAnimation)
notificationShadeDepthController.onPanelExpansionChanged(event)
- verify(shadeAnimation, never()).animateTo(anyInt(), any())
+ verify(shadeAnimation, never()).animateTo(anyInt())
notificationShadeDepthController.onPanelExpansionChanged(
event.copy(fraction = 0.9f, tracking = true))
- verify(shadeAnimation, never()).animateTo(anyInt(), any())
+ verify(shadeAnimation, never()).animateTo(anyInt())
notificationShadeDepthController.onPanelExpansionChanged(
event.copy(fraction = 0.8f, tracking = false))
- verify(shadeAnimation).animateTo(eq(0), any())
+ verify(shadeAnimation).animateTo(eq(0))
}
@Test
@@ -186,7 +186,7 @@
notificationShadeDepthController.onPanelExpansionChanged(
PanelExpansionChangeEvent(
fraction = 0.6f, expanded = true, tracking = true, dragDownPxAmount = 0f))
- verify(shadeAnimation).animateTo(eq(maxBlur), any())
+ verify(shadeAnimation).animateTo(eq(maxBlur))
}
@Test
@@ -212,7 +212,7 @@
statusBarState = StatusBarState.KEYGUARD
statusBarStateListener.onStateChanged(statusBarState)
- verify(shadeAnimation).animateTo(eq(0), any())
+ verify(shadeAnimation).animateTo(eq(0))
}
@Test
@@ -395,13 +395,13 @@
@Test
fun brightnessMirrorVisible_whenVisible() {
notificationShadeDepthController.brightnessMirrorVisible = true
- verify(brightnessSpring).animateTo(eq(maxBlur), any())
+ verify(brightnessSpring).animateTo(eq(maxBlur))
}
@Test
fun brightnessMirrorVisible_whenHidden() {
notificationShadeDepthController.brightnessMirrorVisible = false
- verify(brightnessSpring).animateTo(eq(0), any())
+ verify(brightnessSpring).animateTo(eq(0))
}
@Test
@@ -424,7 +424,7 @@
fun ignoreShadeBlurUntilHidden_whennNull_ignoresIfShadeHasNoBlur() {
`when`(shadeAnimation.radius).thenReturn(0f)
notificationShadeDepthController.blursDisabledForAppLaunch = true
- verify(shadeAnimation, never()).animateTo(anyInt(), any())
+ verify(shadeAnimation, never()).animateTo(anyInt())
}
private fun enableSplitShade() {
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
new file mode 100644
index 0000000..11798a7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2022 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.statusbar.notification.stack
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val MAX_PULSE_HEIGHT = 100000f
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class AmbientStateTest : SysuiTestCase() {
+
+ private val dumpManager = mock<DumpManager>()
+ private val sectionProvider = StackScrollAlgorithm.SectionProvider { _, _ -> false }
+ private val bypassController = StackScrollAlgorithm.BypassController { false }
+ private val statusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>()
+
+ private lateinit var sut: AmbientState
+
+ @Before
+ fun setUp() {
+ sut =
+ AmbientState(
+ context,
+ dumpManager,
+ sectionProvider,
+ bypassController,
+ statusBarKeyguardViewManager,
+ )
+ }
+
+ // region isDimmed
+ @Test
+ fun isDimmed_whenTrue_shouldReturnTrue() {
+ sut.arrangeDimmed(true)
+
+ assertThat(sut.isDimmed).isTrue()
+ }
+
+ @Test
+ fun isDimmed_whenFalse_shouldReturnFalse() {
+ sut.arrangeDimmed(false)
+
+ assertThat(sut.isDimmed).isFalse()
+ }
+
+ @Test
+ fun isDimmed_whenDozeAmountIsEmpty_shouldReturnTrue() {
+ sut.arrangeDimmed(true)
+ sut.dozeAmount = 0f
+
+ assertThat(sut.isDimmed).isTrue()
+ }
+
+ @Test
+ fun isDimmed_whenPulseExpandingIsFalse_shouldReturnTrue() {
+ sut.arrangeDimmed(true)
+ sut.arrangePulseExpanding(false)
+ sut.dozeAmount = 1f // arrangePulseExpanding changes dozeAmount
+
+ assertThat(sut.isDimmed).isTrue()
+ }
+ // endregion
+
+ // region pulseHeight
+ @Test
+ fun pulseHeight_whenValueChanged_shouldCallListener() {
+ var listenerCalledCount = 0
+ sut.pulseHeight = MAX_PULSE_HEIGHT
+ sut.setOnPulseHeightChangedListener { listenerCalledCount++ }
+
+ sut.pulseHeight = 0f
+
+ assertThat(listenerCalledCount).isEqualTo(1)
+ }
+
+ @Test
+ fun pulseHeight_whenSetSameValue_shouldDoNothing() {
+ var listenerCalledCount = 0
+ sut.pulseHeight = MAX_PULSE_HEIGHT
+ sut.setOnPulseHeightChangedListener { listenerCalledCount++ }
+
+ sut.pulseHeight = MAX_PULSE_HEIGHT
+
+ assertThat(listenerCalledCount).isEqualTo(0)
+ }
+
+ @Test
+ fun pulseHeight_whenValueIsFull_shouldReturn0() {
+ sut.pulseHeight = MAX_PULSE_HEIGHT
+
+ assertThat(sut.pulseHeight).isEqualTo(0f)
+ }
+
+ @Test
+ fun pulseHeight_whenValueIsNotFull_shouldReturnValue() {
+ val expected = MAX_PULSE_HEIGHT - 0.1f
+ sut.pulseHeight = expected
+
+ assertThat(sut.pulseHeight).isEqualTo(expected)
+ }
+ // endregion
+
+ // region statusBarState
+ @Test
+ fun statusBarState_whenPreviousStateIsNotKeyguardAndChange_shouldSetIsFlingRequiredToFalse() {
+ sut.setStatusBarState(StatusBarState.SHADE)
+ sut.isFlingRequiredAfterLockScreenSwipeUp = true
+
+ sut.setStatusBarState(StatusBarState.KEYGUARD)
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isFalse()
+ }
+
+ @Test
+ fun statusBarState_whenPreviousStateIsKeyguardAndChange_shouldDoNothing() {
+ sut.setStatusBarState(StatusBarState.KEYGUARD)
+ sut.isFlingRequiredAfterLockScreenSwipeUp = true
+
+ sut.setStatusBarState(StatusBarState.SHADE)
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
+ }
+ // endregion
+
+ // region hideAmount
+ @Test
+ fun hideAmount_whenSetToFullValue_shouldReturnZeroFromPulseHeight() {
+ sut.hideAmount = 0f
+ sut.pulseHeight = 1f
+
+ sut.hideAmount = 1f
+
+ assertThat(sut.pulseHeight).isEqualTo(0f)
+ }
+
+ @Test
+ fun hideAmount_whenSetToAnyNotFullValue_shouldDoNothing() {
+ sut.hideAmount = 1f
+ sut.pulseHeight = 1f
+
+ sut.hideAmount = 0f
+
+ assertThat(sut.pulseHeight).isEqualTo(1f)
+ }
+ // endregion
+
+ // region dozeAmount
+ @Test
+ fun dozeAmount_whenDozeAmountIsSetToFullDozing_shouldReturnZeroFromPulseHeight() {
+ sut.dozeAmount = 0f
+ sut.pulseHeight = 1f
+
+ sut.dozeAmount = 1f
+
+ assertThat(sut.pulseHeight).isEqualTo(0f)
+ }
+
+ @Test
+ fun dozeAmount_whenDozeAmountIsSetToFullAwake_shouldReturnZeroFromPulseHeight() {
+ sut.dozeAmount = 1f
+ sut.pulseHeight = 1f
+
+ sut.dozeAmount = 0f
+
+ assertThat(sut.pulseHeight).isEqualTo(0f)
+ }
+
+ @Test
+ fun dozeAmount_whenDozeAmountIsSetAnyValueNotFullAwakeOrDozing_shouldDoNothing() {
+ sut.dozeAmount = 1f
+ sut.pulseHeight = 1f
+
+ sut.dozeAmount = 0.5f
+
+ assertThat(sut.pulseHeight).isEqualTo(1f)
+ }
+ // endregion
+
+ // region trackedHeadsUpRow
+ @Test
+ fun trackedHeadsUpRow_whenIsAboveTheShelf_shouldReturnInstance() {
+ sut.trackedHeadsUpRow = mock { whenever(isAboveShelf).thenReturn(true) }
+
+ assertThat(sut.trackedHeadsUpRow).isNotNull()
+ }
+
+ @Test
+ fun trackedHeadsUpRow_whenIsNotAboveTheShelf_shouldReturnNull() {
+ sut.trackedHeadsUpRow = mock { whenever(isAboveShelf).thenReturn(false) }
+
+ assertThat(sut.trackedHeadsUpRow).isNull()
+ }
+ // endregion
+
+ // region isSwipingUp
+ @Test
+ fun isSwipingUp_whenValueChangedToTrue_shouldRequireFling() {
+ sut.isSwipingUp = false
+ sut.isFlingRequiredAfterLockScreenSwipeUp = false
+
+ sut.isSwipingUp = true
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isFalse()
+ }
+
+ @Test
+ fun isSwipingUp_whenValueChangedToFalse_shouldRequireFling() {
+ sut.isSwipingUp = true
+ sut.isFlingRequiredAfterLockScreenSwipeUp = false
+
+ sut.isSwipingUp = false
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
+ }
+ // endregion
+
+ // region isFlinging
+ @Test
+ fun isFlinging_shouldNotNeedFling() {
+ sut.arrangeFlinging(true)
+
+ sut.setFlinging(false)
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isFalse()
+ }
+
+ @Test
+ fun isFlinging_whenNotOnLockScreen_shouldDoNothing() {
+ sut.arrangeFlinging(true)
+ sut.setStatusBarState(StatusBarState.SHADE)
+ sut.isFlingRequiredAfterLockScreenSwipeUp = true
+
+ sut.setFlinging(false)
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
+ }
+
+ @Test
+ fun isFlinging_whenValueChangedToTrue_shouldDoNothing() {
+ sut.arrangeFlinging(false)
+
+ sut.setFlinging(true)
+
+ assertThat(sut.isFlingRequiredAfterLockScreenSwipeUp).isTrue()
+ }
+ // endregion
+
+ // region scrollY
+ @Test
+ fun scrollY_shouldSetValueGreaterThanZero() {
+ sut.scrollY = 0
+
+ sut.scrollY = 1
+
+ assertThat(sut.scrollY).isEqualTo(1)
+ }
+
+ @Test
+ fun scrollY_shouldNotSetValueLessThanZero() {
+ sut.scrollY = 0
+
+ sut.scrollY = -1
+
+ assertThat(sut.scrollY).isEqualTo(0)
+ }
+ // endregion
+
+ // region setOverScrollAmount
+ fun setOverScrollAmount_shouldSetValueOnTop() {
+ sut.setOverScrollAmount(/* amount = */ 10f, /* onTop = */ true)
+
+ val resultOnTop = sut.getOverScrollAmount(/* top = */ true)
+ val resultOnBottom = sut.getOverScrollAmount(/* top = */ false)
+
+ assertThat(resultOnTop).isEqualTo(10f)
+ assertThat(resultOnBottom).isEqualTo(0f)
+ }
+
+ fun setOverScrollAmount_shouldSetValueOnBottom() {
+ sut.setOverScrollAmount(/* amount = */ 10f, /* onTop = */ false)
+
+ val resultOnTop = sut.getOverScrollAmount(/* top */ true)
+ val resultOnBottom = sut.getOverScrollAmount(/* top */ false)
+
+ assertThat(resultOnTop).isEqualTo(0f)
+ assertThat(resultOnBottom).isEqualTo(10f)
+ }
+ // endregion
+
+ // region IsPulseExpanding
+ @Test
+ fun isPulseExpanding_shouldReturnTrue() {
+ sut.arrangePulseExpanding(true)
+
+ assertThat(sut.isPulseExpanding).isTrue()
+ }
+
+ @Test
+ fun isPulseExpanding_whenPulseHeightIsMax_shouldReturnFalse() {
+ sut.arrangePulseExpanding(true)
+ sut.pulseHeight = MAX_PULSE_HEIGHT
+
+ assertThat(sut.isPulseExpanding).isFalse()
+ }
+
+ @Test
+ fun isPulseExpanding_whenDozeAmountIsZero_shouldReturnFalse() {
+ sut.arrangePulseExpanding(true)
+ sut.dozeAmount = 0f
+
+ assertThat(sut.isPulseExpanding).isFalse()
+ }
+
+ @Test
+ fun isPulseExpanding_whenHideAmountIsFull_shouldReturnFalse() {
+ sut.arrangePulseExpanding(true)
+ sut.hideAmount = 1f
+
+ assertThat(sut.isPulseExpanding).isFalse()
+ }
+ // endregion
+
+ // region isOnKeyguard
+ @Test
+ fun isOnKeyguard_whenStatusBarStateIsKeyguard_shouldReturnTrue() {
+ sut.setStatusBarState(StatusBarState.KEYGUARD)
+
+ assertThat(sut.isOnKeyguard).isTrue()
+ }
+
+ @Test
+ fun isOnKeyguard_whenStatusBarStateIsNotKeyguard_shouldReturnFalse() {
+ sut.setStatusBarState(StatusBarState.SHADE)
+
+ assertThat(sut.isOnKeyguard).isFalse()
+ }
+ // endregion
+}
+
+// region Arrange helper methods.
+private fun AmbientState.arrangeDimmed(value: Boolean) {
+ isDimmed = value
+ dozeAmount = if (value) 0f else 1f
+ arrangePulseExpanding(!value)
+}
+
+private fun AmbientState.arrangePulseExpanding(value: Boolean) {
+ if (value) {
+ dozeAmount = 1f
+ hideAmount = 0f
+ pulseHeight = 0f
+ } else {
+ dozeAmount = 0f
+ hideAmount = 1f
+ pulseHeight = MAX_PULSE_HEIGHT
+ }
+}
+
+private fun AmbientState.arrangeFlinging(value: Boolean) {
+ setStatusBarState(StatusBarState.KEYGUARD)
+ setFlinging(value)
+ isFlingRequiredAfterLockScreenSwipeUp = true
+}
+// endregion
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 3f19036..7741813 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
@@ -3,15 +3,22 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
+import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
-import junit.framework.Assert.*
+import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.StackScrollAlgorithmState
+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.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito.*
+import org.mockito.Mockito.mock
import org.mockito.Mockito.`when` as whenever
/**
@@ -22,13 +29,18 @@
@RunWithLooper
class NotificationShelfTest : SysuiTestCase() {
- private val shelf = NotificationShelf(context, /* attrs */ null)
+ private val shelf = NotificationShelf(
+ context,
+ /* attrs */ null,
+ /* showNotificationShelf */true
+ )
private val shelfState = shelf.viewState as NotificationShelf.ShelfState
private val ambientState = mock(AmbientState::class.java)
+ private val hostLayoutController: NotificationStackScrollLayoutController = mock()
@Before
fun setUp() {
- shelf.bind(ambientState, /* hostLayoutController */ null)
+ shelf.bind(ambientState, /* hostLayoutController */ hostLayoutController)
shelf.layout(/* left */ 0, /* top */ 0, /* right */ 30, /* bottom */5)
}
@@ -37,7 +49,7 @@
setFractionToShade(0f)
setOnLockscreen(true)
- shelf.updateActualWidth(/* fractionToShade */ 0f, /* shortestWidth */ 10f);
+ shelf.updateActualWidth(/* fractionToShade */ 0f, /* shortestWidth */ 10f)
assertTrue(shelf.actualWidth == 10)
shelf.updateActualWidth(/* fractionToShade */ 0.5f, /* shortestWidth */ 10f)
@@ -155,7 +167,7 @@
whenever(expandableView.actualHeight).thenReturn(20)
whenever(expandableView.minHeight).thenReturn(20)
- whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
+ whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
whenever(expandableView.isInShelf).thenReturn(true)
whenever(ambientState.isOnKeyguard).thenReturn(true)
@@ -182,7 +194,7 @@
whenever(expandableView.actualHeight).thenReturn(20)
whenever(expandableView.minHeight).thenReturn(20)
- whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
+ whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
whenever(expandableView.isInShelf).thenReturn(true)
whenever(ambientState.isOnKeyguard).thenReturn(true)
@@ -209,7 +221,7 @@
whenever(expandableView.actualHeight).thenReturn(25)
whenever(expandableView.minHeight).thenReturn(25)
- whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
+ whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
whenever(expandableView.isInShelf).thenReturn(true)
whenever(ambientState.isOnKeyguard).thenReturn(true)
@@ -236,7 +248,7 @@
whenever(expandableView.actualHeight).thenReturn(10)
whenever(expandableView.minHeight).thenReturn(10)
- whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
+ whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
whenever(expandableView.isInShelf).thenReturn(false)
whenever(ambientState.isExpansionChanging).thenReturn(false)
@@ -251,6 +263,42 @@
assertEquals(0f, amountInShelf)
}
+ @Test
+ fun updateState_expansionChanging_shelfTransparent() {
+ updateState_expansionChanging_shelfAlphaUpdated(
+ expansionFraction = 0.25f,
+ expectedAlpha = 0.0f
+ )
+ }
+
+ @Test
+ fun updateState_expansionChangingWhileBouncerInTransit_shelfTransparent() {
+ whenever(ambientState.isBouncerInTransit).thenReturn(true)
+
+ updateState_expansionChanging_shelfAlphaUpdated(
+ expansionFraction = 0.85f,
+ expectedAlpha = 0.0f
+ )
+ }
+
+ @Test
+ fun updateState_expansionChanging_shelfAlphaUpdated() {
+ updateState_expansionChanging_shelfAlphaUpdated(
+ expansionFraction = 0.6f,
+ expectedAlpha = ShadeInterpolation.getContentAlpha(0.6f)
+ )
+ }
+
+ @Test
+ fun updateState_expansionChangingWhileBouncerInTransit_shelfAlphaUpdated() {
+ whenever(ambientState.isBouncerInTransit).thenReturn(true)
+
+ updateState_expansionChanging_shelfAlphaUpdated(
+ expansionFraction = 0.95f,
+ expectedAlpha = aboutToShowBouncerProgress(0.95f)
+ )
+ }
+
private fun setFractionToShade(fraction: Float) {
whenever(ambientState.fractionToShade).thenReturn(fraction)
}
@@ -258,4 +306,19 @@
private fun setOnLockscreen(isOnLockscreen: Boolean) {
whenever(ambientState.isOnKeyguard).thenReturn(isOnLockscreen)
}
-}
\ No newline at end of file
+
+ private fun updateState_expansionChanging_shelfAlphaUpdated(
+ expansionFraction: Float,
+ expectedAlpha: Float
+ ) {
+ whenever(ambientState.lastVisibleBackgroundChild)
+ .thenReturn(ExpandableNotificationRow(mContext, null))
+ whenever(ambientState.isExpansionChanging).thenReturn(true)
+ whenever(ambientState.expansionFraction).thenReturn(expansionFraction)
+ whenever(hostLayoutController.speedBumpIndex).thenReturn(0)
+
+ shelf.updateState(StackScrollAlgorithmState(), ambientState)
+
+ assertEquals(expectedAlpha, shelf.viewState.alpha)
+ }
+}
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 3c22edc..6ae021b 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
@@ -250,7 +250,7 @@
// Validate that when the animation ends the stackEndHeight is recalculated immediately
clearInvocations(mAmbientState);
mStackScroller.setPanelFlinging(false);
- verify(mAmbientState).setIsFlinging(eq(false));
+ verify(mAmbientState).setFlinging(eq(false));
verify(mAmbientState).setStackEndHeight(anyFloat());
verify(mAmbientState).setStackHeight(anyFloat());
}
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 24eff5f..40aec82 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
@@ -20,7 +20,10 @@
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
+import org.mockito.Mockito.any
+import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@SmallTest
@@ -35,7 +38,6 @@
private val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply {
layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
}
-
private val ambientState = AmbientState(
context,
dumpManager,
@@ -115,29 +117,54 @@
}
@Test
- fun resetViewStates_isExpansionChanging_viewBecomesTransparent() {
+ fun resetViewStates_expansionChanging_notificationBecomesTransparent() {
whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(false)
- ambientState.isExpansionChanging = true
- ambientState.expansionFraction = 0.25f
- stackScrollAlgorithm.initView(context)
-
- stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
-
- val expected = getContentAlpha(0.25f)
- assertThat(notificationRow.viewState.alpha).isEqualTo(expected)
+ resetViewStates_expansionChanging_notificationAlphaUpdated(
+ expansionFraction = 0.25f,
+ expectedAlpha = 0.0f
+ )
}
@Test
- fun resetViewStates_isExpansionChangingWhileBouncerInTransit_viewBecomesTransparent() {
+ fun resetViewStates_expansionChangingWhileBouncerInTransit_viewBecomesTransparent() {
whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(true)
+ resetViewStates_expansionChanging_notificationAlphaUpdated(
+ expansionFraction = 0.85f,
+ expectedAlpha = 0.0f
+ )
+ }
+
+ @Test
+ fun resetViewStates_expansionChanging_notificationAlphaUpdated() {
+ whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(false)
+ resetViewStates_expansionChanging_notificationAlphaUpdated(
+ expansionFraction = 0.6f,
+ expectedAlpha = getContentAlpha(0.6f)
+ )
+ }
+
+ @Test
+ fun resetViewStates_expansionChangingWhileBouncerInTransit_notificationAlphaUpdated() {
+ whenever(mStatusBarKeyguardViewManager.isBouncerInTransit).thenReturn(true)
+ resetViewStates_expansionChanging_notificationAlphaUpdated(
+ expansionFraction = 0.95f,
+ expectedAlpha = aboutToShowBouncerProgress(0.95f)
+ )
+ }
+
+ @Test
+ fun resetViewStates_expansionChanging_shelfUpdated() {
+ ambientState.shelf = notificationShelf
ambientState.isExpansionChanging = true
- ambientState.expansionFraction = 0.25f
+ ambientState.expansionFraction = 0.6f
stackScrollAlgorithm.initView(context)
stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
- val expected = aboutToShowBouncerProgress(0.25f)
- assertThat(notificationRow.viewState.alpha).isEqualTo(expected)
+ verify(notificationShelf).updateState(
+ /* algorithmState= */any(),
+ /* ambientState= */eq(ambientState)
+ )
}
@Test
@@ -479,6 +506,19 @@
/* originalCornerRoundness= */ 1f)
assertEquals(1f, currentRoundness)
}
+
+ private fun resetViewStates_expansionChanging_notificationAlphaUpdated(
+ expansionFraction: Float,
+ expectedAlpha: Float
+ ) {
+ ambientState.isExpansionChanging = true
+ ambientState.expansionFraction = expansionFraction
+ stackScrollAlgorithm.initView(context)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
+
+ assertThat(notificationRow.viewState.alpha).isEqualTo(expectedAlpha)
+ }
}
private fun mockExpandableNotificationRow(): ExpandableNotificationRow {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 5c9871a..9de9db1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -29,6 +29,7 @@
import android.os.PowerManager;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import androidx.test.filters.SmallTest;
@@ -61,10 +62,10 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
-import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
public class DozeServiceHostTest extends SysuiTestCase {
private DozeServiceHost mDozeServiceHost;
@@ -92,6 +93,7 @@
@Mock private View mAmbientIndicationContainer;
@Mock private BiometricUnlockController mBiometricUnlockController;
@Mock private AuthController mAuthController;
+ @Mock private DozeHost.Callback mCallback;
@Before
public void setup() {
@@ -100,7 +102,7 @@
mStatusBarStateController, mDeviceProvisionedController, mHeadsUpManager,
mBatteryController, mScrimController, () -> mBiometricUnlockController,
mKeyguardViewMediator, () -> mAssistManager, mDozeScrimController,
- mKeyguardUpdateMonitor, mPulseExpansionHandler, Optional.empty(),
+ mKeyguardUpdateMonitor, mPulseExpansionHandler,
mNotificationShadeWindowController, mNotificationWakeUpCoordinator,
mAuthController, mNotificationIconAreaController);
@@ -114,16 +116,19 @@
@Test
public void testStartStopDozing() {
+ mDozeServiceHost.addCallback(mCallback);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
assertFalse(mDozeServiceHost.getDozingRequested());
mDozeServiceHost.startDozing();
+ verify(mCallback).onDozingChanged(eq(true));
verify(mStatusBarStateController).setIsDozing(eq(true));
verify(mCentralSurfaces).updateIsKeyguard();
mDozeServiceHost.stopDozing();
+ verify(mCallback).onDozingChanged(eq(false));
verify(mStatusBarStateController).setIsDozing(eq(false));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 2dcdcfc..e790d85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -51,6 +51,7 @@
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
@@ -277,6 +278,17 @@
}
@Test
+ public void onPanelExpansionChanged_neverTranslatesBouncerWhenShadeLocked() {
+ when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ expansionEvent(
+ /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* expanded= */ true,
+ /* tracking= */ false));
+ verify(mBouncer, never()).setExpansion(anyFloat());
+ }
+
+ @Test
public void setOccluded_animatesPanelExpansion_onlyIfBouncerHidden() {
mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
verify(mCentralSurfaces).animateKeyguardUnoccluding();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 6c6d9e1..43103a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -17,7 +17,10 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
import androidx.test.filters.SmallTest
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
@@ -31,6 +34,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
+import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel.Companion.NO_INTERNET
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -67,6 +71,7 @@
underTest = WifiViewModel(
statusBarPipelineFlags,
constants,
+ context,
logger,
interactor
)
@@ -115,7 +120,12 @@
.launchIn(this)
assertThat(latest).isInstanceOf(Icon.Resource::class.java)
- assertThat((latest as Icon.Resource).res).isEqualTo(WIFI_NO_NETWORK)
+ val icon = latest as Icon.Resource
+ assertThat(icon.res).isEqualTo(WIFI_NO_NETWORK)
+ assertThat(icon.contentDescription?.getAsString())
+ .contains(context.getString(WIFI_NO_CONNECTION))
+ assertThat(icon.contentDescription?.getAsString())
+ .contains(context.getString(NO_INTERNET))
job.cancel()
}
@@ -169,7 +179,12 @@
.launchIn(this)
assertThat(latest).isInstanceOf(Icon.Resource::class.java)
- assertThat((latest as Icon.Resource).res).isEqualTo(WIFI_FULL_ICONS[level])
+ val icon = latest as Icon.Resource
+ assertThat(icon.res).isEqualTo(WIFI_FULL_ICONS[level])
+ assertThat(icon.contentDescription?.getAsString())
+ .contains(context.getString(WIFI_CONNECTION_STRENGTH[level]))
+ assertThat(icon.contentDescription?.getAsString())
+ .doesNotContain(context.getString(NO_INTERNET))
job.cancel()
}
@@ -193,7 +208,12 @@
.launchIn(this)
assertThat(latest).isInstanceOf(Icon.Resource::class.java)
- assertThat((latest as Icon.Resource).res).isEqualTo(WIFI_NO_INTERNET_ICONS[level])
+ val icon = latest as Icon.Resource
+ assertThat(icon.res).isEqualTo(WIFI_NO_INTERNET_ICONS[level])
+ assertThat(icon.contentDescription?.getAsString())
+ .contains(context.getString(WIFI_CONNECTION_STRENGTH[level]))
+ assertThat(icon.contentDescription?.getAsString())
+ .contains(context.getString(NO_INTERNET))
job.cancel()
}
@@ -261,6 +281,13 @@
job.cancel()
}
+ private fun ContentDescription.getAsString(): String? {
+ return when (this) {
+ is ContentDescription.Loaded -> this.description
+ is ContentDescription.Resource -> context.getString(this.res)
+ }
+ }
+
companion object {
private const val NETWORK_ID = 2
private val ACTIVE_VALID_WIFI_NETWORK = WifiNetworkModel.Active(NETWORK_ID, ssid = "AB")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
index 4a8170f..8f363ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
@@ -31,6 +31,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
@@ -57,6 +58,8 @@
private DumpManager mDumpManager;
@Mock
private Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy;
+ @Mock
+ private KeyguardUpdateMonitorLogger mLogger;
@Before
public void setup() {
@@ -66,6 +69,7 @@
mKeyguardUpdateMonitor,
mLockPatternUtils,
mKeyguardUnlockAnimationControllerLazy,
+ mLogger,
mDumpManager);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
similarity index 61%
rename from packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index f133068..e616c26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.taptotransfer.common
+package com.android.systemui.temporarydisplay
import android.content.Context
import android.content.pm.ApplicationInfo
@@ -30,6 +30,7 @@
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -38,7 +39,6 @@
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.time.FakeSystemClock
-import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -52,8 +52,8 @@
import org.mockito.MockitoAnnotations
@SmallTest
-class MediaTttChipControllerCommonTest : SysuiTestCase() {
- private lateinit var controllerCommon: TestControllerCommon
+class TemporaryViewDisplayControllerTest : SysuiTestCase() {
+ private lateinit var underTest: TestController
private lateinit var fakeClock: FakeSystemClock
private lateinit var fakeExecutor: FakeExecutor
@@ -72,8 +72,6 @@
@Mock
private lateinit var windowManager: WindowManager
@Mock
- private lateinit var viewUtil: ViewUtil
- @Mock
private lateinit var powerManager: PowerManager
@Before
@@ -97,11 +95,10 @@
fakeClock = FakeSystemClock()
fakeExecutor = FakeExecutor(fakeClock)
- controllerCommon = TestControllerCommon(
+ underTest = TestController(
context,
logger,
windowManager,
- viewUtil,
fakeExecutor,
accessibilityManager,
configurationController,
@@ -110,43 +107,43 @@
}
@Test
- fun displayChip_chipAdded() {
- controllerCommon.displayChip(getState())
+ fun displayView_viewAdded() {
+ underTest.displayView(getState())
verify(windowManager).addView(any(), any())
}
@Test
- fun displayChip_screenOff_screenWakes() {
+ fun displayView_screenOff_screenWakes() {
whenever(powerManager.isScreenOn).thenReturn(false)
- controllerCommon.displayChip(getState())
+ underTest.displayView(getState())
verify(powerManager).wakeUp(any(), any(), any())
}
@Test
- fun displayChip_screenAlreadyOn_screenNotWoken() {
+ fun displayView_screenAlreadyOn_screenNotWoken() {
whenever(powerManager.isScreenOn).thenReturn(true)
- controllerCommon.displayChip(getState())
+ underTest.displayView(getState())
verify(powerManager, never()).wakeUp(any(), any(), any())
}
@Test
- fun displayChip_twice_chipNotAddedTwice() {
- controllerCommon.displayChip(getState())
+ fun displayView_twice_viewNotAddedTwice() {
+ underTest.displayView(getState())
reset(windowManager)
- controllerCommon.displayChip(getState())
+ underTest.displayView(getState())
verify(windowManager, never()).addView(any(), any())
}
@Test
- fun displayChip_chipDoesNotDisappearsBeforeTimeout() {
+ fun displayView_viewDoesNotDisappearsBeforeTimeout() {
val state = getState()
- controllerCommon.displayChip(state)
+ underTest.displayView(state)
reset(windowManager)
fakeClock.advanceTime(TIMEOUT_MS - 1)
@@ -155,9 +152,9 @@
}
@Test
- fun displayChip_chipDisappearsAfterTimeout() {
+ fun displayView_viewDisappearsAfterTimeout() {
val state = getState()
- controllerCommon.displayChip(state)
+ underTest.displayView(state)
reset(windowManager)
fakeClock.advanceTime(TIMEOUT_MS + 1)
@@ -166,176 +163,176 @@
}
@Test
- fun displayChip_calledAgainBeforeTimeout_timeoutReset() {
- // First, display the chip
+ fun displayView_calledAgainBeforeTimeout_timeoutReset() {
+ // First, display the view
val state = getState()
- controllerCommon.displayChip(state)
+ underTest.displayView(state)
- // After some time, re-display the chip
+ // After some time, re-display the view
val waitTime = 1000L
fakeClock.advanceTime(waitTime)
- controllerCommon.displayChip(getState())
+ underTest.displayView(getState())
// Wait until the timeout for the first display would've happened
fakeClock.advanceTime(TIMEOUT_MS - waitTime + 1)
- // Verify we didn't hide the chip
+ // Verify we didn't hide the view
verify(windowManager, never()).removeView(any())
}
@Test
- fun displayChip_calledAgainBeforeTimeout_eventuallyTimesOut() {
- // First, display the chip
+ fun displayView_calledAgainBeforeTimeout_eventuallyTimesOut() {
+ // First, display the view
val state = getState()
- controllerCommon.displayChip(state)
+ underTest.displayView(state)
- // After some time, re-display the chip
+ // After some time, re-display the view
fakeClock.advanceTime(1000L)
- controllerCommon.displayChip(getState())
+ underTest.displayView(getState())
- // Ensure we still hide the chip eventually
+ // Ensure we still hide the view eventually
fakeClock.advanceTime(TIMEOUT_MS + 1)
verify(windowManager).removeView(any())
}
@Test
- fun displayScaleChange_chipReinflatedWithMostRecentState() {
- controllerCommon.displayChip(getState(name = "First name"))
- controllerCommon.displayChip(getState(name = "Second name"))
+ fun displayScaleChange_viewReinflatedWithMostRecentState() {
+ underTest.displayView(getState(name = "First name"))
+ underTest.displayView(getState(name = "Second name"))
reset(windowManager)
getConfigurationListener().onDensityOrFontScaleChanged()
verify(windowManager).removeView(any())
verify(windowManager).addView(any(), any())
- assertThat(controllerCommon.mostRecentChipInfo?.name).isEqualTo("Second name")
+ assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("Second name")
}
@Test
- fun removeChip_chipRemovedAndRemovalLogged() {
- // First, add the chip
- controllerCommon.displayChip(getState())
+ fun removeView_viewRemovedAndRemovalLogged() {
+ // First, add the view
+ underTest.displayView(getState())
// Then, remove it
val reason = "test reason"
- controllerCommon.removeChip(reason)
+ underTest.removeView(reason)
verify(windowManager).removeView(any())
verify(logger).logChipRemoval(reason)
}
@Test
- fun removeChip_noAdd_viewNotRemoved() {
- controllerCommon.removeChip("reason")
+ fun removeView_noAdd_viewNotRemoved() {
+ underTest.removeView("reason")
verify(windowManager, never()).removeView(any())
}
@Test
fun setIcon_nullAppIconDrawableAndNullPackageName_stillHasIcon() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(chipView, appPackageName = null, appIconDrawableOverride = null)
+ underTest.setIcon(view, appPackageName = null, appIconDrawableOverride = null)
- assertThat(chipView.getAppIconView().drawable).isNotNull()
+ assertThat(view.getAppIconView().drawable).isNotNull()
}
@Test
fun setIcon_nullAppIconDrawableAndInvalidPackageName_stillHasIcon() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(
- chipView, appPackageName = "fakePackageName", appIconDrawableOverride = null
+ underTest.setIcon(
+ view, appPackageName = "fakePackageName", appIconDrawableOverride = null
)
- assertThat(chipView.getAppIconView().drawable).isNotNull()
+ assertThat(view.getAppIconView().drawable).isNotNull()
}
@Test
fun setIcon_nullAppIconDrawable_iconIsFromPackageName() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(chipView, PACKAGE_NAME, appIconDrawableOverride = null, null)
+ underTest.setIcon(view, PACKAGE_NAME, appIconDrawableOverride = null, null)
- assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconFromPackageName)
+ assertThat(view.getAppIconView().drawable).isEqualTo(appIconFromPackageName)
}
@Test
fun setIcon_hasAppIconDrawable_iconIsDrawable() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
val drawable = context.getDrawable(R.drawable.ic_alarm)!!
- controllerCommon.setIcon(chipView, PACKAGE_NAME, drawable, null)
+ underTest.setIcon(view, PACKAGE_NAME, drawable, null)
- assertThat(chipView.getAppIconView().drawable).isEqualTo(drawable)
+ assertThat(view.getAppIconView().drawable).isEqualTo(drawable)
}
@Test
fun setIcon_nullAppNameAndNullPackageName_stillHasContentDescription() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(chipView, appPackageName = null, appNameOverride = null)
+ underTest.setIcon(view, appPackageName = null, appNameOverride = null)
- assertThat(chipView.getAppIconView().contentDescription.toString()).isNotEmpty()
+ assertThat(view.getAppIconView().contentDescription.toString()).isNotEmpty()
}
@Test
fun setIcon_nullAppNameAndInvalidPackageName_stillHasContentDescription() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(
- chipView, appPackageName = "fakePackageName", appNameOverride = null
+ underTest.setIcon(
+ view, appPackageName = "fakePackageName", appNameOverride = null
)
- assertThat(chipView.getAppIconView().contentDescription.toString()).isNotEmpty()
+ assertThat(view.getAppIconView().contentDescription.toString()).isNotEmpty()
}
@Test
fun setIcon_nullAppName_iconContentDescriptionIsFromPackageName() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(chipView, PACKAGE_NAME, null, appNameOverride = null)
+ underTest.setIcon(view, PACKAGE_NAME, null, appNameOverride = null)
- assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
+ assertThat(view.getAppIconView().contentDescription).isEqualTo(APP_NAME)
}
@Test
fun setIcon_hasAppName_iconContentDescriptionIsAppNameOverride() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
val appName = "Override App Name"
- controllerCommon.setIcon(chipView, PACKAGE_NAME, null, appName)
+ underTest.setIcon(view, PACKAGE_NAME, null, appName)
- assertThat(chipView.getAppIconView().contentDescription).isEqualTo(appName)
+ assertThat(view.getAppIconView().contentDescription).isEqualTo(appName)
}
@Test
fun setIcon_iconSizeMatchesGetIconSize() {
- controllerCommon.displayChip(getState())
- val chipView = getChipView()
+ underTest.displayView(getState())
+ val view = getView()
- controllerCommon.setIcon(chipView, PACKAGE_NAME)
- chipView.measure(
+ underTest.setIcon(view, PACKAGE_NAME)
+ view.measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
- assertThat(chipView.getAppIconView().measuredWidth).isEqualTo(ICON_SIZE)
- assertThat(chipView.getAppIconView().measuredHeight).isEqualTo(ICON_SIZE)
+ assertThat(view.getAppIconView().measuredWidth).isEqualTo(ICON_SIZE)
+ assertThat(view.getAppIconView().measuredHeight).isEqualTo(ICON_SIZE)
}
- private fun getState(name: String = "name") = ChipInfo(name)
+ private fun getState(name: String = "name") = ViewInfo(name)
- private fun getChipView(): ViewGroup {
+ private fun getView(): ViewGroup {
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
verify(windowManager).addView(viewCaptor.capture(), any())
return viewCaptor.value as ViewGroup
@@ -349,37 +346,35 @@
return callbackCaptor.value
}
- inner class TestControllerCommon(
+ inner class TestController(
context: Context,
logger: MediaTttLogger,
windowManager: WindowManager,
- viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
powerManager: PowerManager,
- ) : MediaTttChipControllerCommon<ChipInfo>(
+ ) : TemporaryViewDisplayController<ViewInfo>(
context,
logger,
windowManager,
- viewUtil,
mainExecutor,
accessibilityManager,
configurationController,
powerManager,
R.layout.media_ttt_chip,
) {
- var mostRecentChipInfo: ChipInfo? = null
+ var mostRecentViewInfo: ViewInfo? = null
override val windowLayoutParams = commonWindowLayoutParams
- override fun updateChipView(newChipInfo: ChipInfo, currentChipView: ViewGroup) {
- super.updateChipView(newChipInfo, currentChipView)
- mostRecentChipInfo = newChipInfo
+ override fun updateView(newInfo: ViewInfo, currentView: ViewGroup) {
+ super.updateView(newInfo, currentView)
+ mostRecentViewInfo = newInfo
}
override fun getIconSize(isAppIcon: Boolean): Int = ICON_SIZE
}
- inner class ChipInfo(val name: String) : ChipInfoCommon {
+ inner class ViewInfo(val name: String) : TemporaryViewInfo {
override fun getTimeoutMs() = 1L
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
index f51f783..8645298 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -18,24 +18,28 @@
import android.hardware.devicestate.DeviceStateManager
import android.hardware.devicestate.DeviceStateManager.FoldStateListener
-import android.os.Handler
import android.os.PowerManager
import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.testing.TestableLooper.RunWithLooper
import android.view.ViewGroup
import android.view.ViewTreeObserver
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.shade.NotificationPanelViewController
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.unfold.util.FoldableDeviceStates
import com.android.systemui.unfold.util.FoldableTestUtils
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.time.FakeSystemClock
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -49,7 +53,6 @@
@RunWith(AndroidTestingRunner::class)
@SmallTest
-@RunWithLooper
class FoldAodAnimationControllerTest : SysuiTestCase() {
@Mock lateinit var deviceStateManager: DeviceStateManager
@@ -74,26 +77,15 @@
private lateinit var deviceStates: FoldableDeviceStates
- private lateinit var testableLooper: TestableLooper
+ lateinit var keyguardRepository: FakeKeyguardRepository
- lateinit var foldAodAnimationController: FoldAodAnimationController
+ lateinit var underTest: FoldAodAnimationController
+ private val fakeExecutor = FakeExecutor(FakeSystemClock())
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- testableLooper = TestableLooper.get(this)
- foldAodAnimationController =
- FoldAodAnimationController(
- Handler(testableLooper.looper),
- context.mainExecutor,
- context,
- deviceStateManager,
- wakefulnessLifecycle,
- globalSettings,
- latencyTracker,
- )
- .apply { initialize(centralSurfaces, lightRevealScrim) }
deviceStates = FoldableTestUtils.findDeviceStates(context)
whenever(notificationPanelViewController.view).thenReturn(viewGroup)
@@ -107,60 +99,102 @@
val onActionStarted = it.arguments[0] as Runnable
onActionStarted.run()
}
- verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
- foldAodAnimationController.setIsDozing(dozing = true)
- setAodEnabled(enabled = true)
- sendFoldEvent(folded = false)
+ keyguardRepository = FakeKeyguardRepository()
+ val keyguardInteractor = KeyguardInteractor(repository = keyguardRepository)
+
+ // Needs to be run on the main thread
+ runBlocking(IMMEDIATE) {
+ underTest =
+ FoldAodAnimationController(
+ fakeExecutor,
+ context,
+ deviceStateManager,
+ wakefulnessLifecycle,
+ globalSettings,
+ latencyTracker,
+ { keyguardInteractor },
+ )
+ .apply { initialize(centralSurfaces, lightRevealScrim) }
+
+ verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
+
+ setAodEnabled(enabled = true)
+ sendFoldEvent(folded = false)
+ }
}
@Test
- fun onFolded_aodDisabled_doesNotLogLatency() {
- setAodEnabled(enabled = false)
+ fun onFolded_aodDisabled_doesNotLogLatency() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.listenForDozing(this)
+ keyguardRepository.setDozing(true)
+ setAodEnabled(enabled = false)
- fold()
- simulateScreenTurningOn()
+ yield()
- verifyNoMoreInteractions(latencyTracker)
- }
+ fold()
+ simulateScreenTurningOn()
+
+ verifyNoMoreInteractions(latencyTracker)
+
+ job.cancel()
+ }
@Test
- fun onFolded_aodEnabled_logsLatency() {
- setAodEnabled(enabled = true)
+ fun onFolded_aodEnabled_logsLatency() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.listenForDozing(this)
+ keyguardRepository.setDozing(true)
+ setAodEnabled(enabled = true)
- fold()
- simulateScreenTurningOn()
+ yield()
- verify(latencyTracker).onActionStart(any())
- verify(latencyTracker).onActionEnd(any())
- }
+ fold()
+ simulateScreenTurningOn()
+
+ verify(latencyTracker).onActionStart(any())
+ verify(latencyTracker).onActionEnd(any())
+
+ job.cancel()
+ }
@Test
- fun onFolded_animationCancelled_doesNotLogLatency() {
- setAodEnabled(enabled = true)
+ fun onFolded_animationCancelled_doesNotLogLatency() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.listenForDozing(this)
+ keyguardRepository.setDozing(true)
+ setAodEnabled(enabled = true)
- fold()
- foldAodAnimationController.onScreenTurningOn({})
- foldAodAnimationController.onStartedWakingUp()
- testableLooper.processAllMessages()
+ yield()
- verify(latencyTracker).onActionStart(any())
- verify(latencyTracker).onActionCancel(any())
- }
+ fold()
+ underTest.onScreenTurningOn({})
+ underTest.onStartedWakingUp()
+ fakeExecutor.runAllReady()
+
+ verify(latencyTracker).onActionStart(any())
+ verify(latencyTracker).onActionCancel(any())
+
+ job.cancel()
+ }
private fun simulateScreenTurningOn() {
- foldAodAnimationController.onScreenTurningOn({})
- foldAodAnimationController.onScreenTurnedOn()
- testableLooper.processAllMessages()
+ underTest.onScreenTurningOn({})
+ underTest.onScreenTurnedOn()
+ fakeExecutor.runAllReady()
}
private fun fold() = sendFoldEvent(folded = true)
- private fun setAodEnabled(enabled: Boolean) =
- foldAodAnimationController.onAlwaysOnChanged(alwaysOn = enabled)
+ private fun setAodEnabled(enabled: Boolean) = underTest.onAlwaysOnChanged(alwaysOn = enabled)
private fun sendFoldEvent(folded: Boolean) {
val state = if (folded) deviceStates.folded else deviceStates.unfolded
foldStateListenerCaptor.value.onStateChanged(state)
}
+
+ companion object {
+ private val IMMEDIATE = Dispatchers.Main.immediate
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
index 460b71f..7df7077 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
@@ -28,12 +28,14 @@
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
import org.junit.Test
import org.junit.runner.RunWith
@@ -140,6 +142,42 @@
}
}
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class SampleFlowTest : SysuiTestCase() {
+ @Test
+ fun simple() = runBlocking {
+ assertThatFlow(flow { yield(); emit(1) }.sample(flowOf(2)) { a, b -> a to b })
+ .emitsExactly(1 to 2)
+ }
+
+ @Test
+ fun otherFlowNoValueYet() = runBlocking {
+ assertThatFlow(flowOf(1).sample(emptyFlow<Unit>()))
+ .emitsNothing()
+ }
+
+ @Test
+ fun multipleSamples() = runBlocking {
+ val samplee = MutableSharedFlow<Int>()
+ val sampler = flow {
+ emit(1)
+ samplee.emit(1)
+ emit(2)
+ samplee.emit(2)
+ samplee.emit(3)
+ emit(3)
+ emit(4)
+ }
+ assertThatFlow(sampler.sample(samplee) { a, b -> a to b })
+ .emitsExactly(
+ 2 to 1,
+ 3 to 3,
+ 4 to 3,
+ )
+ }
+}
+
private fun <T> assertThatFlow(flow: Flow<T>) = object {
suspend fun emitsExactly(vararg emissions: T) =
assertThat(flow.toList()).containsExactly(*emissions).inOrder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
new file mode 100644
index 0000000..6848b83
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 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.util.kotlin
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class RaceSuspendTest : SysuiTestCase() {
+ @Test
+ fun raceSimple() = runBlocking {
+ val winner = CompletableDeferred<Int>()
+ val result = async {
+ race(
+ { winner.await() },
+ { awaitCancellation() },
+ )
+ }
+ winner.complete(1)
+ assertThat(result.await()).isEqualTo(1)
+ }
+
+ @Test
+ fun raceImmediate() = runBlocking {
+ assertThat(
+ race<Int>(
+ { 1 },
+ { 2 },
+ )
+ ).isEqualTo(1)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 11eb4e3..42b434a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -12,6 +12,7 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
+ *
*/
package com.android.systemui.keyguard.data.repository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/FakePowerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
index d058053..8d171be 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
@@ -26,6 +26,7 @@
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatcher
import org.mockito.Mockito
+import org.mockito.stubbing.OngoingStubbing
/**
* Returns Mockito.eq() as nullable type to avoid java.lang.IllegalStateException when
@@ -77,8 +78,18 @@
* Helper function for creating new mocks, without the need to pass in a [Class] instance.
*
* Generic T is nullable because implicitly bounded by Any?.
+ *
+ * @param apply builder function to simplify stub configuration by improving type inference.
*/
-inline fun <reified T : Any> mock(): T = Mockito.mock(T::class.java)
+inline fun <reified T : Any> mock(apply: T.() -> Unit = {}): T = Mockito.mock(T::class.java)
+ .apply(apply)
+
+/**
+ * Helper function for stubbing methods without the need to use backticks.
+ *
+ * @see Mockito.when
+ */
+fun <T> whenever(methodCall: T): OngoingStubbing<T> = Mockito.`when`(methodCall)
/**
* A kotlin implemented wrapper of [ArgumentCaptor] which prevents the following exception when
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index b425420..aec60de 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -582,13 +582,18 @@
}
@Override
- public boolean isCameraDisabled() {
+ public boolean isCameraDisabled(int userId) {
DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
if (dpm == null) {
Slog.e(TAG, "Failed to get the device policy manager service");
return false;
}
- return dpm.getCameraDisabled(null);
+ try {
+ return dpm.getCameraDisabled(null, userId);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
}
};
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 80ce70d..3707c8e 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1211,7 +1211,7 @@
if (info.userId == userId
&& info.agent.isTrusted()
&& info.agent.shouldDisplayTrustGrantedMessage()
- && !TextUtils.isEmpty(info.agent.getMessage())) {
+ && info.agent.getMessage() != null) {
trustGrantedMessages.add(info.agent.getMessage().toString());
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 53f2c71..5c1a877 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -39,6 +39,7 @@
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
@@ -758,7 +759,8 @@
if (isKeyguardGoingAwayTransitOld(transit) && enter) {
a = mTransitionAnimation.loadKeyguardExitAnimation(mNextAppTransitionFlags,
transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER);
- } else if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) {
+ } else if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE
+ || transit == TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM) {
a = null;
} else if (transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE && !enter) {
a = mTransitionAnimation.loadKeyguardUnoccludeAnimation();
@@ -1170,6 +1172,9 @@
case TRANSIT_OLD_KEYGUARD_OCCLUDE: {
return "TRANSIT_OLD_KEYGUARD_OCCLUDE";
}
+ case TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM: {
+ return "TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM";
+ }
case TRANSIT_OLD_KEYGUARD_UNOCCLUDE: {
return "TRANSIT_OLD_KEYGUARD_UNOCCLUDE";
}
@@ -1425,6 +1430,7 @@
static boolean isKeyguardOccludeTransitOld(@TransitionOldType int transit) {
return transit == TRANSIT_OLD_KEYGUARD_OCCLUDE
+ || transit == TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM
|| transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 5ac5f2e..5599f2c 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -38,6 +38,7 @@
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
@@ -363,8 +364,14 @@
// When there is a closing app, the keyguard has already been occluded by an
// activity, and another activity has started on top of that activity, so normal
// app transition animation should be used.
- return closingApps.isEmpty() ? TRANSIT_OLD_KEYGUARD_OCCLUDE
- : TRANSIT_OLD_ACTIVITY_OPEN;
+ if (!closingApps.isEmpty()) {
+ return TRANSIT_OLD_ACTIVITY_OPEN;
+ }
+ if (!openingApps.isEmpty() && openingApps.valueAt(0).getActivityType()
+ == ACTIVITY_TYPE_DREAM) {
+ return TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM;
+ }
+ return TRANSIT_OLD_KEYGUARD_OCCLUDE;
case TRANSIT_KEYGUARD_UNOCCLUDE:
return TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 135fcd4..0e1a6de 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -577,17 +577,16 @@
t.setLayer(targetLeash, target.getLastLayer());
target.getRelativePosition(tmpPos);
t.setPosition(targetLeash, tmpPos.x, tmpPos.y);
- final Rect clipRect;
// No need to clip the display in case seeing the clipped content when during the
// display rotation. No need to clip activities because they rely on clipping on
// task layers.
if (target.asDisplayContent() != null || target.asActivityRecord() != null) {
- clipRect = null;
+ t.setCrop(targetLeash, null /* crop */);
} else {
- clipRect = target.getRequestedOverrideBounds();
- clipRect.offset(-tmpPos.x, -tmpPos.y);
+ // Crop to the requested bounds.
+ final Rect clipRect = target.getRequestedOverrideBounds();
+ t.setWindowCrop(targetLeash, clipRect.width(), clipRect.height());
}
- t.setCrop(targetLeash, clipRect);
t.setCornerRadius(targetLeash, 0);
t.setShadowRadius(targetLeash, 0);
t.setMatrix(targetLeash, 1, 0, 0, 1);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 06fb4b0..6aca14f0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8178,7 +8178,8 @@
}
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)
+ || isCameraServerUid(caller));
if (parent) {
Preconditions.checkCallAuthorization(
@@ -9689,6 +9690,10 @@
return UserHandle.isSameApp(caller.getUid(), Process.SHELL_UID);
}
+ private boolean isCameraServerUid(CallerIdentity caller) {
+ return UserHandle.isSameApp(caller.getUid(), Process.CAMERASERVER_UID);
+ }
+
private @UserIdInt int getCurrentForegroundUserId() {
try {
UserInfo currentUser = mInjector.getIActivityManager().getCurrentUser();