Merge "Update scrim controller directly with occlude animation status." into tm-qpr-dev
diff --git a/core/java/android/app/DisabledWallpaperManager.java b/core/java/android/app/DisabledWallpaperManager.java
index 0d14c0b..fae6887 100644
--- a/core/java/android/app/DisabledWallpaperManager.java
+++ b/core/java/android/app/DisabledWallpaperManager.java
@@ -188,17 +188,17 @@
}
@Override
- public WallpaperInfo getWallpaperInfo(int userId) {
+ public WallpaperInfo getWallpaperInfoForUser(int userId) {
return unsupported();
}
@Override
- public WallpaperInfo getWallpaperInfoWithFlags(@SetWallpaperFlags int which) {
+ public WallpaperInfo getWallpaperInfo(@SetWallpaperFlags int which) {
return unsupported();
}
@Override
- public WallpaperInfo getWallpaperInfoWithFlags(@SetWallpaperFlags int which, int userId) {
+ public WallpaperInfo getWallpaperInfo(@SetWallpaperFlags int which, int userId) {
return unsupported();
}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index c99fa3d..14fe522 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1324,7 +1324,7 @@
* wallpaper component. Otherwise, if the wallpaper is a static image, this returns null.
*/
public WallpaperInfo getWallpaperInfo() {
- return getWallpaperInfo(mContext.getUserId());
+ return getWallpaperInfoForUser(mContext.getUserId());
}
/**
@@ -1334,7 +1334,7 @@
* @param userId Owner of the wallpaper.
* @hide
*/
- public WallpaperInfo getWallpaperInfo(int userId) {
+ public WallpaperInfo getWallpaperInfoForUser(int userId) {
try {
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
@@ -1349,25 +1349,31 @@
/**
* Returns the information about the home screen wallpaper if its current wallpaper is a live
- * wallpaper component. Otherwise, if the wallpaper is a static image, this returns null.
+ * wallpaper component. Otherwise, if the wallpaper is a static image or is not set, this
+ * returns null.
*
- * @param which Specifies wallpaper destination (home or lock).
+ * @param which Specifies wallpaper to request (home or lock).
+ * @throws IllegalArgumentException if {@code which} is not exactly one of
+ * {{@link #FLAG_SYSTEM},{@link #FLAG_LOCK}}.
* @hide
*/
- public WallpaperInfo getWallpaperInfoWithFlags(@SetWallpaperFlags int which) {
+ public WallpaperInfo getWallpaperInfo(@SetWallpaperFlags int which) {
return getWallpaperInfo();
}
/**
* Returns the information about the designated wallpaper if its current wallpaper is a live
- * wallpaper component. Otherwise, if the wallpaper is a static image, this returns null.
+ * wallpaper component. Otherwise, if the wallpaper is a static image or is not set, this
+ * returns null.
*
- * @param which Specifies wallpaper destination (home or lock).
+ * @param which Specifies wallpaper to request (home or lock).
* @param userId Owner of the wallpaper.
+ * @throws IllegalArgumentException if {@code which} is not exactly one of
+ * {{@link #FLAG_SYSTEM},{@link #FLAG_LOCK}}.
* @hide
*/
- public WallpaperInfo getWallpaperInfoWithFlags(@SetWallpaperFlags int which, int userId) {
- return getWallpaperInfo(userId);
+ public WallpaperInfo getWallpaperInfo(@SetWallpaperFlags int which, int userId) {
+ return getWallpaperInfoForUser(userId);
}
/**
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index bdd45e6..dba1a5e 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -52,6 +52,22 @@
/** The maximum allowed device state identifier. */
public static final int MAXIMUM_DEVICE_STATE = 255;
+ /**
+ * Intent needed to launch the rear display overlay activity from SysUI
+ *
+ * @hide
+ */
+ public static final String ACTION_SHOW_REAR_DISPLAY_OVERLAY =
+ "com.android.intent.action.SHOW_REAR_DISPLAY_OVERLAY";
+
+ /**
+ * Intent extra sent to the rear display overlay activity of the current base state
+ *
+ * @hide
+ */
+ public static final String EXTRA_ORIGINAL_DEVICE_BASE_STATE =
+ "original_device_base_state";
+
private final DeviceStateManagerGlobal mGlobal;
/** @hide */
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index 738045d..7756b9c 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -51,7 +51,7 @@
* connection with the device state service couldn't be established.
*/
@Nullable
- static DeviceStateManagerGlobal getInstance() {
+ public static DeviceStateManagerGlobal getInstance() {
synchronized (DeviceStateManagerGlobal.class) {
if (sInstance == null) {
IBinder b = ServiceManager.getService(Context.DEVICE_STATE_SERVICE);
@@ -259,6 +259,22 @@
}
}
+ /**
+ * Provides notification to the system server that a device state feature overlay
+ * was dismissed. This should only be called from the {@link android.app.Activity} that
+ * was showing the overlay corresponding to the feature.
+ *
+ * Validation of there being an overlay visible and pending state request is handled on the
+ * system server.
+ */
+ public void onStateRequestOverlayDismissed(boolean shouldCancelRequest) {
+ try {
+ mDeviceStateManager.onStateRequestOverlayDismissed(shouldCancelRequest);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
private void registerCallbackIfNeededLocked() {
if (mCallback == null) {
mCallback = new DeviceStateManagerCallback();
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
index 7175eae..0993160 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
@@ -103,4 +103,15 @@
@JavaPassthrough(annotation=
"@android.annotation.RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)")
void cancelBaseStateOverride();
+
+ /**
+ * Notifies the system service that the educational overlay that was launched
+ * before entering a requested state was dismissed or closed, and provides
+ * the system information on if the pairing mode should be canceled or not.
+ *
+ * This should only be called from the overlay itself.
+ */
+ @JavaPassthrough(annotation=
+ "@android.annotation.RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)")
+ void onStateRequestOverlayDismissed(boolean shouldCancelRequest);
}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 0f64f6d..292a50b 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -584,6 +584,13 @@
public static final String CLIPBOARD_OVERLAY_SHOW_ACTIONS = "clipboard_overlay_show_actions";
/**
+ * (boolean) Whether to ignore the source package for determining whether to use remote copy
+ * behavior in the clipboard UI.
+ */
+ public static final String CLIPBOARD_IGNORE_REMOTE_COPY_SOURCE =
+ "clipboard_ignore_remote_copy_source";
+
+ /**
* (boolean) Whether to combine the broadcasts APPWIDGET_ENABLED and APPWIDGET_UPDATE
*/
public static final String COMBINED_BROADCAST_ENABLED = "combined_broadcast_enabled";
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index 9e39e13..3e3c77b 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -377,6 +377,11 @@
notifyDeviceStateInfoChanged();
}
+ // No-op in the test since DeviceStateManagerGlobal just calls into the system server with
+ // no business logic around it.
+ @Override
+ public void onStateRequestOverlayDismissed(boolean shouldCancelMode) {}
+
public void setSupportedStates(int[] states) {
mSupportedStates = states;
notifyDeviceStateInfoChanged();
diff --git a/libs/WindowManager/Shell/res/drawable/decor_caption_menu_background.xml b/libs/WindowManager/Shell/res/drawable/decor_caption_menu_background.xml
new file mode 100644
index 0000000..416287d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/decor_caption_menu_background.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+<shape android:shape="rectangle"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@android:color/white" />
+ <corners android:radius="20dp" />
+</shape>
diff --git a/libs/WindowManager/Shell/res/layout/caption_handle_menu.xml b/libs/WindowManager/Shell/res/layout/caption_handle_menu.xml
index d9a140b..582a11c 100644
--- a/libs/WindowManager/Shell/res/layout/caption_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/caption_handle_menu.xml
@@ -20,7 +20,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
-android:background="@drawable/decor_caption_title">
+android:background="@drawable/decor_caption_menu_background">
<Button
style="@style/CaptionButtonStyle"
android:id="@+id/fullscreen_button"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index ebe5c5e..8369569 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -44,6 +44,8 @@
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import androidx.annotation.Nullable;
+
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -71,6 +73,7 @@
private DesktopModeController mDesktopModeController;
private EventReceiver mEventReceiver;
private InputMonitor mInputMonitor;
+ private boolean mTransitionDragActive;
private final SparseArray<CaptionWindowDecoration> mWindowDecorByTaskId = new SparseArray<>();
private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl();
@@ -91,6 +94,7 @@
mDisplayController = displayController;
mSyncQueue = syncQueue;
mDesktopModeController = desktopModeController;
+ mTransitionDragActive = false;
}
@Override
@@ -288,7 +292,7 @@
mDragResizeCallback.onDragResizeEnd(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
if (e.getRawY(dragPointerIdx) <= statusBarHeight
- && windowingMode == WINDOWING_MODE_FREEFORM) {
+ && DesktopModeStatus.isActive(mContext)) {
mDesktopModeController.setDesktopModeActive(false);
}
break;
@@ -306,26 +310,97 @@
@Override
public void onInputEvent(InputEvent event) {
boolean handled = false;
- if (event instanceof MotionEvent
- && ((MotionEvent) event).getActionMasked() == MotionEvent.ACTION_UP) {
+ if (event instanceof MotionEvent) {
handled = true;
- CaptionWindowDecorViewModel.this.handleMotionEvent((MotionEvent) event);
+ CaptionWindowDecorViewModel.this.handleReceivedMotionEvent((MotionEvent) event);
}
finishInputEvent(event, handled);
}
}
- // If any input received is outside of caption bounds, turn off handle menu
- private void handleMotionEvent(MotionEvent ev) {
- int size = mWindowDecorByTaskId.size();
- for (int i = 0; i < size; i++) {
- CaptionWindowDecoration decoration = mWindowDecorByTaskId.valueAt(i);
- if (decoration != null) {
- decoration.closeHandleMenuIfNeeded(ev);
+ /**
+ * Handle MotionEvents relevant to focused task's caption that don't directly touch it
+ * @param ev the {@link MotionEvent} received by {@link EventReceiver}
+ */
+ private void handleReceivedMotionEvent(MotionEvent ev) {
+ if (!DesktopModeStatus.isActive(mContext)) {
+ handleCaptionThroughStatusBar(ev);
+ }
+ handleEventOutsideFocusedCaption(ev);
+ // Prevent status bar from reacting to a caption drag.
+ if (mTransitionDragActive && !DesktopModeStatus.isActive(mContext)) {
+ mInputMonitor.pilferPointers();
+ }
+ }
+
+ // If an UP/CANCEL action is received outside of caption bounds, turn off handle menu
+ private void handleEventOutsideFocusedCaption(MotionEvent ev) {
+ int action = ev.getActionMasked();
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ CaptionWindowDecoration focusedDecor = getFocusedDecor();
+ if (focusedDecor == null) {
+ return;
+ }
+
+ if (!mTransitionDragActive) {
+ focusedDecor.closeHandleMenuIfNeeded(ev);
}
}
}
+ /**
+ * Perform caption actions if not able to through normal means.
+ * Turn on desktop mode if handle is dragged below status bar.
+ */
+ private void handleCaptionThroughStatusBar(MotionEvent ev) {
+ switch (ev.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ // Begin drag through status bar if applicable.
+ CaptionWindowDecoration focusedDecor = getFocusedDecor();
+ if (focusedDecor != null && !DesktopModeStatus.isActive(mContext)
+ && focusedDecor.checkTouchEventInHandle(ev)) {
+ mTransitionDragActive = true;
+ }
+ break;
+ }
+ case MotionEvent.ACTION_UP: {
+ CaptionWindowDecoration focusedDecor = getFocusedDecor();
+ if (focusedDecor == null) {
+ mTransitionDragActive = false;
+ return;
+ }
+ if (mTransitionDragActive) {
+ mTransitionDragActive = false;
+ int statusBarHeight = mDisplayController
+ .getDisplayLayout(focusedDecor.mTaskInfo.displayId).stableInsets().top;
+ if (ev.getY() > statusBarHeight) {
+ mDesktopModeController.setDesktopModeActive(true);
+ return;
+ }
+ }
+ focusedDecor.checkClickEvent(ev);
+ break;
+ }
+ case MotionEvent.ACTION_CANCEL: {
+ mTransitionDragActive = false;
+ }
+ }
+ }
+
+ @Nullable
+ private CaptionWindowDecoration getFocusedDecor() {
+ int size = mWindowDecorByTaskId.size();
+ CaptionWindowDecoration focusedDecor = null;
+ for (int i = 0; i < size; i++) {
+ CaptionWindowDecoration decor = mWindowDecorByTaskId.valueAt(i);
+ if (decor != null && decor.isFocused()) {
+ focusedDecor = decor;
+ break;
+ }
+ }
+ return focusedDecor;
+ }
+
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index affde30..59576cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -23,6 +23,7 @@
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.VectorDrawable;
import android.os.Handler;
@@ -243,7 +244,7 @@
* Sets the visibility of buttons and color of caption based on desktop mode status
*
*/
- public void setButtonVisibility() {
+ void setButtonVisibility() {
mDesktopActive = DesktopModeStatus.isActive(mContext);
int v = mDesktopActive ? View.VISIBLE : View.GONE;
View caption = mResult.mRootView.findViewById(R.id.caption);
@@ -262,7 +263,7 @@
caption.getBackground().setTint(v == View.VISIBLE ? Color.WHITE : Color.TRANSPARENT);
}
- public boolean isHandleMenuActive() {
+ boolean isHandleMenuActive() {
return mHandleMenu != null;
}
@@ -277,7 +278,7 @@
/**
* Create and display handle menu window
*/
- public void createHandleMenu() {
+ void createHandleMenu() {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
final Resources resources = mDecorWindowContext.getResources();
int x = mRelayoutParams.mCaptionX;
@@ -298,7 +299,7 @@
/**
* Close the handle menu window
*/
- public void closeHandleMenu() {
+ void closeHandleMenu() {
if (!isHandleMenuActive()) return;
mHandleMenu.releaseView();
mHandleMenu = null;
@@ -313,24 +314,85 @@
/**
* Close an open handle menu if input is outside of menu coordinates
* @param ev the tapped point to compare against
- * @return
*/
- public void closeHandleMenuIfNeeded(MotionEvent ev) {
- if (mHandleMenu != null) {
- Point positionInParent = mTaskOrganizer.getRunningTaskInfo(mTaskInfo.taskId)
- .positionInParent;
- final Resources resources = mDecorWindowContext.getResources();
- ev.offsetLocation(-mRelayoutParams.mCaptionX, -mRelayoutParams.mCaptionY);
- ev.offsetLocation(-positionInParent.x, -positionInParent.y);
- int width = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionWidthId);
- int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId);
- if (!(ev.getX() >= 0 && ev.getY() >= 0
- && ev.getX() <= width && ev.getY() <= height)) {
+ void closeHandleMenuIfNeeded(MotionEvent ev) {
+ if (isHandleMenuActive()) {
+ if (!checkEventInCaptionView(ev, R.id.caption)) {
closeHandleMenu();
}
}
}
+ boolean isFocused() {
+ return mTaskInfo.isFocused;
+ }
+
+ /**
+ * Offset the coordinates of a {@link MotionEvent} to be in the same coordinate space as caption
+ * @param ev the {@link MotionEvent} to offset
+ * @return the point of the input in local space
+ */
+ private PointF offsetCaptionLocation(MotionEvent ev) {
+ PointF result = new PointF(ev.getX(), ev.getY());
+ Point positionInParent = mTaskOrganizer.getRunningTaskInfo(mTaskInfo.taskId)
+ .positionInParent;
+ result.offset(-mRelayoutParams.mCaptionX, -mRelayoutParams.mCaptionY);
+ result.offset(-positionInParent.x, -positionInParent.y);
+ return result;
+ }
+
+ /**
+ * Determine if a passed MotionEvent is in a view in caption
+ * @param ev the {@link MotionEvent} to check
+ * @param layoutId the id of the view
+ * @return {@code true} if event is inside the specified view, {@code false} if not
+ */
+ private boolean checkEventInCaptionView(MotionEvent ev, int layoutId) {
+ if (mResult.mRootView == null) return false;
+ PointF inputPoint = offsetCaptionLocation(ev);
+ View view = mResult.mRootView.findViewById(layoutId);
+ return view != null && view.pointInView(inputPoint.x, inputPoint.y, 0);
+ }
+
+ boolean checkTouchEventInHandle(MotionEvent ev) {
+ if (isHandleMenuActive()) return false;
+ return checkEventInCaptionView(ev, R.id.caption_handle);
+ }
+
+ /**
+ * Check a passed MotionEvent if a click has occurred on any button on this caption
+ * Note this should only be called when a regular onClick is not possible
+ * (i.e. the button was clicked through status bar layer)
+ * @param ev the MotionEvent to compare
+ */
+ void checkClickEvent(MotionEvent ev) {
+ if (mResult.mRootView == null) return;
+ View caption = mResult.mRootView.findViewById(R.id.caption);
+ PointF inputPoint = offsetCaptionLocation(ev);
+ if (!isHandleMenuActive()) {
+ View handle = caption.findViewById(R.id.caption_handle);
+ clickIfPointInView(inputPoint, handle);
+ } else {
+ View menu = mHandleMenu.mWindowViewHost.getView();
+ View fullscreen = menu.findViewById(R.id.fullscreen_button);
+ if (clickIfPointInView(inputPoint, fullscreen)) return;
+ View desktop = menu.findViewById(R.id.desktop_button);
+ if (clickIfPointInView(inputPoint, desktop)) return;
+ View split = menu.findViewById(R.id.split_screen_button);
+ if (clickIfPointInView(inputPoint, split)) return;
+ View more = menu.findViewById(R.id.more_button);
+ clickIfPointInView(inputPoint, more);
+ }
+ }
+
+ private boolean clickIfPointInView(PointF inputPoint, View v) {
+ if (v.pointInView(inputPoint.x - v.getLeft(), inputPoint.y, 0)) {
+ mOnCaptionButtonClickListener.onClick(v);
+ return true;
+ }
+ return false;
+ }
+
@Override
public void close() {
closeDragResizeListener();
diff --git a/packages/SystemUI/res/drawable/ic_ring_volume.xml b/packages/SystemUI/res/drawable/ic_ring_volume.xml
new file mode 100644
index 0000000..343fe5d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ring_volume.xml
@@ -0,0 +1,26 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:pathData="M11,7V2H13V7ZM17.6,9.85 L16.2,8.4 19.75,4.85 21.15,6.3ZM6.4,9.85 L2.85,6.3 4.25,4.85 7.8,8.4ZM12,12Q14.95,12 17.812,13.188Q20.675,14.375 22.9,16.75Q23.2,17.05 23.2,17.45Q23.2,17.85 22.9,18.15L20.6,20.4Q20.325,20.675 19.963,20.7Q19.6,20.725 19.3,20.5L16.4,18.3Q16.2,18.15 16.1,17.95Q16,17.75 16,17.5V14.65Q15.05,14.35 14.05,14.175Q13.05,14 12,14Q10.95,14 9.95,14.175Q8.95,14.35 8,14.65V17.5Q8,17.75 7.9,17.95Q7.8,18.15 7.6,18.3L4.7,20.5Q4.4,20.725 4.038,20.7Q3.675,20.675 3.4,20.4L1.1,18.15Q0.8,17.85 0.8,17.45Q0.8,17.05 1.1,16.75Q3.3,14.375 6.175,13.188Q9.05,12 12,12ZM6,15.35Q5.275,15.725 4.6,16.212Q3.925,16.7 3.2,17.3L4.2,18.3L6,16.9ZM18,15.4V16.9L19.8,18.3L20.8,17.35Q20.075,16.7 19.4,16.225Q18.725,15.75 18,15.4ZM6,15.35Q6,15.35 6,15.35Q6,15.35 6,15.35ZM18,15.4Q18,15.4 18,15.4Q18,15.4 18,15.4Z"
+ android:fillColor="?android:attr/colorPrimary"/>
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_ring_volume_off.xml b/packages/SystemUI/res/drawable/ic_ring_volume_off.xml
new file mode 100644
index 0000000..74f30d1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ring_volume_off.xml
@@ -0,0 +1,34 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal">
+<path
+ android:pathData="M0.8,4.2l8.1,8.1c-2.2,0.5 -5.2,1.6 -7.8,4.4c-0.4,0.4 -0.4,1 0,1.4l2.3,2.3c0.3,0.3 0.9,0.4 1.3,0.1l2.9,-2.2C7.8,18.1 8,17.8 8,17.5v-2.9c0.9,-0.3 1.7,-0.5 2.7,-0.6l8.5,8.5l1.4,-1.4L2.2,2.8L0.8,4.2z"
+ android:fillColor="?android:attr/colorPrimary"/>
+ <path
+ android:pathData="M11,2h2v5h-2z"
+ android:fillColor="?android:attr/colorPrimary"/>
+ <path
+ android:pathData="M21.2,6.3l-1.4,-1.4l-3.6,3.6l1.4,1.4C17.6,9.8 21,6.3 21.2,6.3z"
+ android:fillColor="?android:attr/colorPrimary"/>
+ <path
+ android:pathData="M22.9,16.7c-2.8,-3 -6.2,-4.1 -8.4,-4.5l7.2,7.2l1.3,-1.3C23.3,17.7 23.3,17.1 22.9,16.7z"
+ android:fillColor="?android:attr/colorPrimary"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_speaker_mute.xml b/packages/SystemUI/res/drawable/ic_speaker_mute.xml
new file mode 100644
index 0000000..4e402cf
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_speaker_mute.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/textColorPrimary"
+ android:autoMirrored="true">
+ <path android:fillColor="#FFFFFFFF"
+ android:pathData="M19.8,22.6 L16.775,19.575Q16.15,19.975 15.45,20.263Q14.75,20.55 14,20.725V18.675Q14.35,18.55 14.688,18.425Q15.025,18.3 15.325,18.125L12,14.8V20L7,15H3V9H6.2L1.4,4.2L2.8,2.8L21.2,21.2ZM19.6,16.8 L18.15,15.35Q18.575,14.575 18.788,13.725Q19,12.875 19,11.975Q19,9.625 17.625,7.775Q16.25,5.925 14,5.275V3.225Q17.1,3.925 19.05,6.362Q21,8.8 21,11.975Q21,13.3 20.638,14.525Q20.275,15.75 19.6,16.8ZM16.25,13.45 L14,11.2V7.95Q15.175,8.5 15.838,9.6Q16.5,10.7 16.5,12Q16.5,12.375 16.438,12.738Q16.375,13.1 16.25,13.45ZM12,9.2 L9.4,6.6 12,4ZM10,15.15V12.8L8.2,11H5V13H7.85ZM9.1,11.9Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_speaker_on.xml b/packages/SystemUI/res/drawable/ic_speaker_on.xml
new file mode 100644
index 0000000..2a90e05
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_speaker_on.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/textColorPrimary"
+ android:autoMirrored="true">
+ <path android:fillColor="#FFFFFFFF"
+ android:pathData="M14,20.725V18.675Q16.25,18.025 17.625,16.175Q19,14.325 19,11.975Q19,9.625 17.625,7.775Q16.25,5.925 14,5.275V3.225Q17.1,3.925 19.05,6.362Q21,8.8 21,11.975Q21,15.15 19.05,17.587Q17.1,20.025 14,20.725ZM3,15V9H7L12,4V20L7,15ZM14,16V7.95Q15.175,8.5 15.838,9.6Q16.5,10.7 16.5,12Q16.5,13.275 15.838,14.362Q15.175,15.45 14,16ZM10,8.85 L7.85,11H5V13H7.85L10,15.15ZM7.5,12Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 6f87169..99bc794 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -24,11 +24,6 @@
<item name="android:windowIsFloating">true</item>
</style>
- <style name="TextAppearance.QS.Status" parent="TextAppearance.QS.TileLabel.Secondary">
- <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
- </style>
-
<!-- Screenshots -->
<style name="LongScreenshotActivity" parent="@android:style/Theme.DeviceDefault.DayNight">
<item name="android:windowNoTitle">true</item>
diff --git a/packages/SystemUI/res/xml/qs_header.xml b/packages/SystemUI/res/xml/qs_header.xml
index 8248fcd..982c422 100644
--- a/packages/SystemUI/res/xml/qs_header.xml
+++ b/packages/SystemUI/res/xml/qs_header.xml
@@ -22,50 +22,104 @@
>
<Constraint
+ android:id="@+id/privacy_container">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
+ app:layout_constraintEnd_toEndOf="@id/end_guide"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/carrier_group"
+ app:layout_constraintHorizontal_bias="1"
+ />
+ </Constraint>
+
+ <Constraint
android:id="@+id/clock">
<Layout
android:layout_width="wrap_content"
- android:layout_height="48dp"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/date"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/privacy_container"
+ app:layout_constraintBottom_toBottomOf="@id/carrier_group"
app:layout_constraintEnd_toStartOf="@id/carrier_group"
app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintHorizontal_chainStyle="spread_inside"
/>
+ <Transform
+ android:scaleX="2.57"
+ android:scaleY="2.57"
+ />
</Constraint>
<Constraint
android:id="@+id/date">
<Layout
android:layout_width="wrap_content"
- android:layout_height="48dp"
+ android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toTopOf="@id/clock"
+ app:layout_constraintEnd_toStartOf="@id/space"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/carrier_group"
app:layout_constraintHorizontal_bias="0"
- />
- <Motion
- app:motionStagger="0.5"
+ app:layout_constraintHorizontal_chainStyle="spread_inside"
/>
</Constraint>
<Constraint
android:id="@+id/carrier_group">
- <CustomAttribute
- app:attributeName="alpha"
- app:customFloatValue="1"
- />
+ <Layout
+ app:layout_constraintWidth_min="48dp"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
+ app:layout_constraintStart_toEndOf="@id/clock"
+ app:layout_constraintTop_toBottomOf="@id/privacy_container"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="1"
+ app:layout_constraintBottom_toTopOf="@id/batteryRemainingIcon"
+ app:layout_constraintHorizontal_chainStyle="spread_inside"
+ />
+ <PropertySet
+ android:alpha="1"
+ />
</Constraint>
<Constraint
- android:id="@+id/privacy_container">
+ android:id="@+id/statusIcons">
<Layout
android:layout_width="wrap_content"
- android:layout_height="48dp"
+ android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintStart_toEndOf="@id/space"
+ app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
+ app:layout_constraintTop_toTopOf="@id/date"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="1"
+ />
+ </Constraint>
+
+ <Constraint
+ android:id="@+id/batteryRemainingIcon">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
+ app:layout_constraintHeight_min="@dimen/new_qs_header_non_clickable_element_height"
+ app:layout_constraintStart_toEndOf="@id/statusIcons"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/date"
- app:layout_constraintBottom_toBottomOf="@id/date"
- />
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="1"
+ app:layout_constraintHorizontal_chainStyle="spread_inside"
+ />
+ </Constraint>
+
+
+ <Constraint
+ android:id="@id/space">
+ <Layout
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintStart_toEndOf="@id/date"
+ app:layout_constraintEnd_toStartOf="@id/statusIcons"
+ />
</Constraint>
</ConstraintSet>
\ No newline at end of file
diff --git a/packages/SystemUI/res/xml/qs_header_new.xml b/packages/SystemUI/res/xml/qs_header_new.xml
deleted file mode 100644
index 982c422..0000000
--- a/packages/SystemUI/res/xml/qs_header_new.xml
+++ /dev/null
@@ -1,125 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<ConstraintSet
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/qs_header_constraint"
->
-
- <Constraint
- android:id="@+id/privacy_container">
- <Layout
- android:layout_width="wrap_content"
- android:layout_height="@dimen/large_screen_shade_header_min_height"
- app:layout_constraintEnd_toEndOf="@id/end_guide"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toTopOf="@id/carrier_group"
- app:layout_constraintHorizontal_bias="1"
- />
- </Constraint>
-
- <Constraint
- android:id="@+id/clock">
- <Layout
- android:layout_width="wrap_content"
- android:layout_height="@dimen/large_screen_shade_header_min_height"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/privacy_container"
- app:layout_constraintBottom_toBottomOf="@id/carrier_group"
- app:layout_constraintEnd_toStartOf="@id/carrier_group"
- app:layout_constraintHorizontal_bias="0"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
- />
- <Transform
- android:scaleX="2.57"
- android:scaleY="2.57"
- />
- </Constraint>
-
- <Constraint
- android:id="@+id/date">
- <Layout
- android:layout_width="wrap_content"
- android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/space"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/carrier_group"
- app:layout_constraintHorizontal_bias="0"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
- />
- </Constraint>
-
- <Constraint
- android:id="@+id/carrier_group">
- <Layout
- app:layout_constraintWidth_min="48dp"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/large_screen_shade_header_min_height"
- app:layout_constraintStart_toEndOf="@id/clock"
- app:layout_constraintTop_toBottomOf="@id/privacy_container"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="1"
- app:layout_constraintBottom_toTopOf="@id/batteryRemainingIcon"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
- />
- <PropertySet
- android:alpha="1"
- />
- </Constraint>
-
- <Constraint
- android:id="@+id/statusIcons">
- <Layout
- android:layout_width="wrap_content"
- android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
- app:layout_constrainedWidth="true"
- app:layout_constraintStart_toEndOf="@id/space"
- app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
- app:layout_constraintTop_toTopOf="@id/date"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintHorizontal_bias="1"
- />
- </Constraint>
-
- <Constraint
- android:id="@+id/batteryRemainingIcon">
- <Layout
- android:layout_width="wrap_content"
- android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
- app:layout_constraintHeight_min="@dimen/new_qs_header_non_clickable_element_height"
- app:layout_constraintStart_toEndOf="@id/statusIcons"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="@id/date"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintHorizontal_bias="1"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
- />
- </Constraint>
-
-
- <Constraint
- android:id="@id/space">
- <Layout
- android:layout_width="0dp"
- android:layout_height="0dp"
- app:layout_constraintStart_toEndOf="@id/date"
- app:layout_constraintEnd_toStartOf="@id/statusIcons"
- />
- </Constraint>
-</ConstraintSet>
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 236aa66..81a85c3 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -613,7 +613,7 @@
private const val CHARGE_ANIM_DURATION_PHASE_1: Long = 1000
// Constants for the animation
- private val MOVE_INTERPOLATOR = Interpolators.STANDARD
+ private val MOVE_INTERPOLATOR = Interpolators.EMPHASIZED
// Calculate the positions of all of the digits...
// Offset each digit by, say, 0.1
@@ -637,7 +637,10 @@
// How much delay to apply to each subsequent digit. This is measured in terms of "fraction"
// (i.e. a value of 0.1 would cause a digit to wait until fraction had hit 0.1, or 0.2 etc
// before moving).
- private const val MOVE_DIGIT_STEP = 0.1f
+ //
+ // The current specs dictate that each digit should have a 33ms gap between them. The
+ // overall time is 1s right now.
+ private const val MOVE_DIGIT_STEP = 0.033f
// Total available transition time for each digit, taking into account the step. If step is
// 0.1, then digit 0 would animate over 0.0 - 0.7, making availableTime 0.7.
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
index f45887c..f6c75a2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
@@ -82,7 +82,8 @@
taskPercent = mDesiredStagePosition != STAGE_POSITION_TOP_OR_LEFT
? mSplitBounds.topTaskPercent
: (1 - (mSplitBounds.topTaskPercent + mSplitBounds.dividerHeightPercent));
- fullscreenTaskHeight = screenHeightPx * taskPercent;
+ // Scale portrait height to that of (actual screen - taskbar inset)
+ fullscreenTaskHeight = (screenHeightPx - taskbarSize) * taskPercent;
canvasScreenRatio = canvasHeight / fullscreenTaskHeight;
} else {
// For landscape, scale the width
diff --git a/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
index 450784e..f59bf8e 100644
--- a/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
+++ b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
@@ -69,10 +69,16 @@
super.reloadColor()
}
- override fun setMessage(msg: CharSequence?) {
+ override fun setMessage(msg: CharSequence?, animate: Boolean) {
if ((msg == textAboutToShow && msg != null) || msg == text) {
return
}
+
+ if (!animate) {
+ super.setMessage(msg, animate)
+ return
+ }
+
textAboutToShow = msg
if (animatorSet.isRunning) {
@@ -89,7 +95,7 @@
hideAnimator.addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
- super@BouncerKeyguardMessageArea.setMessage(msg)
+ super@BouncerKeyguardMessageArea.setMessage(msg, animate)
}
}
)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 92ba619..3e32cf5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -159,10 +159,12 @@
int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
Map<String, Object> arguments = new HashMap<>();
arguments.put("count", secondsRemaining);
- mMessageAreaController.setMessage(PluralsMessageFormatter.format(
- mView.getResources(),
- arguments,
- R.string.kg_too_many_failed_attempts_countdown));
+ mMessageAreaController.setMessage(
+ PluralsMessageFormatter.format(
+ mView.getResources(),
+ arguments,
+ R.string.kg_too_many_failed_attempts_countdown),
+ /* animate= */ false);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index c79fc2c..0e5f8c1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -59,6 +59,7 @@
@Nullable
private ViewGroup mContainer;
private int mTopMargin;
+ protected boolean mAnimate;
public KeyguardMessageArea(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -106,7 +107,7 @@
}
@Override
- public void setMessage(CharSequence msg) {
+ public void setMessage(CharSequence msg, boolean animate) {
if (!TextUtils.isEmpty(msg)) {
securityMessageChanged(msg);
} else {
@@ -115,21 +116,12 @@
}
@Override
- public void setMessage(int resId) {
- CharSequence message = null;
- if (resId != 0) {
- message = getContext().getResources().getText(resId);
- }
- setMessage(message);
- }
-
- @Override
public void formatMessage(int resId, Object... formatArgs) {
CharSequence message = null;
if (resId != 0) {
message = getContext().getString(resId, formatArgs);
}
- setMessage(message);
+ setMessage(message, true);
}
private void securityMessageChanged(CharSequence message) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index db986e0..c29f632 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -92,11 +92,19 @@
}
public void setMessage(CharSequence s) {
- mView.setMessage(s);
+ setMessage(s, true);
+ }
+
+ /**
+ * Sets a message to the underlying text view.
+ */
+ public void setMessage(CharSequence s, boolean animate) {
+ mView.setMessage(s, animate);
}
public void setMessage(int resId) {
- mView.setMessage(resId);
+ String message = resId != 0 ? mView.getResources().getString(resId) : null;
+ setMessage(message);
}
public void setNextMessageColor(ColorStateList colorState) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 1f0bd54..cdbfb24 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -372,10 +372,13 @@
Map<String, Object> arguments = new HashMap<>();
arguments.put("count", secondsRemaining);
- mMessageAreaController.setMessage(PluralsMessageFormatter.format(
- mView.getResources(),
- arguments,
- R.string.kg_too_many_failed_attempts_countdown));
+ mMessageAreaController.setMessage(
+ PluralsMessageFormatter.format(
+ mView.getResources(),
+ arguments,
+ R.string.kg_too_many_failed_attempts_countdown),
+ /* animate= */ false
+ );
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java b/packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java
index 777bd19..3392a1c 100644
--- a/packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java
+++ b/packages/SystemUI/src/com/android/keyguard/SecurityMessageDisplay.java
@@ -23,9 +23,10 @@
/** Set text color for the next security message. */
default void setNextMessageColor(ColorStateList colorState) {}
- void setMessage(CharSequence msg);
-
- void setMessage(int resId);
+ /**
+ * Sets a message to the underlying text view.
+ */
+ void setMessage(CharSequence msg, boolean animate);
void formatMessage(int resId, Object... formatArgs);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 94f7158..68e1f72 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -129,6 +129,7 @@
private final float mTranslationY;
@VisibleForTesting @ContainerState int mContainerState = STATE_UNKNOWN;
private final Set<Integer> mFailedModalities = new HashSet<Integer>();
+ private OnBackInvokedDispatcher mOnBackInvokedDispatcher;
private final OnBackInvokedCallback mBackCallback = this::onBackInvoked;
private final @Background DelayableExecutor mBackgroundExecutor;
@@ -497,9 +498,9 @@
.start();
});
}
- OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher();
- if (dispatcher != null) {
- dispatcher.registerOnBackInvokedCallback(
+ mOnBackInvokedDispatcher = findOnBackInvokedDispatcher();
+ if (mOnBackInvokedDispatcher != null) {
+ mOnBackInvokedDispatcher.registerOnBackInvokedCallback(
OnBackInvokedDispatcher.PRIORITY_DEFAULT, mBackCallback);
}
}
@@ -600,11 +601,11 @@
@Override
public void onDetachedFromWindow() {
- OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher();
- if (dispatcher != null) {
- findOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mBackCallback);
- }
super.onDetachedFromWindow();
+ if (mOnBackInvokedDispatcher != null) {
+ mOnBackInvokedDispatcher.unregisterOnBackInvokedCallback(mBackCallback);
+ mOnBackInvokedDispatcher = null;
+ }
mWakefulnessLifecycle.removeObserver(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index 76cd3f4..e43c0b9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -35,6 +35,8 @@
import android.view.inputmethod.InputMethodManager;
import android.widget.ImeAwareEditText;
import android.widget.TextView;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
@@ -58,6 +60,8 @@
private ViewGroup mAuthCredentialHeader;
private ViewGroup mAuthCredentialInput;
private int mBottomInset = 0;
+ private OnBackInvokedDispatcher mOnBackInvokedDispatcher;
+ private final OnBackInvokedCallback mBackCallback = this::onBackInvoked;
public AuthCredentialPasswordView(Context context,
AttributeSet attrs) {
@@ -79,8 +83,7 @@
return false;
}
if (event.getAction() == KeyEvent.ACTION_UP) {
- mContainerView.sendEarlyUserCanceled();
- mContainerView.animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
+ onBackInvoked();
}
return true;
});
@@ -88,6 +91,11 @@
setOnApplyWindowInsetsListener(this);
}
+ private void onBackInvoked() {
+ mContainerView.sendEarlyUserCanceled();
+ mContainerView.animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
+ }
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -100,6 +108,12 @@
mPasswordField.requestFocus();
mPasswordField.scheduleShowSoftInput();
+
+ mOnBackInvokedDispatcher = findOnBackInvokedDispatcher();
+ if (mOnBackInvokedDispatcher != null) {
+ mOnBackInvokedDispatcher.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT, mBackCallback);
+ }
}
@Override
@@ -137,6 +151,15 @@
}
@Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mOnBackInvokedDispatcher != null) {
+ mOnBackInvokedDispatcher.unregisterOnBackInvokedCallback(mBackCallback);
+ mOnBackInvokedDispatcher = null;
+ }
+ }
+
+ @Override
protected void onCredentialVerified(@NonNull VerifyCredentialResponse response,
int timeoutMs) {
super.onCredentialVerified(response, timeoutMs);
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index c853671..fb37def 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -266,6 +266,7 @@
mExitAnimator.cancel();
}
reset();
+ mClipboardLogger.setClipSource(clipSource);
String accessibilityAnnouncement = mContext.getString(R.string.clipboard_content_copied);
boolean isSensitive = clipData != null && clipData.getDescription().getExtras() != null
@@ -525,21 +526,27 @@
static class ClipboardLogger {
private final UiEventLogger mUiEventLogger;
+ private String mClipSource;
private boolean mGuarded = false;
ClipboardLogger(UiEventLogger uiEventLogger) {
mUiEventLogger = uiEventLogger;
}
+ void setClipSource(String clipSource) {
+ mClipSource = clipSource;
+ }
+
void logSessionComplete(@NonNull UiEventLogger.UiEventEnum event) {
if (!mGuarded) {
mGuarded = true;
- mUiEventLogger.log(event);
+ mUiEventLogger.log(event, 0, mClipSource);
}
}
void reset() {
mGuarded = false;
+ mClipSource = null;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java
index cece764..c194e66 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtils.java
@@ -20,7 +20,10 @@
import android.content.ClipDescription;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Build;
+import android.provider.DeviceConfig;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.R;
import javax.inject.Inject;
@@ -35,6 +38,12 @@
if (clipData != null && clipData.getDescription().getExtras() != null
&& clipData.getDescription().getExtras().getBoolean(
ClipDescription.EXTRA_IS_REMOTE_DEVICE)) {
+ if (Build.isDebuggable() && DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.CLIPBOARD_IGNORE_REMOTE_COPY_SOURCE,
+ false)) {
+ return true;
+ }
ComponentName remoteComponent = ComponentName.unflattenFromString(
context.getResources().getString(R.string.config_remoteCopyPackage));
if (remoteComponent != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTransitionListener.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeTransitionListener.kt
new file mode 100644
index 0000000..12ceedd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTransitionListener.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.doze
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.policy.CallbackController
+import javax.inject.Inject
+
+/** Receives doze transition events, and passes those events to registered callbacks. */
+@SysUISingleton
+class DozeTransitionListener @Inject constructor() :
+ DozeMachine.Part, CallbackController<DozeTransitionCallback> {
+ val callbacks = mutableSetOf<DozeTransitionCallback>()
+ var oldState = DozeMachine.State.UNINITIALIZED
+ var newState = DozeMachine.State.UNINITIALIZED
+
+ override fun transitionTo(oldState: DozeMachine.State, newState: DozeMachine.State) {
+ this.oldState = oldState
+ this.newState = newState
+ callbacks.forEach { it.onDozeTransition(oldState, newState) }
+ }
+
+ override fun addCallback(callback: DozeTransitionCallback) {
+ callbacks.add(callback)
+ }
+
+ override fun removeCallback(callback: DozeTransitionCallback) {
+ callbacks.remove(callback)
+ }
+}
+
+interface DozeTransitionCallback {
+ fun onDozeTransition(oldState: DozeMachine.State, newState: DozeMachine.State)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
index 98cd2d7..069344f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
@@ -35,6 +35,7 @@
import com.android.systemui.doze.DozeSensors;
import com.android.systemui.doze.DozeSuppressor;
import com.android.systemui.doze.DozeSuspendScreenStatePreventingAdapter;
+import com.android.systemui.doze.DozeTransitionListener;
import com.android.systemui.doze.DozeTriggers;
import com.android.systemui.doze.DozeUi;
import com.android.systemui.doze.DozeWallpaperState;
@@ -83,7 +84,7 @@
DozeUi dozeUi, DozeScreenState dozeScreenState,
DozeScreenBrightness dozeScreenBrightness, DozeWallpaperState dozeWallpaperState,
DozeDockHandler dozeDockHandler, DozeAuthRemover dozeAuthRemover,
- DozeSuppressor dozeSuppressor) {
+ DozeSuppressor dozeSuppressor, DozeTransitionListener dozeTransitionListener) {
return new DozeMachine.Part[]{
dozePauser,
dozeFalsingManagerAdapter,
@@ -94,7 +95,8 @@
dozeWallpaperState,
dozeDockHandler,
dozeAuthRemover,
- dozeSuppressor
+ dozeSuppressor,
+ dozeTransitionListener
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index ef1ad90..0060999 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -153,8 +153,8 @@
unreleasedFlag(216, "customizable_lock_screen_quick_affordances", teamfood = false)
/** Shows chipbar UI whenever the device is unlocked by ActiveUnlock (watch). */
- // TODO(b/240196500): Tracking Bug
- @JvmField val ACTIVE_UNLOCK_CHIPBAR = unreleasedFlag(217, "active_unlock_chipbar")
+ // TODO(b/256513609): Tracking Bug
+ @JvmField val ACTIVE_UNLOCK_CHIPBAR = releasedFlag(217, "active_unlock_chipbar")
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@@ -181,9 +181,6 @@
"qs_user_detail_shortcut"
)
- // TODO(b/254512747): Tracking Bug
- val NEW_HEADER = releasedFlag(505, "new_header")
-
// TODO(b/254512383): Tracking Bug
@JvmField
val FULL_SCREEN_USER_SWITCHER =
@@ -391,7 +388,9 @@
// 1700 - clipboard
@JvmField val CLIPBOARD_OVERLAY_REFACTOR = releasedFlag(1700, "clipboard_overlay_refactor")
- @JvmField val CLIPBOARD_REMOTE_BEHAVIOR = unreleasedFlag(1701, "clipboard_remote_behavior")
+ @JvmField
+ val CLIPBOARD_REMOTE_BEHAVIOR =
+ unreleasedFlag(1701, "clipboard_remote_behavior", teamfood = true)
// 1800 - shade container
@JvmField
@@ -412,4 +411,7 @@
@JvmField val UDFPS_NEW_TOUCH_DETECTION = unreleasedFlag(2200, "udfps_new_touch_detection")
@JvmField val UDFPS_ELLIPSE_DEBUG_UI = unreleasedFlag(2201, "udfps_ellipse_debug")
@JvmField val UDFPS_ELLIPSE_DETECTION = unreleasedFlag(2202, "udfps_ellipse_detection")
+
+ // TODO(b259590361): Tracking bug
+ val EXPERIMENTAL_FLAG = unreleasedFlag(2, "exp_flag_release")
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e680162..8403fe6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -899,25 +899,32 @@
@NonNull
@Override
public LaunchAnimator.State createAnimatorState() {
- final int width = getLaunchContainer().getWidth();
- final int height = getLaunchContainer().getHeight();
-
- final float initialHeight = height / 3f;
- final float initialWidth = width / 3f;
+ final int fullWidth = getLaunchContainer().getWidth();
+ final int fullHeight = getLaunchContainer().getHeight();
if (mUpdateMonitor.isSecureCameraLaunchedOverKeyguard()) {
+ final float initialHeight = fullHeight / 3f;
+ final float initialWidth = fullWidth / 3f;
+
// Start the animation near the power button, at one-third size, since the
// camera was launched from the power button.
return new LaunchAnimator.State(
(int) (mPowerButtonY - initialHeight / 2f) /* top */,
(int) (mPowerButtonY + initialHeight / 2f) /* bottom */,
- (int) (width - initialWidth) /* left */,
- width /* right */,
+ (int) (fullWidth - initialWidth) /* left */,
+ fullWidth /* right */,
mWindowCornerRadius, mWindowCornerRadius);
} else {
- // Start the animation in the center of the screen, scaled down.
+ final float initialHeight = fullHeight / 2f;
+ final float initialWidth = fullWidth / 2f;
+
+ // Start the animation in the center of the screen, scaled down to half
+ // size.
return new LaunchAnimator.State(
- height / 2, height / 2, width / 2, width / 2,
+ (int) (fullHeight - initialHeight) / 2,
+ (int) (initialHeight + (fullHeight - initialHeight) / 2),
+ (int) (fullWidth - initialWidth) / 2,
+ (int) (initialWidth + (fullWidth - initialWidth) / 2),
mWindowCornerRadius, mWindowCornerRadius);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
index 9a90fe7..783f752 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
@@ -23,10 +23,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
import com.android.systemui.statusbar.phone.KeyguardBouncer
import javax.inject.Inject
-import kotlinx.coroutines.channels.BufferOverflow
-import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
/** Encapsulates app state for the lock screen primary and alternate bouncer. */
@@ -71,12 +68,8 @@
private val _keyguardAuthenticated = MutableStateFlow<Boolean?>(null)
/** Determines if user is already unlocked */
val keyguardAuthenticated = _keyguardAuthenticated.asStateFlow()
- private val _showMessage =
- MutableSharedFlow<BouncerShowMessageModel?>(
- replay = 1,
- onBufferOverflow = BufferOverflow.DROP_OLDEST
- )
- val showMessage = _showMessage.asSharedFlow()
+ private val _showMessage = MutableStateFlow<BouncerShowMessageModel?>(null)
+ val showMessage = _showMessage.asStateFlow()
private val _resourceUpdateRequests = MutableStateFlow(false)
val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
val bouncerPromptReason: Int
@@ -125,7 +118,7 @@
}
fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) {
- _showMessage.tryEmit(bouncerShowMessageModel)
+ _showMessage.value = bouncerShowMessageModel
}
fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?) {
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 9d5d8bb..796f2b4 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
@@ -23,9 +23,14 @@
import com.android.systemui.common.shared.model.Position
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.doze.DozeHost
+import com.android.systemui.doze.DozeMachine
+import com.android.systemui.doze.DozeTransitionCallback
+import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -108,6 +113,9 @@
*/
val dozeAmount: Flow<Float>
+ /** Doze state information, as it transitions */
+ val dozeTransitionModel: Flow<DozeTransitionModel>
+
/** Observable for the [StatusBarState] */
val statusBarState: Flow<StatusBarState>
@@ -154,6 +162,7 @@
biometricUnlockController: BiometricUnlockController,
private val keyguardStateController: KeyguardStateController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val dozeTransitionListener: DozeTransitionListener,
) : KeyguardRepository {
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
override val animateBottomAreaDozingTransitions =
@@ -286,6 +295,37 @@
awaitClose { statusBarStateController.removeCallback(callback) }
}
+ override val dozeTransitionModel: Flow<DozeTransitionModel> = conflatedCallbackFlow {
+ val callback =
+ object : DozeTransitionCallback {
+ override fun onDozeTransition(
+ oldState: DozeMachine.State,
+ newState: DozeMachine.State
+ ) {
+ trySendWithFailureLogging(
+ DozeTransitionModel(
+ from = dozeMachineStateToModel(oldState),
+ to = dozeMachineStateToModel(newState),
+ ),
+ TAG,
+ "doze transition model"
+ )
+ }
+ }
+
+ dozeTransitionListener.addCallback(callback)
+ trySendWithFailureLogging(
+ DozeTransitionModel(
+ from = dozeMachineStateToModel(dozeTransitionListener.oldState),
+ to = dozeMachineStateToModel(dozeTransitionListener.newState),
+ ),
+ TAG,
+ "initial doze transition model"
+ )
+
+ awaitClose { dozeTransitionListener.removeCallback(callback) }
+ }
+
override fun isKeyguardShowing(): Boolean {
return keyguardStateController.isShowing
}
@@ -407,6 +447,25 @@
}
}
+ private fun dozeMachineStateToModel(state: DozeMachine.State): DozeStateModel {
+ return when (state) {
+ DozeMachine.State.UNINITIALIZED -> DozeStateModel.UNINITIALIZED
+ DozeMachine.State.INITIALIZED -> DozeStateModel.INITIALIZED
+ DozeMachine.State.DOZE -> DozeStateModel.DOZE
+ DozeMachine.State.DOZE_SUSPEND_TRIGGERS -> DozeStateModel.DOZE_SUSPEND_TRIGGERS
+ DozeMachine.State.DOZE_AOD -> DozeStateModel.DOZE_AOD
+ DozeMachine.State.DOZE_REQUEST_PULSE -> DozeStateModel.DOZE_REQUEST_PULSE
+ DozeMachine.State.DOZE_PULSING -> DozeStateModel.DOZE_PULSING
+ DozeMachine.State.DOZE_PULSING_BRIGHT -> DozeStateModel.DOZE_PULSING_BRIGHT
+ DozeMachine.State.DOZE_PULSE_DONE -> DozeStateModel.DOZE_PULSE_DONE
+ DozeMachine.State.FINISH -> DozeStateModel.FINISH
+ DozeMachine.State.DOZE_AOD_PAUSED -> DozeStateModel.DOZE_AOD_PAUSED
+ DozeMachine.State.DOZE_AOD_PAUSING -> DozeStateModel.DOZE_AOD_PAUSING
+ DozeMachine.State.DOZE_AOD_DOCKED -> DozeStateModel.DOZE_AOD_DOCKED
+ else -> throw IllegalArgumentException("Invalid DozeMachine.State: state")
+ }
+ }
+
companion object {
private const val TAG = "KeyguardRepositoryImpl"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
index e5521c7..2dbacd5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
@@ -21,10 +21,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
-import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isSleepingOrStartingToSleep
-import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isWakingOrStartingToWake
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -39,27 +38,24 @@
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
-) : TransitionInteractor("AOD<->LOCKSCREEN") {
+) : TransitionInteractor(AodLockscreenTransitionInteractor::class.simpleName!!) {
override fun start() {
+ listenForTransitionToAodFromLockscreen()
+ listenForTransitionToLockscreenFromAod()
+ }
+
+ private fun listenForTransitionToAodFromLockscreen() {
scope.launch {
- /*
- * Listening to the startedKeyguardTransitionStep (last started step) allows this code
- * to interrupt an active transition, as long as they were either going to LOCKSCREEN or
- * AOD state. One example is when the user presses the power button in the middle of an
- * active transition.
- */
- keyguardInteractor.wakefulnessState
+ keyguardInteractor
+ .dozeTransitionTo(DozeStateModel.DOZE_AOD)
.sample(
keyguardTransitionInteractor.startedKeyguardTransitionStep,
{ a, b -> Pair(a, b) }
)
.collect { pair ->
- val (wakefulnessState, lastStartedStep) = pair
- if (
- isSleepingOrStartingToSleep(wakefulnessState) &&
- lastStartedStep.to == KeyguardState.LOCKSCREEN
- ) {
+ val (dozeToAod, lastStartedStep) = pair
+ if (lastStartedStep.to == KeyguardState.LOCKSCREEN) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
@@ -68,10 +64,22 @@
getAnimator(),
)
)
- } else if (
- isWakingOrStartingToWake(wakefulnessState) &&
- lastStartedStep.to == KeyguardState.AOD
- ) {
+ }
+ }
+ }
+ }
+
+ private fun listenForTransitionToLockscreenFromAod() {
+ scope.launch {
+ keyguardInteractor
+ .dozeTransitionTo(DozeStateModel.FINISH)
+ .sample(
+ keyguardTransitionInteractor.startedKeyguardTransitionStep,
+ { a, b -> Pair(a, b) }
+ )
+ .collect { pair ->
+ val (dozeToAod, lastStartedStep) = pair
+ if (lastStartedStep.to == KeyguardState.AOD) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodToGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodToGoneTransitionInteractor.kt
index 7e01db3..2a220fc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodToGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodToGoneTransitionInteractor.kt
@@ -40,7 +40,7 @@
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
-) : TransitionInteractor("AOD->GONE") {
+) : TransitionInteractor(AodToGoneTransitionInteractor::class.simpleName!!) {
private val wakeAndUnlockModes =
setOf(WAKE_AND_UNLOCK, WAKE_AND_UNLOCK_FROM_DREAM, WAKE_AND_UNLOCK_PULSING)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerToGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerToGoneTransitionInteractor.kt
index dd29673..056c44d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerToGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerToGoneTransitionInteractor.kt
@@ -40,7 +40,7 @@
private val shadeRepository: ShadeRepository,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor
-) : TransitionInteractor("BOUNCER->GONE") {
+) : TransitionInteractor(BouncerToGoneTransitionInteractor::class.simpleName!!) {
private var transitionId: UUID? = null
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingLockscreenTransitionInteractor.kt
index c44cda4..9cbf9ea 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingLockscreenTransitionInteractor.kt
@@ -21,12 +21,14 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@SysUISingleton
@@ -37,32 +39,43 @@
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
-) : TransitionInteractor("DREAMING<->LOCKSCREEN") {
+) : TransitionInteractor(DreamingLockscreenTransitionInteractor::class.simpleName!!) {
override fun start() {
scope.launch {
keyguardInteractor.isDreaming
- .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
- .collect { pair ->
- val (isDreaming, keyguardState) = pair
- if (isDreaming && keyguardState == KeyguardState.LOCKSCREEN) {
- keyguardTransitionRepository.startTransition(
- TransitionInfo(
- name,
- KeyguardState.LOCKSCREEN,
- KeyguardState.DREAMING,
- getAnimator(),
+ .sample(
+ combine(
+ keyguardInteractor.dozeTransitionModel,
+ keyguardTransitionInteractor.finishedKeyguardState
+ ) { a, b -> Pair(a, b) },
+ { a, bc -> Triple(a, bc.first, bc.second) }
+ )
+ .collect { triple ->
+ val (isDreaming, dozeTransitionModel, keyguardState) = triple
+ // Dozing/AOD and dreaming have overlapping events. If the state remains in
+ // FINISH, it means that doze mode is not running and DREAMING is ok to
+ // commence.
+ if (dozeTransitionModel.to == DozeStateModel.FINISH) {
+ if (isDreaming && keyguardState == KeyguardState.LOCKSCREEN) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.DREAMING,
+ getAnimator(),
+ )
)
- )
- } else if (!isDreaming && keyguardState == KeyguardState.DREAMING) {
- keyguardTransitionRepository.startTransition(
- TransitionInfo(
- name,
- KeyguardState.DREAMING,
- KeyguardState.LOCKSCREEN,
- getAnimator(),
+ } else if (!isDreaming && keyguardState == KeyguardState.DREAMING) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.DREAMING,
+ KeyguardState.LOCKSCREEN,
+ getAnimator(),
+ )
)
- )
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 5a1c702..7cfd117 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -20,10 +20,13 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
/**
* Encapsulates business-logic related to the keyguard but not to a more specific part within it.
@@ -41,6 +44,8 @@
val dozeAmount: Flow<Float> = repository.dozeAmount
/** Whether the system is in doze mode. */
val isDozing: Flow<Boolean> = repository.isDozing
+ /** Doze transition information. */
+ val dozeTransitionModel: Flow<DozeTransitionModel> = repository.dozeTransitionModel
/**
* Whether the system is dreaming. [isDreaming] will be always be true when [isDozing] is true,
* but not vice-versa.
@@ -62,6 +67,10 @@
*/
val biometricUnlockState: Flow<BiometricUnlockModel> = repository.biometricUnlockState
+ fun dozeTransitionTo(state: DozeStateModel): Flow<DozeTransitionModel> {
+ return dozeTransitionModel.filter { it.to == state }
+ }
+
fun isKeyguardShowing(): Boolean {
return repository.isKeyguardShowing()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
index cca2d56..3bb8241 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
@@ -44,7 +44,7 @@
private val shadeRepository: ShadeRepository,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor
-) : TransitionInteractor("LOCKSCREEN<->BOUNCER") {
+) : TransitionInteractor(LockscreenBouncerTransitionInteractor::class.simpleName!!) {
private var transitionId: UUID? = null
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
index 910cdf2..3b31dcf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
@@ -271,6 +271,11 @@
repository.setKeyguardAuthenticated(null)
}
+ /** Notifies that the message was shown. */
+ fun onMessageShown() {
+ repository.setShowMessage(null)
+ }
+
/** Notify that view visibility has changed. */
fun notifyBouncerVisibilityHasChanged(visibility: Int) {
primaryBouncerCallbackInteractor.dispatchVisibilityChanged(visibility)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/DozeStateModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/DozeStateModel.kt
new file mode 100644
index 0000000..7039188
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/DozeStateModel.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.keyguard.shared.model
+
+/** Model device doze states. */
+enum class DozeStateModel {
+ /** Default state. Transition to INITIALIZED to get Doze going. */
+ UNINITIALIZED,
+ /** Doze components are set up. Followed by transition to DOZE or DOZE_AOD. */
+ INITIALIZED,
+ /** Regular doze. Device is asleep and listening for pulse triggers. */
+ DOZE,
+ /** Deep doze. Device is asleep and is not listening for pulse triggers. */
+ DOZE_SUSPEND_TRIGGERS,
+ /** Always-on doze. Device is asleep, showing UI and listening for pulse triggers. */
+ DOZE_AOD,
+ /** Pulse has been requested. Device is awake and preparing UI */
+ DOZE_REQUEST_PULSE,
+ /** Pulse is showing. Device is awake and showing UI. */
+ DOZE_PULSING,
+ /** Pulse is showing with bright wallpaper. Device is awake and showing UI. */
+ DOZE_PULSING_BRIGHT,
+ /** Pulse is done showing. Followed by transition to DOZE or DOZE_AOD. */
+ DOZE_PULSE_DONE,
+ /** Doze is done. DozeService is finished. */
+ FINISH,
+ /** AOD, but the display is temporarily off. */
+ DOZE_AOD_PAUSED,
+ /** AOD, prox is near, transitions to DOZE_AOD_PAUSED after a timeout. */
+ DOZE_AOD_PAUSING,
+ /** Always-on doze. Device is awake, showing docking UI and listening for pulse triggers. */
+ DOZE_AOD_DOCKED
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/DozeTransitionModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/DozeTransitionModel.kt
new file mode 100644
index 0000000..e96ace2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/DozeTransitionModel.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.keyguard.shared.model
+
+/** Doze transition information. */
+data class DozeTransitionModel(
+ val from: DozeStateModel = DozeStateModel.UNINITIALIZED,
+ val to: DozeStateModel = DozeStateModel.UNINITIALIZED,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index 7739a45..3c927ee 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -32,7 +32,6 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
import kotlinx.coroutines.awaitCancellation
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
@@ -182,6 +181,7 @@
launch {
viewModel.bouncerShowMessage.collect {
hostViewController.showMessage(it.message, it.colorStateList)
+ viewModel.onMessageShown()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
index 526ae74..503c8ba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
@@ -86,6 +86,11 @@
interactor.notifyKeyguardAuthenticatedHandled()
}
+ /** Notifies that the message was shown. */
+ fun onMessageShown() {
+ interactor.onMessageShown()
+ }
+
/** Observe whether back button is enabled. */
fun observeOnIsBackButtonEnabled(systemUiVisibility: () -> Int): Flow<Int> {
return interactor.isBackButtonEnabled.map { enabled ->
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 691953a..cc5e256 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
@@ -56,7 +56,7 @@
* TODO(b/245610654): Re-name this to be MediaTttReceiverCoordinator.
*/
@SysUISingleton
-class MediaTttChipControllerReceiver @Inject constructor(
+open class MediaTttChipControllerReceiver @Inject constructor(
private val commandQueue: CommandQueue,
context: Context,
@MediaTttReceiverLogger logger: MediaTttLogger,
@@ -183,15 +183,28 @@
val appIconView = view.getAppIconView()
appIconView.animate()
.translationYBy(-1 * getTranslationAmount().toFloat())
- .setDuration(30.frames)
+ .setDuration(ICON_TRANSLATION_ANIM_DURATION)
.start()
appIconView.animate()
.alpha(1f)
- .setDuration(5.frames)
+ .setDuration(ICON_ALPHA_ANIM_DURATION)
.start()
// Using withEndAction{} doesn't apply a11y focus when screen is unlocked.
appIconView.postOnAnimation { view.requestAccessibilityFocus() }
- startRipple(view.requireViewById(R.id.ripple))
+ expandRipple(view.requireViewById(R.id.ripple))
+ }
+
+ override fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) {
+ val appIconView = view.getAppIconView()
+ appIconView.animate()
+ .translationYBy(getTranslationAmount().toFloat())
+ .setDuration(ICON_TRANSLATION_ANIM_DURATION)
+ .start()
+ appIconView.animate()
+ .alpha(0f)
+ .setDuration(ICON_ALPHA_ANIM_DURATION)
+ .start()
+ (view.requireViewById(R.id.ripple) as ReceiverChipRippleView).collapseRipple(onAnimationEnd)
}
override fun getTouchableRegion(view: View, outRect: Rect) {
@@ -205,11 +218,22 @@
return context.resources.getDimensionPixelSize(R.dimen.media_ttt_receiver_vert_translation)
}
- private fun startRipple(rippleView: ReceiverChipRippleView) {
+ private fun expandRipple(rippleView: ReceiverChipRippleView) {
if (rippleView.rippleInProgress()) {
// Skip if ripple is still playing
return
}
+
+ // In case the device orientation changes, we need to reset the layout.
+ rippleView.addOnLayoutChangeListener (
+ View.OnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
+ if (v == null) return@OnLayoutChangeListener
+
+ val layoutChangedRippleView = v as ReceiverChipRippleView
+ layoutRipple(layoutChangedRippleView)
+ layoutChangedRippleView.invalidate()
+ }
+ )
rippleView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
override fun onViewDetachedFromWindow(view: View?) {}
@@ -219,7 +243,7 @@
}
val attachedRippleView = view as ReceiverChipRippleView
layoutRipple(attachedRippleView)
- attachedRippleView.startRipple()
+ attachedRippleView.expandRipple()
attachedRippleView.removeOnAttachStateChangeListener(this)
}
})
@@ -242,6 +266,9 @@
}
}
+val ICON_TRANSLATION_ANIM_DURATION = 30.frames
+val ICON_ALPHA_ANIM_DURATION = 5.frames
+
data class ChipReceiverInfo(
val routeInfo: MediaRoute2Info,
val appIconDrawableOverride: Drawable?,
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
index 1ea2025..6e9fc5c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
@@ -16,6 +16,8 @@
package com.android.systemui.media.taptotransfer.receiver
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
import android.content.Context
import android.util.AttributeSet
import com.android.systemui.surfaceeffects.ripple.RippleShader
@@ -25,10 +27,36 @@
* An expanding ripple effect for the media tap-to-transfer receiver chip.
*/
class ReceiverChipRippleView(context: Context?, attrs: AttributeSet?) : RippleView(context, attrs) {
+
+ // Indicates whether the ripple started expanding.
+ private var isStarted: Boolean
+
init {
setupShader(RippleShader.RippleShape.ELLIPSE)
setRippleFill(true)
setSparkleStrength(0f)
duration = 3000L
+ isStarted = false
+ }
+
+ fun expandRipple(onAnimationEnd: Runnable? = null) {
+ isStarted = true
+ super.startRipple(onAnimationEnd)
+ }
+
+ /** Used to animate out the ripple. No-op if the ripple was never started via [startRipple]. */
+ fun collapseRipple(onAnimationEnd: Runnable? = null) {
+ if (!isStarted) {
+ return // Ignore if ripple is not started yet.
+ }
+ // Reset all listeners to animator.
+ animator.removeAllListeners()
+ animator.addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ onAnimationEnd?.run()
+ isStarted = false
+ }
+ })
+ animator.reverse()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index 2d1d8b7..d33d113 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -29,6 +29,8 @@
import android.hardware.SensorPrivacyManager.Sources.DIALOG
import android.os.Bundle
import android.os.Handler
+import android.window.OnBackInvokedDispatcher
+import androidx.annotation.OpenForTesting
import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION
import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__CANCEL
import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE
@@ -45,7 +47,8 @@
*
* <p>The dialog is started for the user the app is running for which might be a secondary users.
*/
-class SensorUseStartedActivity @Inject constructor(
+@OpenForTesting
+open class SensorUseStartedActivity @Inject constructor(
private val sensorPrivacyController: IndividualSensorPrivacyController,
private val keyguardStateController: KeyguardStateController,
private val keyguardDismissUtil: KeyguardDismissUtil,
@@ -67,9 +70,10 @@
private lateinit var sensorUsePackageName: String
private var unsuppressImmediately = false
- private lateinit var sensorPrivacyListener: IndividualSensorPrivacyController.Callback
+ private var sensorPrivacyListener: IndividualSensorPrivacyController.Callback? = null
private var mDialog: AlertDialog? = null
+ private val mBackCallback = this::onBackInvoked
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -84,15 +88,14 @@
if (intent.getBooleanExtra(EXTRA_ALL_SENSORS, false)) {
sensor = ALL_SENSORS
- sensorPrivacyListener =
- IndividualSensorPrivacyController.Callback { _, _ ->
- if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) &&
- !sensorPrivacyController.isSensorBlocked(CAMERA)) {
- finish()
- }
- }
-
- sensorPrivacyController.addCallback(sensorPrivacyListener)
+ val callback = IndividualSensorPrivacyController.Callback { _, _ ->
+ if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) &&
+ !sensorPrivacyController.isSensorBlocked(CAMERA)) {
+ finish()
+ }
+ }
+ sensorPrivacyListener = callback
+ sensorPrivacyController.addCallback(callback)
if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) &&
!sensorPrivacyController.isSensorBlocked(CAMERA)) {
finish()
@@ -105,14 +108,14 @@
return
}
}
- sensorPrivacyListener =
- IndividualSensorPrivacyController.Callback { whichSensor: Int,
- isBlocked: Boolean ->
- if (whichSensor == sensor && !isBlocked) {
- finish()
- }
- }
- sensorPrivacyController.addCallback(sensorPrivacyListener)
+ val callback = IndividualSensorPrivacyController.Callback {
+ whichSensor: Int, isBlocked: Boolean ->
+ if (whichSensor == sensor && !isBlocked) {
+ finish()
+ }
+ }
+ sensorPrivacyListener = callback
+ sensorPrivacyController.addCallback(callback)
if (!sensorPrivacyController.isSensorBlocked(sensor)) {
finish()
@@ -122,6 +125,10 @@
mDialog = SensorUseDialog(this, sensor, this, this)
mDialog!!.show()
+
+ onBackInvokedDispatcher.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ mBackCallback)
}
override fun onStart() {
@@ -180,10 +187,15 @@
override fun onDestroy() {
super.onDestroy()
mDialog?.dismiss()
- sensorPrivacyController.removeCallback(sensorPrivacyListener)
+ sensorPrivacyListener?.also { sensorPrivacyController.removeCallback(it) }
+ onBackInvokedDispatcher.unregisterOnBackInvokedCallback(mBackCallback)
}
override fun onBackPressed() {
+ onBackInvoked()
+ }
+
+ fun onBackInvoked() {
// do not allow backing out
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 63d0d16..31e4464 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -331,13 +331,8 @@
// Use resources.getXml instead of passing the resource id due to bug b/205018300
header.getConstraintSet(QQS_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.qqs_header))
- val qsConstraints = if (featureFlags.isEnabled(Flags.NEW_HEADER)) {
- R.xml.qs_header_new
- } else {
- R.xml.qs_header
- }
header.getConstraintSet(QS_HEADER_CONSTRAINT)
- .load(context, resources.getXml(qsConstraints))
+ .load(context, resources.getXml(R.xml.qs_header))
header.getConstraintSet(LARGE_SCREEN_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.large_screen_shade_header))
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 92dc459..5f5055b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -279,6 +279,11 @@
private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek";
private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1);
private static final Rect EMPTY_RECT = new Rect();
+ /**
+ * Duration to use for the animator when the keyguard status view alignment changes, and a
+ * custom clock animation is in use.
+ */
+ private static final int KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION = 1000;
private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
private final Resources mResources;
@@ -675,7 +680,7 @@
};
private final Runnable mMaybeHideExpandedRunnable = () -> {
if (getExpansionFraction() == 0.0f) {
- getView().post(mHideExpandedRunnable);
+ postToView(mHideExpandedRunnable);
}
};
@@ -1592,7 +1597,7 @@
// Use linear here, so the actual clock can pick its own interpolator.
adapter.setInterpolator(Interpolators.LINEAR);
- adapter.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ adapter.setDuration(KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION);
adapter.addTarget(clockView);
set.addTransition(adapter);
@@ -2810,7 +2815,7 @@
return top + mNotificationStackScrollLayoutController.getHeight()
+ mSplitShadeNotificationsScrimMarginBottom;
} else {
- return getView().getBottom();
+ return mView.getBottom();
}
}
@@ -2825,7 +2830,7 @@
private int calculateRightQsClippingBound() {
if (mIsFullWidth) {
- return getView().getRight() + mDisplayRightInset;
+ return mView.getRight() + mDisplayRightInset;
} else {
return mNotificationStackScrollLayoutController.getRight();
}
@@ -5190,6 +5195,26 @@
return mView;
}
+ /** */
+ public boolean postToView(Runnable action) {
+ return mView.post(action);
+ }
+
+ /** */
+ public boolean sendInterceptTouchEventToView(MotionEvent event) {
+ return mView.onInterceptTouchEvent(event);
+ }
+
+ /** */
+ public void requestLayoutOnView() {
+ mView.requestLayout();
+ }
+
+ /** */
+ public void resetViewAlphas() {
+ ViewGroupFadeHelper.reset(mView);
+ }
+
private void beginJankMonitoring() {
if (mInteractionJankMonitor == null) {
return;
@@ -5499,8 +5524,11 @@
if (!animatingUnlockedShadeToKeyguard) {
// Only make the status bar visible if we're not animating the screen off, since
// we only want to be showing the clock/notifications during the animation.
- mShadeLog.v("Updating keyguard status bar state to "
- + (keyguardShowing ? "visible" : "invisible"));
+ if (keyguardShowing) {
+ mShadeLog.v("Updating keyguard status bar state to visible");
+ } else {
+ mShadeLog.v("Updating keyguard status bar state to invisible");
+ }
mKeyguardStatusBarViewController.updateViewState(
/* alpha= */ 1f,
keyguardShowing ? View.VISIBLE : View.INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 8379e51..d773c01 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -316,7 +316,7 @@
MotionEvent cancellation = MotionEvent.obtain(ev);
cancellation.setAction(MotionEvent.ACTION_CANCEL);
mStackScrollLayout.onInterceptTouchEvent(cancellation);
- mNotificationPanelViewController.getView().onInterceptTouchEvent(cancellation);
+ mNotificationPanelViewController.sendInterceptTouchEventToView(cancellation);
cancellation.recycle();
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index eaf7fae..d783293 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -160,7 +160,7 @@
if (getCentralSurfaces().getNotificationShadeWindowView()
.isVisibleToUser()) {
getNotificationPanelViewController().removeOnGlobalLayoutListener(this);
- getNotificationPanelViewController().getView().post(executable);
+ getNotificationPanelViewController().postToView(executable);
}
}
});
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 41dbf1d..962eeb9 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
@@ -3733,7 +3733,7 @@
}
}
- private void debugLog(@CompileTimeConstant String s) {
+ private void debugLog(@CompileTimeConstant final String s) {
if (mLogger == null) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index f72f1bc..182d397 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -763,6 +763,15 @@
}
@Override
+ public void onKeyguardBouncerStateChanged(boolean bouncerIsOrWillBeShowing) {
+ // When the bouncer is dismissed, treat this as a reset of the unlock mode. The user
+ // may have gone back instead of successfully unlocking
+ if (!bouncerIsOrWillBeShowing) {
+ resetMode();
+ }
+ }
+
+ @Override
public void dump(PrintWriter pw, String[] args) {
pw.println(" BiometricUnlockController:");
pw.print(" mMode="); pw.println(mMode);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
index 5512bed..3d6bebb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
@@ -72,9 +72,9 @@
//resize the layout. Let's
// make sure that the window stays small for one frame until the
//touchableRegion is set.
- mNotificationPanelViewController.getView().requestLayout();
+ mNotificationPanelViewController.requestLayoutOnView();
mNotificationShadeWindowController.setForceWindowCollapsed(true);
- mNotificationPanelViewController.getView().post(() -> {
+ mNotificationPanelViewController.postToView(() -> {
mNotificationShadeWindowController.setForceWindowCollapsed(false);
});
}
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 01a1ebe..fcf33b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -20,6 +20,7 @@
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_DISMISS_BOUNCER;
+import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_SHOW_BOUNCER;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_COLLAPSING;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
@@ -77,7 +78,6 @@
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
import com.android.systemui.statusbar.phone.KeyguardBouncer.PrimaryBouncerExpansionCallback;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -218,7 +218,7 @@
protected LockPatternUtils mLockPatternUtils;
protected ViewMediatorCallback mViewMediatorCallback;
- protected CentralSurfaces mCentralSurfaces;
+ @Nullable protected CentralSurfaces mCentralSurfaces;
private NotificationPanelViewController mNotificationPanelViewController;
private BiometricUnlockController mBiometricUnlockController;
private boolean mCentralSurfacesRegistered;
@@ -266,7 +266,7 @@
private final KeyguardUpdateMonitor mKeyguardUpdateManager;
private final LatencyTracker mLatencyTracker;
private final KeyguardSecurityModel mKeyguardSecurityModel;
- private KeyguardBypassController mBypassController;
+ @Nullable private KeyguardBypassController mBypassController;
@Nullable private AlternateBouncer mAlternateBouncer;
private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
@@ -478,6 +478,7 @@
} else if (mKeyguardStateController.isShowing() && !hideBouncerOverDream) {
if (!isWakeAndUnlocking()
&& !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER)
+ && !(mBiometricUnlockController.getMode() == MODE_SHOW_BOUNCER)
&& !isUnlockCollapsing()) {
if (mPrimaryBouncer != null) {
mPrimaryBouncer.setExpansion(fraction);
@@ -742,6 +743,12 @@
}
private void updateAlternateBouncerShowing(boolean updateScrim) {
+ if (!mCentralSurfacesRegistered) {
+ // if CentralSurfaces hasn't been registered yet, then the controllers below haven't
+ // been initialized yet so there's no need to attempt to forward them events.
+ return;
+ }
+
final boolean isShowingAlternateBouncer = isShowingAlternateBouncer();
if (mKeyguardMessageAreaController != null) {
mKeyguardMessageAreaController.setIsVisible(isShowingAlternateBouncer);
@@ -1009,7 +1016,7 @@
public void onKeyguardFadedAway() {
mNotificationContainer.postDelayed(() -> mNotificationShadeWindowController
.setKeyguardFadingAway(false), 100);
- ViewGroupFadeHelper.reset(mNotificationPanelViewController.getView());
+ mNotificationPanelViewController.resetViewAlphas();
mCentralSurfaces.finishKeyguardFadingAway();
mBiometricUnlockController.finishKeyguardFadingAway();
WindowManagerGlobal.getInstance().trimMemory(
diff --git a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
index 2ad8243..ae28a8b 100644
--- a/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/surfaceeffects/ripple/RippleView.kt
@@ -41,7 +41,7 @@
private set
private val ripplePaint = Paint()
- private val animator = ValueAnimator.ofFloat(0f, 1f)
+ protected val animator: ValueAnimator = ValueAnimator.ofFloat(0f, 1f)
var duration: Long = 1750
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index 0f06144..6216acd 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -92,6 +92,7 @@
deviceStateManager.registerCallback(executor, FoldListener())
wakefulnessLifecycle.addObserver(this)
+ // TODO(b/254878364): remove this call to NPVC.getView()
centralSurfaces.notificationPanelViewController.view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) { listenForDozing(this) }
}
@@ -157,6 +158,7 @@
// 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)
+ // TODO(b/254878364): remove this call to NPVC.getView()
OneShotPreDrawListener.add(
centralSurfaces.notificationPanelViewController.view,
onReady
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 6ed3a09..d411e34 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -15,7 +15,7 @@
*/
package com.android.systemui.unfold
-import android.animation.ValueAnimator
+import android.content.ContentResolver
import android.content.Context
import android.graphics.PixelFormat
import android.hardware.devicestate.DeviceStateManager
@@ -39,6 +39,7 @@
import com.android.systemui.statusbar.LinearLightRevealEffect
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.updates.RotationChangeProvider
+import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider.Companion.areAnimationsEnabled
import com.android.systemui.util.traceSection
import com.android.wm.shell.displayareahelper.DisplayAreaHelper
import java.util.Optional
@@ -52,6 +53,7 @@
constructor(
private val context: Context,
private val deviceStateManager: DeviceStateManager,
+ private val contentResolver: ContentResolver,
private val displayManager: DisplayManager,
private val unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
private val displayAreaHelper: Optional<DisplayAreaHelper>,
@@ -117,7 +119,7 @@
Trace.beginSection("UnfoldLightRevealOverlayAnimation#onScreenTurningOn")
try {
// Add the view only if we are unfolding and this is the first screen on
- if (!isFolded && !isUnfoldHandled && ValueAnimator.areAnimatorsEnabled()) {
+ if (!isFolded && !isUnfoldHandled && contentResolver.areAnimationsEnabled()) {
addView(onOverlayReady)
isUnfoldHandled = true
} else {
@@ -162,11 +164,10 @@
// blocker (turn on the brightness) only when the content is actually visible as it
// might be presented only in the next frame.
// See b/197538198
- transaction
- .setFrameTimelineVsync(vsyncId)
- .apply()
+ transaction.setFrameTimelineVsync(vsyncId).apply()
- transaction.setFrameTimelineVsync(vsyncId + 1)
+ transaction
+ .setFrameTimelineVsync(vsyncId + 1)
.addTransactionCommittedListener(backgroundExecutor) {
Trace.endAsyncSection("UnfoldLightRevealOverlayAnimation#relayout", 0)
callback.run()
@@ -218,8 +219,7 @@
}
private fun getUnfoldedDisplayInfo(): DisplayInfo =
- displayManager
- .displays
+ displayManager.displays
.asSequence()
.map { DisplayInfo().apply { it.getDisplayInfo(this) } }
.filter { it.type == Display.TYPE_INTERNAL }
@@ -266,5 +266,6 @@
isUnfoldHandled = false
}
this.isFolded = isFolded
- })
+ }
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index 83f0711..0e4f224 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -117,7 +117,7 @@
private val callbacks = mutableSetOf<UserCallback>()
private val userInfos =
combine(repository.userSwitcherSettings, repository.userInfos) { settings, userInfos ->
- userInfos.filter { !it.isGuest || canCreateGuestUser(settings) }
+ userInfos.filter { !it.isGuest || canCreateGuestUser(settings) }.filter { it.isFull }
}
/** List of current on-device users to select from. */
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/CombinedCondition.kt b/packages/SystemUI/src/com/android/systemui/util/condition/CombinedCondition.kt
new file mode 100644
index 0000000..da81d54
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/CombinedCondition.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.condition
+
+/**
+ * A higher order [Condition] which combines multiple conditions with a specified
+ * [Evaluator.ConditionOperand].
+ */
+internal class CombinedCondition
+constructor(
+ private val conditions: Collection<Condition>,
+ @Evaluator.ConditionOperand private val operand: Int
+) : Condition(null, false), Condition.Callback {
+
+ override fun start() {
+ onConditionChanged(this)
+ conditions.forEach { it.addCallback(this) }
+ }
+
+ override fun onConditionChanged(condition: Condition) {
+ Evaluator.evaluate(conditions, operand)?.also { value -> updateCondition(value) }
+ ?: clearCondition()
+ }
+
+ override fun stop() {
+ conditions.forEach { it.removeCallback(this) }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Condition.java b/packages/SystemUI/src/com/android/systemui/util/condition/Condition.java
index 2c317dd..b39adef 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/Condition.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/Condition.java
@@ -24,7 +24,10 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Iterator;
+import java.util.List;
/**
* Base class for a condition that needs to be fulfilled in order for {@link Monitor} to inform
@@ -181,6 +184,42 @@
}
/**
+ * Creates a new condition which will only be true when both this condition and all the provided
+ * conditions are true.
+ */
+ public Condition and(Collection<Condition> others) {
+ final List<Condition> conditions = new ArrayList<>(others);
+ conditions.add(this);
+ return new CombinedCondition(conditions, Evaluator.OP_AND);
+ }
+
+ /**
+ * Creates a new condition which will only be true when both this condition and the provided
+ * condition is true.
+ */
+ public Condition and(Condition other) {
+ return new CombinedCondition(Arrays.asList(this, other), Evaluator.OP_AND);
+ }
+
+ /**
+ * Creates a new condition which will only be true when either this condition or any of the
+ * provided conditions are true.
+ */
+ public Condition or(Collection<Condition> others) {
+ final List<Condition> conditions = new ArrayList<>(others);
+ conditions.add(this);
+ return new CombinedCondition(conditions, Evaluator.OP_OR);
+ }
+
+ /**
+ * Creates a new condition which will only be true when either this condition or the provided
+ * condition is true.
+ */
+ public Condition or(Condition other) {
+ return new CombinedCondition(Arrays.asList(this, other), Evaluator.OP_OR);
+ }
+
+ /**
* Callback that receives updates about whether the condition has been fulfilled.
*/
public interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Evaluator.kt b/packages/SystemUI/src/com/android/systemui/util/condition/Evaluator.kt
new file mode 100644
index 0000000..cf44e84
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/Evaluator.kt
@@ -0,0 +1,92 @@
+package com.android.systemui.util.condition
+
+import android.annotation.IntDef
+
+/**
+ * Helper for evaluating a collection of [Condition] objects with a given
+ * [Evaluator.ConditionOperand]
+ */
+internal object Evaluator {
+ /** Operands for combining multiple conditions together */
+ @Retention(AnnotationRetention.SOURCE)
+ @IntDef(value = [OP_AND, OP_OR])
+ annotation class ConditionOperand
+
+ /**
+ * 3-valued logical AND operand, with handling for unknown values (represented as null)
+ *
+ * ```
+ * +-----+----+---+---+
+ * | AND | T | F | U |
+ * +-----+----+---+---+
+ * | T | T | F | U |
+ * | F | F | F | F |
+ * | U | U | F | U |
+ * +-----+----+---+---+
+ * ```
+ */
+ const val OP_AND = 0
+
+ /**
+ * 3-valued logical OR operand, with handling for unknown values (represented as null)
+ *
+ * ```
+ * +-----+----+---+---+
+ * | OR | T | F | U |
+ * +-----+----+---+---+
+ * | T | T | T | T |
+ * | F | T | F | U |
+ * | U | T | U | U |
+ * +-----+----+---+---+
+ * ```
+ */
+ const val OP_OR = 1
+
+ /**
+ * Evaluates a set of conditions with a given operand
+ *
+ * If overriding conditions are present, they take precedence over normal conditions if set.
+ *
+ * @param conditions The collection of conditions to evaluate. If empty, null is returned.
+ * @param operand The operand to use when evaluating.
+ * @return Either true or false if the value is known, or null if value is unknown
+ */
+ fun evaluate(conditions: Collection<Condition>, @ConditionOperand operand: Int): Boolean? {
+ if (conditions.isEmpty()) return null
+ // If there are overriding conditions with values set, they take precedence.
+ val targetConditions =
+ conditions
+ .filter { it.isConditionSet && it.isOverridingCondition }
+ .ifEmpty { conditions }
+ return when (operand) {
+ OP_AND ->
+ threeValuedAndOrOr(conditions = targetConditions, returnValueIfAnyMatches = false)
+ OP_OR ->
+ threeValuedAndOrOr(conditions = targetConditions, returnValueIfAnyMatches = true)
+ else -> null
+ }
+ }
+
+ /**
+ * Helper for evaluating 3-valued logical AND/OR.
+ *
+ * @param returnValueIfAnyMatches AND returns false if any value is false. OR returns true if
+ * any value is true.
+ */
+ private fun threeValuedAndOrOr(
+ conditions: Collection<Condition>,
+ returnValueIfAnyMatches: Boolean
+ ): Boolean? {
+ var hasUnknown = false
+ for (condition in conditions) {
+ if (!condition.isConditionSet) {
+ hasUnknown = true
+ continue
+ }
+ if (condition.isConditionMet == returnValueIfAnyMatches) {
+ return returnValueIfAnyMatches
+ }
+ }
+ return if (hasUnknown) null else !returnValueIfAnyMatches
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
index cb430ba..24bc907 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
@@ -24,12 +24,10 @@
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.Executor;
-import java.util.stream.Collectors;
import javax.inject.Inject;
@@ -57,21 +55,10 @@
}
public void update() {
- // Only consider set conditions.
- final Collection<Condition> setConditions = mSubscription.mConditions.stream()
- .filter(Condition::isConditionSet).collect(Collectors.toSet());
-
- // Overriding conditions do not override each other
- final Collection<Condition> overridingConditions = setConditions.stream()
- .filter(Condition::isOverridingCondition).collect(Collectors.toSet());
-
- final Collection<Condition> targetCollection = overridingConditions.isEmpty()
- ? setConditions : overridingConditions;
-
- final boolean newAllConditionsMet = targetCollection.isEmpty() ? true : targetCollection
- .stream()
- .map(Condition::isConditionMet)
- .allMatch(conditionMet -> conditionMet);
+ final Boolean result = Evaluator.INSTANCE.evaluate(mSubscription.mConditions,
+ Evaluator.OP_AND);
+ // Consider unknown (null) as true
+ final boolean newAllConditionsMet = result == null || result;
if (mAllConditionsMet != null && newAllConditionsMet == mAllConditionsMet) {
return;
@@ -109,6 +96,7 @@
/**
* Registers a callback and the set of conditions to trigger it.
+ *
* @param subscription A {@link Subscription} detailing the desired conditions and callback.
* @return A {@link Subscription.Token} that can be used to remove the subscription.
*/
@@ -139,6 +127,7 @@
/**
* Removes a subscription from participating in future callbacks.
+ *
* @param token The {@link Subscription.Token} returned when the {@link Subscription} was
* originally added.
*/
@@ -179,7 +168,9 @@
private final Set<Condition> mConditions;
private final Callback mCallback;
- /** */
+ /**
+ *
+ */
public Subscription(Set<Condition> conditions, Callback callback) {
this.mConditions = Collections.unmodifiableSet(conditions);
this.mCallback = callback;
@@ -209,7 +200,6 @@
/**
* Default constructor specifying the {@link Callback} for the {@link Subscription}.
- * @param callback
*/
public Builder(Callback callback) {
mCallback = callback;
@@ -218,7 +208,7 @@
/**
* Adds a {@link Condition} to be associated with the {@link Subscription}.
- * @param condition
+ *
* @return The updated {@link Builder}.
*/
public Builder addCondition(Condition condition) {
@@ -228,7 +218,7 @@
/**
* Adds a set of {@link Condition} to be associated with the {@link Subscription}.
- * @param condition
+ *
* @return The updated {@link Builder}.
*/
public Builder addConditions(Set<Condition> condition) {
@@ -238,6 +228,7 @@
/**
* Builds the {@link Subscription}.
+ *
* @return The resulting {@link Subscription}.
*/
public Subscription build() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 0d96272..63b98bb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -72,6 +72,7 @@
import android.os.SystemClock;
import android.os.Trace;
import android.os.VibrationEffect;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.text.InputFilter;
@@ -108,6 +109,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.view.RotationPolicy;
@@ -125,11 +128,15 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.AlphaTintDrawableWrapper;
+import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.RoundedCornerProgressDrawable;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -186,6 +193,9 @@
private ViewGroup mDialogRowsView;
private ViewGroup mRinger;
+ private DeviceConfigProxy mDeviceConfigProxy;
+ private Executor mExecutor;
+
/**
* Container for the top part of the dialog, which contains the ringer, the ringer drawer, the
* volume rows, and the ellipsis button. This does not include the live caption button.
@@ -274,6 +284,13 @@
private BackgroundBlurDrawable mDialogRowsViewBackground;
private final InteractionJankMonitor mInteractionJankMonitor;
+ private boolean mSeparateNotification;
+
+ @VisibleForTesting
+ int mVolumeRingerIconDrawableId;
+ @VisibleForTesting
+ int mVolumeRingerMuteIconDrawableId;
+
public VolumeDialogImpl(
Context context,
VolumeDialogController volumeDialogController,
@@ -283,7 +300,9 @@
MediaOutputDialogFactory mediaOutputDialogFactory,
VolumePanelFactory volumePanelFactory,
ActivityStarter activityStarter,
- InteractionJankMonitor interactionJankMonitor) {
+ InteractionJankMonitor interactionJankMonitor,
+ DeviceConfigProxy deviceConfigProxy,
+ Executor executor) {
mContext =
new ContextThemeWrapper(context, R.style.volume_dialog_theme);
mController = volumeDialogController;
@@ -323,6 +342,50 @@
}
initDimens();
+
+ mDeviceConfigProxy = deviceConfigProxy;
+ mExecutor = executor;
+ mSeparateNotification = mDeviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false);
+ updateRingerModeIconSet();
+ }
+
+ /**
+ * If ringer and notification are the same stream (T and earlier), use notification-like bell
+ * icon set.
+ * If ringer and notification are separated, then use generic speaker icons.
+ */
+ private void updateRingerModeIconSet() {
+ if (mSeparateNotification) {
+ mVolumeRingerIconDrawableId = R.drawable.ic_speaker_on;
+ mVolumeRingerMuteIconDrawableId = R.drawable.ic_speaker_mute;
+ } else {
+ mVolumeRingerIconDrawableId = R.drawable.ic_volume_ringer;
+ mVolumeRingerMuteIconDrawableId = R.drawable.ic_volume_ringer_mute;
+ }
+
+ if (mRingerDrawerMuteIcon != null) {
+ mRingerDrawerMuteIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
+ }
+ if (mRingerDrawerNormalIcon != null) {
+ mRingerDrawerNormalIcon.setImageResource(mVolumeRingerIconDrawableId);
+ }
+ }
+
+ /**
+ * Change icon for ring stream (not ringer mode icon)
+ */
+ private void updateRingRowIcon() {
+ Optional<VolumeRow> volumeRow = mRows.stream().filter(row -> row.stream == STREAM_RING)
+ .findFirst();
+ if (volumeRow.isPresent()) {
+ VolumeRow volRow = volumeRow.get();
+ volRow.iconRes = mSeparateNotification ? R.drawable.ic_ring_volume
+ : R.drawable.ic_volume_ringer;
+ volRow.iconMuteRes = mSeparateNotification ? R.drawable.ic_ring_volume_off
+ : R.drawable.ic_volume_ringer_mute;
+ volRow.setIcon(volRow.iconRes, mContext.getTheme());
+ }
}
@Override
@@ -339,6 +402,9 @@
mController.getState();
mConfigurationController.addCallback(this);
+
+ mDeviceConfigProxy.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ mExecutor, this::onDeviceConfigChange);
}
@Override
@@ -346,6 +412,24 @@
mController.removeCallback(mControllerCallbackH);
mHandler.removeCallbacksAndMessages(null);
mConfigurationController.removeCallback(this);
+ mDeviceConfigProxy.removeOnPropertiesChangedListener(this::onDeviceConfigChange);
+ }
+
+ /**
+ * Update ringer mode icon based on the config
+ */
+ private void onDeviceConfigChange(DeviceConfig.Properties properties) {
+ Set<String> changeSet = properties.getKeyset();
+ if (changeSet.contains(SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION)) {
+ boolean newVal = properties.getBoolean(
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false);
+ if (newVal != mSeparateNotification) {
+ mSeparateNotification = newVal;
+ updateRingerModeIconSet();
+ updateRingRowIcon();
+
+ }
+ }
}
@Override
@@ -552,6 +636,9 @@
mRingerDrawerNormalIcon = mDialog.findViewById(R.id.volume_drawer_normal_icon);
mRingerDrawerNewSelectionBg = mDialog.findViewById(R.id.volume_drawer_selection_background);
+ mRingerDrawerMuteIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
+ mRingerDrawerNormalIcon.setImageResource(mVolumeRingerIconDrawableId);
+
setupRingerDrawer();
mODICaptionsView = mDialog.findViewById(R.id.odi_captions);
@@ -575,8 +662,14 @@
addRow(AudioManager.STREAM_MUSIC,
R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true);
if (!AudioSystem.isSingleVolume(mContext)) {
- addRow(AudioManager.STREAM_RING,
- R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true, false);
+ if (mSeparateNotification) {
+ addRow(AudioManager.STREAM_RING, R.drawable.ic_ring_volume,
+ R.drawable.ic_ring_volume_off, true, false);
+ } else {
+ addRow(AudioManager.STREAM_RING, R.drawable.ic_volume_ringer,
+ R.drawable.ic_volume_ringer, true, false);
+ }
+
addRow(STREAM_ALARM,
R.drawable.ic_alarm, R.drawable.ic_volume_alarm_mute, true, false);
addRow(AudioManager.STREAM_VOICE_CALL,
@@ -1532,8 +1625,8 @@
mRingerIcon.setTag(Events.ICON_STATE_VIBRATE);
break;
case AudioManager.RINGER_MODE_SILENT:
- mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
- mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
+ mRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
+ mSelectedRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
mRingerIcon.setTag(Events.ICON_STATE_MUTE);
addAccessibilityDescription(mRingerIcon, RINGER_MODE_SILENT,
mContext.getString(R.string.volume_ringer_hint_unmute));
@@ -1542,14 +1635,14 @@
default:
boolean muted = (mAutomute && ss.level == 0) || ss.muted;
if (!isZenMuted && muted) {
- mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
- mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
+ mRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
+ mSelectedRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL,
mContext.getString(R.string.volume_ringer_hint_unmute));
mRingerIcon.setTag(Events.ICON_STATE_MUTE);
} else {
- mRingerIcon.setImageResource(R.drawable.ic_volume_ringer);
- mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer);
+ mRingerIcon.setImageResource(mVolumeRingerIconDrawableId);
+ mSelectedRingerIcon.setImageResource(mVolumeRingerIconDrawableId);
if (mController.hasVibrator()) {
addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL,
mContext.getString(R.string.volume_ringer_hint_vibrate));
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index c5792b9..8f10fa6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -20,6 +20,7 @@
import android.media.AudioManager;
import com.android.internal.jank.InteractionJankMonitor;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.VolumeDialog;
@@ -27,11 +28,14 @@
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.volume.VolumeDialogComponent;
import com.android.systemui.volume.VolumeDialogImpl;
import com.android.systemui.volume.VolumePanelFactory;
+import java.util.concurrent.Executor;
+
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -55,7 +59,9 @@
MediaOutputDialogFactory mediaOutputDialogFactory,
VolumePanelFactory volumePanelFactory,
ActivityStarter activityStarter,
- InteractionJankMonitor interactionJankMonitor) {
+ InteractionJankMonitor interactionJankMonitor,
+ DeviceConfigProxy deviceConfigProxy,
+ @Main Executor executor) {
VolumeDialogImpl impl = new VolumeDialogImpl(
context,
volumeDialogController,
@@ -65,7 +71,9 @@
mediaOutputDialogFactory,
volumePanelFactory,
activityStarter,
- interactionJankMonitor);
+ interactionJankMonitor,
+ deviceConfigProxy,
+ executor);
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
impl.setAutomute(true);
impl.setSilentMode(false);
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 78c28ea..d8331ab 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -127,6 +127,12 @@
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true" />
+ <activity android:name=".sensorprivacy.SensorUseStartedActivityTest$SensorUseStartedActivityTestable"
+ android:exported="false"
+ android:theme="@style/Theme.SystemUI.Dialog.Alert"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true" />
+
<provider
android:name="androidx.startup.InitializationProvider"
tools:replace="android:authorities"
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java
index 0a9c745..ffedb30 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AuthKeyguardMessageAreaTest.java
@@ -46,7 +46,7 @@
@Test
public void testShowsTextField() {
mKeyguardMessageArea.setVisibility(View.INVISIBLE);
- mKeyguardMessageArea.setMessage("oobleck");
+ mKeyguardMessageArea.setMessage("oobleck", /* animate= */ true);
assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mKeyguardMessageArea.getText()).isEqualTo("oobleck");
}
@@ -55,7 +55,7 @@
public void testHiddenWhenBouncerHidden() {
mKeyguardMessageArea.setIsVisible(false);
mKeyguardMessageArea.setVisibility(View.INVISIBLE);
- mKeyguardMessageArea.setMessage("oobleck");
+ mKeyguardMessageArea.setMessage("oobleck", /* animate= */ true);
assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.INVISIBLE);
assertThat(mKeyguardMessageArea.getText()).isEqualTo("oobleck");
}
@@ -63,7 +63,7 @@
@Test
public void testClearsTextField() {
mKeyguardMessageArea.setVisibility(View.VISIBLE);
- mKeyguardMessageArea.setMessage("");
+ mKeyguardMessageArea.setMessage("", /* animate= */ true);
assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.INVISIBLE);
assertThat(mKeyguardMessageArea.getText()).isEqualTo("");
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/BouncerKeyguardMessageAreaTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/BouncerKeyguardMessageAreaTest.kt
index 7b9b39f..ba46a87 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/BouncerKeyguardMessageAreaTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/BouncerKeyguardMessageAreaTest.kt
@@ -49,30 +49,30 @@
@Test
fun testSetSameMessage() {
val underTestSpy = spy(underTest)
- underTestSpy.setMessage("abc")
- underTestSpy.setMessage("abc")
+ underTestSpy.setMessage("abc", animate = true)
+ underTestSpy.setMessage("abc", animate = true)
verify(underTestSpy, times(1)).text = "abc"
}
@Test
fun testSetDifferentMessage() {
- underTest.setMessage("abc")
- underTest.setMessage("def")
+ underTest.setMessage("abc", animate = true)
+ underTest.setMessage("def", animate = true)
assertThat(underTest.text).isEqualTo("def")
}
@Test
fun testSetNullMessage() {
- underTest.setMessage(null)
+ underTest.setMessage(null, animate = true)
assertThat(underTest.text).isEqualTo("")
}
@Test
fun testSetNullClearsPreviousMessage() {
- underTest.setMessage("something not null")
+ underTest.setMessage("something not null", animate = true)
assertThat(underTest.text).isEqualTo("something not null")
- underTest.setMessage(null)
+ underTest.setMessage(null, animate = true)
assertThat(underTest.text).isEqualTo("")
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
index 8290084..0e837d2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
@@ -85,7 +85,7 @@
@Test
public void testClearsTextField() {
mMessageAreaController.setMessage("");
- verify(mKeyguardMessageArea).setMessage("");
+ verify(mKeyguardMessageArea).setMessage("", /* animate= */ true);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index eaef159..5e6acd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -111,6 +111,21 @@
}
@Test
+ fun testCredentialPasswordDismissesOnBack() {
+ val container = initializeCredentialPasswordContainer(addToView = true)
+ assertThat(container.parent).isNotNull()
+ val root = container.rootView
+
+ // Simulate back invocation
+ container.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK))
+ container.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK))
+ waitForIdleSync()
+
+ assertThat(container.parent).isNull()
+ assertThat(root.isAttachedToWindow).isFalse()
+ }
+
+ @Test
fun testIgnoresAnimatedInWhenDismissed() {
val container = initializeFingerprintContainer(addToView = false)
container.dismissFromSystemServer()
@@ -355,20 +370,7 @@
@Test
fun testCredentialUI_disablesClickingOnBackground() {
- whenever(userManager.getCredentialOwnerProfile(anyInt())).thenReturn(20)
- whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(20))).thenReturn(
- DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
- )
-
- // In the credential view, clicking on the background (to cancel authentication) is not
- // valid. Thus, the listener should be null, and it should not be in the accessibility
- // hierarchy.
- val container = initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
- )
- waitForIdleSync()
-
- assertThat(container.hasCredentialPasswordView()).isTrue()
+ val container = initializeCredentialPasswordContainer()
assertThat(container.hasBiometricPrompt()).isFalse()
assertThat(
container.findViewById<View>(R.id.background)?.isImportantForAccessibility
@@ -428,6 +430,27 @@
verify(callback).onTryAgainPressed(authContainer?.requestId ?: 0L)
}
+ private fun initializeCredentialPasswordContainer(
+ addToView: Boolean = true,
+ ): TestAuthContainerView {
+ whenever(userManager.getCredentialOwnerProfile(anyInt())).thenReturn(20)
+ whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(20))).thenReturn(
+ DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
+ )
+
+ // In the credential view, clicking on the background (to cancel authentication) is not
+ // valid. Thus, the listener should be null, and it should not be in the accessibility
+ // hierarchy.
+ val container = initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
+ addToView = addToView,
+ )
+ waitForIdleSync()
+
+ assertThat(container.hasCredentialPasswordView()).isTrue()
+ return container
+ }
+
private fun initializeFingerprintContainer(
authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
addToView: Boolean = true
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index a872e4b..d6e621f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -25,6 +25,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.animation.Animator;
@@ -171,7 +172,7 @@
mCallbacks.onShareButtonTapped();
- verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_SHARE_TAPPED);
+ verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_SHARE_TAPPED, 0, "");
verify(mClipboardOverlayView, times(1)).getExitAnimation();
}
@@ -181,7 +182,7 @@
mCallbacks.onDismissButtonTapped();
- verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
+ verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED, 0, "");
verify(mClipboardOverlayView, times(1)).getExitAnimation();
}
@@ -192,7 +193,7 @@
mCallbacks.onSwipeDismissInitiated(mAnimator);
mCallbacks.onDismissButtonTapped();
- verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_SWIPE_DISMISSED);
+ verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_SWIPE_DISMISSED, 0, null);
verify(mUiEventLogger, never()).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
}
@@ -224,4 +225,16 @@
verify(mTimeoutHandler).resetTimeout();
}
+
+ @Test
+ public void test_logsUseLastClipSource() {
+ mOverlayController.setClipData(mSampleClipData, "first.package");
+ mCallbacks.onDismissButtonTapped();
+ mOverlayController.setClipData(mSampleClipData, "second.package");
+ mCallbacks.onDismissButtonTapped();
+
+ verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED, 0, "first.package");
+ verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED, 0, "second.package");
+ verifyNoMoreInteractions(mUiEventLogger);
+ }
}
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 6ba0634..13fc9fc 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
@@ -22,18 +22,25 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Position
import com.android.systemui.doze.DozeHost
+import com.android.systemui.doze.DozeMachine
+import com.android.systemui.doze.DozeTransitionCallback
+import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -52,6 +59,7 @@
@Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
+ @Mock private lateinit var dozeTransitionListener: DozeTransitionListener
private lateinit var underTest: KeyguardRepositoryImpl
@@ -67,272 +75,349 @@
biometricUnlockController,
keyguardStateController,
keyguardUpdateMonitor,
+ dozeTransitionListener,
)
}
@Test
- fun animateBottomAreaDozingTransitions() = runBlockingTest {
- assertThat(underTest.animateBottomAreaDozingTransitions.value).isEqualTo(false)
+ fun animateBottomAreaDozingTransitions() =
+ runTest(UnconfinedTestDispatcher()) {
+ assertThat(underTest.animateBottomAreaDozingTransitions.value).isEqualTo(false)
- underTest.setAnimateDozingTransitions(true)
- assertThat(underTest.animateBottomAreaDozingTransitions.value).isTrue()
+ underTest.setAnimateDozingTransitions(true)
+ assertThat(underTest.animateBottomAreaDozingTransitions.value).isTrue()
- underTest.setAnimateDozingTransitions(false)
- assertThat(underTest.animateBottomAreaDozingTransitions.value).isFalse()
+ underTest.setAnimateDozingTransitions(false)
+ assertThat(underTest.animateBottomAreaDozingTransitions.value).isFalse()
- underTest.setAnimateDozingTransitions(true)
- assertThat(underTest.animateBottomAreaDozingTransitions.value).isTrue()
- }
+ underTest.setAnimateDozingTransitions(true)
+ assertThat(underTest.animateBottomAreaDozingTransitions.value).isTrue()
+ }
@Test
- fun bottomAreaAlpha() = runBlockingTest {
- assertThat(underTest.bottomAreaAlpha.value).isEqualTo(1f)
+ fun bottomAreaAlpha() =
+ runTest(UnconfinedTestDispatcher()) {
+ assertThat(underTest.bottomAreaAlpha.value).isEqualTo(1f)
- underTest.setBottomAreaAlpha(0.1f)
- assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.1f)
+ underTest.setBottomAreaAlpha(0.1f)
+ assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.1f)
- underTest.setBottomAreaAlpha(0.2f)
- assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.2f)
+ underTest.setBottomAreaAlpha(0.2f)
+ assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.2f)
- underTest.setBottomAreaAlpha(0.3f)
- assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.3f)
+ underTest.setBottomAreaAlpha(0.3f)
+ assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.3f)
- underTest.setBottomAreaAlpha(0.5f)
- assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.5f)
+ underTest.setBottomAreaAlpha(0.5f)
+ assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.5f)
- underTest.setBottomAreaAlpha(1.0f)
- assertThat(underTest.bottomAreaAlpha.value).isEqualTo(1f)
- }
+ underTest.setBottomAreaAlpha(1.0f)
+ assertThat(underTest.bottomAreaAlpha.value).isEqualTo(1f)
+ }
@Test
- fun clockPosition() = runBlockingTest {
- assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 0))
+ fun clockPosition() =
+ runTest(UnconfinedTestDispatcher()) {
+ assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 0))
- underTest.setClockPosition(0, 1)
- assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 1))
+ underTest.setClockPosition(0, 1)
+ assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 1))
- underTest.setClockPosition(1, 9)
- assertThat(underTest.clockPosition.value).isEqualTo(Position(1, 9))
+ underTest.setClockPosition(1, 9)
+ assertThat(underTest.clockPosition.value).isEqualTo(Position(1, 9))
- underTest.setClockPosition(1, 0)
- assertThat(underTest.clockPosition.value).isEqualTo(Position(1, 0))
+ underTest.setClockPosition(1, 0)
+ assertThat(underTest.clockPosition.value).isEqualTo(Position(1, 0))
- underTest.setClockPosition(3, 1)
- assertThat(underTest.clockPosition.value).isEqualTo(Position(3, 1))
- }
+ underTest.setClockPosition(3, 1)
+ assertThat(underTest.clockPosition.value).isEqualTo(Position(3, 1))
+ }
@Test
- fun isKeyguardShowing() = runBlockingTest {
- whenever(keyguardStateController.isShowing).thenReturn(false)
- var latest: Boolean? = null
- val job = underTest.isKeyguardShowing.onEach { latest = it }.launchIn(this)
+ fun isKeyguardShowing() =
+ runTest(UnconfinedTestDispatcher()) {
+ whenever(keyguardStateController.isShowing).thenReturn(false)
+ var latest: Boolean? = null
+ val job = underTest.isKeyguardShowing.onEach { latest = it }.launchIn(this)
- assertThat(latest).isFalse()
- assertThat(underTest.isKeyguardShowing()).isFalse()
+ assertThat(latest).isFalse()
+ assertThat(underTest.isKeyguardShowing()).isFalse()
- val captor = argumentCaptor<KeyguardStateController.Callback>()
- verify(keyguardStateController).addCallback(captor.capture())
+ val captor = argumentCaptor<KeyguardStateController.Callback>()
+ verify(keyguardStateController).addCallback(captor.capture())
- whenever(keyguardStateController.isShowing).thenReturn(true)
- captor.value.onKeyguardShowingChanged()
- assertThat(latest).isTrue()
- assertThat(underTest.isKeyguardShowing()).isTrue()
+ whenever(keyguardStateController.isShowing).thenReturn(true)
+ captor.value.onKeyguardShowingChanged()
+ assertThat(latest).isTrue()
+ assertThat(underTest.isKeyguardShowing()).isTrue()
- whenever(keyguardStateController.isShowing).thenReturn(false)
- captor.value.onKeyguardShowingChanged()
- assertThat(latest).isFalse()
- assertThat(underTest.isKeyguardShowing()).isFalse()
+ whenever(keyguardStateController.isShowing).thenReturn(false)
+ captor.value.onKeyguardShowingChanged()
+ assertThat(latest).isFalse()
+ assertThat(underTest.isKeyguardShowing()).isFalse()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun isDozing() = runBlockingTest {
- var latest: Boolean? = null
- val job = underTest.isDozing.onEach { latest = it }.launchIn(this)
+ fun isDozing() =
+ runTest(UnconfinedTestDispatcher()) {
+ var latest: Boolean? = null
+ val job = underTest.isDozing.onEach { latest = it }.launchIn(this)
- val captor = argumentCaptor<DozeHost.Callback>()
- verify(dozeHost).addCallback(captor.capture())
+ val captor = argumentCaptor<DozeHost.Callback>()
+ verify(dozeHost).addCallback(captor.capture())
- captor.value.onDozingChanged(true)
- assertThat(latest).isTrue()
+ captor.value.onDozingChanged(true)
+ assertThat(latest).isTrue()
- captor.value.onDozingChanged(false)
- assertThat(latest).isFalse()
+ captor.value.onDozingChanged(false)
+ assertThat(latest).isFalse()
- job.cancel()
- verify(dozeHost).removeCallback(captor.value)
- }
+ job.cancel()
+ verify(dozeHost).removeCallback(captor.value)
+ }
@Test
- fun `isDozing - starts with correct initial value for isDozing`() = runBlockingTest {
- var latest: Boolean? = null
+ fun `isDozing - starts with correct initial value for isDozing`() =
+ runTest(UnconfinedTestDispatcher()) {
+ var latest: Boolean? = null
- whenever(statusBarStateController.isDozing).thenReturn(true)
- var job = underTest.isDozing.onEach { latest = it }.launchIn(this)
- assertThat(latest).isTrue()
- job.cancel()
+ whenever(statusBarStateController.isDozing).thenReturn(true)
+ var job = underTest.isDozing.onEach { latest = it }.launchIn(this)
+ assertThat(latest).isTrue()
+ job.cancel()
- whenever(statusBarStateController.isDozing).thenReturn(false)
- job = underTest.isDozing.onEach { latest = it }.launchIn(this)
- assertThat(latest).isFalse()
- job.cancel()
- }
+ whenever(statusBarStateController.isDozing).thenReturn(false)
+ job = underTest.isDozing.onEach { latest = it }.launchIn(this)
+ assertThat(latest).isFalse()
+ job.cancel()
+ }
@Test
- fun dozeAmount() = runBlockingTest {
- val values = mutableListOf<Float>()
- val job = underTest.dozeAmount.onEach(values::add).launchIn(this)
+ fun dozeAmount() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Float>()
+ val job = underTest.dozeAmount.onEach(values::add).launchIn(this)
- val captor = argumentCaptor<StatusBarStateController.StateListener>()
- verify(statusBarStateController).addCallback(captor.capture())
+ val captor = argumentCaptor<StatusBarStateController.StateListener>()
+ verify(statusBarStateController).addCallback(captor.capture())
- captor.value.onDozeAmountChanged(0.433f, 0.4f)
- captor.value.onDozeAmountChanged(0.498f, 0.5f)
- captor.value.onDozeAmountChanged(0.661f, 0.65f)
+ captor.value.onDozeAmountChanged(0.433f, 0.4f)
+ captor.value.onDozeAmountChanged(0.498f, 0.5f)
+ captor.value.onDozeAmountChanged(0.661f, 0.65f)
- assertThat(values).isEqualTo(listOf(0f, 0.4f, 0.5f, 0.65f))
+ assertThat(values).isEqualTo(listOf(0f, 0.4f, 0.5f, 0.65f))
- job.cancel()
- verify(statusBarStateController).removeCallback(captor.value)
- }
+ job.cancel()
+ verify(statusBarStateController).removeCallback(captor.value)
+ }
@Test
- fun wakefulness() = runBlockingTest {
- val values = mutableListOf<WakefulnessModel>()
- val job = underTest.wakefulnessState.onEach(values::add).launchIn(this)
+ fun wakefulness() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<WakefulnessModel>()
+ val job = underTest.wakefulnessState.onEach(values::add).launchIn(this)
- val captor = argumentCaptor<WakefulnessLifecycle.Observer>()
- verify(wakefulnessLifecycle).addObserver(captor.capture())
+ val captor = argumentCaptor<WakefulnessLifecycle.Observer>()
+ verify(wakefulnessLifecycle).addObserver(captor.capture())
- captor.value.onStartedWakingUp()
- captor.value.onFinishedWakingUp()
- captor.value.onStartedGoingToSleep()
- captor.value.onFinishedGoingToSleep()
+ captor.value.onStartedWakingUp()
+ captor.value.onFinishedWakingUp()
+ captor.value.onStartedGoingToSleep()
+ captor.value.onFinishedGoingToSleep()
- assertThat(values)
- .isEqualTo(
- listOf(
- // Initial value will be ASLEEP
- WakefulnessModel.ASLEEP,
- WakefulnessModel.STARTING_TO_WAKE,
- WakefulnessModel.AWAKE,
- WakefulnessModel.STARTING_TO_SLEEP,
- WakefulnessModel.ASLEEP,
+ assertThat(values)
+ .isEqualTo(
+ listOf(
+ // Initial value will be ASLEEP
+ WakefulnessModel.ASLEEP,
+ WakefulnessModel.STARTING_TO_WAKE,
+ WakefulnessModel.AWAKE,
+ WakefulnessModel.STARTING_TO_SLEEP,
+ WakefulnessModel.ASLEEP,
+ )
)
- )
- job.cancel()
- verify(wakefulnessLifecycle).removeObserver(captor.value)
- }
+ job.cancel()
+ verify(wakefulnessLifecycle).removeObserver(captor.value)
+ }
@Test
- fun isUdfpsSupported() = runBlockingTest {
- whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(true)
- assertThat(underTest.isUdfpsSupported()).isTrue()
+ fun isUdfpsSupported() =
+ runTest(UnconfinedTestDispatcher()) {
+ whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(true)
+ assertThat(underTest.isUdfpsSupported()).isTrue()
- whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(false)
- assertThat(underTest.isUdfpsSupported()).isFalse()
- }
+ whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(false)
+ assertThat(underTest.isUdfpsSupported()).isFalse()
+ }
@Test
- fun isBouncerShowing() = runBlockingTest {
- whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
- var latest: Boolean? = null
- val job = underTest.isBouncerShowing.onEach { latest = it }.launchIn(this)
+ fun isBouncerShowing() =
+ runTest(UnconfinedTestDispatcher()) {
+ whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
+ var latest: Boolean? = null
+ val job = underTest.isBouncerShowing.onEach { latest = it }.launchIn(this)
- assertThat(latest).isFalse()
+ assertThat(latest).isFalse()
- val captor = argumentCaptor<KeyguardStateController.Callback>()
- verify(keyguardStateController).addCallback(captor.capture())
+ val captor = argumentCaptor<KeyguardStateController.Callback>()
+ verify(keyguardStateController).addCallback(captor.capture())
- whenever(keyguardStateController.isBouncerShowing).thenReturn(true)
- captor.value.onBouncerShowingChanged()
- assertThat(latest).isTrue()
+ whenever(keyguardStateController.isBouncerShowing).thenReturn(true)
+ captor.value.onBouncerShowingChanged()
+ assertThat(latest).isTrue()
- whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
- captor.value.onBouncerShowingChanged()
- assertThat(latest).isFalse()
+ whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
+ captor.value.onBouncerShowingChanged()
+ assertThat(latest).isFalse()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun isKeyguardGoingAway() = runBlockingTest {
- whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
- var latest: Boolean? = null
- val job = underTest.isKeyguardGoingAway.onEach { latest = it }.launchIn(this)
+ fun isKeyguardGoingAway() =
+ runTest(UnconfinedTestDispatcher()) {
+ whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
+ var latest: Boolean? = null
+ val job = underTest.isKeyguardGoingAway.onEach { latest = it }.launchIn(this)
- assertThat(latest).isFalse()
+ assertThat(latest).isFalse()
- val captor = argumentCaptor<KeyguardStateController.Callback>()
- verify(keyguardStateController).addCallback(captor.capture())
+ val captor = argumentCaptor<KeyguardStateController.Callback>()
+ verify(keyguardStateController).addCallback(captor.capture())
- whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(true)
- captor.value.onKeyguardGoingAwayChanged()
- assertThat(latest).isTrue()
+ whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(true)
+ captor.value.onKeyguardGoingAwayChanged()
+ assertThat(latest).isTrue()
- whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
- captor.value.onKeyguardGoingAwayChanged()
- assertThat(latest).isFalse()
+ whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
+ captor.value.onKeyguardGoingAwayChanged()
+ assertThat(latest).isFalse()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun isDreaming() = runBlockingTest {
- whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(false)
- var latest: Boolean? = null
- val job = underTest.isDreaming.onEach { latest = it }.launchIn(this)
+ fun isDreaming() =
+ runTest(UnconfinedTestDispatcher()) {
+ whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(false)
+ var latest: Boolean? = null
+ val job = underTest.isDreaming.onEach { latest = it }.launchIn(this)
- assertThat(latest).isFalse()
+ assertThat(latest).isFalse()
- val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
- verify(keyguardUpdateMonitor).registerCallback(captor.capture())
+ val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(captor.capture())
- captor.value.onDreamingStateChanged(true)
- assertThat(latest).isTrue()
+ captor.value.onDreamingStateChanged(true)
+ assertThat(latest).isTrue()
- captor.value.onDreamingStateChanged(false)
- assertThat(latest).isFalse()
+ captor.value.onDreamingStateChanged(false)
+ assertThat(latest).isFalse()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun biometricUnlockState() = runBlockingTest {
- val values = mutableListOf<BiometricUnlockModel>()
- val job = underTest.biometricUnlockState.onEach(values::add).launchIn(this)
+ fun biometricUnlockState() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<BiometricUnlockModel>()
+ val job = underTest.biometricUnlockState.onEach(values::add).launchIn(this)
- val captor = argumentCaptor<BiometricUnlockController.BiometricModeListener>()
- verify(biometricUnlockController).addBiometricModeListener(captor.capture())
+ val captor = argumentCaptor<BiometricUnlockController.BiometricModeListener>()
+ verify(biometricUnlockController).addBiometricModeListener(captor.capture())
- captor.value.onModeChanged(BiometricUnlockController.MODE_NONE)
- captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK)
- captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING)
- captor.value.onModeChanged(BiometricUnlockController.MODE_SHOW_BOUNCER)
- captor.value.onModeChanged(BiometricUnlockController.MODE_ONLY_WAKE)
- captor.value.onModeChanged(BiometricUnlockController.MODE_UNLOCK_COLLAPSING)
- captor.value.onModeChanged(BiometricUnlockController.MODE_DISMISS_BOUNCER)
- captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_NONE)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_SHOW_BOUNCER)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_ONLY_WAKE)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_UNLOCK_COLLAPSING)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_DISMISS_BOUNCER)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
- assertThat(values)
- .isEqualTo(
- listOf(
- // Initial value will be NONE, followed by onModeChanged() call
- BiometricUnlockModel.NONE,
- BiometricUnlockModel.NONE,
- BiometricUnlockModel.WAKE_AND_UNLOCK,
- BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING,
- BiometricUnlockModel.SHOW_BOUNCER,
- BiometricUnlockModel.ONLY_WAKE,
- BiometricUnlockModel.UNLOCK_COLLAPSING,
- BiometricUnlockModel.DISMISS_BOUNCER,
- BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM,
+ assertThat(values)
+ .isEqualTo(
+ listOf(
+ // Initial value will be NONE, followed by onModeChanged() call
+ BiometricUnlockModel.NONE,
+ BiometricUnlockModel.NONE,
+ BiometricUnlockModel.WAKE_AND_UNLOCK,
+ BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING,
+ BiometricUnlockModel.SHOW_BOUNCER,
+ BiometricUnlockModel.ONLY_WAKE,
+ BiometricUnlockModel.UNLOCK_COLLAPSING,
+ BiometricUnlockModel.DISMISS_BOUNCER,
+ BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM,
+ )
)
+
+ job.cancel()
+ verify(biometricUnlockController).removeBiometricModeListener(captor.value)
+ }
+
+ @Test
+ fun dozeTransitionModel() =
+ runTest(UnconfinedTestDispatcher()) {
+ // For the initial state
+ whenever(dozeTransitionListener.oldState).thenReturn(DozeMachine.State.UNINITIALIZED)
+ whenever(dozeTransitionListener.newState).thenReturn(DozeMachine.State.UNINITIALIZED)
+
+ val values = mutableListOf<DozeTransitionModel>()
+ val job = underTest.dozeTransitionModel.onEach(values::add).launchIn(this)
+
+ val listener =
+ withArgCaptor<DozeTransitionCallback> {
+ verify(dozeTransitionListener).addCallback(capture())
+ }
+
+ // These don't have to reflect real transitions from the DozeMachine. Only that the
+ // transitions are properly emitted
+ listener.onDozeTransition(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE)
+ listener.onDozeTransition(DozeMachine.State.DOZE, DozeMachine.State.DOZE_AOD)
+ listener.onDozeTransition(DozeMachine.State.DOZE_AOD_DOCKED, DozeMachine.State.FINISH)
+ listener.onDozeTransition(
+ DozeMachine.State.DOZE_REQUEST_PULSE,
+ DozeMachine.State.DOZE_PULSING
+ )
+ listener.onDozeTransition(
+ DozeMachine.State.DOZE_SUSPEND_TRIGGERS,
+ DozeMachine.State.DOZE_PULSE_DONE
+ )
+ listener.onDozeTransition(
+ DozeMachine.State.DOZE_AOD_PAUSING,
+ DozeMachine.State.DOZE_AOD_PAUSED
)
- job.cancel()
- verify(biometricUnlockController).removeBiometricModeListener(captor.value)
- }
+ assertThat(values)
+ .isEqualTo(
+ listOf(
+ // Initial value will be UNINITIALIZED
+ DozeTransitionModel(
+ DozeStateModel.UNINITIALIZED,
+ DozeStateModel.UNINITIALIZED
+ ),
+ DozeTransitionModel(DozeStateModel.INITIALIZED, DozeStateModel.DOZE),
+ DozeTransitionModel(DozeStateModel.DOZE, DozeStateModel.DOZE_AOD),
+ DozeTransitionModel(DozeStateModel.DOZE_AOD_DOCKED, DozeStateModel.FINISH),
+ DozeTransitionModel(
+ DozeStateModel.DOZE_REQUEST_PULSE,
+ DozeStateModel.DOZE_PULSING
+ ),
+ DozeTransitionModel(
+ DozeStateModel.DOZE_SUSPEND_TRIGGERS,
+ DozeStateModel.DOZE_PULSE_DONE
+ ),
+ DozeTransitionModel(
+ DozeStateModel.DOZE_AOD_PAUSING,
+ DozeStateModel.DOZE_AOD_PAUSED
+ ),
+ )
+ )
+
+ job.cancel()
+ verify(dozeTransitionListener).removeCallback(listener)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
index 3269f5a..559f183 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -43,6 +43,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Answers
+import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
@@ -170,8 +171,10 @@
@Test
fun testShowMessage() {
+ val argCaptor = ArgumentCaptor.forClass(BouncerShowMessageModel::class.java)
mPrimaryBouncerInteractor.showMessage("abc", null)
- verify(repository).setShowMessage(BouncerShowMessageModel("abc", null))
+ verify(repository).setShowMessage(argCaptor.capture())
+ assertThat(argCaptor.value.message).isEqualTo("abc")
}
@Test
@@ -195,6 +198,12 @@
}
@Test
+ fun testNotifyShowedMessage() {
+ mPrimaryBouncerInteractor.onMessageShown()
+ verify(repository).setShowMessage(null)
+ }
+
+ @Test
fun testOnScreenTurnedOff() {
mPrimaryBouncerInteractor.onScreenTurnedOff()
verify(repository).setOnScreenTurnedOff(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt
new file mode 100644
index 0000000..3727134
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.BouncerView
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardBouncerViewModelTest : SysuiTestCase() {
+ lateinit var underTest: KeyguardBouncerViewModel
+ @Mock lateinit var bouncerView: BouncerView
+ @Mock lateinit var bouncerInteractor: PrimaryBouncerInteractor
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest = KeyguardBouncerViewModel(bouncerView, bouncerInteractor)
+ }
+
+ @Test
+ fun setMessage() =
+ runBlocking(Dispatchers.Main.immediate) {
+ val flow = MutableStateFlow<BouncerShowMessageModel?>(null)
+ var message: BouncerShowMessageModel? = null
+ Mockito.`when`(bouncerInteractor.showMessage)
+ .thenReturn(flow as Flow<BouncerShowMessageModel>)
+ // Reinitialize the view model.
+ underTest = KeyguardBouncerViewModel(bouncerView, bouncerInteractor)
+
+ flow.value = BouncerShowMessageModel(message = "abc", colorStateList = null)
+
+ val job = underTest.bouncerShowMessage.onEach { message = it }.launchIn(this)
+ assertThat(message?.message).isEqualTo("abc")
+ job.cancel()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
new file mode 100644
index 0000000..4aa982e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.media.taptotransfer.receiver
+
+import android.content.Context
+import android.os.Handler
+import android.os.PowerManager
+import android.view.ViewGroup
+import android.view.WindowManager
+import android.view.accessibility.AccessibilityManager
+import com.android.systemui.media.taptotransfer.MediaTttFlags
+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.util.concurrency.DelayableExecutor
+import com.android.systemui.util.view.ViewUtil
+import com.android.systemui.util.wakelock.WakeLock
+
+class FakeMediaTttChipControllerReceiver(
+ commandQueue: CommandQueue,
+ context: Context,
+ logger: MediaTttLogger,
+ windowManager: WindowManager,
+ mainExecutor: DelayableExecutor,
+ accessibilityManager: AccessibilityManager,
+ configurationController: ConfigurationController,
+ powerManager: PowerManager,
+ mainHandler: Handler,
+ mediaTttFlags: MediaTttFlags,
+ uiEventLogger: MediaTttReceiverUiEventLogger,
+ viewUtil: ViewUtil,
+ wakeLockBuilder: WakeLock.Builder,
+) :
+ MediaTttChipControllerReceiver(
+ commandQueue,
+ context,
+ logger,
+ windowManager,
+ mainExecutor,
+ accessibilityManager,
+ configurationController,
+ powerManager,
+ mainHandler,
+ mediaTttFlags,
+ uiEventLogger,
+ viewUtil,
+ wakeLockBuilder,
+ ) {
+ override fun animateViewOut(view: ViewGroup, onAnimationEnd: Runnable) {
+ // Just bypass the animation in tests
+ onAnimationEnd.run()
+ }
+}
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 885cc54..23f7cdb 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
@@ -114,7 +114,7 @@
fakeWakeLockBuilder = WakeLockFake.Builder(context)
fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
- controllerReceiver = MediaTttChipControllerReceiver(
+ controllerReceiver = FakeMediaTttChipControllerReceiver(
commandQueue,
context,
logger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/sensorprivacy/SensorUseStartedActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/sensorprivacy/SensorUseStartedActivityTest.kt
new file mode 100644
index 0000000..333e634
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/sensorprivacy/SensorUseStartedActivityTest.kt
@@ -0,0 +1,38 @@
+package com.android.systemui.sensorprivacy
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@TestableLooper.RunWithLooper
+class SensorUseStartedActivityTest : SysuiTestCase() {
+ open class SensorUseStartedActivityTestable :
+ SensorUseStartedActivity(
+ sensorPrivacyController = mock(),
+ keyguardStateController = mock(),
+ keyguardDismissUtil = mock(),
+ bgHandler = mock(),
+ )
+
+ @get:Rule val activityRule = ActivityScenarioRule(SensorUseStartedActivityTestable::class.java)
+
+ @Test
+ fun onBackPressed_doNothing() {
+ activityRule.scenario.onActivity { activity ->
+ assertThat(activity.isFinishing).isFalse()
+
+ activity.onBackPressed()
+
+ assertThat(activity.isFinishing).isFalse()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
index bc17c19..2a3d32e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
@@ -43,7 +43,7 @@
load(context, context.resources.getXml(R.xml.qqs_header))
}
qsConstraint = ConstraintSet().apply {
- load(context, context.resources.getXml(R.xml.qs_header_new))
+ load(context, context.resources.getXml(R.xml.qs_header))
}
largeScreenConstraint = ConstraintSet().apply {
load(context, context.resources.getXml(R.xml.large_screen_shade_header))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
index 14a3bc1..e1007fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
@@ -179,7 +179,6 @@
whenever(iconManagerFactory.create(any(), any())).thenReturn(iconManager)
whenever(featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)).thenReturn(true)
- whenever(featureFlags.isEnabled(Flags.NEW_HEADER)).thenReturn(true)
setUpDefaultInsets()
setUpMotionLayout(view)
@@ -212,7 +211,7 @@
assertThat(captor.value.getResId()).isEqualTo(R.xml.qqs_header)
verify(qsConstraints).load(eq(context), capture(captor))
- assertThat(captor.value.getResId()).isEqualTo(R.xml.qs_header_new)
+ assertThat(captor.value.getResId()).isEqualTo(R.xml.qs_header)
verify(largeScreenConstraints).load(eq(context), capture(captor))
assertThat(captor.value.getResId()).isEqualTo(R.xml.large_screen_shade_header)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 41912f5..157b99d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -355,7 +355,6 @@
when(mStackScrollerController.getView()).thenReturn(mStackScroller);
when(mStackScroller.generateLayoutParams(any())).thenReturn(new LayoutParams(0, 0));
- when(mNotificationPanelViewController.getView()).thenReturn(mNotificationPanelView);
when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0));
when(powerManagerService.isInteractive()).thenReturn(true);
when(mStackScroller.getActivatedChild()).thenReturn(null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 320a083..e2843a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -76,7 +76,6 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- `when`(notificationPanelViewController.view).thenReturn(panelView)
`when`(sysuiUnfoldComponent.getStatusBarMoveFromCenterAnimationController())
.thenReturn(moveFromCenterAnimation)
// create the view and controller on main thread as it requires main looper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index 5aa7f92..27b1da0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -25,7 +25,6 @@
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -41,9 +40,6 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- // TODO(b/197137564): Setting up a panel view and its controller feels unnecessary when
- // testing just [PhoneStatusBarView].
- `when`(notificationPanelViewController.view).thenReturn(panelView)
view = PhoneStatusBarView(mContext, null)
}
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 9f70565..bf5186b 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
@@ -307,6 +307,23 @@
}
@Test
+ public void onPanelExpansionChanged_neverTranslatesBouncerWhenShowBouncer() {
+ // Since KeyguardBouncer.EXPANSION_VISIBLE = 0 panel expansion, if the unlock is dismissing
+ // the bouncer, there may be an onPanelExpansionChanged(0) call to collapse the panel
+ // which would mistakenly cause the bouncer to show briefly before its visibility
+ // is set to hide. Therefore, we don't want to propagate panelExpansionChanged to the
+ // bouncer if the bouncer is dismissing as a result of a biometric unlock.
+ when(mBiometricUnlockController.getMode())
+ .thenReturn(BiometricUnlockController.MODE_SHOW_BOUNCER);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ expansionEvent(
+ /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* expanded= */ true,
+ /* tracking= */ false));
+ verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
+ }
+
+ @Test
public void onPanelExpansionChanged_neverTranslatesBouncerWhenShadeLocked() {
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
@@ -570,4 +587,40 @@
mStatusBarKeyguardViewManager.hideBouncer(false);
verify(mPrimaryBouncerInteractor, never()).hide();
}
+
+ @Test
+ public void hideAlternateBouncer_beforeCentralSurfacesRegistered() {
+ mStatusBarKeyguardViewManager =
+ new StatusBarKeyguardViewManager(
+ getContext(),
+ mViewMediatorCallback,
+ mLockPatternUtils,
+ mStatusBarStateController,
+ mock(ConfigurationController.class),
+ mKeyguardUpdateMonitor,
+ mDreamOverlayStateController,
+ mock(NavigationModeController.class),
+ mock(DockManager.class),
+ mock(NotificationShadeWindowController.class),
+ mKeyguardStateController,
+ mock(NotificationMediaManager.class),
+ mKeyguardBouncerFactory,
+ mKeyguardMessageAreaFactory,
+ Optional.of(mSysUiUnfoldComponent),
+ () -> mShadeController,
+ mLatencyTracker,
+ mKeyguardSecurityModel,
+ mFeatureFlags,
+ mPrimaryBouncerCallbackInteractor,
+ mPrimaryBouncerInteractor,
+ mBouncerView) {
+ @Override
+ public ViewRootImpl getViewRootImpl() {
+ return mViewRootImpl;
+ }
+ };
+
+ // the following call before registering centralSurfaces should NOT throw a NPE:
+ mStatusBarKeyguardViewManager.hideAlternateBouncer(true);
+ }
}
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 8645298..89402de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -88,6 +88,7 @@
deviceStates = FoldableTestUtils.findDeviceStates(context)
+ // TODO(b/254878364): remove this call to NPVC.getView()
whenever(notificationPanelViewController.view).thenReturn(viewGroup)
whenever(viewGroup.viewTreeObserver).thenReturn(viewTreeObserver)
whenever(wakefulnessLifecycle.lastSleepReason)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
index fc2a78a..e1e54a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
@@ -15,14 +15,13 @@
*/
package com.android.systemui.unfold.util
-import android.animation.ValueAnimator
import android.content.ContentResolver
import android.database.ContentObserver
+import android.provider.Settings
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.TestUnfoldTransitionProvider
-import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.util.mockito.any
import org.junit.Before
@@ -30,6 +29,7 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
+import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
@@ -38,30 +38,25 @@
@SmallTest
class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
- @Mock
- lateinit var contentResolver: ContentResolver
-
- @Mock
- lateinit var sinkProvider: TransitionProgressListener
+ @Mock lateinit var sinkProvider: TransitionProgressListener
private val sourceProvider = TestUnfoldTransitionProvider()
- lateinit var progressProvider: ScaleAwareTransitionProgressProvider
+ private lateinit var contentResolver: ContentResolver
+ private lateinit var progressProvider: ScaleAwareTransitionProgressProvider
private val animatorDurationScaleListenerCaptor =
- ArgumentCaptor.forClass(ContentObserver::class.java)
+ ArgumentCaptor.forClass(ContentObserver::class.java)
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ contentResolver = spy(context.contentResolver)
- progressProvider = ScaleAwareTransitionProgressProvider(
- sourceProvider,
- contentResolver
- )
+ progressProvider = ScaleAwareTransitionProgressProvider(sourceProvider, contentResolver)
- verify(contentResolver).registerContentObserver(any(), any(),
- animatorDurationScaleListenerCaptor.capture())
+ verify(contentResolver)
+ .registerContentObserver(any(), any(), animatorDurationScaleListenerCaptor.capture())
progressProvider.addCallback(sinkProvider)
}
@@ -121,12 +116,20 @@
}
private fun setAnimationsEnabled(enabled: Boolean) {
- val durationScale = if (enabled) {
- 1f
- } else {
- 0f
- }
- ValueAnimator.setDurationScale(durationScale)
+ val durationScale =
+ if (enabled) {
+ 1f
+ } else {
+ 0f
+ }
+
+ // It uses [TestableSettingsProvider] and it will be cleared after the test
+ Settings.Global.putString(
+ contentResolver,
+ Settings.Global.ANIMATOR_DURATION_SCALE,
+ durationScale.toString()
+ )
+
animatorDurationScaleListenerCaptor.value.dispatchChange(/* selfChange= */false)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index 4b49420..d877209 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -771,6 +771,28 @@
)
}
+ @Test
+ fun `users - secondary user - managed profile is not included`() =
+ runBlocking(IMMEDIATE) {
+ var userInfos = createUserInfos(count = 3, includeGuest = false).toMutableList()
+ userInfos.add(
+ UserInfo(
+ 50,
+ "Work Profile",
+ /* iconPath= */ "",
+ /* flags= */ UserInfo.FLAG_MANAGED_PROFILE
+ )
+ )
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[1])
+ userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
+
+ var res: List<UserModel>? = null
+ val job = underTest.users.onEach { res = it }.launchIn(this)
+ assertThat(res?.size == 3).isTrue()
+ job.cancel()
+ }
+
private fun assertUsers(
models: List<UserModel>?,
count: Int,
@@ -893,9 +915,9 @@
name,
/* iconPath= */ "",
/* flags= */ if (isPrimary) {
- UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN
+ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL
} else {
- 0
+ UserInfo.FLAG_FULL
},
if (isGuest) {
UserManager.USER_TYPE_FULL_GUEST
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index db348b80..795ff17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -281,7 +281,7 @@
USER_ID_0,
USER_NAME_0.text!!,
/* iconPath */ "",
- /* flags */ 0,
+ /* flags */ UserInfo.FLAG_FULL,
/* userType */ UserManager.USER_TYPE_FULL_SYSTEM
)
@@ -290,7 +290,7 @@
USER_ID_1,
USER_NAME_1.text!!,
/* iconPath */ "",
- /* flags */ 0,
+ /* flags */ UserInfo.FLAG_FULL,
/* userType */ UserManager.USER_TYPE_FULL_SYSTEM
)
@@ -299,7 +299,7 @@
USER_ID_2,
USER_NAME_2.text!!,
/* iconPath */ "",
- /* flags */ 0,
+ /* flags */ UserInfo.FLAG_FULL,
/* userType */ UserManager.USER_TYPE_FULL_SYSTEM
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index eac7fc2..1730b75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -178,21 +178,21 @@
/* id= */ 0,
/* name= */ "zero",
/* iconPath= */ "",
- /* flags= */ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN,
+ /* flags= */ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
UserManager.USER_TYPE_FULL_SYSTEM,
),
UserInfo(
/* id= */ 1,
/* name= */ "one",
/* iconPath= */ "",
- /* flags= */ 0,
+ /* flags= */ UserInfo.FLAG_FULL,
UserManager.USER_TYPE_FULL_SYSTEM,
),
UserInfo(
/* id= */ 2,
/* name= */ "two",
/* iconPath= */ "",
- /* flags= */ 0,
+ /* flags= */ UserInfo.FLAG_FULL,
UserManager.USER_TYPE_FULL_SYSTEM,
),
)
@@ -361,10 +361,10 @@
/* iconPath= */ "",
/* flags= */ if (index == 0) {
// This is the primary user.
- UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN
+ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL
} else {
// This isn't the primary user.
- 0
+ UserInfo.FLAG_FULL
},
UserManager.USER_TYPE_FULL_SYSTEM,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java
index 0b53133..2878864 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java
@@ -141,4 +141,158 @@
mCondition.clearCondition();
assertThat(mCondition.isConditionSet()).isFalse();
}
+
+ @Test
+ public void combineConditionsWithOr_allFalse_reportsNotMet() {
+ mCondition.fakeUpdateCondition(false);
+
+ final Condition combinedCondition = mCondition.or(
+ new FakeCondition(/* initialValue= */ false));
+
+ final Condition.Callback callback = mock(Condition.Callback.class);
+ combinedCondition.addCallback(callback);
+
+ assertThat(combinedCondition.isConditionSet()).isTrue();
+ assertThat(combinedCondition.isConditionMet()).isFalse();
+ verify(callback, times(1)).onConditionChanged(combinedCondition);
+ }
+
+ @Test
+ public void combineConditionsWithOr_allTrue_reportsMet() {
+ mCondition.fakeUpdateCondition(true);
+
+ final Condition combinedCondition = mCondition.or(
+ new FakeCondition(/* initialValue= */ true));
+
+ final Condition.Callback callback = mock(Condition.Callback.class);
+ combinedCondition.addCallback(callback);
+
+ assertThat(combinedCondition.isConditionSet()).isTrue();
+ assertThat(combinedCondition.isConditionMet()).isTrue();
+ verify(callback, times(1)).onConditionChanged(combinedCondition);
+ }
+
+ @Test
+ public void combineConditionsWithOr_singleTrue_reportsMet() {
+ mCondition.fakeUpdateCondition(false);
+
+ final Condition combinedCondition = mCondition.or(
+ new FakeCondition(/* initialValue= */ true));
+
+ final Condition.Callback callback = mock(Condition.Callback.class);
+ combinedCondition.addCallback(callback);
+
+ assertThat(combinedCondition.isConditionSet()).isTrue();
+ assertThat(combinedCondition.isConditionMet()).isTrue();
+ verify(callback, times(1)).onConditionChanged(combinedCondition);
+ }
+
+ @Test
+ public void combineConditionsWithOr_unknownAndTrue_reportsMet() {
+ mCondition.fakeUpdateCondition(true);
+
+ // Combine with an unset condition.
+ final Condition combinedCondition = mCondition.or(
+ new FakeCondition(/* initialValue= */ null));
+
+ final Condition.Callback callback = mock(Condition.Callback.class);
+ combinedCondition.addCallback(callback);
+
+ assertThat(combinedCondition.isConditionSet()).isTrue();
+ assertThat(combinedCondition.isConditionMet()).isTrue();
+ verify(callback, times(1)).onConditionChanged(combinedCondition);
+ }
+
+ @Test
+ public void combineConditionsWithOr_unknownAndFalse_reportsNotMet() {
+ mCondition.fakeUpdateCondition(false);
+
+ // Combine with an unset condition.
+ final Condition combinedCondition = mCondition.or(
+ new FakeCondition(/* initialValue= */ null));
+
+ final Condition.Callback callback = mock(Condition.Callback.class);
+ combinedCondition.addCallback(callback);
+
+ assertThat(combinedCondition.isConditionSet()).isFalse();
+ assertThat(combinedCondition.isConditionMet()).isFalse();
+ verify(callback, never()).onConditionChanged(combinedCondition);
+ }
+
+ @Test
+ public void combineConditionsWithAnd_allFalse_reportsNotMet() {
+ mCondition.fakeUpdateCondition(false);
+
+ final Condition combinedCondition = mCondition.and(
+ new FakeCondition(/* initialValue= */ false));
+
+ final Condition.Callback callback = mock(Condition.Callback.class);
+ combinedCondition.addCallback(callback);
+
+ assertThat(combinedCondition.isConditionSet()).isTrue();
+ assertThat(combinedCondition.isConditionMet()).isFalse();
+ verify(callback, times(1)).onConditionChanged(combinedCondition);
+ }
+
+ @Test
+ public void combineConditionsWithAnd_allTrue_reportsMet() {
+ mCondition.fakeUpdateCondition(true);
+
+ final Condition combinedCondition = mCondition.and(
+ new FakeCondition(/* initialValue= */ true));
+
+ final Condition.Callback callback = mock(Condition.Callback.class);
+ combinedCondition.addCallback(callback);
+
+ assertThat(combinedCondition.isConditionSet()).isTrue();
+ assertThat(combinedCondition.isConditionMet()).isTrue();
+ verify(callback, times(1)).onConditionChanged(combinedCondition);
+ }
+
+ @Test
+ public void combineConditionsWithAnd_singleTrue_reportsNotMet() {
+ mCondition.fakeUpdateCondition(true);
+
+ final Condition combinedCondition = mCondition.and(
+ new FakeCondition(/* initialValue= */ false));
+
+ final Condition.Callback callback = mock(Condition.Callback.class);
+ combinedCondition.addCallback(callback);
+
+ assertThat(combinedCondition.isConditionSet()).isTrue();
+ assertThat(combinedCondition.isConditionMet()).isFalse();
+ verify(callback, times(1)).onConditionChanged(combinedCondition);
+ }
+
+ @Test
+ public void combineConditionsWithAnd_unknownAndTrue_reportsNotMet() {
+ mCondition.fakeUpdateCondition(true);
+
+ // Combine with an unset condition.
+ final Condition combinedCondition = mCondition.and(
+ new FakeCondition(/* initialValue= */ null));
+
+ final Condition.Callback callback = mock(Condition.Callback.class);
+ combinedCondition.addCallback(callback);
+
+ assertThat(combinedCondition.isConditionSet()).isFalse();
+ assertThat(combinedCondition.isConditionMet()).isFalse();
+ verify(callback, never()).onConditionChanged(combinedCondition);
+ }
+
+ @Test
+ public void combineConditionsWithAnd_unknownAndFalse_reportsMet() {
+ mCondition.fakeUpdateCondition(false);
+
+ // Combine with an unset condition.
+ final Condition combinedCondition = mCondition.and(
+ new FakeCondition(/* initialValue= */ null));
+
+ final Condition.Callback callback = mock(Condition.Callback.class);
+ combinedCondition.addCallback(callback);
+
+ assertThat(combinedCondition.isConditionSet()).isTrue();
+ assertThat(combinedCondition.isConditionMet()).isFalse();
+ verify(callback, times(1)).onConditionChanged(combinedCondition);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 2e74bf5..a0b4eab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -18,6 +18,7 @@
import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -28,6 +29,7 @@
import android.app.KeyguardManager;
import android.media.AudioManager;
import android.os.SystemClock;
+import android.provider.DeviceConfig;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.InputDevice;
@@ -38,6 +40,7 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.Prefs;
import com.android.systemui.R;
@@ -49,6 +52,9 @@
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.DeviceConfigProxyFake;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -71,6 +77,8 @@
View mDrawerVibrate;
View mDrawerMute;
View mDrawerNormal;
+ private DeviceConfigProxyFake mDeviceConfigProxy;
+ private FakeExecutor mExecutor;
@Mock
VolumeDialogController mVolumeDialogController;
@@ -97,6 +105,9 @@
getContext().addMockSystemService(KeyguardManager.class, mKeyguard);
+ mDeviceConfigProxy = new DeviceConfigProxyFake();
+ mExecutor = new FakeExecutor(new FakeSystemClock());
+
mDialog = new VolumeDialogImpl(
getContext(),
mVolumeDialogController,
@@ -106,7 +117,9 @@
mMediaOutputDialogFactory,
mVolumePanelFactory,
mActivityStarter,
- mInteractionJankMonitor);
+ mInteractionJankMonitor,
+ mDeviceConfigProxy,
+ mExecutor);
mDialog.init(0, null);
State state = createShellState();
mDialog.onStateChangedH(state);
@@ -123,6 +136,9 @@
VolumePrefs.SHOW_RINGER_TOAST_COUNT + 1);
Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, false);
+
+ mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
}
private State createShellState() {
@@ -292,6 +308,35 @@
AudioManager.RINGER_MODE_NORMAL, false);
}
+ /**
+ * Ideally we would look at the ringer ImageView and check its assigned drawable id, but that
+ * API does not exist. So we do the next best thing; we check the cached icon id.
+ */
+ @Test
+ public void notificationVolumeSeparated_theRingerIconChanges() {
+ mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false);
+
+ mExecutor.runAllReady(); // for the config change to take effect
+
+ // assert icon is new based on res id
+ assertEquals(mDialog.mVolumeRingerIconDrawableId,
+ R.drawable.ic_speaker_on);
+ assertEquals(mDialog.mVolumeRingerMuteIconDrawableId,
+ R.drawable.ic_speaker_mute);
+ }
+
+ @Test
+ public void notificationVolumeNotSeparated_theRingerIconRemainsTheSame() {
+ mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
+
+ mExecutor.runAllReady();
+
+ assertEquals(mDialog.mVolumeRingerIconDrawableId, R.drawable.ic_volume_ringer);
+ assertEquals(mDialog.mVolumeRingerMuteIconDrawableId, R.drawable.ic_volume_ringer_mute);
+ }
+
/*
@Test
public void testContentDescriptions() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index a798f40..3601667 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -19,6 +19,7 @@
import com.android.systemui.common.shared.model.Position
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import kotlinx.coroutines.flow.Flow
@@ -53,6 +54,9 @@
private val _statusBarState = MutableStateFlow(StatusBarState.SHADE)
override val statusBarState: Flow<StatusBarState> = _statusBarState
+ private val _dozeTransitionModel = MutableStateFlow(DozeTransitionModel())
+ override val dozeTransitionModel: Flow<DozeTransitionModel> = _dozeTransitionModel
+
private val _wakefulnessState = MutableStateFlow(WakefulnessModel.ASLEEP)
override val wakefulnessState: Flow<WakefulnessModel> = _wakefulnessState
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/condition/FakeCondition.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/condition/FakeCondition.java
index 1353ad2..07ed110 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/condition/FakeCondition.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/condition/FakeCondition.java
@@ -25,15 +25,21 @@
super();
}
- FakeCondition(Boolean initialValue, Boolean overriding) {
+ FakeCondition(Boolean initialValue) {
+ super(initialValue, false);
+ }
+
+ FakeCondition(Boolean initialValue, boolean overriding) {
super(initialValue, overriding);
}
@Override
- public void start() {}
+ public void start() {
+ }
@Override
- public void stop() {}
+ public void stop() {
+ }
public void fakeUpdateCondition(boolean isConditionMet) {
updateCondition(isConditionMet);
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
index 5c92b34..06ca153 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
@@ -14,7 +14,6 @@
*/
package com.android.systemui.unfold.util
-import android.animation.ValueAnimator
import android.content.ContentResolver
import android.database.ContentObserver
import android.provider.Settings
@@ -46,13 +45,15 @@
contentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
/* notifyForDescendants= */ false,
- animatorDurationScaleObserver)
+ animatorDurationScaleObserver
+ )
onAnimatorScaleChanged()
}
private fun onAnimatorScaleChanged() {
- val animationsEnabled = ValueAnimator.areAnimatorsEnabled()
- scopedUnfoldTransitionProgressProvider.setReadyToHandleTransition(animationsEnabled)
+ scopedUnfoldTransitionProgressProvider.setReadyToHandleTransition(
+ contentResolver.areAnimationsEnabled()
+ )
}
override fun addCallback(listener: TransitionProgressListener) {
@@ -74,4 +75,18 @@
progressProvider: UnfoldTransitionProgressProvider
): ScaleAwareTransitionProgressProvider
}
+
+ companion object {
+ fun ContentResolver.areAnimationsEnabled(): Boolean {
+ val animationScale =
+ Settings.Global.getStringForUser(
+ this,
+ Settings.Global.ANIMATOR_DURATION_SCALE,
+ this.userId
+ )
+ ?.toFloatOrNull()
+ ?: 1f
+ return animationScale != 0f
+ }
+ }
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java
index f8d4b8f..42fe9d8 100644
--- a/services/core/java/com/android/server/devicestate/DeviceState.java
+++ b/services/core/java/com/android/server/devicestate/DeviceState.java
@@ -18,11 +18,11 @@
import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
-import static android.view.Display.DEFAULT_DISPLAY;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.hardware.devicestate.DeviceStateManager;
import com.android.internal.util.Preconditions;
@@ -55,6 +55,15 @@
*/
public static final int FLAG_APP_INACCESSIBLE = 1 << 1;
+ /**
+ * Some device states can be both entered through a physical configuration as well as emulation
+ * through {@link DeviceStateManager#requestState}, while some states can only be entered
+ * through emulation and have no physical configuration to match.
+ *
+ * This flag indicates that the corresponding state can only be entered through emulation.
+ */
+ public static final int FLAG_EMULATED_ONLY = 1 << 2;
+
/** @hide */
@IntDef(prefix = {"FLAG_"}, flag = true, value = {
FLAG_CANCEL_OVERRIDE_REQUESTS,
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 44c8e18..925fc21 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -17,6 +17,11 @@
package com.android.server.devicestate;
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.hardware.devicestate.DeviceStateManager.ACTION_SHOW_REAR_DISPLAY_OVERLAY;
+import static android.hardware.devicestate.DeviceStateManager.EXTRA_ORIGINAL_DEVICE_BASE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
@@ -31,7 +36,10 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.app.WindowConfiguration;
import android.content.Context;
+import android.content.Intent;
import android.hardware.devicestate.DeviceStateInfo;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateManagerInternal;
@@ -157,6 +165,15 @@
private Set<Integer> mDeviceStatesAvailableForAppRequests;
+ private Set<Integer> mFoldedDeviceStates;
+
+ @Nullable
+ private DeviceState mRearDisplayState;
+
+ // TODO(259328837) Generalize for all pending feature requests in the future
+ @Nullable
+ private OverrideRequest mRearDisplayPendingOverrideRequest;
+
@VisibleForTesting
interface SystemPropertySetter {
void setDebugTracingDeviceStateProperty(String value);
@@ -201,6 +218,7 @@
synchronized (mLock) {
readStatesAvailableForRequestFromApps();
+ mFoldedDeviceStates = readFoldedStates();
}
}
@@ -350,6 +368,8 @@
mOverrideRequestController.handleNewSupportedStates(newStateIdentifiers);
updatePendingStateLocked();
+ setRearDisplayStateLocked();
+
if (!mPendingState.isPresent()) {
// If the change in the supported states didn't result in a change of the pending
// state commitPendingState() will never be called and the callbacks will never be
@@ -361,6 +381,15 @@
}
}
+ @GuardedBy("mLock")
+ private void setRearDisplayStateLocked() {
+ int rearDisplayIdentifier = getContext().getResources().getInteger(
+ R.integer.config_deviceStateRearDisplay);
+ if (rearDisplayIdentifier != INVALID_DEVICE_STATE) {
+ mRearDisplayState = mDeviceStates.get(rearDisplayIdentifier);
+ }
+ }
+
/**
* Returns {@code true} if the provided state is supported. Requires that
* {@link #mDeviceStates} is sorted prior to calling.
@@ -398,6 +427,10 @@
// Base state hasn't changed. Nothing to do.
return;
}
+ // There is a pending rear display request, so we check if the overlay should be closed
+ if (mRearDisplayPendingOverrideRequest != null) {
+ handleRearDisplayBaseStateChangedLocked(identifier);
+ }
mBaseState = Optional.of(baseState);
if (baseState.hasFlag(FLAG_CANCEL_OVERRIDE_REQUESTS)) {
@@ -663,7 +696,7 @@
}
private void requestStateInternal(int state, int flags, int callingPid,
- @NonNull IBinder token) {
+ @NonNull IBinder token, boolean hasControlDeviceStatePermission) {
synchronized (mLock) {
final ProcessRecord processRecord = mProcessRecords.get(callingPid);
if (processRecord == null) {
@@ -685,10 +718,34 @@
OverrideRequest request = new OverrideRequest(token, callingPid, state, flags,
OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
- mOverrideRequestController.addRequest(request);
+
+ // If we don't have the CONTROL_DEVICE_STATE permission, we want to show the overlay
+ if (!hasControlDeviceStatePermission && mRearDisplayState != null
+ && state == mRearDisplayState.getIdentifier()) {
+ showRearDisplayEducationalOverlayLocked(request);
+ } else {
+ mOverrideRequestController.addRequest(request);
+ }
}
}
+ /**
+ * If we get a request to enter rear display mode, we need to display an educational
+ * overlay to let the user know what will happen. This creates the pending request and then
+ * launches the {@link RearDisplayEducationActivity}
+ */
+ @GuardedBy("mLock")
+ private void showRearDisplayEducationalOverlayLocked(OverrideRequest request) {
+ mRearDisplayPendingOverrideRequest = request;
+
+ Intent intent = new Intent(ACTION_SHOW_REAR_DISPLAY_OVERLAY);
+ intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(EXTRA_ORIGINAL_DEVICE_BASE_STATE, mBaseState.get().getIdentifier());
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
+ getUiContext().startActivity(intent, options.toBundle());
+ }
+
private void cancelStateRequestInternal(int callingPid) {
synchronized (mLock) {
final ProcessRecord processRecord = mProcessRecords.get(callingPid);
@@ -738,6 +795,27 @@
}
}
+ /**
+ * Adds the rear display state request to the {@link OverrideRequestController} if the
+ * educational overlay was closed in a way that should enable the feature, and cancels the
+ * request if it was dismissed in a way that should cancel the feature.
+ */
+ private void onStateRequestOverlayDismissedInternal(boolean shouldCancelRequest) {
+ if (mRearDisplayPendingOverrideRequest != null) {
+ synchronized (mLock) {
+ if (shouldCancelRequest) {
+ ProcessRecord processRecord = mProcessRecords.get(
+ mRearDisplayPendingOverrideRequest.getPid());
+ processRecord.notifyRequestCanceledAsync(
+ mRearDisplayPendingOverrideRequest.getToken());
+ } else {
+ mOverrideRequestController.addRequest(mRearDisplayPendingOverrideRequest);
+ }
+ mRearDisplayPendingOverrideRequest = null;
+ }
+ }
+ }
+
private void dumpInternal(PrintWriter pw) {
pw.println("DEVICE STATE MANAGER (dumpsys device_state)");
@@ -823,6 +901,16 @@
}
}
+ private Set<Integer> readFoldedStates() {
+ Set<Integer> foldedStates = new HashSet();
+ int[] mFoldedStatesArray = getContext().getResources().getIntArray(
+ com.android.internal.R.array.config_foldedDeviceStates);
+ for (int i = 0; i < mFoldedStatesArray.length; i++) {
+ foldedStates.add(mFoldedStatesArray[i]);
+ }
+ return foldedStates;
+ }
+
@GuardedBy("mLock")
private boolean isValidState(int state) {
for (int i = 0; i < mDeviceStates.size(); i++) {
@@ -833,6 +921,28 @@
return false;
}
+ /**
+ * If the device is being opened, in response to the rear display educational overlay, we should
+ * dismiss the overlay and enter the mode.
+ */
+ @GuardedBy("mLock")
+ private void handleRearDisplayBaseStateChangedLocked(int newBaseState) {
+ if (isDeviceOpeningLocked(newBaseState)) {
+ onStateRequestOverlayDismissedInternal(false);
+ }
+ }
+
+ /**
+ * Determines if the device is being opened and if we are going from a folded state to a
+ * non-folded state.
+ */
+ @GuardedBy("mLock")
+ private boolean isDeviceOpeningLocked(int newBaseState) {
+ return mBaseState.filter(
+ deviceState -> mFoldedDeviceStates.contains(deviceState.getIdentifier())
+ && !mFoldedDeviceStates.contains(newBaseState)).isPresent();
+ }
+
private final class DeviceStateProviderListener implements DeviceStateProvider.Listener {
@IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int mCurrentBaseState;
@@ -850,6 +960,7 @@
if (identifier < MINIMUM_DEVICE_STATE || identifier > MAXIMUM_DEVICE_STATE) {
throw new IllegalArgumentException("Invalid identifier: " + identifier);
}
+
mCurrentBaseState = identifier;
setBaseState(identifier);
}
@@ -977,9 +1088,12 @@
throw new IllegalArgumentException("Request token must not be null.");
}
+ boolean hasControlStatePermission = getContext().checkCallingOrSelfPermission(
+ CONTROL_DEVICE_STATE) == PERMISSION_GRANTED;
+
final long callingIdentity = Binder.clearCallingIdentity();
try {
- requestStateInternal(state, flags, callingPid, token);
+ requestStateInternal(state, flags, callingPid, token, hasControlStatePermission);
} finally {
Binder.restoreCallingIdentity(callingIdentity);
}
@@ -1034,6 +1148,21 @@
}
@Override // Binder call
+ public void onStateRequestOverlayDismissed(boolean shouldCancelRequest) {
+
+ getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
+ "CONTROL_DEVICE_STATE permission required to control the state request "
+ + "overlay");
+
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ onStateRequestOverlayDismissedInternal(shouldCancelRequest);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override // Binder call
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver result) {
new DeviceStateManagerShellCommand(DeviceStateManagerService.this)
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 912b1b2..c131ed6 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -523,8 +523,10 @@
* changed
*/
public void defaultDisplayDeviceUpdated(DisplayDeviceConfig displayDeviceConfig) {
- mSettingsObserver.setRefreshRates(displayDeviceConfig);
- mBrightnessObserver.updateBlockingZoneThresholds(displayDeviceConfig);
+ mSettingsObserver.setRefreshRates(displayDeviceConfig,
+ /* attemptLoadingFromDeviceConfig= */ true);
+ mBrightnessObserver.updateBlockingZoneThresholds(displayDeviceConfig,
+ /* attemptLoadingFromDeviceConfig= */ true);
}
/**
@@ -1142,19 +1144,25 @@
SettingsObserver(@NonNull Context context, @NonNull Handler handler) {
super(handler);
mContext = context;
- setRefreshRates(/* displayDeviceConfig= */ null);
+ // We don't want to load from the DeviceConfig while constructing since this leads to
+ // a spike in the latency of DisplayManagerService startup. This happens because
+ // reading from the DeviceConfig is an intensive IO operation and having it in the
+ // startup phase where we thrive to keep the latency very low has significant impact.
+ setRefreshRates(/* displayDeviceConfig= */ null,
+ /* attemptLoadingFromDeviceConfig= */ false);
}
/**
* This is used to update the refresh rate configs from the DeviceConfig, which
* if missing from DisplayDeviceConfig, and finally fallback to config.xml.
*/
- public void setRefreshRates(DisplayDeviceConfig displayDeviceConfig) {
- setDefaultPeakRefreshRate(displayDeviceConfig);
+ public void setRefreshRates(DisplayDeviceConfig displayDeviceConfig,
+ boolean attemptLoadingFromDeviceConfig) {
+ setDefaultPeakRefreshRate(displayDeviceConfig, attemptLoadingFromDeviceConfig);
mDefaultRefreshRate =
(displayDeviceConfig == null) ? (float) mContext.getResources().getInteger(
- R.integer.config_defaultRefreshRate)
- : (float) displayDeviceConfig.getDefaultRefreshRate();
+ R.integer.config_defaultRefreshRate)
+ : (float) displayDeviceConfig.getDefaultRefreshRate();
}
public void observe() {
@@ -1215,13 +1223,27 @@
}
}
- private void setDefaultPeakRefreshRate(DisplayDeviceConfig displayDeviceConfig) {
+ @VisibleForTesting
+ float getDefaultRefreshRate() {
+ return mDefaultRefreshRate;
+ }
+
+ @VisibleForTesting
+ float getDefaultPeakRefreshRate() {
+ return mDefaultPeakRefreshRate;
+ }
+
+ private void setDefaultPeakRefreshRate(DisplayDeviceConfig displayDeviceConfig,
+ boolean attemptLoadingFromDeviceConfig) {
Float defaultPeakRefreshRate = null;
- try {
- defaultPeakRefreshRate =
+
+ if (attemptLoadingFromDeviceConfig) {
+ try {
+ defaultPeakRefreshRate =
mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
- } catch (Exception exception) {
- // Do nothing
+ } catch (Exception exception) {
+ // Do nothing
+ }
}
if (defaultPeakRefreshRate == null) {
defaultPeakRefreshRate =
@@ -1544,7 +1566,8 @@
mContext = context;
mHandler = handler;
mInjector = injector;
- updateBlockingZoneThresholds(/* displayDeviceConfig= */ null);
+ updateBlockingZoneThresholds(/* displayDeviceConfig= */ null,
+ /* attemptLoadingFromDeviceConfig= */ false);
mRefreshRateInHighZone = context.getResources().getInteger(
R.integer.config_fixedRefreshRateInHighZone);
}
@@ -1553,22 +1576,44 @@
* This is used to update the blocking zone thresholds from the DeviceConfig, which
* if missing from DisplayDeviceConfig, and finally fallback to config.xml.
*/
- public void updateBlockingZoneThresholds(DisplayDeviceConfig displayDeviceConfig) {
- loadLowBrightnessThresholds(displayDeviceConfig);
- loadHighBrightnessThresholds(displayDeviceConfig);
+ public void updateBlockingZoneThresholds(DisplayDeviceConfig displayDeviceConfig,
+ boolean attemptLoadingFromDeviceConfig) {
+ loadLowBrightnessThresholds(displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ loadHighBrightnessThresholds(displayDeviceConfig, attemptLoadingFromDeviceConfig);
}
- private void loadLowBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig) {
+ @VisibleForTesting
+ int[] getLowDisplayBrightnessThreshold() {
+ return mLowDisplayBrightnessThresholds;
+ }
+
+ @VisibleForTesting
+ int[] getLowAmbientBrightnessThreshold() {
+ return mLowAmbientBrightnessThresholds;
+ }
+
+ @VisibleForTesting
+ int[] getHighDisplayBrightnessThreshold() {
+ return mHighDisplayBrightnessThresholds;
+ }
+
+ @VisibleForTesting
+ int[] getHighAmbientBrightnessThreshold() {
+ return mHighAmbientBrightnessThresholds;
+ }
+
+ private void loadLowBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig,
+ boolean attemptLoadingFromDeviceConfig) {
mLowDisplayBrightnessThresholds = loadBrightnessThresholds(
() -> mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getLowDisplayBrightnessThresholds(),
R.array.config_brightnessThresholdsOfPeakRefreshRate,
- displayDeviceConfig);
+ displayDeviceConfig, attemptLoadingFromDeviceConfig);
mLowAmbientBrightnessThresholds = loadBrightnessThresholds(
() -> mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getLowAmbientBrightnessThresholds(),
R.array.config_ambientThresholdsOfPeakRefreshRate,
- displayDeviceConfig);
+ displayDeviceConfig, attemptLoadingFromDeviceConfig);
if (mLowDisplayBrightnessThresholds.length != mLowAmbientBrightnessThresholds.length) {
throw new RuntimeException("display low brightness threshold array and ambient "
+ "brightness threshold array have different length: "
@@ -1579,17 +1624,18 @@
}
}
- private void loadHighBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig) {
+ private void loadHighBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig,
+ boolean attemptLoadingFromDeviceConfig) {
mHighDisplayBrightnessThresholds = loadBrightnessThresholds(
() -> mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getHighDisplayBrightnessThresholds(),
R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig);
+ displayDeviceConfig, attemptLoadingFromDeviceConfig);
mHighAmbientBrightnessThresholds = loadBrightnessThresholds(
() -> mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getHighAmbientBrightnessThresholds(),
R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig);
+ displayDeviceConfig, attemptLoadingFromDeviceConfig);
if (mHighDisplayBrightnessThresholds.length
!= mHighAmbientBrightnessThresholds.length) {
throw new RuntimeException("display high brightness threshold array and ambient "
@@ -1605,13 +1651,16 @@
Callable<int[]> loadFromDeviceConfigDisplaySettingsCallable,
Callable<int[]> loadFromDisplayDeviceConfigCallable,
int brightnessThresholdOfFixedRefreshRateKey,
- DisplayDeviceConfig displayDeviceConfig) {
+ DisplayDeviceConfig displayDeviceConfig, boolean attemptLoadingFromDeviceConfig) {
int[] brightnessThresholds = null;
- try {
- brightnessThresholds =
+
+ if (attemptLoadingFromDeviceConfig) {
+ try {
+ brightnessThresholds =
loadFromDeviceConfigDisplaySettingsCallable.call();
- } catch (Exception exception) {
- // Do nothing
+ } catch (Exception exception) {
+ // Do nothing
+ }
}
if (brightnessThresholds == null) {
try {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 36bff20..c426e69 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1677,7 +1677,7 @@
mTempBrightnessEvent.rbcStrength = mCdsi != null
? mCdsi.getReduceBrightColorsStrength() : -1;
mTempBrightnessEvent.powerFactor = mPowerRequest.screenLowPowerBrightnessFactor;
-
+ mTempBrightnessEvent.wasShortTermModelActive = hadUserBrightnessPoint;
// Temporary is what we use during slider interactions. We avoid logging those so that
// we don't spam logcat when the slider is being used.
boolean tempToTempTransition =
@@ -1687,12 +1687,6 @@
|| brightnessAdjustmentFlags != 0) {
float lastBrightness = mLastBrightnessEvent.brightness;
mTempBrightnessEvent.initialBrightness = lastBrightness;
- mTempBrightnessEvent.fastAmbientLux =
- mAutomaticBrightnessController == null
- ? -1f : mAutomaticBrightnessController.getFastAmbientLux();
- mTempBrightnessEvent.slowAmbientLux =
- mAutomaticBrightnessController == null
- ? -1f : mAutomaticBrightnessController.getSlowAmbientLux();
mTempBrightnessEvent.automaticBrightnessEnabled = mPowerRequest.useAutoBrightness;
mLastBrightnessEvent.copyFrom(mTempBrightnessEvent);
BrightnessEvent newEvent = new BrightnessEvent(mTempBrightnessEvent);
@@ -2579,7 +2573,7 @@
pw.println(" mCachedBrightnessInfo.hbmTransitionPoint=" +
mCachedBrightnessInfo.hbmTransitionPoint.value);
pw.println(" mCachedBrightnessInfo.brightnessMaxReason =" +
- mCachedBrightnessInfo.brightnessMaxReason .value);
+ mCachedBrightnessInfo.brightnessMaxReason.value);
}
pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
@@ -2816,9 +2810,9 @@
FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED,
convertToNits(event.initialBrightness),
convertToNits(event.brightness),
- event.slowAmbientLux,
+ event.lux,
event.physicalDisplayId,
- event.isShortTermModelActive(),
+ event.wasShortTermModelActive,
appliedPowerFactor,
appliedRbcStrength,
appliedHbmMaxNits,
@@ -2841,8 +2835,6 @@
public int displayId;
public String physicalDisplayId;
public float lux;
- public float fastAmbientLux;
- public float slowAmbientLux;
public float preThresholdLux;
public long time;
public float brightness;
@@ -2854,6 +2846,7 @@
public float thermalMax;
public float powerFactor;
public int hbmMode;
+ public boolean wasShortTermModelActive;
public int flags;
public int adjustmentFlags;
public boolean automaticBrightnessEnabled;
@@ -2872,8 +2865,6 @@
physicalDisplayId = that.physicalDisplayId;
time = that.time;
lux = that.lux;
- fastAmbientLux = that.fastAmbientLux;
- slowAmbientLux = that.slowAmbientLux;
preThresholdLux = that.preThresholdLux;
brightness = that.brightness;
initialBrightness = that.initialBrightness;
@@ -2885,6 +2876,7 @@
powerFactor = that.powerFactor;
flags = that.flags;
hbmMode = that.hbmMode;
+ wasShortTermModelActive = that.wasShortTermModelActive;
reason.set(that.reason);
adjustmentFlags = that.adjustmentFlags;
automaticBrightnessEnabled = that.automaticBrightnessEnabled;
@@ -2897,13 +2889,12 @@
initialBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
recommendedBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
lux = 0f;
- fastAmbientLux = 0f;
- slowAmbientLux = 0f;
preThresholdLux = 0f;
preThresholdBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
hbmMax = PowerManager.BRIGHTNESS_MAX;
rbcStrength = 0;
powerFactor = 1f;
+ wasShortTermModelActive = false;
thermalMax = PowerManager.BRIGHTNESS_MAX;
flags = 0;
hbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
@@ -2938,10 +2929,6 @@
&& Float.floatToRawIntBits(preThresholdBrightness)
== Float.floatToRawIntBits(that.preThresholdBrightness)
&& Float.floatToRawIntBits(lux) == Float.floatToRawIntBits(that.lux)
- && Float.floatToRawIntBits(fastAmbientLux)
- == Float.floatToRawIntBits(that.fastAmbientLux)
- && Float.floatToRawIntBits(slowAmbientLux)
- == Float.floatToRawIntBits(that.slowAmbientLux)
&& Float.floatToRawIntBits(preThresholdLux)
== Float.floatToRawIntBits(that.preThresholdLux)
&& rbcStrength == that.rbcStrength
@@ -2951,6 +2938,7 @@
== Float.floatToRawIntBits(that.thermalMax)
&& Float.floatToRawIntBits(powerFactor)
== Float.floatToRawIntBits(that.powerFactor)
+ && wasShortTermModelActive == that.wasShortTermModelActive
&& flags == that.flags
&& adjustmentFlags == that.adjustmentFlags
&& reason.equals(that.reason)
@@ -2967,14 +2955,13 @@
+ ", rcmdBrt=" + recommendedBrightness
+ ", preBrt=" + preThresholdBrightness
+ ", lux=" + lux
- + ", fastAmbientLux=" + fastAmbientLux
- + ", slowAmbientLux=" + slowAmbientLux
+ ", preLux=" + preThresholdLux
+ ", hbmMax=" + hbmMax
+ ", hbmMode=" + BrightnessInfo.hbmToString(hbmMode)
+ ", rbcStrength=" + rbcStrength
+ ", powerFactor=" + powerFactor
+ ", thrmMax=" + thermalMax
+ + ", wasShortTermModelActive=" + wasShortTermModelActive
+ ", flags=" + flagsToString()
+ ", reason=" + reason.toString(adjustmentFlags)
+ ", autoBrightness=" + automaticBrightnessEnabled;
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 9b7d19a..f867808 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -96,6 +96,7 @@
private static final String CONFIG_FILE_NAME = "device_state_configuration.xml";
private static final String FLAG_CANCEL_OVERRIDE_REQUESTS = "FLAG_CANCEL_OVERRIDE_REQUESTS";
private static final String FLAG_APP_INACCESSIBLE = "FLAG_APP_INACCESSIBLE";
+ private static final String FLAG_EMULATED_ONLY = "FLAG_EMULATED_ONLY";
/** Interface that allows reading the device state configuration. */
interface ReadableConfig {
@@ -149,6 +150,8 @@
case FLAG_APP_INACCESSIBLE:
flags |= DeviceState.FLAG_APP_INACCESSIBLE;
break;
+ case FLAG_EMULATED_ONLY:
+ flags |= DeviceState.FLAG_EMULATED_ONLY;
default:
Slog.w(TAG, "Parsed unknown flag with name: "
+ configFlagString);
@@ -225,7 +228,13 @@
}
final Conditions conditions = stateConditions.get(i);
if (conditions == null) {
- mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER);
+ // If this state has the FLAG_EMULATED_ONLY flag on it, it should never be triggered
+ // by a physical hardware change, and should always return false for it's conditions
+ if (deviceStates.get(i).hasFlag(DeviceState.FLAG_EMULATED_ONLY)) {
+ mStateConditions.put(state, FALSE_BOOLEAN_SUPPLIER);
+ } else {
+ mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER);
+ }
continue;
}
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 509b1e6..2e716ae 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -34,6 +34,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
@@ -322,9 +323,10 @@
+ " is not in a task belong to the organizer app.");
return null;
}
- if (task.isAllowedToEmbedActivity(activity, mOrganizerUid) != EMBEDDING_ALLOWED) {
+ if (task.isAllowedToEmbedActivity(activity, mOrganizerUid) != EMBEDDING_ALLOWED
+ || !task.isAllowedToEmbedActivityInTrustedMode(activity, mOrganizerUid)) {
Slog.d(TAG, "Reparent activity=" + activity.token
- + " is not allowed to be embedded.");
+ + " is not allowed to be embedded in trusted mode.");
return null;
}
@@ -350,7 +352,7 @@
activity.token, task.mTaskId);
return new TaskFragmentTransaction.Change(TYPE_ACTIVITY_REPARENTED_TO_TASK)
.setTaskId(task.mTaskId)
- .setActivityIntent(activity.intent)
+ .setActivityIntent(trimIntent(activity.intent))
.setActivityToken(activityToken);
}
@@ -1095,4 +1097,15 @@
return false;
}
}
+
+ /**
+ * Trims the given Intent to only those that are needed to for embedding rules. This helps to
+ * make it safer for cross-uid embedding even if we only send the Intent for trusted embedding.
+ */
+ private static Intent trimIntent(@NonNull Intent intent) {
+ return new Intent()
+ .setComponent(intent.getComponent())
+ .setPackage(intent.getPackage())
+ .setAction(intent.getAction());
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 4edda74..aa51ac3 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -181,26 +181,34 @@
// is set with the suggestedDisplayArea. If it is set, but the eventual TaskDisplayArea is
// different, we should recalculating the bounds.
boolean hasInitialBoundsForSuggestedDisplayAreaInFreeformWindow = false;
- final boolean canApplyFreeformPolicy =
+ // Note that initial bounds needs to be set to fullscreen tasks too as it's used as restore
+ // bounds.
+ final boolean canCalculateBoundsForFullscreenTask =
+ canCalculateBoundsForFullscreenTask(suggestedDisplayArea, launchMode);
+ final boolean canApplyFreeformWindowPolicy =
canApplyFreeformWindowPolicy(suggestedDisplayArea, launchMode);
- if (mSupervisor.canUseActivityOptionsLaunchBounds(options)
- && (canApplyFreeformPolicy || canApplyPipWindowPolicy(launchMode))) {
+ final boolean canApplyWindowLayout = layout != null
+ && (canApplyFreeformWindowPolicy || canCalculateBoundsForFullscreenTask);
+ final boolean canApplyBoundsFromActivityOptions =
+ mSupervisor.canUseActivityOptionsLaunchBounds(options)
+ && (canApplyFreeformWindowPolicy
+ || canApplyPipWindowPolicy(launchMode)
+ || canCalculateBoundsForFullscreenTask);
+
+ if (canApplyBoundsFromActivityOptions) {
hasInitialBounds = true;
- launchMode = launchMode == WINDOWING_MODE_UNDEFINED
+ // |launchMode| at this point can be fullscreen, PIP, MultiWindow, etc. Only set
+ // freeform windowing mode if appropriate by checking |canApplyFreeformWindowPolicy|.
+ launchMode = launchMode == WINDOWING_MODE_UNDEFINED && canApplyFreeformWindowPolicy
? WINDOWING_MODE_FREEFORM
: launchMode;
outParams.mBounds.set(options.getLaunchBounds());
if (DEBUG) appendLog("activity-options-bounds=" + outParams.mBounds);
- } else if (launchMode == WINDOWING_MODE_PINNED) {
- // System controls PIP window's bounds, so don't apply launch bounds.
- if (DEBUG) appendLog("empty-window-layout-for-pip");
- } else if (launchMode == WINDOWING_MODE_FULLSCREEN) {
- if (DEBUG) appendLog("activity-options-fullscreen=" + outParams.mBounds);
- } else if (layout != null && canApplyFreeformPolicy) {
+ } else if (canApplyWindowLayout) {
mTmpBounds.set(currentParams.mBounds);
getLayoutBounds(suggestedDisplayArea, root, layout, mTmpBounds);
if (!mTmpBounds.isEmpty()) {
- launchMode = WINDOWING_MODE_FREEFORM;
+ launchMode = canApplyFreeformWindowPolicy ? WINDOWING_MODE_FREEFORM : launchMode;
outParams.mBounds.set(mTmpBounds);
hasInitialBounds = true;
hasInitialBoundsForSuggestedDisplayAreaInFreeformWindow = true;
@@ -210,6 +218,8 @@
}
} else if (launchMode == WINDOWING_MODE_MULTI_WINDOW
&& options != null && options.getLaunchBounds() != null) {
+ // TODO: Investigate whether we can migrate this clause to the
+ // |canApplyBoundsFromActivityOptions| case above.
outParams.mBounds.set(options.getLaunchBounds());
hasInitialBounds = true;
if (DEBUG) appendLog("multiwindow-activity-options-bounds=" + outParams.mBounds);
@@ -249,11 +259,9 @@
if (!currentParams.mBounds.isEmpty()) {
// Carry over bounds from callers regardless of launch mode because bounds is still
// used to restore last non-fullscreen bounds when launch mode is not freeform.
- // Therefore it's not a resolution step for non-freeform launch mode and only
- // consider it fully resolved only when launch mode is freeform.
outParams.mBounds.set(currentParams.mBounds);
+ fullyResolvedCurrentParam = true;
if (launchMode == WINDOWING_MODE_FREEFORM) {
- fullyResolvedCurrentParam = true;
if (DEBUG) appendLog("inherit-bounds=" + outParams.mBounds);
}
}
@@ -368,7 +376,7 @@
// an existing task.
adjustBoundsToAvoidConflictInDisplayArea(taskDisplayArea, outParams.mBounds);
}
- } else if (taskDisplayArea.inFreeformWindowingMode()) {
+ } else {
if (source != null && source.inFreeformWindowingMode()
&& resolvedMode == WINDOWING_MODE_FREEFORM
&& outParams.mBounds.isEmpty()
@@ -545,10 +553,19 @@
return display.getDisplayId() == source.getDisplayId();
}
+ private boolean canCalculateBoundsForFullscreenTask(@NonNull TaskDisplayArea displayArea,
+ int launchMode) {
+ return mSupervisor.mService.mSupportsFreeformWindowManagement
+ && ((displayArea.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+ && launchMode == WINDOWING_MODE_UNDEFINED)
+ || launchMode == WINDOWING_MODE_FULLSCREEN);
+ }
+
private boolean canApplyFreeformWindowPolicy(@NonNull TaskDisplayArea suggestedDisplayArea,
int launchMode) {
return mSupervisor.mService.mSupportsFreeformWindowManagement
- && (suggestedDisplayArea.inFreeformWindowingMode()
+ && ((suggestedDisplayArea.inFreeformWindowingMode()
+ && launchMode == WINDOWING_MODE_UNDEFINED)
|| launchMode == WINDOWING_MODE_FREEFORM);
}
@@ -710,16 +727,10 @@
private void getTaskBounds(@NonNull ActivityRecord root, @NonNull TaskDisplayArea displayArea,
@NonNull ActivityInfo.WindowLayout layout, int resolvedMode, boolean hasInitialBounds,
@NonNull Rect inOutBounds) {
- if (resolvedMode == WINDOWING_MODE_FULLSCREEN) {
- // We don't handle letterboxing here. Letterboxing will be handled by valid checks
- // later.
- inOutBounds.setEmpty();
- if (DEBUG) appendLog("maximized-bounds");
- return;
- }
-
- if (resolvedMode != WINDOWING_MODE_FREEFORM) {
- // We don't apply freeform bounds adjustment to other windowing modes.
+ if (resolvedMode != WINDOWING_MODE_FREEFORM
+ && resolvedMode != WINDOWING_MODE_FULLSCREEN) {
+ // This function should be used only for freeform bounds adjustment. Freeform bounds
+ // needs to be set to fullscreen tasks too as restore bounds.
if (DEBUG) {
appendLog("skip-bounds-" + WindowConfiguration.windowingModeToString(resolvedMode));
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index a47c529..fa4a9de 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -1522,6 +1522,7 @@
gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY, USER_ID_1);
}
+ @Test
public void testResetInterventions_onDeviceConfigReset() throws Exception {
mockModifyGameModeGranted();
String configStringBefore =
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 18dd264..fb0cdfa 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -21,6 +21,7 @@
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE;
@@ -31,6 +32,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -48,6 +51,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
@@ -76,6 +80,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.Preconditions;
import com.android.internal.util.test.FakeSettingsProvider;
@@ -1855,16 +1860,83 @@
@Test
public void testNotifyDefaultDisplayDeviceUpdated() {
- DisplayDeviceConfig displayDeviceConfig = mock(DisplayDeviceConfig.class);
- when(displayDeviceConfig.getLowDisplayBrightnessThresholds()).thenReturn(new int[]{});
- when(displayDeviceConfig.getLowAmbientBrightnessThresholds()).thenReturn(new int[]{});
- when(displayDeviceConfig.getHighDisplayBrightnessThresholds()).thenReturn(new int[]{});
- when(displayDeviceConfig.getHighAmbientBrightnessThresholds()).thenReturn(new int[]{});
+ Resources resources = mock(Resources.class);
+ when(mContext.getResources()).thenReturn(resources);
+ when(resources.getInteger(com.android.internal.R.integer.config_defaultPeakRefreshRate))
+ .thenReturn(75);
+ when(resources.getInteger(R.integer.config_defaultRefreshRate))
+ .thenReturn(45);
+ when(resources.getIntArray(R.array.config_brightnessThresholdsOfPeakRefreshRate))
+ .thenReturn(new int[]{5});
+ when(resources.getIntArray(R.array.config_ambientThresholdsOfPeakRefreshRate))
+ .thenReturn(new int[]{10});
+ when(
+ resources.getIntArray(R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate))
+ .thenReturn(new int[]{250});
+ when(
+ resources.getIntArray(R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate))
+ .thenReturn(new int[]{7000});
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0);
+ // We don't expect any interaction with DeviceConfig when the director is initialized
+ // because we explicitly avoid doing this as this can lead to a latency spike in the
+ // startup of DisplayManagerService
+ // Verify all the loaded values are from DisplayDeviceConfig
+ assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 45, 0.0);
+ assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 75,
+ 0.0);
+ assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(),
+ new int[]{250});
+ assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(),
+ new int[]{7000});
+ assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThreshold(),
+ new int[]{5});
+ assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(),
+ new int[]{10});
+
+ // Notify that the default display is updated, such that DisplayDeviceConfig has new values
+ DisplayDeviceConfig displayDeviceConfig = mock(DisplayDeviceConfig.class);
+ when(displayDeviceConfig.getDefaultRefreshRate()).thenReturn(50);
+ when(displayDeviceConfig.getDefaultPeakRefreshRate()).thenReturn(55);
+ when(displayDeviceConfig.getLowDisplayBrightnessThresholds()).thenReturn(new int[]{25});
+ when(displayDeviceConfig.getLowAmbientBrightnessThresholds()).thenReturn(new int[]{30});
+ when(displayDeviceConfig.getHighDisplayBrightnessThresholds()).thenReturn(new int[]{210});
+ when(displayDeviceConfig.getHighAmbientBrightnessThresholds()).thenReturn(new int[]{2100});
director.defaultDisplayDeviceUpdated(displayDeviceConfig);
- verify(displayDeviceConfig).getDefaultRefreshRate();
- verify(displayDeviceConfig).getDefaultPeakRefreshRate();
+
+ assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 50, 0.0);
+ assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 55,
+ 0.0);
+ assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(),
+ new int[]{210});
+ assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(),
+ new int[]{2100});
+ assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThreshold(),
+ new int[]{25});
+ assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(),
+ new int[]{30});
+
+ // Notify that the default display is updated, such that DeviceConfig has new values
+ FakeDeviceConfig config = mInjector.getDeviceConfig();
+ config.setDefaultPeakRefreshRate(60);
+ config.setLowAmbientBrightnessThresholds(new int[]{20});
+ config.setLowDisplayBrightnessThresholds(new int[]{10});
+ config.setHighDisplayBrightnessThresholds(new int[]{255});
+ config.setHighAmbientBrightnessThresholds(new int[]{8000});
+
+ director.defaultDisplayDeviceUpdated(displayDeviceConfig);
+
+ assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 50, 0.0);
+ assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 60,
+ 0.0);
+ assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(),
+ new int[]{255});
+ assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(),
+ new int[]{8000});
+ assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThreshold(),
+ new int[]{10});
+ assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(),
+ new int[]{20});
}
private Temperature getSkinTemp(@Temperature.ThrottlingStatus int status) {
@@ -1954,6 +2026,12 @@
String.valueOf(fps));
}
+ void setDefaultPeakRefreshRate(int fps) {
+ putPropertyAndNotify(
+ DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_PEAK_REFRESH_RATE_DEFAULT,
+ String.valueOf(fps));
+ }
+
void setHighDisplayBrightnessThresholds(int[] brightnessThresholds) {
String thresholds = toPropertyValue(brightnessThresholds);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java
index 1246d1e..1be9de7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java
@@ -35,6 +35,7 @@
import junit.framework.Assert;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -72,6 +73,7 @@
mLetterboxConfigurationPersister.start();
}
+ @After
public void tearDown() throws InterruptedException {
deleteConfiguration(mLetterboxConfigurationPersister, mPersisterQueue);
waitForCompletion(mPersisterQueue);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 4202f46..c535182 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -62,10 +62,12 @@
import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
+import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -403,7 +405,7 @@
final TaskFragmentTransaction.Change change = changes.get(0);
assertEquals(TYPE_ACTIVITY_REPARENTED_TO_TASK, change.getType());
assertEquals(task.mTaskId, change.getTaskId());
- assertEquals(activity.intent, change.getActivityIntent());
+ assertIntentsEqualForOrganizer(activity.intent, change.getActivityIntent());
assertNotEquals(activity.token, change.getActivityToken());
mTransaction.reparentActivityToTaskFragment(mFragmentToken, change.getActivityToken());
assertApplyTransactionAllowed(mTransaction);
@@ -415,6 +417,62 @@
}
@Test
+ public void testOnActivityReparentedToTask_untrustedEmbed_notReported() {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizer.getOrganizerToken(), uid,
+ DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME);
+ mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment);
+ final Task task = createTask(mDisplayContent);
+ task.addChild(mTaskFragment, POSITION_TOP);
+ final ActivityRecord activity = createActivityRecord(task);
+
+ // Make sure the activity is embedded in untrusted mode.
+ activity.info.applicationInfo.uid = uid + 1;
+ doReturn(pid + 1).when(activity).getPid();
+ task.effectiveUid = uid;
+ doReturn(EMBEDDING_ALLOWED).when(task).isAllowedToEmbedActivity(activity, uid);
+ doReturn(false).when(task).isAllowedToEmbedActivityInTrustedMode(activity, uid);
+ doReturn(true).when(task).isAllowedToEmbedActivityInUntrustedMode(activity);
+
+ // Notify organizer if it was embedded before entered Pip.
+ // Create a temporary token since the activity doesn't belong to the same process.
+ clearInvocations(mOrganizer);
+ activity.mLastTaskFragmentOrganizerBeforePip = mIOrganizer;
+ mController.onActivityReparentedToTask(activity);
+ mController.dispatchPendingEvents();
+
+ // Disallow organizer to reparent activity that is untrusted embedded.
+ verify(mOrganizer, never()).onTransactionReady(mTransactionCaptor.capture());
+ }
+
+ @Test
+ public void testOnActivityReparentedToTask_trimReportedIntent() {
+ // Make sure the activity pid/uid is the same as the organizer caller.
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final ActivityRecord activity = createActivityRecord(mDisplayContent);
+ final Task task = activity.getTask();
+ activity.info.applicationInfo.uid = uid;
+ doReturn(pid).when(activity).getPid();
+ task.effectiveUid = uid;
+ activity.mLastTaskFragmentOrganizerBeforePip = mIOrganizer;
+
+ // Test the Intent trim in #assertIntentTrimmed
+ activity.intent.setComponent(new ComponentName("TestPackage", "TestClass"))
+ .setPackage("TestPackage")
+ .setAction("TestAction")
+ .setData(mock(Uri.class))
+ .putExtra("Test", 123)
+ .setFlags(10);
+
+ mController.onActivityReparentedToTask(activity);
+ mController.dispatchPendingEvents();
+
+ assertActivityReparentedToTaskTransaction(task.mTaskId, activity.intent, activity.token);
+ }
+
+ @Test
public void testRegisterRemoteAnimations() {
mController.registerRemoteAnimations(mIOrganizer, TASK_ID, mDefinition);
@@ -1425,7 +1483,8 @@
final TaskFragmentTransaction.Change change = changes.remove(0);
assertEquals(TYPE_ACTIVITY_REPARENTED_TO_TASK, change.getType());
assertEquals(taskId, change.getTaskId());
- assertEquals(intent, change.getActivityIntent());
+ assertIntentsEqualForOrganizer(intent, change.getActivityIntent());
+ assertIntentTrimmed(change.getActivityIntent());
assertEquals(activityToken, change.getActivityToken());
}
@@ -1452,4 +1511,17 @@
mockParent.lastActiveTime = 100;
doReturn(true).when(mockParent).shouldBeVisible(any());
}
+
+ private static void assertIntentsEqualForOrganizer(@NonNull Intent expected,
+ @NonNull Intent actual) {
+ assertEquals(expected.getComponent(), actual.getComponent());
+ assertEquals(expected.getPackage(), actual.getPackage());
+ assertEquals(expected.getAction(), actual.getAction());
+ }
+
+ private static void assertIntentTrimmed(@NonNull Intent intent) {
+ assertNull(intent.getData());
+ assertNull(intent.getExtras());
+ assertEquals(0, intent.getFlags());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index a6c5fd8..dce9f66 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -571,6 +571,29 @@
}
@Test
+ public void testBoundsInOptionsInfersFullscreenWithBoundsOnFreeformSupportFullscreenDisplay() {
+ final TestDisplayContent fullscreenDisplay = createNewDisplayContent(
+ WINDOWING_MODE_FULLSCREEN);
+ mAtm.mTaskSupervisor.mService.mSupportsFreeformWindowManagement = true;
+
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ final Rect expectedBounds = new Rect(0, 0, 100, 100);
+ options.setLaunchBounds(expectedBounds);
+
+ mCurrent.mPreferredTaskDisplayArea = fullscreenDisplay.getDefaultTaskDisplayArea();
+
+ assertEquals(RESULT_CONTINUE,
+ new CalculateRequestBuilder().setOptions(options).calculate());
+
+ // Setting bounds shouldn't lead to freeform windowing mode on fullscreen display by
+ // default (even with freeform support), but we need to check here if the bounds is set even
+ // with fullscreen windowing mode in case it's restored later.
+ assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
+ WINDOWING_MODE_FULLSCREEN);
+ assertEquals(expectedBounds, mResult.mBounds);
+ }
+
+ @Test
public void testInheritsFreeformModeFromSourceOnFullscreenDisplay() {
final TestDisplayContent fullscreenDisplay = createNewDisplayContent(
WINDOWING_MODE_FULLSCREEN);
@@ -952,6 +975,8 @@
WINDOWING_MODE_FULLSCREEN);
final ActivityRecord source = createSourceActivity(fullscreenDisplay);
source.getTask().setWindowingMode(WINDOWING_MODE_FREEFORM);
+ // Set some bounds to avoid conflict with the other activity.
+ source.setBounds(100, 100, 200, 200);
final ActivityOptions options = ActivityOptions.makeBasic();
final Rect expected = new Rect(0, 0, 150, 150);