summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/ViewRootImpl.java71
-rw-r--r--core/java/android/window/WindowContainerTransaction.java41
-rw-r--r--libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java77
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java67
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java32
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java21
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java25
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java3
-rw-r--r--packages/SettingsProvider/res/values/defaults.xml3
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java14
-rw-r--r--packages/SystemUI/AndroidManifest.xml23
-rw-r--r--packages/SystemUI/res/drawable/ic_note_task_button.xml28
-rw-r--r--packages/SystemUI/res/layout/media_session_view.xml1
-rw-r--r--packages/SystemUI/res/values/flags.xml4
-rw-r--r--packages/SystemUI/res/values/strings.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt81
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt79
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java150
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt164
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java89
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java71
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java72
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java205
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java103
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java64
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/DataConnectionState.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/HomeWifiViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/KeyguardWifiViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/QsWifiViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt76
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt83
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt164
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepositoryTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt13
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java40
-rw-r--r--services/core/java/com/android/server/display/BrightnessTracker.java89
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java4
-rw-r--r--services/core/java/com/android/server/display/PersistentDataStore.java12
-rw-r--r--services/core/java/com/android/server/notification/SnoozeHelper.java23
-rw-r--r--services/core/java/com/android/server/policy/DeviceStateProviderImpl.java7
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java33
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java33
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java7
-rw-r--r--services/core/java/com/android/server/wm/Task.java14
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java13
-rw-r--r--services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java6
-rw-r--r--services/core/java/com/android/server/wm/Transition.java11
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java24
-rw-r--r--services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java103
-rw-r--r--services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java197
-rw-r--r--services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java34
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java31
102 files changed, 2568 insertions, 824 deletions
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b4698dee6019..d21ba014e166 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -77,6 +77,7 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
@@ -502,6 +503,13 @@ public final class ViewRootImpl implements ViewParent,
Region mTouchableRegion;
Region mPreviousTouchableRegion;
+ private int mMeasuredWidth;
+ private int mMeasuredHeight;
+
+ // This indicates that we've already known the window size but without measuring the views.
+ // If this is true, we must measure the views before laying out them.
+ private boolean mViewMeasureDeferred;
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
int mWidth;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -2566,7 +2574,8 @@ public final class ViewRootImpl implements ViewParent,
}
private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
- final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
+ final Resources res, final int desiredWindowWidth, final int desiredWindowHeight,
+ boolean forRootSizeOnly) {
int childWidthMeasureSpec;
int childHeightMeasureSpec;
boolean windowSizeMayChange = false;
@@ -2622,7 +2631,15 @@ public final class ViewRootImpl implements ViewParent,
lp.privateFlags);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height,
lp.privateFlags);
- performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
+ if (!forRootSizeOnly || !setMeasuredRootSizeFromSpec(
+ childWidthMeasureSpec, childHeightMeasureSpec)) {
+ performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
+ } else {
+ // We already know how big the window should be before measuring the views.
+ // We can measure the views before laying out them. This is to avoid unnecessary
+ // measure.
+ mViewMeasureDeferred = true;
+ }
if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
windowSizeMayChange = true;
}
@@ -2638,6 +2655,25 @@ public final class ViewRootImpl implements ViewParent,
}
/**
+ * Sets the measured root size for requesting the window frame.
+ *
+ * @param widthMeasureSpec contains the size and the mode of the width.
+ * @param heightMeasureSpec contains the size and the mode of the height.
+ * @return {@code true} if we actually set the measured size; {@code false} otherwise.
+ */
+ private boolean setMeasuredRootSizeFromSpec(int widthMeasureSpec, int heightMeasureSpec) {
+ final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY) {
+ // We don't know the exact size. We need to measure the hierarchy to know that.
+ return false;
+ }
+ mMeasuredWidth = MeasureSpec.getSize(widthMeasureSpec);
+ mMeasuredHeight = MeasureSpec.getSize(heightMeasureSpec);
+ return true;
+ }
+
+ /**
* Modifies the input matrix such that it maps view-local coordinates to
* on-screen coordinates.
*
@@ -2724,6 +2760,14 @@ public final class ViewRootImpl implements ViewParent,
|| lp.type == TYPE_VOLUME_OVERLAY;
}
+ /**
+ * @return {@code true} if we should reduce unnecessary measure for the window.
+ * TODO(b/260382739): Apply this to all windows.
+ */
+ private static boolean shouldOptimizeMeasure(final WindowManager.LayoutParams lp) {
+ return lp.type == TYPE_NOTIFICATION_SHADE;
+ }
+
private Rect getWindowBoundsInsetSystemBars() {
final Rect bounds = new Rect(
mContext.getResources().getConfiguration().windowConfiguration.getBounds());
@@ -2774,6 +2818,7 @@ public final class ViewRootImpl implements ViewParent,
mAppVisibilityChanged = false;
final boolean viewUserVisibilityChanged = !mFirst &&
((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE));
+ final boolean shouldOptimizeMeasure = shouldOptimizeMeasure(lp);
WindowManager.LayoutParams params = null;
CompatibilityInfo compatibilityInfo =
@@ -2895,7 +2940,7 @@ public final class ViewRootImpl implements ViewParent,
// Ask host how big it wants to be
windowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(),
- desiredWindowWidth, desiredWindowHeight);
+ desiredWindowWidth, desiredWindowHeight, shouldOptimizeMeasure);
}
if (collectViewAttributes()) {
@@ -2935,8 +2980,8 @@ public final class ViewRootImpl implements ViewParent,
// we don't need to go through two layout passes when things
// change due to fitting system windows, which can happen a lot.
windowSizeMayChange |= measureHierarchy(host, lp,
- mView.getContext().getResources(),
- desiredWindowWidth, desiredWindowHeight);
+ mView.getContext().getResources(), desiredWindowWidth, desiredWindowHeight,
+ shouldOptimizeMeasure);
}
}
@@ -3351,6 +3396,13 @@ public final class ViewRootImpl implements ViewParent,
maybeHandleWindowMove(frame);
}
+ if (mViewMeasureDeferred) {
+ // It's time to measure the views since we are going to layout them.
+ performMeasure(
+ MeasureSpec.makeMeasureSpec(frame.width(), MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(frame.height(), MeasureSpec.EXACTLY));
+ }
+
if (!mRelayoutRequested && mCheckIfCanDraw) {
// We had a sync previously, but we didn't call IWindowSession#relayout in this
// traversal. So we don't know if the sync is complete that we can continue to draw.
@@ -3940,6 +3992,9 @@ public final class ViewRootImpl implements ViewParent,
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
+ mMeasuredWidth = mView.getMeasuredWidth();
+ mMeasuredHeight = mView.getMeasuredHeight();
+ mViewMeasureDeferred = false;
}
/**
@@ -4035,7 +4090,7 @@ public final class ViewRootImpl implements ViewParent,
view.requestLayout();
}
measureHierarchy(host, lp, mView.getContext().getResources(),
- desiredWindowWidth, desiredWindowHeight);
+ desiredWindowWidth, desiredWindowHeight, false /* forRootSizeOnly */);
mInLayout = true;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
@@ -8101,8 +8156,8 @@ public final class ViewRootImpl implements ViewParent,
final WindowConfiguration winConfigFromWm =
mLastReportedMergedConfiguration.getGlobalConfiguration().windowConfiguration;
final WindowConfiguration winConfig = getCompatWindowConfiguration();
- final int measuredWidth = mView.getMeasuredWidth();
- final int measuredHeight = mView.getMeasuredHeight();
+ final int measuredWidth = mMeasuredWidth;
+ final int measuredHeight = mMeasuredHeight;
final boolean relayoutAsync;
if (LOCAL_LAYOUT
&& (mViewFrameInfo.flags & FrameInfo.FLAG_WINDOW_VISIBILITY_CHANGED) == 0
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 15be5f563d88..f90353adffc1 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -827,6 +827,26 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
+ * Sets/removes the reparent leaf task flag for this {@code windowContainer}.
+ * When this is set, the server side will try to reparent the leaf task to task display area
+ * if there is an existing activity in history during the activity launch. This operation only
+ * support on the organized root task.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction setReparentLeafTaskIfRelaunch(
+ @NonNull WindowContainerToken windowContainer, boolean reparentLeafTaskIfRelaunch) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(
+ HierarchyOp.HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH)
+ .setContainer(windowContainer.asBinder())
+ .setReparentLeafTaskIfRelaunch(reparentLeafTaskIfRelaunch)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
* Merges another WCT into this one.
* @param transfer When true, this will transfer everything from other potentially leaving
* other in an unusable state. When false, other is left alone, but
@@ -1241,6 +1261,7 @@ public final class WindowContainerTransaction implements Parcelable {
public static final int HIERARCHY_OP_TYPE_REMOVE_TASK = 20;
public static final int HIERARCHY_OP_TYPE_FINISH_ACTIVITY = 21;
public static final int HIERARCHY_OP_TYPE_SET_COMPANION_TASK_FRAGMENT = 22;
+ public static final int HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH = 23;
// The following key(s) are for use with mLaunchOptions:
// When launching a task (eg. from recents), this is the taskId to be launched.
@@ -1293,6 +1314,8 @@ public final class WindowContainerTransaction implements Parcelable {
private boolean mAlwaysOnTop;
+ private boolean mReparentLeafTaskIfRelaunch;
+
public static HierarchyOp createForReparent(
@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT)
@@ -1398,6 +1421,7 @@ public final class WindowContainerTransaction implements Parcelable {
mPendingIntent = copy.mPendingIntent;
mShortcutInfo = copy.mShortcutInfo;
mAlwaysOnTop = copy.mAlwaysOnTop;
+ mReparentLeafTaskIfRelaunch = copy.mReparentLeafTaskIfRelaunch;
}
protected HierarchyOp(Parcel in) {
@@ -1420,6 +1444,7 @@ public final class WindowContainerTransaction implements Parcelable {
mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
mShortcutInfo = in.readTypedObject(ShortcutInfo.CREATOR);
mAlwaysOnTop = in.readBoolean();
+ mReparentLeafTaskIfRelaunch = in.readBoolean();
}
public int getType() {
@@ -1494,6 +1519,10 @@ public final class WindowContainerTransaction implements Parcelable {
return mAlwaysOnTop;
}
+ public boolean isReparentLeafTaskIfRelaunch() {
+ return mReparentLeafTaskIfRelaunch;
+ }
+
@Nullable
public TaskFragmentCreationParams getTaskFragmentCreationOptions() {
return mTaskFragmentCreationOptions;
@@ -1572,6 +1601,9 @@ public final class WindowContainerTransaction implements Parcelable {
case HIERARCHY_OP_TYPE_SET_COMPANION_TASK_FRAGMENT:
return "{setCompanionTaskFragment: container = " + mContainer + " companion = "
+ mReparent + "}";
+ case HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH:
+ return "{setReparentLeafTaskIfRelaunch: container= " + mContainer
+ + " reparentLeafTaskIfRelaunch= " + mReparentLeafTaskIfRelaunch + "}";
default:
return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
+ " mToTop=" + mToTop
@@ -1602,6 +1634,7 @@ public final class WindowContainerTransaction implements Parcelable {
dest.writeTypedObject(mPendingIntent, flags);
dest.writeTypedObject(mShortcutInfo, flags);
dest.writeBoolean(mAlwaysOnTop);
+ dest.writeBoolean(mReparentLeafTaskIfRelaunch);
}
@Override
@@ -1662,6 +1695,8 @@ public final class WindowContainerTransaction implements Parcelable {
private boolean mAlwaysOnTop;
+ private boolean mReparentLeafTaskIfRelaunch;
+
Builder(int type) {
mType = type;
}
@@ -1732,6 +1767,11 @@ public final class WindowContainerTransaction implements Parcelable {
return this;
}
+ Builder setReparentLeafTaskIfRelaunch(boolean reparentLeafTaskIfRelaunch) {
+ mReparentLeafTaskIfRelaunch = reparentLeafTaskIfRelaunch;
+ return this;
+ }
+
Builder setShortcutInfo(@Nullable ShortcutInfo shortcutInfo) {
mShortcutInfo = shortcutInfo;
return this;
@@ -1757,6 +1797,7 @@ public final class WindowContainerTransaction implements Parcelable {
hierarchyOp.mAlwaysOnTop = mAlwaysOnTop;
hierarchyOp.mTaskFragmentCreationOptions = mTaskFragmentCreationOptions;
hierarchyOp.mShortcutInfo = mShortcutInfo;
+ hierarchyOp.mReparentLeafTaskIfRelaunch = mReparentLeafTaskIfRelaunch;
return hierarchyOp;
}
diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
index e6ae28207970..29945937788b 100644
--- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
+++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2019 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.
--->
+ ~ 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="48dp"
android:height="48dp"
@@ -25,13 +25,12 @@
android:fillAlpha="0.8"
android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0"/>
<group
- android:translateX="12"
- android:translateY="12">
+ android:scaleX="0.8"
+ android:scaleY="0.8"
+ android:translateX="10"
+ android:translateY="10">
<path
- android:fillColor="@color/compat_controls_text"
- android:pathData="M6,13c0,-1.65 0.67,-3.15 1.76,-4.24L6.34,7.34C4.9,8.79 4,10.79 4,13c0,4.08 3.05,7.44 7,7.93v-2.02C8.17,18.43 6,15.97 6,13z"/>
- <path
- android:fillColor="@color/compat_controls_text"
- android:pathData="M20,13c0,-4.42 -3.58,-8 -8,-8c-0.06,0 -0.12,0.01 -0.18,0.01v0l1.09,-1.09L11.5,2.5L8,6l3.5,3.5l1.41,-1.41l-1.08,-1.08C11.89,7.01 11.95,7 12,7c3.31,0 6,2.69 6,6c0,2.97 -2.17,5.43 -5,5.91v2.02C16.95,20.44 20,17.08 20,13z"/>
+ android:pathData="M0,36V24.5H3V30.85L10.4,23.45L12.55,25.6L5.15,33H11.5V36H0ZM24.5,36V33H30.85L23.5,25.65L25.65,23.5L33,30.85V24.5H36V36H24.5ZM10.35,12.5L3,5.15V11.5H0V0H11.5V3H5.15L12.5,10.35L10.35,12.5ZM25.65,12.5L23.5,10.35L30.85,3H24.5V0H36V11.5H33V5.15L25.65,12.5Z"
+ android:fillColor="@color/compat_controls_text"/>
</group>
</vector>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index 6e116b958ac9..c836b95ffab8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -51,8 +51,6 @@ import com.android.wm.shell.R;
import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.common.SurfaceUtils;
-import java.util.function.Consumer;
-
/**
* Handles split decor like showing resizing hint for a specific split.
*/
@@ -72,17 +70,18 @@ public class SplitDecorManager extends WindowlessWindowManager {
private SurfaceControl mIconLeash;
private SurfaceControl mBackgroundLeash;
private SurfaceControl mGapBackgroundLeash;
+ private SurfaceControl mScreenshot;
private boolean mShown;
private boolean mIsResizing;
private final Rect mBounds = new Rect();
- private final Rect mResizingBounds = new Rect();
private final Rect mTempRect = new Rect();
private ValueAnimator mFadeAnimator;
private int mIconSize;
private int mOffsetX;
private int mOffsetY;
+ private int mRunningAnimationCount = 0;
public SplitDecorManager(Configuration configuration, IconProvider iconProvider,
SurfaceSession surfaceSession) {
@@ -173,7 +172,6 @@ public class SplitDecorManager extends WindowlessWindowManager {
mIsResizing = true;
mBounds.set(newBounds);
}
- mResizingBounds.set(newBounds);
mOffsetX = offsetX;
mOffsetY = offsetY;
@@ -227,33 +225,41 @@ public class SplitDecorManager extends WindowlessWindowManager {
t.setVisibility(mBackgroundLeash, show);
t.setVisibility(mIconLeash, show);
} else {
- startFadeAnimation(show, null /* finishedConsumer */);
+ startFadeAnimation(show, false, null);
}
mShown = show;
}
}
/** Stops showing resizing hint. */
- public void onResized(SurfaceControl.Transaction t) {
- if (!mShown && mIsResizing) {
- mTempRect.set(mResizingBounds);
- mTempRect.offsetTo(-mOffsetX, -mOffsetY);
- final SurfaceControl screenshot = ScreenshotUtils.takeScreenshot(t,
- mHostLeash, mTempRect, Integer.MAX_VALUE - 1);
+ public void onResized(SurfaceControl.Transaction t, Runnable animFinishedCallback) {
+ if (mScreenshot != null) {
+ t.setPosition(mScreenshot, mOffsetX, mOffsetY);
final SurfaceControl.Transaction animT = new SurfaceControl.Transaction();
final ValueAnimator va = ValueAnimator.ofFloat(1, 0);
va.addUpdateListener(valueAnimator -> {
final float progress = (float) valueAnimator.getAnimatedValue();
- animT.setAlpha(screenshot, progress);
+ animT.setAlpha(mScreenshot, progress);
animT.apply();
});
va.addListener(new AnimatorListenerAdapter() {
@Override
+ public void onAnimationStart(Animator animation) {
+ mRunningAnimationCount++;
+ }
+
+ @Override
public void onAnimationEnd(@androidx.annotation.NonNull Animator animation) {
- animT.remove(screenshot);
+ mRunningAnimationCount--;
+ animT.remove(mScreenshot);
animT.apply();
animT.close();
+ mScreenshot = null;
+
+ if (mRunningAnimationCount == 0 && animFinishedCallback != null) {
+ animFinishedCallback.run();
+ }
}
});
va.start();
@@ -285,10 +291,34 @@ public class SplitDecorManager extends WindowlessWindowManager {
mFadeAnimator.cancel();
}
if (mShown) {
- fadeOutDecor(null /* finishedCallback */);
+ fadeOutDecor(animFinishedCallback);
} else {
// Decor surface is hidden so release it directly.
releaseDecor(t);
+ if (mRunningAnimationCount == 0 && animFinishedCallback != null) {
+ animFinishedCallback.run();
+ }
+ }
+ }
+
+ /** Screenshot host leash and attach on it if meet some conditions */
+ public void screenshotIfNeeded(SurfaceControl.Transaction t) {
+ if (!mShown && mIsResizing) {
+ mTempRect.set(mBounds);
+ mTempRect.offsetTo(0, 0);
+ mScreenshot = ScreenshotUtils.takeScreenshot(t, mHostLeash, mTempRect,
+ Integer.MAX_VALUE - 1);
+ }
+ }
+
+ /** Set screenshot and attach on host leash it if meet some conditions */
+ public void setScreenshotIfNeeded(SurfaceControl screenshot, SurfaceControl.Transaction t) {
+ if (screenshot == null || !screenshot.isValid()) return;
+
+ if (!mShown && mIsResizing) {
+ mScreenshot = screenshot;
+ t.reparent(screenshot, mHostLeash);
+ t.setLayer(screenshot, Integer.MAX_VALUE - 1);
}
}
@@ -296,18 +326,15 @@ public class SplitDecorManager extends WindowlessWindowManager {
* directly. */
public void fadeOutDecor(Runnable finishedCallback) {
if (mShown) {
- startFadeAnimation(false /* show */, transaction -> {
- releaseDecor(transaction);
- if (finishedCallback != null) finishedCallback.run();
- });
+ startFadeAnimation(false /* show */, true, finishedCallback);
mShown = false;
} else {
if (finishedCallback != null) finishedCallback.run();
}
}
- private void startFadeAnimation(boolean show,
- Consumer<SurfaceControl.Transaction> finishedConsumer) {
+ private void startFadeAnimation(boolean show, boolean releaseSurface,
+ Runnable finishedCallback) {
final SurfaceControl.Transaction animT = new SurfaceControl.Transaction();
mFadeAnimator = ValueAnimator.ofFloat(0f, 1f);
mFadeAnimator.setDuration(FADE_DURATION);
@@ -324,6 +351,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
mFadeAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(@NonNull Animator animation) {
+ mRunningAnimationCount++;
if (show) {
animT.show(mBackgroundLeash).show(mIconLeash);
}
@@ -335,6 +363,7 @@ public class SplitDecorManager extends WindowlessWindowManager {
@Override
public void onAnimationEnd(@NonNull Animator animation) {
+ mRunningAnimationCount--;
if (!show) {
if (mBackgroundLeash != null) {
animT.hide(mBackgroundLeash);
@@ -343,11 +372,15 @@ public class SplitDecorManager extends WindowlessWindowManager {
animT.hide(mIconLeash);
}
}
- if (finishedConsumer != null) {
- finishedConsumer.accept(animT);
+ if (releaseSurface) {
+ releaseDecor(animT);
}
animT.apply();
animT.close();
+
+ if (mRunningAnimationCount == 0 && finishedCallback != null) {
+ finishedCallback.run();
+ }
}
});
mFadeAnimator.start();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 962be9da2111..ad0adc6c84c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -687,10 +687,13 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
- static Optional<DesktopModeController> providesDesktopModeController(
- @DynamicOverride Optional<DesktopModeController> desktopModeController) {
+ static Optional<DesktopModeController> provideDesktopModeController(
+ @DynamicOverride Optional<Lazy<DesktopModeController>> desktopModeController) {
+ // Use optional-of-lazy for the dependency that this provider relies on.
+ // Lazy ensures that this provider will not be the cause the dependency is created
+ // when it will not be returned due to the condition below.
if (DesktopModeStatus.IS_SUPPORTED) {
- return desktopModeController;
+ return desktopModeController.map(Lazy::get);
}
return Optional.empty();
}
@@ -701,10 +704,13 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
- static Optional<DesktopModeTaskRepository> providesDesktopTaskRepository(
- @DynamicOverride Optional<DesktopModeTaskRepository> desktopModeTaskRepository) {
+ static Optional<DesktopModeTaskRepository> provideDesktopTaskRepository(
+ @DynamicOverride Optional<Lazy<DesktopModeTaskRepository>> desktopModeTaskRepository) {
+ // Use optional-of-lazy for the dependency that this provider relies on.
+ // Lazy ensures that this provider will not be the cause the dependency is created
+ // when it will not be returned due to the condition below.
if (DesktopModeStatus.IS_SUPPORTED) {
- return desktopModeTaskRepository;
+ return desktopModeTaskRepository.map(Lazy::get);
}
return Optional.empty();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index f1670cd792cf..6be83054ae59 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -189,7 +189,7 @@ public abstract class WMShellModule {
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
SyncTransactionQueue syncQueue,
- @DynamicOverride DesktopModeController desktopModeController) {
+ Optional<DesktopModeController> desktopModeController) {
return new CaptionWindowDecorViewModel(
context,
mainHandler,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
index 17ba246910d5..5824f51a1d33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
@@ -100,7 +100,9 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll
mDesktopModeTaskRepository = desktopModeTaskRepository;
mMainExecutor = mainExecutor;
mSettingsObserver = new SettingsObserver(mContext, mainHandler);
- shellInit.addInitCallback(this::onInit, this);
+ if (DesktopModeStatus.isSupported()) {
+ shellInit.addInitCallback(this::onInit, this);
+ }
}
private void onInit() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
index 2fafe67664f8..e3eb2b746969 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
@@ -37,6 +37,13 @@ public class DesktopModeStatus {
"persist.wm.debug.desktop_mode", false);
/**
+ * Return {@code true} if desktop mode support is enabled
+ */
+ public static boolean isSupported() {
+ return IS_SUPPORTED;
+ }
+
+ /**
* Check if desktop mode is active
*
* @return {@code true} if active
@@ -54,4 +61,5 @@ public class DesktopModeStatus {
return false;
}
}
+
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
new file mode 100644
index 000000000000..926cfb3b12ef
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
@@ -0,0 +1,2 @@
+# WM shell sub-module desktop owners
+madym@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
new file mode 100644
index 000000000000..0c2d5c49f830
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
@@ -0,0 +1,2 @@
+# WM shell sub-module freeform owners
+madym@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 21a13103616c..1cf3a896b68e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -47,6 +47,7 @@ import android.window.WindowContainerTransactionCallback;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.OneShotRemoteHandler;
import com.android.wm.shell.transition.Transitions;
@@ -64,6 +65,7 @@ class SplitScreenTransitions {
DismissTransition mPendingDismiss = null;
TransitSession mPendingEnter = null;
TransitSession mPendingRecent = null;
+ TransitSession mPendingResize = null;
private IBinder mAnimatingTransition = null;
OneShotRemoteHandler mPendingRemoteHandler = null;
@@ -177,6 +179,43 @@ class SplitScreenTransitions {
onFinish(null /* wct */, null /* wctCB */);
}
+ void applyResizeTransition(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback,
+ @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
+ @NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) {
+ mFinishCallback = finishCallback;
+ mAnimatingTransition = transition;
+ mFinishTransaction = finishTransaction;
+
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (mainRoot.equals(change.getContainer()) || sideRoot.equals(change.getContainer())) {
+ final SurfaceControl leash = change.getLeash();
+ startTransaction.setPosition(leash, change.getEndAbsBounds().left,
+ change.getEndAbsBounds().top);
+ startTransaction.setWindowCrop(leash, change.getEndAbsBounds().width(),
+ change.getEndAbsBounds().height());
+
+ SplitDecorManager decor = mainRoot.equals(change.getContainer())
+ ? mainDecor : sideDecor;
+ ValueAnimator va = new ValueAnimator();
+ mAnimations.add(va);
+ decor.setScreenshotIfNeeded(change.getSnapshot(), startTransaction);
+ decor.onResized(startTransaction, () -> {
+ mTransitions.getMainExecutor().execute(() -> {
+ mAnimations.remove(va);
+ onFinish(null /* wct */, null /* wctCB */);
+ });
+ });
+ }
+ }
+
+ startTransaction.apply();
+ onFinish(null /* wct */, null /* wctCB */);
+ }
+
boolean isPendingTransition(IBinder transition) {
return getPendingTransition(transition) != null;
}
@@ -193,6 +232,10 @@ class SplitScreenTransitions {
return mPendingDismiss != null && mPendingDismiss.mTransition == transition;
}
+ boolean isPendingResize(IBinder transition) {
+ return mPendingResize != null && mPendingResize.mTransition == transition;
+ }
+
@Nullable
private TransitSession getPendingTransition(IBinder transition) {
if (isPendingEnter(transition)) {
@@ -201,11 +244,14 @@ class SplitScreenTransitions {
return mPendingRecent;
} else if (isPendingDismiss(transition)) {
return mPendingDismiss;
+ } else if (isPendingResize(transition)) {
+ return mPendingResize;
}
return null;
}
+
/** Starts a transition to enter split with a remote transition animator. */
IBinder startEnterTransition(
@WindowManager.TransitionType int transitType,
@@ -258,6 +304,21 @@ class SplitScreenTransitions {
exitReasonToString(reason), stageTypeToString(dismissTop));
}
+ IBinder startResizeTransition(WindowContainerTransaction wct,
+ Transitions.TransitionHandler handler,
+ @Nullable TransitionFinishedCallback finishCallback) {
+ IBinder transition = mTransitions.startTransition(TRANSIT_CHANGE, wct, handler);
+ setResizeTransition(transition, finishCallback);
+ return transition;
+ }
+
+ void setResizeTransition(@NonNull IBinder transition,
+ @Nullable TransitionFinishedCallback finishCallback) {
+ mPendingResize = new TransitSession(transition, null /* consumedCb */, finishCallback);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ + " deduced Resize split screen");
+ }
+
void setRecentTransition(@NonNull IBinder transition,
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionFinishedCallback finishCallback) {
@@ -324,6 +385,9 @@ class SplitScreenTransitions {
mPendingRecent.onConsumed(aborted);
mPendingRecent = null;
mPendingRemoteHandler = null;
+ } else if (isPendingResize(transition)) {
+ mPendingResize.onConsumed(aborted);
+ mPendingResize = null;
}
}
@@ -340,6 +404,9 @@ class SplitScreenTransitions {
} else if (isPendingDismiss(mAnimatingTransition)) {
mPendingDismiss.onFinished(wct, mFinishTransaction);
mPendingDismiss = null;
+ } else if (isPendingResize(mAnimatingTransition)) {
+ mPendingResize.onFinished(wct, mFinishTransaction);
+ mPendingResize = null;
}
mPendingRemoteHandler = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index aa0512b64a16..38fb2df11df9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -1667,15 +1667,29 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
public void onLayoutSizeChanged(SplitLayout layout) {
// Reset this flag every time onLayoutSizeChanged.
mShowDecorImmediately = false;
+
+ if (!ENABLE_SHELL_TRANSITIONS) {
+ // Only need screenshot for legacy case because shell transition should screenshot
+ // itself during transition.
+ final SurfaceControl.Transaction startT = mTransactionPool.acquire();
+ mMainStage.screenshotIfNeeded(startT);
+ mSideStage.screenshotIfNeeded(startT);
+ mTransactionPool.release(startT);
+ }
+
final WindowContainerTransaction wct = new WindowContainerTransaction();
updateWindowBounds(layout, wct);
sendOnBoundsChanged();
- mSyncQueue.queue(wct);
- mSyncQueue.runInSync(t -> {
- updateSurfaceBounds(layout, t, false /* applyResizingOffset */);
- mMainStage.onResized(t);
- mSideStage.onResized(t);
- });
+ if (ENABLE_SHELL_TRANSITIONS) {
+ mSplitTransitions.startResizeTransition(wct, this, null /* callback */);
+ } else {
+ mSyncQueue.queue(wct);
+ mSyncQueue.runInSync(t -> {
+ updateSurfaceBounds(layout, t, false /* applyResizingOffset */);
+ mMainStage.onResized(t);
+ mSideStage.onResized(t);
+ });
+ }
mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
}
@@ -2029,6 +2043,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
} else if (mSplitTransitions.isPendingDismiss(transition)) {
shouldAnimate = startPendingDismissAnimation(
mSplitTransitions.mPendingDismiss, info, startTransaction, finishTransaction);
+ } else if (mSplitTransitions.isPendingResize(transition)) {
+ mSplitTransitions.applyResizeTransition(transition, info, startTransaction,
+ finishTransaction, finishCallback, mMainStage.mRootTaskInfo.token,
+ mSideStage.mRootTaskInfo.token, mMainStage.getSplitDecorManager(),
+ mSideStage.getSplitDecorManager());
+ return true;
}
if (!shouldAnimate) return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 358f712f76b5..8a52c8750ba6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -292,7 +292,13 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
void onResized(SurfaceControl.Transaction t) {
if (mSplitDecorManager != null) {
- mSplitDecorManager.onResized(t);
+ mSplitDecorManager.onResized(t, null);
+ }
+ }
+
+ void screenshotIfNeeded(SurfaceControl.Transaction t) {
+ if (mSplitDecorManager != null) {
+ mSplitDecorManager.screenshotIfNeeded(t);
}
}
@@ -304,6 +310,10 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
}
}
+ SplitDecorManager getSplitDecorManager() {
+ return mSplitDecorManager;
+ }
+
void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) {
// Clear overridden bounds and windowing mode to make sure the child task can inherit
// windowing mode and bounds from split root.
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 e7036c726a27..56554020f3cc 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
@@ -55,6 +55,7 @@ import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.transition.Transitions;
+import java.util.Optional;
import java.util.function.Supplier;
/**
@@ -74,7 +75,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
private final DisplayController mDisplayController;
private final SyncTransactionQueue mSyncQueue;
private FreeformTaskTransitionStarter mTransitionStarter;
- private DesktopModeController mDesktopModeController;
+ private Optional<DesktopModeController> mDesktopModeController;
private boolean mTransitionDragActive;
private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -90,7 +91,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
SyncTransactionQueue syncQueue,
- DesktopModeController desktopModeController) {
+ Optional<DesktopModeController> desktopModeController) {
this(
context,
mainHandler,
@@ -110,7 +111,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
SyncTransactionQueue syncQueue,
- DesktopModeController desktopModeController,
+ Optional<DesktopModeController> desktopModeController,
CaptionWindowDecoration.Factory captionWindowDecorFactory,
Supplier<InputManager> inputManagerSupplier) {
@@ -246,10 +247,10 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
} else if (id == R.id.caption_handle) {
decoration.createHandleMenu();
} else if (id == R.id.desktop_button) {
- mDesktopModeController.setDesktopModeActive(true);
+ mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true));
decoration.closeHandleMenu();
} else if (id == R.id.fullscreen_button) {
- mDesktopModeController.setDesktopModeActive(false);
+ mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false));
decoration.closeHandleMenu();
decoration.setButtonVisibility();
}
@@ -304,9 +305,9 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
*/
private void handleEventForMove(MotionEvent e) {
RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
- int windowingMode = mDesktopModeController
- .getDisplayAreaWindowingMode(taskInfo.displayId);
- if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
+ if (mDesktopModeController.isPresent()
+ && mDesktopModeController.get().getDisplayAreaWindowingMode(taskInfo.displayId)
+ == WINDOWING_MODE_FULLSCREEN) {
return;
}
switch (e.getActionMasked()) {
@@ -331,7 +332,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
if (e.getRawY(dragPointerIdx) <= statusBarHeight
&& DesktopModeStatus.isActive(mContext)) {
- mDesktopModeController.setDesktopModeActive(false);
+ mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false));
}
break;
}
@@ -471,7 +472,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
int statusBarHeight = mDisplayController
.getDisplayLayout(focusedDecor.mTaskInfo.displayId).stableInsets().top;
if (ev.getY() > statusBarHeight) {
- mDesktopModeController.setDesktopModeActive(true);
+ mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true));
return;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
index 01584a067cc2..dad913300711 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
@@ -36,7 +36,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -100,15 +100,14 @@ public class DesktopModeControllerTest extends ShellTestCase {
@Before
public void setUp() {
mMockitoSession = mockitoSession().mockStatic(DesktopModeStatus.class).startMocking();
+ when(DesktopModeStatus.isSupported()).thenReturn(true);
when(DesktopModeStatus.isActive(any())).thenReturn(true);
mShellInit = Mockito.spy(new ShellInit(mTestExecutor));
mDesktopModeTaskRepository = new DesktopModeTaskRepository();
- mController = new DesktopModeController(mContext, mShellInit, mShellController,
- mShellTaskOrganizer, mRootTaskDisplayAreaOrganizer, mTransitions,
- mDesktopModeTaskRepository, mMockHandler, new TestShellExecutor());
+ mController = createController();
when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>());
@@ -125,7 +124,17 @@ public class DesktopModeControllerTest extends ShellTestCase {
@Test
public void instantiate_addInitCallback() {
- verify(mShellInit, times(1)).addInitCallback(any(), any());
+ verify(mShellInit).addInitCallback(any(), any());
+ }
+
+ @Test
+ public void instantiate_flagOff_doNotAddInitCallback() {
+ when(DesktopModeStatus.isSupported()).thenReturn(false);
+ clearInvocations(mShellInit);
+
+ createController();
+
+ verify(mShellInit, never()).addInitCallback(any(), any());
}
@Test
@@ -353,6 +362,12 @@ public class DesktopModeControllerTest extends ShellTestCase {
assertThat(wct).isNotNull();
}
+ private DesktopModeController createController() {
+ return new DesktopModeController(mContext, mShellInit, mShellController,
+ mShellTaskOrganizer, mRootTaskDisplayAreaOrganizer, mTransitions,
+ mDesktopModeTaskRepository, mMockHandler, new TestShellExecutor());
+ }
+
private DisplayAreaInfo createMockDisplayArea() {
DisplayAreaInfo displayAreaInfo = new DisplayAreaInfo(new MockToken().mToken,
mContext.getDisplayId(), 0);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java
index 9b37b97a4c24..ad6fcedd3166 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java
@@ -54,6 +54,7 @@ import org.mockito.Mock;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import java.util.function.Supplier;
/** Tests of {@link CaptionWindowDecorViewModel} */
@@ -101,7 +102,7 @@ public class CaptionWindowDecorViewModelTests extends ShellTestCase {
mTaskOrganizer,
mDisplayController,
mSyncQueue,
- mDesktopModeController,
+ Optional.of(mDesktopModeController),
mCaptionWindowDecorFactory,
new MockObjectSupplier<>(mMockInputManagers, () -> mock(InputManager.class)));
mCaptionWindowDecorViewModel.setEventReceiverFactory(mEventReceiverFactory);
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 7732da40aac2..46a94fdfcd17 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -314,4 +314,7 @@
<!-- Whether tilt to bright is enabled by default. -->
<bool name="def_wearable_tiltToBrightEnabled">false</bool>
+
+ <!-- Whether vibrate icon is shown in the status bar by default. -->
+ <integer name="def_statusBarVibrateIconEnabled">0</integer>
</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index a6edb0f0e2e3..19bbcff0bd79 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3631,7 +3631,7 @@ public class SettingsProvider extends ContentProvider {
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 210;
+ private static final int SETTINGS_VERSION = 211;
private final int mUserId;
@@ -5512,7 +5512,17 @@ public class SettingsProvider extends ContentProvider {
// removed now that feature is enabled for everyone
currentVersion = 210;
}
-
+ if (currentVersion == 210) {
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+ final int defaultValueVibrateIconEnabled = getContext().getResources()
+ .getInteger(R.integer.def_statusBarVibrateIconEnabled);
+ secureSettings.insertSettingOverrideableByRestoreLocked(
+ Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
+ String.valueOf(defaultValueVibrateIconEnabled),
+ null /* tag */, true /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ currentVersion = 211;
+ }
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 844e88a09b05..acaa008f2a0c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -904,6 +904,29 @@
<service android:name=".controls.controller.AuxiliaryPersistenceWrapper$DeletionJobService"
android:permission="android.permission.BIND_JOB_SERVICE"/>
+ <!-- region Note Task -->
+ <activity
+ android:name=".notetask.shortcut.CreateNoteTaskShortcutActivity"
+ android:enabled="false"
+ android:exported="true"
+ android:excludeFromRecents="true"
+ android:theme="@android:style/Theme.NoDisplay"
+ android:label="@string/note_task_button_label"
+ android:icon="@drawable/ic_note_task_button">
+
+ <intent-filter>
+ <action android:name="android.intent.action.CREATE_SHORTCUT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name=".notetask.shortcut.LaunchNoteTaskActivity"
+ android:exported="true"
+ android:excludeFromRecents="true"
+ android:theme="@android:style/Theme.NoDisplay" />
+ <!-- endregion -->
+
<!-- started from ControlsRequestReceiver -->
<activity
android:name=".controls.management.ControlsRequestDialog"
diff --git a/packages/SystemUI/res/drawable/ic_note_task_button.xml b/packages/SystemUI/res/drawable/ic_note_task_button.xml
new file mode 100644
index 000000000000..bb5e224ea315
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_note_task_button.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <path
+ android:fillColor="#636C6F"
+ android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z" />
+ <path
+ android:fillColor="#636C6F"
+ android:fillType="evenOdd"
+ android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z" />
+</vector>
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index 530db0d0304a..13c9a5ea05b1 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -35,7 +35,6 @@
android:layout_height="@dimen/qs_media_session_height_expanded"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:translationZ="0dp"
android:scaleType="centerCrop"
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 49dd574af829..fd2e3249d59a 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -36,4 +36,8 @@
avatar will no longer show on the lockscreen -->
<bool name="flag_user_switcher_chip">false</bool>
+ <!-- Whether the battery icon is allowed to display a shield when battery life is being
+ protected. -->
+ <bool name="flag_battery_shield_icon">false</bool>
+
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 30788cf90e6a..52a931bdd6ec 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2669,6 +2669,10 @@
<xliff:g id="weather_condition" example="Partly cloudy">%1$s</xliff:g>, <xliff:g id="temperature" example="7°C">%2$s</xliff:g>
</string>
+ <!-- TODO(b/259369672): Replace with final resource. -->
+ <!-- [CHAR LIMIT=30] Label used to open Note Task -->
+ <string name="note_task_button_label">Notetaking</string>
+
<!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, media app is broadcasting -->
<string name="broadcasting_description_is_broadcasting">Broadcasting</string>
<!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, title -->
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 7c05356dd5fc..389639d23795 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -83,8 +83,7 @@ object Flags {
val SEMI_STABLE_SORT = unreleasedFlag(115, "semi_stable_sort", teamfood = true)
@JvmField
- val NOTIFICATION_GROUP_CORNER =
- unreleasedFlag(116, "notification_group_corner", teamfood = true)
+ val USE_ROUNDNESS_SOURCETYPES = unreleasedFlag(116, "use_roundness_sourcetype", teamfood = true)
// TODO(b/259217907)
@JvmField
@@ -229,7 +228,9 @@ object Flags {
val NEW_STATUS_BAR_WIFI_ICON_BACKEND = unreleasedFlag(609, "new_status_bar_wifi_icon_backend")
// TODO(b/256623670): Tracking Bug
- @JvmField val BATTERY_SHIELD_ICON = unreleasedFlag(610, "battery_shield_icon")
+ @JvmField
+ val BATTERY_SHIELD_ICON =
+ resourceBooleanFlag(610, R.bool.flag_battery_shield_icon, "battery_shield_icon")
// TODO(b/260881289): Tracking Bug
val NEW_STATUS_BAR_ICONS_DEBUG_COLORING =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 827ac789073a..df8fb9149f30 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -940,19 +940,9 @@ public class MediaControlPanel {
if (mIsSeekBarEnabled) {
return ConstraintSet.VISIBLE;
}
- // If disabled and "neighbours" are visible, set progress bar to INVISIBLE instead of GONE
- // so layout weights still work.
- return areAnyExpandedBottomActionsVisible() ? ConstraintSet.INVISIBLE : ConstraintSet.GONE;
- }
-
- private boolean areAnyExpandedBottomActionsVisible() {
- ConstraintSet expandedSet = mMediaViewController.getExpandedLayout();
- for (int id : MediaViewHolder.Companion.getExpandedBottomActionIds()) {
- if (expandedSet.getVisibility(id) == ConstraintSet.VISIBLE) {
- return true;
- }
- }
- return false;
+ // Set progress bar to INVISIBLE to keep the positions of text and buttons similar to the
+ // original positions when seekbar is enabled.
+ return ConstraintSet.INVISIBLE;
}
private void setGenericButton(
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index 647beb95a3bc..b10abb569717 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -48,52 +48,66 @@ class MediaTttCommandLineHelper @Inject constructor(
/** All commands for the sender device. */
inner class SenderCommand : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
- val commandName = args[1]
+ if (args.size < 2) {
+ help(pw)
+ return
+ }
+
+ val senderArgs = processArgs(args)
+
@StatusBarManager.MediaTransferSenderState
val displayState: Int?
try {
- displayState = ChipStateSender.getSenderStateIdFromName(commandName)
+ displayState = ChipStateSender.getSenderStateIdFromName(senderArgs.commandName)
} catch (ex: IllegalArgumentException) {
- pw.println("Invalid command name $commandName")
+ pw.println("Invalid command name ${senderArgs.commandName}")
return
}
@SuppressLint("WrongConstant") // sysui allowed to call STATUS_BAR_SERVICE
val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE)
as StatusBarManager
- val routeInfo = MediaRoute2Info.Builder(if (args.size >= 4) args[3] else "id", args[0])
+ val routeInfo = MediaRoute2Info.Builder(senderArgs.id, senderArgs.deviceName)
.addFeature("feature")
- val useAppIcon = !(args.size >= 3 && args[2] == "useAppIcon=false")
- if (useAppIcon) {
+ if (senderArgs.useAppIcon) {
routeInfo.setClientPackageName(TEST_PACKAGE_NAME)
}
+ var undoExecutor: Executor? = null
+ var undoRunnable: Runnable? = null
+ if (isSucceededState(displayState) && senderArgs.showUndo) {
+ undoExecutor = mainExecutor
+ undoRunnable = Runnable { Log.i(CLI_TAG, "Undo triggered for $displayState") }
+ }
+
statusBarManager.updateMediaTapToTransferSenderDisplay(
displayState,
routeInfo.build(),
- getUndoExecutor(displayState),
- getUndoCallback(displayState)
+ undoExecutor,
+ undoRunnable,
)
}
- private fun getUndoExecutor(
- @StatusBarManager.MediaTransferSenderState displayState: Int
- ): Executor? {
- return if (isSucceededState(displayState)) {
- mainExecutor
- } else {
- null
+ private fun processArgs(args: List<String>): SenderArgs {
+ val senderArgs = SenderArgs(
+ deviceName = args[0],
+ commandName = args[1],
+ )
+
+ if (args.size == 2) {
+ return senderArgs
}
- }
- private fun getUndoCallback(
- @StatusBarManager.MediaTransferSenderState displayState: Int
- ): Runnable? {
- return if (isSucceededState(displayState)) {
- Runnable { Log.i(CLI_TAG, "Undo triggered for $displayState") }
- } else {
- null
+ // Process any optional arguments
+ args.subList(2, args.size).forEach {
+ when {
+ it == "useAppIcon=false" -> senderArgs.useAppIcon = false
+ it == "showUndo=false" -> senderArgs.showUndo = false
+ it.substring(0, 3) == "id=" -> senderArgs.id = it.substring(3)
+ }
}
+
+ return senderArgs
}
private fun isSucceededState(
@@ -106,14 +120,31 @@ class MediaTttCommandLineHelper @Inject constructor(
}
override fun help(pw: PrintWriter) {
- pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND " +
- "<deviceName> <chipState> useAppIcon=[true|false] <id>")
+ pw.println(
+ "Usage: adb shell cmd statusbar $SENDER_COMMAND " +
+ "<deviceName> <chipState> " +
+ "useAppIcon=[true|false] id=<id> showUndo=[true|false]"
+ )
+ pw.println("Note: useAppIcon, id, and showUndo are optional additional commands.")
}
}
+ private data class SenderArgs(
+ val deviceName: String,
+ val commandName: String,
+ var id: String = "id",
+ var useAppIcon: Boolean = true,
+ var showUndo: Boolean = true,
+ )
+
/** All commands for the receiver device. */
inner class ReceiverCommand : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
+ if (args.isEmpty()) {
+ help(pw)
+ return
+ }
+
val commandName = args[0]
@StatusBarManager.MediaTransferReceiverState
val displayState: Int?
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 3fd1aa73c033..e2f55f0d81b8 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -145,7 +145,7 @@ public class NavigationBarController implements
boolean willApplyConfig = mConfigChanges.applyNewConfig(mContext.getResources());
boolean largeScreenChanged = mIsTablet != isOldConfigTablet;
// TODO(b/243765256): Disable this logging once b/243765256 is fixed.
- Log.d(DEBUG_MISSING_GESTURE_TAG, "NavbarController: newConfig=" + newConfig
+ Log.i(DEBUG_MISSING_GESTURE_TAG, "NavbarController: newConfig=" + newConfig
+ " mTaskbarDelegate initialized=" + mTaskbarDelegate.isInitialized()
+ " willApplyConfigToNavbars=" + willApplyConfig
+ " navBarCount=" + mNavigationBars.size());
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index cb0f3e26c6a7..d03ac3b419f6 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -958,7 +958,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
}
// TODO(b/243765256): Disable this logging once b/243765256 is fixed.
- Log.d(DEBUG_MISSING_GESTURE_TAG, "Config changed: newConfig=" + newConfig
+ Log.i(DEBUG_MISSING_GESTURE_TAG, "Config changed: newConfig=" + newConfig
+ " lastReportedConfig=" + mLastReportedConfig);
mLastReportedConfig.updateFrom(newConfig);
updateDisplaySize();
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index b964b76795b8..6dd60d043a06 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -17,10 +17,12 @@
package com.android.systemui.notetask
import android.app.KeyguardManager
+import android.content.ComponentName
import android.content.Context
+import android.content.pm.PackageManager
import android.os.UserManager
-import android.view.KeyEvent
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
import com.android.systemui.util.kotlin.getOrNull
import com.android.wm.shell.bubbles.Bubbles
import java.util.Optional
@@ -45,15 +47,22 @@ constructor(
@NoteTaskEnabledKey private val isEnabled: Boolean,
) {
- fun handleSystemKey(keyCode: Int) {
+ /**
+ * Shows a note task. How the task is shown will depend on when the method is invoked.
+ *
+ * If in multi-window mode, notes will open as a full screen experience. That is particularly
+ * important for Large screen devices. These devices may support a taskbar that let users to
+ * drag and drop a shortcut into multi-window mode, and notes should comply with this behaviour.
+ *
+ * If the keyguard is locked, notes will open as a full screen experience. A locked device has
+ * no contextual information which let us use the whole screen space available.
+ *
+ * If no in multi-window or the keyguard is unlocked, notes will open as a floating experience.
+ * That will let users open other apps in full screen, and take contextual notes.
+ */
+ fun showNoteTask(isInMultiWindowMode: Boolean = false) {
if (!isEnabled) return
- if (keyCode == KeyEvent.KEYCODE_VIDEO_APP_1) {
- showNoteTask()
- }
- }
-
- private fun showNoteTask() {
val bubbles = optionalBubbles.getOrNull() ?: return
val keyguardManager = optionalKeyguardManager.getOrNull() ?: return
val userManager = optionalUserManager.getOrNull() ?: return
@@ -62,11 +71,35 @@ constructor(
// TODO(b/249954038): We should handle direct boot (isUserUnlocked). For now, we do nothing.
if (!userManager.isUserUnlocked) return
- if (keyguardManager.isKeyguardLocked) {
+ if (isInMultiWindowMode || keyguardManager.isKeyguardLocked) {
context.startActivity(intent)
} else {
// TODO(b/254606432): Should include Intent.EXTRA_FLOATING_WINDOW_MODE parameter.
bubbles.showAppBubble(intent)
}
}
+
+ /**
+ * Set `android:enabled` property in the `AndroidManifest` associated with the Shortcut
+ * component to [value].
+ *
+ * If the shortcut entry `android:enabled` is set to `true`, the shortcut will be visible in the
+ * Widget Picker to all users.
+ */
+ fun setNoteTaskShortcutEnabled(value: Boolean) {
+ val componentName = ComponentName(context, CreateNoteTaskShortcutActivity::class.java)
+
+ val enabledState =
+ if (value) {
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ } else {
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ }
+
+ context.packageManager.setComponentEnabledSetting(
+ componentName,
+ enabledState,
+ PackageManager.DONT_KILL_APP,
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index 0a5b6008981b..d14b7a766762 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -16,9 +16,10 @@
package com.android.systemui.notetask
+import android.view.KeyEvent
+import androidx.annotation.VisibleForTesting
import com.android.systemui.statusbar.CommandQueue
import com.android.wm.shell.bubbles.Bubbles
-import dagger.Lazy
import java.util.Optional
import javax.inject.Inject
@@ -27,15 +28,18 @@ internal class NoteTaskInitializer
@Inject
constructor(
private val optionalBubbles: Optional<Bubbles>,
- private val lazyNoteTaskController: Lazy<NoteTaskController>,
+ private val noteTaskController: NoteTaskController,
private val commandQueue: CommandQueue,
@NoteTaskEnabledKey private val isEnabled: Boolean,
) {
- private val callbacks =
+ @VisibleForTesting
+ val callbacks =
object : CommandQueue.Callbacks {
override fun handleSystemKey(keyCode: Int) {
- lazyNoteTaskController.get().handleSystemKey(keyCode)
+ if (keyCode == KeyEvent.KEYCODE_VIDEO_APP_1) {
+ noteTaskController.showNoteTask()
+ }
}
}
@@ -43,5 +47,6 @@ constructor(
if (isEnabled && optionalBubbles.isPresent) {
commandQueue.addCallback(callbacks)
}
+ noteTaskController.setNoteTaskShortcutEnabled(isEnabled)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
index 035396a6fc76..8bdf3195d53b 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
@@ -16,32 +16,47 @@
package com.android.systemui.notetask
+import android.app.Activity
import android.app.KeyguardManager
import android.content.Context
import android.os.UserManager
import androidx.core.content.getSystemService
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
+import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
+import dagger.Binds
import dagger.Module
import dagger.Provides
-import java.util.*
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import java.util.Optional
/** Compose all dependencies required by Note Task feature. */
@Module
-internal class NoteTaskModule {
+internal interface NoteTaskModule {
- @[Provides NoteTaskEnabledKey]
- fun provideIsNoteTaskEnabled(featureFlags: FeatureFlags): Boolean {
- return featureFlags.isEnabled(Flags.NOTE_TASKS)
- }
+ @[Binds IntoMap ClassKey(LaunchNoteTaskActivity::class)]
+ fun bindNoteTaskLauncherActivity(activity: LaunchNoteTaskActivity): Activity?
- @Provides
- fun provideOptionalKeyguardManager(context: Context): Optional<KeyguardManager> {
- return Optional.ofNullable(context.getSystemService())
- }
+ @[Binds IntoMap ClassKey(CreateNoteTaskShortcutActivity::class)]
+ fun bindNoteTaskShortcutActivity(activity: CreateNoteTaskShortcutActivity): Activity?
+
+ companion object {
+
+ @[Provides NoteTaskEnabledKey]
+ fun provideIsNoteTaskEnabled(featureFlags: FeatureFlags): Boolean {
+ return featureFlags.isEnabled(Flags.NOTE_TASKS)
+ }
+
+ @Provides
+ fun provideOptionalKeyguardManager(context: Context): Optional<KeyguardManager> {
+ return Optional.ofNullable(context.getSystemService())
+ }
- @Provides
- fun provideOptionalUserManager(context: Context): Optional<UserManager> {
- return Optional.ofNullable(context.getSystemService())
+ @Provides
+ fun provideOptionalUserManager(context: Context): Optional<UserManager> {
+ return Optional.ofNullable(context.getSystemService())
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
new file mode 100644
index 000000000000..f6a623e4f001
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.notetask.shortcut
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.annotation.DrawableRes
+import androidx.core.content.pm.ShortcutInfoCompat
+import androidx.core.content.pm.ShortcutManagerCompat
+import androidx.core.graphics.drawable.IconCompat
+import com.android.systemui.R
+import javax.inject.Inject
+
+/**
+ * Activity responsible for create a shortcut for notes action. If the shortcut is enabled, a new
+ * shortcut will appear in the widget picker. If the shortcut is selected, the Activity here will be
+ * launched, creating a new shortcut for [CreateNoteTaskShortcutActivity], and will finish.
+ *
+ * @see <a
+ * href="https://developer.android.com/develop/ui/views/launch/shortcuts/creating-shortcuts#custom-pinned">Creating
+ * a custom shortcut activity</a>
+ */
+internal class CreateNoteTaskShortcutActivity @Inject constructor() : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val intent =
+ createShortcutIntent(
+ id = SHORTCUT_ID,
+ shortLabel = getString(R.string.note_task_button_label),
+ intent = LaunchNoteTaskActivity.newIntent(context = this),
+ iconResource = R.drawable.ic_note_task_button,
+ )
+ setResult(Activity.RESULT_OK, intent)
+
+ finish()
+ }
+
+ private fun createShortcutIntent(
+ id: String,
+ shortLabel: String,
+ intent: Intent,
+ @DrawableRes iconResource: Int,
+ ): Intent {
+ val shortcutInfo =
+ ShortcutInfoCompat.Builder(this, id)
+ .setIntent(intent)
+ .setShortLabel(shortLabel)
+ .setLongLived(true)
+ .setIcon(IconCompat.createWithResource(this, iconResource))
+ .build()
+
+ return ShortcutManagerCompat.createShortcutResultIntent(
+ this,
+ shortcutInfo,
+ )
+ }
+
+ private companion object {
+ private const val SHORTCUT_ID = "note-task-shortcut-id"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
new file mode 100644
index 000000000000..47fe67638cd0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.notetask.shortcut
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import com.android.systemui.notetask.NoteTaskController
+import com.android.systemui.notetask.NoteTaskIntentResolver
+import javax.inject.Inject
+
+/** Activity responsible for launching the note experience, and finish. */
+internal class LaunchNoteTaskActivity
+@Inject
+constructor(
+ private val noteTaskController: NoteTaskController,
+) : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ noteTaskController.showNoteTask(isInMultiWindowMode)
+
+ finish()
+ }
+
+ companion object {
+
+ /** Creates a new [Intent] set to start [LaunchNoteTaskActivity]. */
+ fun newIntent(context: Context): Intent {
+ return Intent(context, LaunchNoteTaskActivity::class.java).apply {
+ // Intent's action must be set in shortcuts, or an exception will be thrown.
+ // TODO(b/254606432): Use Intent.ACTION_NOTES instead.
+ action = NoteTaskIntentResolver.NOTES_ACTION
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
index 67bc76998597..5dbf0f8dcdce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java
@@ -247,7 +247,7 @@ public class QSSecurityFooterUtils implements DialogInterface.OnClickListener {
Icon icon;
ContentDescription contentDescription = null;
- if (isParentalControlsEnabled) {
+ if (isParentalControlsEnabled && securityModel.getDeviceAdminIcon() != null) {
icon = new Icon.Loaded(securityModel.getDeviceAdminIcon(), contentDescription);
} else if (vpnName != null || vpnNameWorkProfile != null) {
if (securityModel.isVpnBranded()) {
@@ -476,7 +476,7 @@ public class QSSecurityFooterUtils implements DialogInterface.OnClickListener {
@VisibleForTesting
View createDialogView(Context quickSettingsContext) {
if (mSecurityController.isParentalControlsEnabled()) {
- return createParentalControlsDialogView();
+ return createParentalControlsDialogView(quickSettingsContext);
}
return createOrganizationDialogView(quickSettingsContext);
}
@@ -579,8 +579,8 @@ public class QSSecurityFooterUtils implements DialogInterface.OnClickListener {
return dialogView;
}
- private View createParentalControlsDialogView() {
- View dialogView = LayoutInflater.from(mContext)
+ private View createParentalControlsDialogView(Context quickSettingsContext) {
+ View dialogView = LayoutInflater.from(quickSettingsContext)
.inflate(R.layout.quick_settings_footer_dialog_parental_controls, null, false);
DeviceAdminInfo info = mSecurityController.getDeviceAdminInfo();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index d7eddf53dea5..56c34a0b3665 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -39,6 +39,7 @@ import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.statusbar.notification.LegacySourceType;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -66,6 +67,8 @@ public class NotificationShelf extends ActivatableNotificationView implements
// the next icon has translated out of the way, to avoid overlapping.
private static final Interpolator ICON_ALPHA_INTERPOLATOR =
new PathInterpolator(0.6f, 0f, 0.6f, 0f);
+ private static final SourceType BASE_VALUE = SourceType.from("BaseValue");
+ private static final SourceType SHELF_SCROLL = SourceType.from("ShelfScroll");
private NotificationIconContainer mShelfIcons;
private int[] mTmp = new int[2];
@@ -112,19 +115,24 @@ public class NotificationShelf extends ActivatableNotificationView implements
setClipChildren(false);
setClipToPadding(false);
mShelfIcons.setIsStaticLayout(false);
- requestBottomRoundness(1.0f, /* animate = */ false, SourceType.DefaultValue);
- requestTopRoundness(1f, false, SourceType.DefaultValue);
+ requestRoundness(/* top = */ 1f, /* bottom = */ 1f, BASE_VALUE, /* animate = */ false);
- // Setting this to first in section to get the clipping to the top roundness correct. This
- // value determines the way we are clipping to the top roundness of the overall shade
- setFirstInSection(true);
+ if (!mUseRoundnessSourceTypes) {
+ // Setting this to first in section to get the clipping to the top roundness correct.
+ // This value determines the way we are clipping to the top roundness of the overall
+ // shade
+ setFirstInSection(true);
+ }
updateResources();
}
public void bind(AmbientState ambientState,
- NotificationStackScrollLayoutController hostLayoutController) {
+ NotificationStackScrollLayoutController hostLayoutController) {
mAmbientState = ambientState;
mHostLayoutController = hostLayoutController;
+ hostLayoutController.setOnNotificationRemovedListener((child, isTransferInProgress) -> {
+ child.requestRoundnessReset(SHELF_SCROLL);
+ });
}
private void updateResources() {
@@ -185,9 +193,11 @@ public class NotificationShelf extends ActivatableNotificationView implements
+ " indexOfFirstViewInShelf=" + mIndexOfFirstViewInShelf + ')';
}
- /** Update the state of the shelf. */
+ /**
+ * Update the state of the shelf.
+ */
public void updateState(StackScrollAlgorithm.StackScrollAlgorithmState algorithmState,
- AmbientState ambientState) {
+ AmbientState ambientState) {
ExpandableView lastView = ambientState.getLastVisibleBackgroundChild();
ShelfState viewState = (ShelfState) getViewState();
if (mShowNotificationShelf && lastView != null) {
@@ -246,7 +256,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
/**
* @param fractionToShade Fraction of lockscreen to shade transition
- * @param shortestWidth Shortest width to use for lockscreen shelf
+ * @param shortestWidth Shortest width to use for lockscreen shelf
*/
@VisibleForTesting
public void updateActualWidth(float fractionToShade, float shortestWidth) {
@@ -281,9 +291,9 @@ public class NotificationShelf extends ActivatableNotificationView implements
/**
* @param localX Click x from left of screen
- * @param slop Margin of error within which we count x for valid click
- * @param left Left of shelf, from left of screen
- * @param right Right of shelf, from left of screen
+ * @param slop Margin of error within which we count x for valid click
+ * @param left Left of shelf, from left of screen
+ * @param right Right of shelf, from left of screen
* @return Whether click x was in view
*/
@VisibleForTesting
@@ -293,8 +303,8 @@ public class NotificationShelf extends ActivatableNotificationView implements
/**
* @param localY Click y from top of shelf
- * @param slop Margin of error within which we count y for valid click
- * @param top Top of shelf
+ * @param slop Margin of error within which we count y for valid click
+ * @param top Top of shelf
* @param bottom Height of shelf
* @return Whether click y was in view
*/
@@ -306,7 +316,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
/**
* @param localX Click x
* @param localY Click y
- * @param slop Margin of error for valid click
+ * @param slop Margin of error for valid click
* @return Whether this click was on the visible (non-clipped) part of the shelf
*/
@Override
@@ -478,13 +488,15 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
}
- private void updateCornerRoundnessOnScroll(ActivatableNotificationView anv, float viewStart,
+ private void updateCornerRoundnessOnScroll(
+ ActivatableNotificationView anv,
+ float viewStart,
float shelfStart) {
final boolean isUnlockedHeadsUp = !mAmbientState.isOnKeyguard()
&& !mAmbientState.isShadeExpanded()
&& anv instanceof ExpandableNotificationRow
- && ((ExpandableNotificationRow) anv).isHeadsUp();
+ && anv.isHeadsUp();
final boolean isHunGoingToShade = mAmbientState.isShadeExpanded()
&& anv == mAmbientState.getTrackedHeadsUpRow();
@@ -506,41 +518,40 @@ public class NotificationShelf extends ActivatableNotificationView implements
* mAmbientState.getExpansionFraction();
final float cornerAnimationTop = shelfStart - cornerAnimationDistance;
- if (viewEnd >= cornerAnimationTop) {
- // Round bottom corners within animation bounds
- final float changeFraction = MathUtils.saturate(
- (viewEnd - cornerAnimationTop) / cornerAnimationDistance);
- anv.requestBottomRoundness(
- /* value = */ anv.isLastInSection() ? 1f : changeFraction,
- /* animate = */ false,
- SourceType.OnScroll);
-
- } else if (viewEnd < cornerAnimationTop) {
- // Fast scroll skips frames and leaves corners with unfinished rounding.
- // Reset top and bottom corners outside of animation bounds.
- anv.requestBottomRoundness(
- /* value = */ anv.isLastInSection() ? 1f : 0f,
- /* animate = */ false,
- SourceType.OnScroll);
+ final SourceType sourceType;
+ if (mUseRoundnessSourceTypes) {
+ sourceType = SHELF_SCROLL;
+ } else {
+ sourceType = LegacySourceType.OnScroll;
}
- if (viewStart >= cornerAnimationTop) {
+ final float topValue;
+ if (!mUseRoundnessSourceTypes && anv.isFirstInSection()) {
+ topValue = 1f;
+ } else if (viewStart >= cornerAnimationTop) {
// Round top corners within animation bounds
- final float changeFraction = MathUtils.saturate(
+ topValue = MathUtils.saturate(
(viewStart - cornerAnimationTop) / cornerAnimationDistance);
- anv.requestTopRoundness(
- /* value = */ anv.isFirstInSection() ? 1f : changeFraction,
- /* animate = */ false,
- SourceType.OnScroll);
+ } else {
+ // Fast scroll skips frames and leaves corners with unfinished rounding.
+ // Reset top and bottom corners outside of animation bounds.
+ topValue = 0f;
+ }
+ anv.requestTopRoundness(topValue, sourceType, /* animate = */ false);
- } else if (viewStart < cornerAnimationTop) {
+ final float bottomValue;
+ if (!mUseRoundnessSourceTypes && anv.isLastInSection()) {
+ bottomValue = 1f;
+ } else if (viewEnd >= cornerAnimationTop) {
+ // Round bottom corners within animation bounds
+ bottomValue = MathUtils.saturate(
+ (viewEnd - cornerAnimationTop) / cornerAnimationDistance);
+ } else {
// Fast scroll skips frames and leaves corners with unfinished rounding.
// Reset top and bottom corners outside of animation bounds.
- anv.requestTopRoundness(
- /* value = */ anv.isFirstInSection() ? 1f : 0f,
- /* animate = */ false,
- SourceType.OnScroll);
+ bottomValue = 0f;
}
+ anv.requestBottomRoundness(bottomValue, sourceType, /* animate = */ false);
}
/**
@@ -626,10 +637,11 @@ public class NotificationShelf extends ActivatableNotificationView implements
/**
* Update the clipping of this view.
+ *
* @return the amount that our own top should be clipped
*/
private int updateNotificationClipHeight(ExpandableView view,
- float notificationClipEnd, int childIndex) {
+ float notificationClipEnd, int childIndex) {
float viewEnd = view.getTranslationY() + view.getActualHeight();
boolean isPinned = (view.isPinned() || view.isHeadsUpAnimatingAway())
&& !mAmbientState.isDozingAndNotPulsing(view);
@@ -657,7 +669,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
@Override
public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd,
- int outlineTranslation) {
+ int outlineTranslation) {
if (!mHasItemsInStableShelf) {
shadowIntensity = 0.0f;
}
@@ -665,18 +677,24 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
/**
- * @param i Index of the view in the host layout.
- * @param view The current ExpandableView.
- * @param scrollingFast Whether we are scrolling fast.
+ * @param i Index of the view in the host layout.
+ * @param view The current ExpandableView.
+ * @param scrollingFast Whether we are scrolling fast.
* @param expandingAnimated Whether we are expanding a notification.
- * @param isLastChild Whether this is the last view.
- * @param shelfClipStart The point at which notifications start getting clipped by the shelf.
+ * @param isLastChild Whether this is the last view.
+ * @param shelfClipStart The point at which notifications start getting clipped by the shelf.
* @return The amount how much this notification is in the shelf.
- * 0f is not in shelf. 1f is completely in shelf.
+ * 0f is not in shelf. 1f is completely in shelf.
*/
@VisibleForTesting
- public float getAmountInShelf(int i, ExpandableView view, boolean scrollingFast,
- boolean expandingAnimated, boolean isLastChild, float shelfClipStart) {
+ public float getAmountInShelf(
+ int i,
+ ExpandableView view,
+ boolean scrollingFast,
+ boolean expandingAnimated,
+ boolean isLastChild,
+ float shelfClipStart
+ ) {
// Let's calculate how much the view is in the shelf
float viewStart = view.getTranslationY();
@@ -755,8 +773,13 @@ public class NotificationShelf extends ActivatableNotificationView implements
return start;
}
- private void updateIconPositioning(ExpandableView view, float iconTransitionAmount,
- boolean scrollingFast, boolean expandingAnimated, boolean isLastChild) {
+ private void updateIconPositioning(
+ ExpandableView view,
+ float iconTransitionAmount,
+ boolean scrollingFast,
+ boolean expandingAnimated,
+ boolean isLastChild
+ ) {
StatusBarIconView icon = view.getShelfIcon();
NotificationIconContainer.IconState iconState = getIconState(icon);
if (iconState == null) {
@@ -817,7 +840,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
|| row.showingPulsing()
|| row.getTranslationZ() > mAmbientState.getBaseZHeight();
- iconState.iconAppearAmount = iconState.hidden? 0f : transitionAmount;
+ iconState.iconAppearAmount = iconState.hidden ? 0f : transitionAmount;
// Fade in icons at shelf start
// This is important for conversation icons, which are badged and need x reset
@@ -847,7 +870,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
private float getFullyClosedTranslation() {
- return - (getIntrinsicHeight() - mStatusBarHeight) / 2;
+ return -(getIntrinsicHeight() - mStatusBarHeight) / 2;
}
@Override
@@ -904,7 +927,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
/**
* @return whether the shelf has any icons in it when a potential animation has finished, i.e
- * if the current state would be applied right now
+ * if the current state would be applied right now
*/
public boolean hasItemsInStableShelf() {
return mHasItemsInStableShelf;
@@ -962,7 +985,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
- int oldTop, int oldRight, int oldBottom) {
+ int oldTop, int oldRight, int oldBottom) {
updateRelativeOffset();
}
@@ -981,12 +1004,11 @@ public class NotificationShelf extends ActivatableNotificationView implements
/**
* This method resets the OnScroll roundness of a view to 0f
- *
+ * <p>
* Note: This should be the only class that handles roundness {@code SourceType.OnScroll}
*/
- public static void resetOnScrollRoundness(ExpandableView expandableView) {
- expandableView.requestTopRoundness(0f, false, SourceType.OnScroll);
- expandableView.requestBottomRoundness(0f, false, SourceType.OnScroll);
+ public static void resetLegacyOnScrollRoundness(ExpandableView expandableView) {
+ expandableView.requestRoundnessReset(LegacySourceType.OnScroll);
}
public class ShelfState extends ExpandableViewState {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java
index 3b1fa1779c17..bb84c758d87d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar;
import android.view.View;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
@@ -42,14 +44,17 @@ public class NotificationShelfController {
private AmbientState mAmbientState;
@Inject
- public NotificationShelfController(NotificationShelf notificationShelf,
+ public NotificationShelfController(
+ NotificationShelf notificationShelf,
ActivatableNotificationViewController activatableNotificationViewController,
KeyguardBypassController keyguardBypassController,
- SysuiStatusBarStateController statusBarStateController) {
+ SysuiStatusBarStateController statusBarStateController,
+ FeatureFlags featureFlags) {
mView = notificationShelf;
mActivatableNotificationViewController = activatableNotificationViewController;
mKeyguardBypassController = keyguardBypassController;
mStatusBarStateController = statusBarStateController;
+ mView.useRoundnessSourceTypes(featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES));
mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
@@ -88,7 +93,7 @@ public class NotificationShelfController {
public @View.Visibility int getVisibility() {
return mView.getVisibility();
- };
+ }
public void setCollapsedIcons(NotificationIconContainer notificationIcons) {
mView.setCollapsedIcons(notificationIcons);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 97a47b5d1407..362764d1f89e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -78,6 +78,7 @@ import com.android.systemui.plugins.log.LogBuffer;
import com.android.systemui.plugins.log.LogLevel;
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DataSaverControllerImpl;
@@ -193,6 +194,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
private final Executor mBgExecutor;
// Handler that all callbacks are made on.
private final CallbackHandler mCallbackHandler;
+ private final StatusBarPipelineFlags mStatusBarPipelineFlags;
private int mEmergencySource;
private boolean mIsEmergency;
@@ -243,6 +245,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
TelephonyListenerManager telephonyListenerManager,
@Nullable WifiManager wifiManager,
AccessPointControllerImpl accessPointController,
+ StatusBarPipelineFlags statusBarPipelineFlags,
DemoModeController demoModeController,
CarrierConfigTracker carrierConfigTracker,
WifiStatusTrackerFactory trackerFactory,
@@ -261,6 +264,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
bgExecutor,
callbackHandler,
accessPointController,
+ statusBarPipelineFlags,
new DataUsageController(context),
new SubscriptionDefaults(),
deviceProvisionedController,
@@ -288,6 +292,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
Executor bgExecutor,
CallbackHandler callbackHandler,
AccessPointControllerImpl accessPointController,
+ StatusBarPipelineFlags statusBarPipelineFlags,
DataUsageController dataUsageController,
SubscriptionDefaults defaultsHandler,
DeviceProvisionedController deviceProvisionedController,
@@ -309,6 +314,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
mBgLooper = bgLooper;
mBgExecutor = bgExecutor;
mCallbackHandler = callbackHandler;
+ mStatusBarPipelineFlags = statusBarPipelineFlags;
mDataSaverController = new DataSaverControllerImpl(context);
mBroadcastDispatcher = broadcastDispatcher;
mMobileFactory = mobileFactory;
@@ -1334,7 +1340,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
mWifiSignalController.notifyListeners();
}
String sims = args.getString("sims");
- if (sims != null) {
+ if (sims != null && !mStatusBarPipelineFlags.useNewMobileIcons()) {
int num = MathUtils.constrain(Integer.parseInt(sims), 1, 8);
List<SubscriptionInfo> subs = new ArrayList<>();
if (num != mMobileSignalControllers.size()) {
@@ -1357,7 +1363,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected);
}
String mobile = args.getString("mobile");
- if (mobile != null) {
+ if (mobile != null && !mStatusBarPipelineFlags.useNewMobileIcons()) {
boolean show = mobile.equals("show");
String datatype = args.getString("datatype");
String slotString = args.getString("slot");
@@ -1442,7 +1448,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
controller.notifyListeners();
}
String carrierNetworkChange = args.getString("carriernetworkchange");
- if (carrierNetworkChange != null) {
+ if (carrierNetworkChange != null && !mStatusBarPipelineFlags.useNewMobileIcons()) {
boolean show = carrierNetworkChange.equals("show");
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController controller = mMobileSignalControllers.valueAt(i);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
index ed7f648081c8..0eb00008e289 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
@@ -74,8 +74,8 @@ interface Roundable {
@JvmDefault
fun requestTopRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float,
- animate: Boolean,
sourceType: SourceType,
+ animate: Boolean,
): Boolean {
val roundnessMap = roundableState.topRoundnessMap
val lastValue = roundnessMap.values.maxOrNull() ?: 0f
@@ -105,6 +105,30 @@ interface Roundable {
}
/**
+ * Request the top roundness [value] for a specific [sourceType]. Animate the roundness if the
+ * view is shown.
+ *
+ * The top roundness of a [Roundable] can be defined by different [sourceType]. In case more
+ * origins require different roundness, for the same property, the maximum value will always be
+ * chosen.
+ *
+ * @param value a value between 0f and 1f.
+ * @param sourceType the source from which the request for roundness comes.
+ * @return Whether the roundness was changed.
+ */
+ @JvmDefault
+ fun requestTopRoundness(
+ @FloatRange(from = 0.0, to = 1.0) value: Float,
+ sourceType: SourceType,
+ ): Boolean {
+ return requestTopRoundness(
+ value = value,
+ sourceType = sourceType,
+ animate = roundableState.targetView.isShown
+ )
+ }
+
+ /**
* Request the bottom roundness [value] for a specific [sourceType].
*
* The bottom roundness of a [Roundable] can be defined by different [sourceType]. In case more
@@ -119,8 +143,8 @@ interface Roundable {
@JvmDefault
fun requestBottomRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float,
- animate: Boolean,
sourceType: SourceType,
+ animate: Boolean,
): Boolean {
val roundnessMap = roundableState.bottomRoundnessMap
val lastValue = roundnessMap.values.maxOrNull() ?: 0f
@@ -149,9 +173,101 @@ interface Roundable {
return false
}
+ /**
+ * Request the bottom roundness [value] for a specific [sourceType]. Animate the roundness if
+ * the view is shown.
+ *
+ * The bottom roundness of a [Roundable] can be defined by different [sourceType]. In case more
+ * origins require different roundness, for the same property, the maximum value will always be
+ * chosen.
+ *
+ * @param value value between 0f and 1f.
+ * @param sourceType the source from which the request for roundness comes.
+ * @return Whether the roundness was changed.
+ */
+ @JvmDefault
+ fun requestBottomRoundness(
+ @FloatRange(from = 0.0, to = 1.0) value: Float,
+ sourceType: SourceType,
+ ): Boolean {
+ return requestBottomRoundness(
+ value = value,
+ sourceType = sourceType,
+ animate = roundableState.targetView.isShown
+ )
+ }
+
+ /**
+ * Request the roundness [value] for a specific [sourceType].
+ *
+ * The top/bottom roundness of a [Roundable] can be defined by different [sourceType]. In case
+ * more origins require different roundness, for the same property, the maximum value will
+ * always be chosen.
+ *
+ * @param top top value between 0f and 1f.
+ * @param bottom bottom value between 0f and 1f.
+ * @param sourceType the source from which the request for roundness comes.
+ * @param animate true if it should animate to that value.
+ * @return Whether the roundness was changed.
+ */
+ @JvmDefault
+ fun requestRoundness(
+ @FloatRange(from = 0.0, to = 1.0) top: Float,
+ @FloatRange(from = 0.0, to = 1.0) bottom: Float,
+ sourceType: SourceType,
+ animate: Boolean,
+ ): Boolean {
+ val hasTopChanged =
+ requestTopRoundness(value = top, sourceType = sourceType, animate = animate)
+ val hasBottomChanged =
+ requestBottomRoundness(value = bottom, sourceType = sourceType, animate = animate)
+ return hasTopChanged || hasBottomChanged
+ }
+
+ /**
+ * Request the roundness [value] for a specific [sourceType]. Animate the roundness if the view
+ * is shown.
+ *
+ * The top/bottom roundness of a [Roundable] can be defined by different [sourceType]. In case
+ * more origins require different roundness, for the same property, the maximum value will
+ * always be chosen.
+ *
+ * @param top top value between 0f and 1f.
+ * @param bottom bottom value between 0f and 1f.
+ * @param sourceType the source from which the request for roundness comes.
+ * @return Whether the roundness was changed.
+ */
+ @JvmDefault
+ fun requestRoundness(
+ @FloatRange(from = 0.0, to = 1.0) top: Float,
+ @FloatRange(from = 0.0, to = 1.0) bottom: Float,
+ sourceType: SourceType,
+ ): Boolean {
+ return requestRoundness(
+ top = top,
+ bottom = bottom,
+ sourceType = sourceType,
+ animate = roundableState.targetView.isShown,
+ )
+ }
+
+ /**
+ * Request the roundness 0f for a [SourceType]. Animate the roundness if the view is shown.
+ *
+ * The top/bottom roundness of a [Roundable] can be defined by different [sourceType]. In case
+ * more origins require different roundness, for the same property, the maximum value will
+ * always be chosen.
+ *
+ * @param sourceType the source from which the request for roundness comes.
+ */
+ @JvmDefault
+ fun requestRoundnessReset(sourceType: SourceType) {
+ requestRoundness(top = 0f, bottom = 0f, sourceType = sourceType)
+ }
+
/** Apply the roundness changes, usually means invalidate the [RoundableState.targetView]. */
@JvmDefault
- fun applyRoundness() {
+ fun applyRoundnessAndInvalidate() {
roundableState.targetView.invalidate()
}
@@ -227,7 +343,7 @@ class RoundableState(
/** Set the current top roundness */
internal fun setTopRoundness(
value: Float,
- animated: Boolean = targetView.isShown,
+ animated: Boolean,
) {
PropertyAnimator.setProperty(targetView, topAnimatable, value, DURATION, animated)
}
@@ -235,11 +351,19 @@ class RoundableState(
/** Set the current bottom roundness */
internal fun setBottomRoundness(
value: Float,
- animated: Boolean = targetView.isShown,
+ animated: Boolean,
) {
PropertyAnimator.setProperty(targetView, bottomAnimatable, value, DURATION, animated)
}
+ fun debugString() = buildString {
+ append("TargetView: ${targetView.hashCode()} ")
+ append("Top: $topRoundness ")
+ append(topRoundnessMap.map { "${it.key} ${it.value}" })
+ append(" Bottom: $bottomRoundness ")
+ append(bottomRoundnessMap.map { "${it.key} ${it.value}" })
+ }
+
companion object {
private val DURATION: AnimationProperties =
AnimationProperties()
@@ -252,7 +376,7 @@ class RoundableState(
override fun setValue(view: View, value: Float) {
roundable.roundableState.topRoundness = value
- roundable.applyRoundness()
+ roundable.applyRoundnessAndInvalidate()
}
},
R.id.top_roundess_animator_tag,
@@ -267,7 +391,7 @@ class RoundableState(
override fun setValue(view: View, value: Float) {
roundable.roundableState.bottomRoundness = value
- roundable.applyRoundness()
+ roundable.applyRoundnessAndInvalidate()
}
},
R.id.bottom_roundess_animator_tag,
@@ -277,7 +401,31 @@ class RoundableState(
}
}
-enum class SourceType {
+/**
+ * Interface used to define the owner of a roundness. Usually the [SourceType] is defined as a
+ * private property of a class.
+ */
+interface SourceType {
+ companion object {
+ /**
+ * This is the most convenient way to define a new [SourceType].
+ *
+ * For example:
+ *
+ * ```kotlin
+ * private val SECTION = SourceType.from("Section")
+ * ```
+ */
+ @JvmStatic
+ fun from(name: String) =
+ object : SourceType {
+ override fun toString() = name
+ }
+ }
+}
+
+@Deprecated("Use SourceType.from() instead", ReplaceWith("SourceType.from()"))
+enum class LegacySourceType : SourceType {
DefaultValue,
OnDismissAnimation,
OnScroll,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index d29298a2f637..fbe88dff07f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -39,9 +39,13 @@ import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import java.util.HashSet;
+import java.util.Set;
+
/**
* Base class for both {@link ExpandableNotificationRow} and {@link NotificationShelf}
* to implement dimming/activating on Keyguard for the double-tap gesture
@@ -91,6 +95,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
= new PathInterpolator(0.6f, 0, 0.5f, 1);
private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
= new PathInterpolator(0, 0, 0.5f, 1);
+ private final Set<SourceType> mOnDetachResetRoundness = new HashSet<>();
private int mTintedRippleColor;
private int mNormalRippleColor;
private Gefingerpoken mTouchHandler;
@@ -134,6 +139,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
private boolean mDismissed;
private boolean mRefocusOnDismiss;
private AccessibilityManager mAccessibilityManager;
+ protected boolean mUseRoundnessSourceTypes;
public ActivatableNotificationView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -613,9 +619,9 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
protected void resetAllContentAlphas() {}
@Override
- public void applyRoundness() {
- super.applyRoundness();
+ public void applyRoundnessAndInvalidate() {
applyBackgroundRoundness(getTopCornerRadius(), getBottomCornerRadius());
+ super.applyRoundnessAndInvalidate();
}
@Override
@@ -775,6 +781,33 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
mAccessibilityManager = accessibilityManager;
}
+ /**
+ * Enable the support for rounded corner based on the SourceType
+ * @param enabled true if is supported
+ */
+ public void useRoundnessSourceTypes(boolean enabled) {
+ mUseRoundnessSourceTypes = enabled;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mUseRoundnessSourceTypes && !mOnDetachResetRoundness.isEmpty()) {
+ for (SourceType sourceType : mOnDetachResetRoundness) {
+ requestRoundnessReset(sourceType);
+ }
+ mOnDetachResetRoundness.clear();
+ }
+ }
+
+ /**
+ * SourceType which should be reset when this View is detached
+ * @param sourceType will be reset on View detached
+ */
+ public void addOnDetachResetRoundness(SourceType sourceType) {
+ mOnDetachResetRoundness.add(sourceType);
+ }
+
public interface OnActivatedListener {
void onActivated(ActivatableNotificationView view);
void onActivationReset(ActivatableNotificationView view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index d7d5ac961249..c7c1634ea105 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -91,6 +91,7 @@ import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.FeedbackIcon;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
+import com.android.systemui.statusbar.notification.LegacySourceType;
import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -143,6 +144,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private static final int MENU_VIEW_INDEX = 0;
public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f;
private static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
+ private static final SourceType BASE_VALUE = SourceType.from("BaseValue");
+ private static final SourceType FROM_PARENT = SourceType.from("FromParent(ENR)");
+ private static final SourceType PINNED = SourceType.from("Pinned");
// We don't correctly track dark mode until the content views are inflated, so always update
// the background on first content update just in case it happens to be during a theme change.
@@ -150,6 +154,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private boolean mNotificationTranslationFinished = false;
private boolean mIsSnoozed;
private boolean mIsFaded;
+ private boolean mAnimatePinnedRoundness = false;
/**
* Listener for when {@link ExpandableNotificationRow} is laid out.
@@ -376,7 +381,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private float mTopRoundnessDuringLaunchAnimation;
private float mBottomRoundnessDuringLaunchAnimation;
- private boolean mIsNotificationGroupCornerEnabled;
+ private float mSmallRoundness;
/**
* Returns whether the given {@code statusBarNotification} is a system notification.
@@ -844,7 +849,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
onAttachedChildrenCountChanged();
row.setIsChildInGroup(false, null);
- row.requestBottomRoundness(0.0f, /* animate = */ false, SourceType.DefaultValue);
+ if (!mUseRoundnessSourceTypes) {
+ row.requestBottomRoundness(0.0f, LegacySourceType.DefaultValue, /* animate = */ false);
+ }
}
/**
@@ -860,7 +867,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (child.keepInParentForDismissAnimation()) {
mChildrenContainer.removeNotification(child);
child.setIsChildInGroup(false, null);
- child.requestBottomRoundness(0.0f, /* animate = */ false, SourceType.DefaultValue);
+ if (!mUseRoundnessSourceTypes) {
+ LegacySourceType sourceType = LegacySourceType.DefaultValue;
+ child.requestBottomRoundness(0f, sourceType, /* animate = */ false);
+ }
child.setKeepInParentForDismissAnimation(false);
logKeepInParentChildDetached(child);
childCountChanged = true;
@@ -915,6 +925,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mNotificationParent.updateBackgroundForGroupState();
}
updateBackgroundClipping();
+ if (mUseRoundnessSourceTypes) {
+ updateBaseRoundness();
+ }
}
@Override
@@ -1033,6 +1046,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (isAboveShelf() != wasAboveShelf) {
mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
}
+ if (mUseRoundnessSourceTypes) {
+ if (pinned) {
+ // Should be animated if someone explicitly set it to 0 and the row is shown.
+ boolean animated = mAnimatePinnedRoundness && isShown();
+ requestRoundness(/* top = */ 1f, /* bottom = */ 1f, PINNED, animated);
+ } else {
+ requestRoundnessReset(PINNED);
+ mAnimatePinnedRoundness = true;
+ }
+ }
}
@Override
@@ -1607,6 +1630,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
super(context, attrs);
mImageResolver = new NotificationInlineImageResolver(context,
new NotificationInlineImageCache());
+ float radius = getResources().getDimension(R.dimen.notification_corner_radius_small);
+ mSmallRoundness = radius / getMaxRadius();
initDimens();
}
@@ -1839,7 +1864,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mChildrenContainer.setIsLowPriority(mIsLowPriority);
mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this);
mChildrenContainer.onNotificationUpdated();
- mChildrenContainer.enableNotificationGroupCorner(mIsNotificationGroupCornerEnabled);
+ mChildrenContainer.useRoundnessSourceTypes(mUseRoundnessSourceTypes);
mTranslateableViews.add(mChildrenContainer);
});
@@ -2271,7 +2296,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
public float getTopRoundness() {
- if (mExpandAnimationRunning) {
+ if (!mUseRoundnessSourceTypes && mExpandAnimationRunning) {
return mTopRoundnessDuringLaunchAnimation;
}
@@ -2280,7 +2305,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
public float getBottomRoundness() {
- if (mExpandAnimationRunning) {
+ if (!mUseRoundnessSourceTypes && mExpandAnimationRunning) {
return mBottomRoundnessDuringLaunchAnimation;
}
@@ -3436,17 +3461,24 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
@Override
- public void applyRoundness() {
- super.applyRoundness();
+ public void applyRoundnessAndInvalidate() {
applyChildrenRoundness();
+ super.applyRoundnessAndInvalidate();
}
private void applyChildrenRoundness() {
if (mIsSummaryWithChildren) {
- mChildrenContainer.requestBottomRoundness(
- getBottomRoundness(),
- /* animate = */ false,
- SourceType.DefaultValue);
+ if (mUseRoundnessSourceTypes) {
+ mChildrenContainer.requestRoundness(
+ /* top = */ getTopRoundness(),
+ /* bottom = */ getBottomRoundness(),
+ FROM_PARENT);
+ } else {
+ mChildrenContainer.requestBottomRoundness(
+ getBottomRoundness(),
+ LegacySourceType.DefaultValue,
+ /* animate = */ false);
+ }
}
}
@@ -3605,6 +3637,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
} else {
pw.println("no viewState!!!");
}
+ pw.println("Roundness: " + getRoundableState().debugString());
if (mIsSummaryWithChildren) {
pw.println();
@@ -3649,14 +3682,38 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return mTargetPoint;
}
+ /** Update the minimum roundness based on current state */
+ private void updateBaseRoundness() {
+ if (isChildInGroup()) {
+ requestRoundnessReset(BASE_VALUE);
+ } else {
+ requestRoundness(mSmallRoundness, mSmallRoundness, BASE_VALUE);
+ }
+ }
+
/**
- * Enable the support for rounded corner in notification group
+ * Enable the support for rounded corner based on the SourceType
* @param enabled true if is supported
*/
- public void enableNotificationGroupCorner(boolean enabled) {
- mIsNotificationGroupCornerEnabled = enabled;
+ @Override
+ public void useRoundnessSourceTypes(boolean enabled) {
+ super.useRoundnessSourceTypes(enabled);
if (mChildrenContainer != null) {
- mChildrenContainer.enableNotificationGroupCorner(mIsNotificationGroupCornerEnabled);
+ mChildrenContainer.useRoundnessSourceTypes(mUseRoundnessSourceTypes);
+ }
+ }
+
+ @Override
+ public String toString() {
+ String roundableStateDebug = "RoundableState = " + getRoundableState().debugString();
+ return "ExpandableNotificationRow:" + hashCode() + " { " + roundableStateDebug + " }";
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (mUseRoundnessSourceTypes) {
+ updateBaseRoundness();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 8a400d5fef99..d1138608805b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -255,8 +255,8 @@ public class ExpandableNotificationRowController implements NotifViewController
mStatusBarStateController.removeCallback(mStatusBarStateListener);
}
});
- mView.enableNotificationGroupCorner(
- mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_CORNER));
+ mView.useRoundnessSourceTypes(
+ mFeatureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES));
}
private final StatusBarStateController.StateListener mStatusBarStateListener =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 232462714f7d..0213b969551e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -219,9 +219,9 @@ public abstract class ExpandableOutlineView extends ExpandableView {
}
@Override
- public void applyRoundness() {
+ public void applyRoundnessAndInvalidate() {
invalidateOutline();
- super.applyRoundness();
+ super.applyRoundnessAndInvalidate();
}
protected void setBackgroundTop(int backgroundTop) {
@@ -233,7 +233,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
public void onDensityOrFontScaleChanged() {
initDimens();
- applyRoundness();
+ applyRoundnessAndInvalidate();
}
@Override
@@ -241,7 +241,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
int previousHeight = getActualHeight();
super.setActualHeight(actualHeight, notifyListeners);
if (previousHeight != actualHeight) {
- applyRoundness();
+ applyRoundnessAndInvalidate();
}
}
@@ -250,7 +250,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
int previousAmount = getClipTopAmount();
super.setClipTopAmount(clipTopAmount);
if (previousAmount != clipTopAmount) {
- applyRoundness();
+ applyRoundnessAndInvalidate();
}
}
@@ -259,14 +259,14 @@ public abstract class ExpandableOutlineView extends ExpandableView {
int previousAmount = getClipBottomAmount();
super.setClipBottomAmount(clipBottomAmount);
if (previousAmount != clipBottomAmount) {
- applyRoundness();
+ applyRoundnessAndInvalidate();
}
}
protected void setOutlineAlpha(float alpha) {
if (alpha != mOutlineAlpha) {
mOutlineAlpha = alpha;
- applyRoundness();
+ applyRoundnessAndInvalidate();
}
}
@@ -280,7 +280,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
setOutlineRect(rect.left, rect.top, rect.right, rect.bottom);
} else {
mCustomOutline = false;
- applyRoundness();
+ applyRoundnessAndInvalidate();
}
}
@@ -340,7 +340,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
// Outlines need to be at least 1 dp
mOutlineRect.bottom = (int) Math.max(top, mOutlineRect.bottom);
mOutlineRect.right = (int) Math.max(left, mOutlineRect.right);
- applyRoundness();
+ applyRoundnessAndInvalidate();
}
public Path getCustomClipPath(View child) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index f13e48d55ae4..1f664cb16179 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -71,6 +71,8 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple
private View mFeedbackIcon;
private boolean mIsLowPriority;
private boolean mTransformLowPriorityTitle;
+ private boolean mUseRoundnessSourceTypes;
+ private RoundnessChangedListener mRoundnessChangedListener;
protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
super(ctx, view, row);
@@ -117,6 +119,20 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple
return mRoundableState;
}
+ @Override
+ public void applyRoundnessAndInvalidate() {
+ if (mUseRoundnessSourceTypes && mRoundnessChangedListener != null) {
+ // We cannot apply the rounded corner to this View, so our parents (in drawChild()) will
+ // clip our canvas. So we should invalidate our parent.
+ mRoundnessChangedListener.applyRoundnessAndInvalidate();
+ }
+ Roundable.super.applyRoundnessAndInvalidate();
+ }
+
+ public void setOnRoundnessChangedListener(RoundnessChangedListener listener) {
+ mRoundnessChangedListener = listener;
+ }
+
protected void resolveHeaderViews() {
mIcon = mView.findViewById(com.android.internal.R.id.icon);
mHeaderText = mView.findViewById(com.android.internal.R.id.header_text);
@@ -343,4 +359,23 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple
}
}
}
+
+ /**
+ * Enable the support for rounded corner based on the SourceType
+ *
+ * @param enabled true if is supported
+ */
+ public void useRoundnessSourceTypes(boolean enabled) {
+ mUseRoundnessSourceTypes = enabled;
+ }
+
+ /**
+ * Interface that handle the Roundness changes
+ */
+ public interface RoundnessChangedListener {
+ /**
+ * This method will be called when this class call applyRoundnessAndInvalidate()
+ */
+ void applyRoundnessAndInvalidate();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index d43ca823089f..4a8e2dbb5334 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -47,6 +47,7 @@ import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.NotificationGroupingUtil;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.FeedbackIcon;
+import com.android.systemui.statusbar.notification.LegacySourceType;
import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.Roundable;
@@ -83,6 +84,7 @@ public class NotificationChildrenContainer extends ViewGroup
return mAnimationFilter;
}
}.setDuration(200);
+ private static final SourceType FROM_PARENT = SourceType.from("FromParent(NCC)");
private final List<View> mDividers = new ArrayList<>();
private final List<ExpandableNotificationRow> mAttachedChildren = new ArrayList<>();
@@ -131,7 +133,7 @@ public class NotificationChildrenContainer extends ViewGroup
private int mUntruncatedChildCount;
private boolean mContainingNotificationIsFaded = false;
private RoundableState mRoundableState;
- private boolean mIsNotificationGroupCornerEnabled;
+ private boolean mUseRoundnessSourceTypes;
public NotificationChildrenContainer(Context context) {
this(context, null);
@@ -313,9 +315,12 @@ public class NotificationChildrenContainer extends ViewGroup
row.setContentTransformationAmount(0, false /* isLastChild */);
row.setNotificationFaded(mContainingNotificationIsFaded);
- // This is a workaround, the NotificationShelf should be the owner of `OnScroll` roundness.
- // Here we should reset the `OnScroll` roundness only on top-level rows.
- NotificationShelf.resetOnScrollRoundness(row);
+ if (!mUseRoundnessSourceTypes) {
+ // This is a workaround, the NotificationShelf should be the owner of `OnScroll`
+ // roundness.
+ // Here we should reset the `OnScroll` roundness only on top-level rows.
+ NotificationShelf.resetLegacyOnScrollRoundness(row);
+ }
// It doesn't make sense to keep old animations around, lets cancel them!
ExpandableViewState viewState = row.getViewState();
@@ -323,6 +328,10 @@ public class NotificationChildrenContainer extends ViewGroup
viewState.cancelAnimations(row);
row.cancelAppearDrawing();
}
+
+ if (mUseRoundnessSourceTypes) {
+ applyRoundnessAndInvalidate();
+ }
}
private void ensureRemovedFromTransientContainer(View v) {
@@ -356,6 +365,11 @@ public class NotificationChildrenContainer extends ViewGroup
if (!row.isRemoved()) {
mGroupingUtil.restoreChildNotification(row);
}
+
+ if (mUseRoundnessSourceTypes) {
+ row.requestRoundnessReset(FROM_PARENT);
+ applyRoundnessAndInvalidate();
+ }
}
/**
@@ -382,6 +396,10 @@ public class NotificationChildrenContainer extends ViewGroup
getContext(),
mNotificationHeader,
mContainingNotification);
+ mNotificationHeaderWrapper.useRoundnessSourceTypes(mUseRoundnessSourceTypes);
+ if (mUseRoundnessSourceTypes) {
+ mNotificationHeaderWrapper.setOnRoundnessChangedListener(this::invalidate);
+ }
addView(mNotificationHeader, 0);
invalidate();
} else {
@@ -419,6 +437,12 @@ public class NotificationChildrenContainer extends ViewGroup
getContext(),
mNotificationHeaderLowPriority,
mContainingNotification);
+ mNotificationHeaderWrapperLowPriority.useRoundnessSourceTypes(
+ mUseRoundnessSourceTypes
+ );
+ if (mUseRoundnessSourceTypes) {
+ mNotificationHeaderWrapper.setOnRoundnessChangedListener(this::invalidate);
+ }
addView(mNotificationHeaderLowPriority, 0);
invalidate();
} else {
@@ -841,7 +865,7 @@ public class NotificationChildrenContainer extends ViewGroup
isCanvasChanged = true;
canvas.save();
- if (mIsNotificationGroupCornerEnabled && translation != 0f) {
+ if (mUseRoundnessSourceTypes && translation != 0f) {
clipPath.offset(translation, 0f);
canvas.clipPath(clipPath);
clipPath.offset(-translation, 0f);
@@ -1392,24 +1416,28 @@ public class NotificationChildrenContainer extends ViewGroup
}
@Override
- public void applyRoundness() {
- Roundable.super.applyRoundness();
+ public void applyRoundnessAndInvalidate() {
boolean last = true;
for (int i = mAttachedChildren.size() - 1; i >= 0; i--) {
ExpandableNotificationRow child = mAttachedChildren.get(i);
if (child.getVisibility() == View.GONE) {
continue;
}
- child.requestTopRoundness(
- /* value = */ 0f,
- /* animate = */ isShown(),
- SourceType.DefaultValue);
- child.requestBottomRoundness(
- /* value = */ last ? getBottomRoundness() : 0f,
- /* animate = */ isShown(),
- SourceType.DefaultValue);
+ if (mUseRoundnessSourceTypes) {
+ child.requestRoundness(
+ /* top = */ 0f,
+ /* bottom = */ last ? getBottomRoundness() : 0f,
+ FROM_PARENT);
+ } else {
+ child.requestRoundness(
+ /* top = */ 0f,
+ /* bottom = */ last ? getBottomRoundness() : 0f,
+ LegacySourceType.DefaultValue,
+ /* animate = */ isShown());
+ }
last = false;
}
+ Roundable.super.applyRoundnessAndInvalidate();
}
public void setHeaderVisibleAmount(float headerVisibleAmount) {
@@ -1467,10 +1495,17 @@ public class NotificationChildrenContainer extends ViewGroup
}
/**
- * Enable the support for rounded corner in notification group
+ * Enable the support for rounded corner based on the SourceType
+ *
* @param enabled true if is supported
*/
- public void enableNotificationGroupCorner(boolean enabled) {
- mIsNotificationGroupCornerEnabled = enabled;
+ public void useRoundnessSourceTypes(boolean enabled) {
+ mUseRoundnessSourceTypes = enabled;
+ }
+
+ @Override
+ public String toString() {
+ String roundableStateDebug = "RoundableState = " + getRoundableState().debugString();
+ return "NotificationChildrenContainer:" + hashCode() + " { " + roundableStateDebug + " }";
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index 6810055ad3bf..fde8c4d453ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -25,6 +25,9 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.statusbar.notification.LegacySourceType;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.Roundable;
import com.android.systemui.statusbar.notification.SourceType;
@@ -45,6 +48,7 @@ import javax.inject.Inject;
public class NotificationRoundnessManager implements Dumpable {
private static final String TAG = "NotificationRoundnessManager";
+ private static final SourceType DISMISS_ANIMATION = SourceType.from("DismissAnimation");
private final ExpandableView[] mFirstInSectionViews;
private final ExpandableView[] mLastInSectionViews;
@@ -63,12 +67,14 @@ public class NotificationRoundnessManager implements Dumpable {
private ExpandableView mSwipedView = null;
private Roundable mViewBeforeSwipedView = null;
private Roundable mViewAfterSwipedView = null;
+ private boolean mUseRoundnessSourceTypes;
@Inject
NotificationRoundnessManager(
NotificationSectionsFeatureManager sectionsFeatureManager,
NotificationRoundnessLogger notifLogger,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ FeatureFlags featureFlags) {
int numberOfSections = sectionsFeatureManager.getNumberOfBuckets();
mFirstInSectionViews = new ExpandableView[numberOfSections];
mLastInSectionViews = new ExpandableView[numberOfSections];
@@ -76,6 +82,7 @@ public class NotificationRoundnessManager implements Dumpable {
mTmpLastInSectionViews = new ExpandableView[numberOfSections];
mNotifLogger = notifLogger;
mDumpManager = dumpManager;
+ mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
mDumpManager.registerDumpable(TAG, this);
}
@@ -94,6 +101,7 @@ public class NotificationRoundnessManager implements Dumpable {
}
public void updateView(ExpandableView view, boolean animate) {
+ if (mUseRoundnessSourceTypes) return;
boolean changed = updateViewWithoutCallback(view, animate);
if (changed) {
mRoundingChangedCallback.run();
@@ -110,6 +118,7 @@ public class NotificationRoundnessManager implements Dumpable {
boolean updateViewWithoutCallback(
ExpandableView view,
boolean animate) {
+ if (mUseRoundnessSourceTypes) return false;
if (view == null
|| view == mViewBeforeSwipedView
|| view == mViewAfterSwipedView) {
@@ -118,13 +127,13 @@ public class NotificationRoundnessManager implements Dumpable {
final boolean isTopChanged = view.requestTopRoundness(
getRoundnessDefaultValue(view, true /* top */),
- animate,
- SourceType.DefaultValue);
+ LegacySourceType.DefaultValue,
+ animate);
final boolean isBottomChanged = view.requestBottomRoundness(
getRoundnessDefaultValue(view, /* top = */ false),
- animate,
- SourceType.DefaultValue);
+ LegacySourceType.DefaultValue,
+ animate);
final boolean isFirstInSection = isFirstInSection(view);
final boolean isLastInSection = isLastInSection(view);
@@ -139,6 +148,7 @@ public class NotificationRoundnessManager implements Dumpable {
}
private boolean isFirstInSection(ExpandableView view) {
+ if (mUseRoundnessSourceTypes) return false;
for (int i = 0; i < mFirstInSectionViews.length; i++) {
if (view == mFirstInSectionViews[i]) {
return true;
@@ -148,6 +158,7 @@ public class NotificationRoundnessManager implements Dumpable {
}
private boolean isLastInSection(ExpandableView view) {
+ if (mUseRoundnessSourceTypes) return false;
for (int i = mLastInSectionViews.length - 1; i >= 0; i--) {
if (view == mLastInSectionViews[i]) {
return true;
@@ -160,9 +171,6 @@ public class NotificationRoundnessManager implements Dumpable {
Roundable viewBefore,
ExpandableView viewSwiped,
Roundable viewAfter) {
- final boolean animate = true;
- final SourceType source = SourceType.OnDismissAnimation;
-
// This method requires you to change the roundness of the current View targets and reset
// the roundness of the old View targets (if any) to 0f.
// To avoid conflicts, it generates a set of old Views and removes the current Views
@@ -172,31 +180,34 @@ public class NotificationRoundnessManager implements Dumpable {
if (mSwipedView != null) oldViews.add(mSwipedView);
if (mViewAfterSwipedView != null) oldViews.add(mViewAfterSwipedView);
+ final SourceType source;
+ if (mUseRoundnessSourceTypes) {
+ source = DISMISS_ANIMATION;
+ } else {
+ source = LegacySourceType.OnDismissAnimation;
+ }
+
mViewBeforeSwipedView = viewBefore;
if (viewBefore != null) {
oldViews.remove(viewBefore);
- viewBefore.requestTopRoundness(0f, animate, source);
- viewBefore.requestBottomRoundness(1f, animate, source);
+ viewBefore.requestRoundness(/* top = */ 0f, /* bottom = */ 1f, source);
}
mSwipedView = viewSwiped;
if (viewSwiped != null) {
oldViews.remove(viewSwiped);
- viewSwiped.requestTopRoundness(1f, animate, source);
- viewSwiped.requestBottomRoundness(1f, animate, source);
+ viewSwiped.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, source);
}
mViewAfterSwipedView = viewAfter;
if (viewAfter != null) {
oldViews.remove(viewAfter);
- viewAfter.requestTopRoundness(1f, animate, source);
- viewAfter.requestBottomRoundness(0f, animate, source);
+ viewAfter.requestRoundness(/* top = */ 1f, /* bottom = */ 0f, source);
}
// After setting the current Views, reset the views that are still present in the set.
for (Roundable oldView : oldViews) {
- oldView.requestTopRoundness(0f, animate, source);
- oldView.requestBottomRoundness(0f, animate, source);
+ oldView.requestRoundnessReset(source);
}
}
@@ -204,7 +215,23 @@ public class NotificationRoundnessManager implements Dumpable {
mIsClearAllInProgress = isClearingAll;
}
+ /**
+ * Check if "Clear all" notifications is in progress.
+ */
+ public boolean isClearAllInProgress() {
+ return mIsClearAllInProgress;
+ }
+
+ /**
+ * Check if we can request the `Pulsing` roundness for notification.
+ */
+ public boolean shouldRoundNotificationPulsing() {
+ return mRoundForPulsingViews;
+ }
+
private float getRoundnessDefaultValue(Roundable view, boolean top) {
+ if (mUseRoundnessSourceTypes) return 0f;
+
if (view == null) {
return 0f;
}
@@ -250,6 +277,7 @@ public class NotificationRoundnessManager implements Dumpable {
}
public void setExpanded(float expandedHeight, float appearFraction) {
+ if (mUseRoundnessSourceTypes) return;
mExpanded = expandedHeight != 0.0f;
mAppearFraction = appearFraction;
if (mTrackedHeadsUp != null) {
@@ -258,6 +286,7 @@ public class NotificationRoundnessManager implements Dumpable {
}
public void updateRoundedChildren(NotificationSection[] sections) {
+ if (mUseRoundnessSourceTypes) return;
boolean anyChanged = false;
for (int i = 0; i < sections.length; i++) {
mTmpFirstInSectionViews[i] = mFirstInSectionViews[i];
@@ -280,6 +309,7 @@ public class NotificationRoundnessManager implements Dumpable {
NotificationSection[] sections,
ExpandableView[] oldViews,
boolean first) {
+ if (mUseRoundnessSourceTypes) return false;
boolean anyChanged = false;
for (ExpandableView oldView : oldViews) {
if (oldView != null) {
@@ -313,6 +343,7 @@ public class NotificationRoundnessManager implements Dumpable {
NotificationSection[] sections,
ExpandableView[] oldViews,
boolean first) {
+ if (mUseRoundnessSourceTypes) return false;
boolean anyChanged = false;
for (NotificationSection section : sections) {
ExpandableView newView =
@@ -339,6 +370,15 @@ public class NotificationRoundnessManager implements Dumpable {
mAnimatedChildren = animatedChildren;
}
+ /**
+ * Check if the view should be animated
+ * @param view target view
+ * @return true, if is in the AnimatedChildren set
+ */
+ public boolean isAnimatedChild(ExpandableView view) {
+ return mAnimatedChildren.contains(view);
+ }
+
public void setOnRoundingChangedCallback(Runnable roundingChangedCallback) {
mRoundingChangedCallback = roundingChangedCallback;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index a1b77acb9a5e..070b4394291f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -19,8 +19,11 @@ import android.annotation.ColorInt
import android.util.Log
import android.view.View
import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.media.controls.ui.KeyguardMediaController
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.SourceType
import com.android.systemui.statusbar.notification.collection.render.MediaContainerController
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
import com.android.systemui.statusbar.notification.dagger.AlertingHeader
@@ -44,12 +47,16 @@ class NotificationSectionsManager @Inject internal constructor(
private val keyguardMediaController: KeyguardMediaController,
private val sectionsFeatureManager: NotificationSectionsFeatureManager,
private val mediaContainerController: MediaContainerController,
+ private val notificationRoundnessManager: NotificationRoundnessManager,
@IncomingHeader private val incomingHeaderController: SectionHeaderController,
@PeopleHeader private val peopleHeaderController: SectionHeaderController,
@AlertingHeader private val alertingHeaderController: SectionHeaderController,
- @SilentHeader private val silentHeaderController: SectionHeaderController
+ @SilentHeader private val silentHeaderController: SectionHeaderController,
+ featureFlags: FeatureFlags
) : SectionProvider {
+ private val useRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES)
+
private val configurationListener = object : ConfigurationController.ConfigurationListener {
override fun onLocaleListChanged() {
reinflateViews()
@@ -177,11 +184,49 @@ class NotificationSectionsManager @Inject internal constructor(
size = sections.size,
operation = SectionBounds::addNotif
)
+
+ // Build a set of the old first/last Views of the sections
+ val oldFirstChildren = sections.mapNotNull { it.firstVisibleChild }.toSet().toMutableSet()
+ val oldLastChildren = sections.mapNotNull { it.lastVisibleChild }.toSet().toMutableSet()
+
// Update each section with the associated boundary, tracking if there was a change
val changed = sections.fold(false) { changed, section ->
val bounds = sectionBounds[section.bucket] ?: SectionBounds.None
- bounds.updateSection(section) || changed
+ val isSectionChanged = bounds.updateSection(section)
+ isSectionChanged || changed
+ }
+
+ if (useRoundnessSourceTypes) {
+ val newFirstChildren = sections.mapNotNull { it.firstVisibleChild }
+ val newLastChildren = sections.mapNotNull { it.lastVisibleChild }
+
+ // Update the roundness of Views that weren't already in the first/last position
+ newFirstChildren.forEach { firstChild ->
+ val wasFirstChild = oldFirstChildren.remove(firstChild)
+ if (!wasFirstChild) {
+ val notAnimatedChild = !notificationRoundnessManager.isAnimatedChild(firstChild)
+ val animated = firstChild.isShown && notAnimatedChild
+ firstChild.requestTopRoundness(1f, SECTION, animated)
+ }
+ }
+ newLastChildren.forEach { lastChild ->
+ val wasLastChild = oldLastChildren.remove(lastChild)
+ if (!wasLastChild) {
+ val notAnimatedChild = !notificationRoundnessManager.isAnimatedChild(lastChild)
+ val animated = lastChild.isShown && notAnimatedChild
+ lastChild.requestBottomRoundness(1f, SECTION, animated)
+ }
+ }
+
+ // The Views left in the set are no longer in the first/last position
+ oldFirstChildren.forEach { noMoreFirstChild ->
+ noMoreFirstChild.requestTopRoundness(0f, SECTION)
+ }
+ oldLastChildren.forEach { noMoreLastChild ->
+ noMoreLastChild.requestBottomRoundness(0f, SECTION)
+ }
}
+
if (DEBUG) {
logSections(sections)
}
@@ -215,5 +260,6 @@ class NotificationSectionsManager @Inject internal constructor(
companion object {
private const val TAG = "NotifSectionsManager"
private const val DEBUG = false
+ private val SECTION = SourceType.from("Section")
}
}
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 7c3e52cf9630..21e2bd877bae 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
@@ -191,10 +191,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private final boolean mDebugLines;
private Paint mDebugPaint;
- /** Used to track the Y positions that were already used to draw debug text labels. */
+ /**
+ * Used to track the Y positions that were already used to draw debug text labels.
+ */
private Set<Integer> mDebugTextUsedYPositions;
private final boolean mDebugRemoveAnimation;
private final boolean mSimplifiedAppearFraction;
+ private final boolean mUseRoundnessSourceTypes;
private int mContentHeight;
private float mIntrinsicContentHeight;
@@ -355,15 +358,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private final Rect mQsHeaderBound = new Rect();
private boolean mContinuousShadowUpdate;
private boolean mContinuousBackgroundUpdate;
- private final ViewTreeObserver.OnPreDrawListener mShadowUpdater
- = () -> {
- updateViewShadows();
- return true;
- };
+ private final ViewTreeObserver.OnPreDrawListener mShadowUpdater = () -> {
+ updateViewShadows();
+ return true;
+ };
private final ViewTreeObserver.OnPreDrawListener mBackgroundUpdater = () -> {
- updateBackground();
- return true;
- };
+ updateBackground();
+ return true;
+ };
private final Comparator<ExpandableView> mViewPositionComparator = (view, otherView) -> {
float endY = view.getTranslationY() + view.getActualHeight();
float otherEndY = otherView.getTranslationY() + otherView.getActualHeight();
@@ -419,7 +421,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
*/
private int mMaxDisplayedNotifications = -1;
private float mKeyguardBottomPadding = -1;
- @VisibleForTesting int mStatusBarHeight;
+ @VisibleForTesting
+ int mStatusBarHeight;
private int mMinInteractionHeight;
private final Rect mClipRect = new Rect();
private boolean mIsClipped;
@@ -568,6 +571,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@Nullable
private OnClickListener mManageButtonClickListener;
+ @Nullable
+ private OnNotificationRemovedListener mOnNotificationRemovedListener;
public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
super(context, attrs, 0, 0);
@@ -576,6 +581,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
mSimplifiedAppearFraction = featureFlags.isEnabled(Flags.SIMPLIFIED_APPEAR_FRACTION);
+ mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
mSectionsManager = Dependency.get(NotificationSectionsManager.class);
mScreenOffAnimationController =
Dependency.get(ScreenOffAnimationController.class);
@@ -741,7 +747,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
private void logHunSkippedForUnexpectedState(ExpandableNotificationRow enr,
- boolean expected, boolean actual) {
+ boolean expected, boolean actual) {
if (mLogger == null) return;
mLogger.hunSkippedForUnexpectedState(enr.getEntry(), expected, actual);
}
@@ -868,13 +874,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
/**
* Draws round rects for each background section.
- *
+ * <p>
* We want to draw a round rect for each background section as defined by {@link #mSections}.
* However, if two sections are directly adjacent with no gap between them (e.g. on the
* lockscreen where the shelf can appear directly below the high priority section, or while
* scrolling the shade so that the top of the shelf is right at the bottom of the high priority
* section), we don't want to round the adjacent corners.
- *
+ * <p>
* Since {@link Canvas} doesn't provide a way to draw a half-rounded rect, this means that we
* need to coalesce the backgrounds for adjacent sections and draw them as a single round rect.
* This method tracks the top of each rect we need to draw, then iterates through the visible
@@ -883,7 +889,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* the current section. When we're done iterating we will always have one rect left to draw.
*/
private void drawBackgroundRects(Canvas canvas, int left, int right, int top,
- int animationYOffset) {
+ int animationYOffset) {
int backgroundRectTop = top;
int lastSectionBottom =
mSections[0].getCurrentBounds().bottom + animationYOffset;
@@ -974,7 +980,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
void initView(Context context, NotificationSwipeHelper swipeHelper,
- NotificationStackSizeCalculator notificationStackSizeCalculator) {
+ NotificationStackSizeCalculator notificationStackSizeCalculator) {
mScroller = new OverScroller(getContext());
mSwipeHelper = swipeHelper;
mNotificationStackSizeCalculator = notificationStackSizeCalculator;
@@ -1304,18 +1310,19 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
/**
* @return Whether we should skip stack height updates.
* True when
- * 1) Unlock hint is running
- * 2) Swiping up on lockscreen or flinging down after swipe up
+ * 1) Unlock hint is running
+ * 2) Swiping up on lockscreen or flinging down after swipe up
*/
private boolean shouldSkipHeightUpdate() {
return mAmbientState.isOnKeyguard()
&& (mAmbientState.isUnlockHintRunning()
- || mAmbientState.isSwipingUp()
- || mAmbientState.isFlingingAfterSwipeUpOnLockscreen());
+ || mAmbientState.isSwipingUp()
+ || mAmbientState.isFlingingAfterSwipeUpOnLockscreen());
}
/**
* Apply expansion fraction to the y position and height of the notifications panel.
+ *
* @param listenerNeedsAnimation does the listener need to animate?
*/
private void updateStackPosition(boolean listenerNeedsAnimation) {
@@ -1708,7 +1715,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
*/
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
ExpandableView getChildAtPosition(float touchX, float touchY,
- boolean requireMinHeight, boolean ignoreDecors) {
+ boolean requireMinHeight, boolean ignoreDecors) {
// find the view under the pointer, accounting for GONE views
final int count = getChildCount();
for (int childIdx = 0; childIdx < count; childIdx++) {
@@ -1868,9 +1875,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
public void dismissViewAnimated(
View child, Consumer<Boolean> endRunnable, int delay, long duration) {
if (child instanceof SectionHeaderView) {
- ((StackScrollerDecorView) child).setContentVisible(
- false /* visible */, true /* animate */, endRunnable);
- return;
+ ((StackScrollerDecorView) child).setContentVisible(
+ false /* visible */, true /* animate */, endRunnable);
+ return;
}
mSwipeHelper.dismissChild(
child,
@@ -2032,10 +2039,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
/**
* Scrolls by the given delta, overscrolling if needed. If called during a fling and the delta
* would cause us to exceed the provided maximum overscroll, springs back instead.
- *
+ * <p>
* This method performs the determination of whether we're exceeding the overscroll and clamps
* the scroll amount if so. The actual scrolling/overscrolling happens in
* {@link #onCustomOverScrolled(int, boolean)}
+ *
* @param deltaY The (signed) number of pixels to scroll.
* @param scrollY The current scroll position (absolute scrolling only).
* @param scrollRangeY The maximum allowable scroll position (absolute scrolling only).
@@ -2098,7 +2106,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
*/
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
- boolean cancelAnimators) {
+ boolean cancelAnimators) {
setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
}
@@ -2114,7 +2122,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
*/
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
- boolean cancelAnimators, boolean isRubberbanded) {
+ boolean cancelAnimators, boolean isRubberbanded) {
if (cancelAnimators) {
mStateAnimator.cancelOverScrollAnimators(onTop);
}
@@ -2123,7 +2131,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
- boolean isRubberbanded) {
+ boolean isRubberbanded) {
amount = Math.max(0, amount);
if (animate) {
mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
@@ -2179,7 +2187,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* Scrolls to the given position, overscrolling if needed. If called during a fling and the
* position exceeds the provided maximum overscroll, springs back instead.
*
- * @param scrollY The target scroll position.
+ * @param scrollY The target scroll position.
* @param clampedY Whether this value was clamped by the calling method, meaning we've reached
* the overscroll limit.
*/
@@ -2288,8 +2296,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
List<ExpandableNotificationRow> notificationChildren =
row.getAttachedChildren();
- for (int childIndex = 0; childIndex < notificationChildren.size();
- childIndex++) {
+ int childrenSize = notificationChildren.size();
+ for (int childIndex = 0; childIndex < childrenSize; childIndex++) {
ExpandableNotificationRow rowChild = notificationChildren.get(childIndex);
if (rowChild.getTranslationY() + rowTranslation >= translationY) {
return rowChild;
@@ -2365,10 +2373,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
/**
* Calculate the gap height between two different views
*
- * @param previous the previousView
- * @param current the currentView
+ * @param previous the previousView
+ * @param current the currentView
* @param visibleIndex the visible index in the list
- *
* @return the gap height needed before the current view
*/
public float calculateGapHeight(
@@ -2376,7 +2383,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
ExpandableView current,
int visibleIndex
) {
- return mStackScrollAlgorithm.getGapHeightForChild(mSectionsManager, visibleIndex, current,
+ return mStackScrollAlgorithm.getGapHeightForChild(mSectionsManager, visibleIndex, current,
previous, mAmbientState.getFractionToShade(), mAmbientState.isOnKeyguard());
}
@@ -2642,15 +2649,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return mScrolledToTopOnFirstDown
&& !mExpandedInThisMotion
&& (initialVelocity > mMinimumVelocity
- || (topOverScroll > mMinTopOverScrollToEscape && initialVelocity > 0));
+ || (topOverScroll > mMinTopOverScrollToEscape && initialVelocity > 0));
}
/**
* Updates the top padding of the notifications, taking {@link #getIntrinsicPadding()} into
* account.
*
- * @param qsHeight the top padding imposed by the quick settings panel
- * @param animate whether to animate the change
+ * @param qsHeight the top padding imposed by the quick settings panel
+ * @param animate whether to animate the change
*/
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
public void updateTopPadding(float qsHeight, boolean animate) {
@@ -2724,21 +2731,34 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
-
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setChildTransferInProgress(boolean childTransferInProgress) {
Assert.isMainThread();
mChildTransferInProgress = childTransferInProgress;
}
+ /**
+ * Set the remove notification listener
+ * @param listener callback for notification removed
+ */
+ public void setOnNotificationRemovedListener(OnNotificationRemovedListener listener) {
+ mOnNotificationRemovedListener = listener;
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@Override
public void onViewRemoved(View child) {
super.onViewRemoved(child);
// we only call our internal methods if this is actually a removal and not just a
// notification which becomes a child notification
+ ExpandableView expandableView = (ExpandableView) child;
if (!mChildTransferInProgress) {
- onViewRemovedInternal((ExpandableView) child, this);
+ onViewRemovedInternal(expandableView, this);
+ }
+ if (mOnNotificationRemovedListener != null) {
+ mOnNotificationRemovedListener.onNotificationRemoved(
+ expandableView,
+ mChildTransferInProgress);
}
}
@@ -2998,8 +3018,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mAnimateNextSectionBoundsChange = false;
}
mAmbientState.setLastVisibleBackgroundChild(lastChild);
- // TODO: Refactor SectionManager and put the RoundnessManager there.
- mController.getNotificationRoundnessManager().updateRoundedChildren(mSections);
+ if (!mUseRoundnessSourceTypes) {
+ // TODO: Refactor SectionManager and put the RoundnessManager there.
+ mController.getNotificationRoundnessManager().updateRoundedChildren(mSections);
+ }
mAnimateBottomOnLayout = false;
invalidate();
}
@@ -3206,7 +3228,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
// Only animate if we still have pinned heads up, otherwise we just have the
// regular collapse animation of the lock screen
|| (mKeyguardBypassEnabled && onKeyguard()
- && mInHeadsUpPinnedMode);
+ && mInHeadsUpPinnedMode);
if (performDisappearAnimation && !isHeadsUp) {
type = row.wasJustClicked()
? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
@@ -3351,7 +3373,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
AnimationEvent animEvent = duration == null
? new AnimationEvent(child, AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION)
: new AnimationEvent(
- child, AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION, duration);
+ child, AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION, duration);
mAnimationEvents.add(animEvent);
}
mChildrenChangingPositions.clear();
@@ -3442,7 +3464,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
- return new StackScrollAlgorithm(context, this);
+ StackScrollAlgorithm stackScrollAlgorithm = new StackScrollAlgorithm(context, this);
+ stackScrollAlgorithm.useRoundnessSourceTypes(mUseRoundnessSourceTypes);
+ return stackScrollAlgorithm;
}
/**
@@ -3772,7 +3796,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
private void logEmptySpaceClick(MotionEvent ev, boolean isTouchBelowLastNotification,
- int statusBarState, boolean touchIsClick) {
+ int statusBarState, boolean touchIsClick) {
if (mLogger == null) {
return;
}
@@ -3957,7 +3981,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mOnEmptySpaceClickListener = listener;
}
- /** @hide */
+ /**
+ * @hide
+ */
@Override
@ShadeViewRefactor(RefactorComponent.INPUT)
public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
@@ -4728,7 +4754,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return touchY > mTopPadding + mStackTranslation;
}
- /** @hide */
+ /**
+ * @hide
+ */
@Override
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
@@ -5353,7 +5381,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return canChildBeCleared(row) && matchesSelection(row, selection);
}
- /** Register a {@link View.OnClickListener} to be invoked when the Manage button is clicked. */
+ /**
+ * Register a {@link View.OnClickListener} to be invoked when the Manage button is clicked.
+ */
public void setManageButtonClickListener(@Nullable OnClickListener listener) {
mManageButtonClickListener = listener;
if (mFooterView != null) {
@@ -5418,6 +5448,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
/**
* Set how far the wake up is when waking up from pulsing. This is a height and will adjust the
* notification positions accordingly.
+ *
* @param height the new wake up height
* @return the overflow how much the height is further than he lowest notification
*/
@@ -5649,7 +5680,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* Set rounded rect clipping bounds on this view.
*/
public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
- int bottomRadius) {
+ int bottomRadius) {
if (mRoundedRectClippingLeft == left && mRoundedRectClippingRight == right
&& mRoundedRectClippingBottom == bottom && mRoundedRectClippingTop == top
&& mBgCornerRadii[0] == topRadius && mBgCornerRadii[5] == bottomRadius) {
@@ -5710,7 +5741,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mLaunchingNotification = launching;
mLaunchingNotificationNeedsToBeClipped = mLaunchAnimationParams != null
&& (mLaunchAnimationParams.getStartRoundedTopClipping() > 0
- || mLaunchAnimationParams.getParentStartRoundedTopClipping() > 0);
+ || mLaunchAnimationParams.getParentStartRoundedTopClipping() > 0);
if (!mLaunchingNotificationNeedsToBeClipped || !mLaunchingNotification) {
mLaunchedNotificationClipPath.reset();
}
@@ -5748,7 +5779,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mLaunchAnimationParams.getProgress(0,
NotificationLaunchAnimatorController.ANIMATION_DURATION_TOP_ROUNDING));
int top = (int) Math.min(MathUtils.lerp(mRoundedRectClippingTop,
- mLaunchAnimationParams.getTop(), expandProgress),
+ mLaunchAnimationParams.getTop(), expandProgress),
mRoundedRectClippingTop);
float topRadius = mLaunchAnimationParams.getTopCornerRadius();
float bottomRadius = mLaunchAnimationParams.getBottomCornerRadius();
@@ -5872,25 +5903,25 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public interface OnOverscrollTopChangedListener {
- /**
- * Notifies a listener that the overscroll has changed.
- *
- * @param amount the amount of overscroll, in pixels
- * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
- * unrubberbanded motion to directly expand overscroll view (e.g
- * expand
- * QS)
- */
- void onOverscrollTopChanged(float amount, boolean isRubberbanded);
+ /**
+ * Notifies a listener that the overscroll has changed.
+ *
+ * @param amount the amount of overscroll, in pixels
+ * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an
+ * unrubberbanded motion to directly expand overscroll view (e.g
+ * expand
+ * QS)
+ */
+ void onOverscrollTopChanged(float amount, boolean isRubberbanded);
- /**
- * Notify a listener that the scroller wants to escape from the scrolling motion and
- * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
- *
- * @param velocity The velocity that the Scroller had when over flinging
- * @param open Should the fling open or close the overscroll view.
- */
- void flingTopOverscroll(float velocity, boolean open);
+ /**
+ * Notify a listener that the scroller wants to escape from the scrolling motion and
+ * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
+ *
+ * @param velocity The velocity that the Scroller had when over flinging
+ * @param open Should the fling open or close the overscroll view.
+ */
+ void flingTopOverscroll(float velocity, boolean open);
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -6252,7 +6283,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
};
- public HeadsUpTouchHelper.Callback getHeadsUpCallback() { return mHeadsUpCallback; }
+ public HeadsUpTouchHelper.Callback getHeadsUpCallback() {
+ return mHeadsUpCallback;
+ }
void onGroupExpandChanged(ExpandableNotificationRow changedRow, boolean expanded) {
boolean animated = mAnimationsEnabled && (mIsExpanded || changedRow.isPinned());
@@ -6357,15 +6390,25 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return mLastSentExpandedHeight;
}
- /** Enum for selecting some or all notification rows (does not included non-notif views). */
+ /**
+ * Enum for selecting some or all notification rows (does not included non-notif views).
+ */
@Retention(SOURCE)
@IntDef({ROWS_ALL, ROWS_HIGH_PRIORITY, ROWS_GENTLE})
- @interface SelectedRows {}
- /** All rows representing notifs. */
+ @interface SelectedRows {
+ }
+
+ /**
+ * All rows representing notifs.
+ */
public static final int ROWS_ALL = 0;
- /** Only rows where entry.isHighPriority() is true. */
+ /**
+ * Only rows where entry.isHighPriority() is true.
+ */
public static final int ROWS_HIGH_PRIORITY = 1;
- /** Only rows where entry.isHighPriority() is false. */
+ /**
+ * Only rows where entry.isHighPriority() is false.
+ */
public static final int ROWS_GENTLE = 2;
interface ClearAllListener {
@@ -6380,4 +6423,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
void onAnimationEnd(
List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows);
}
+
+ /**
+ *
+ */
+ public interface OnNotificationRemovedListener {
+ /**
+ *
+ * @param child
+ * @param isTransferInProgress
+ */
+ void onNotificationRemoved(ExpandableView child, boolean isTransferInProgress);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 2d6d0a9cbeca..4bcc0b6923a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -182,10 +182,12 @@ public class NotificationStackScrollLayoutController {
private NotificationStackScrollLayout mView;
private boolean mFadeNotificationsOnDismiss;
private NotificationSwipeHelper mSwipeHelper;
- @Nullable private Boolean mHistoryEnabled;
+ @Nullable
+ private Boolean mHistoryEnabled;
private int mBarState;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private final FeatureFlags mFeatureFlags;
+ private final boolean mUseRoundnessSourceTypes;
private final NotificationTargetsHelper mNotificationTargetsHelper;
private View mLongPressedView;
@@ -391,7 +393,7 @@ public class NotificationStackScrollLayoutController {
if (item != null) {
Point origin = provider.getRevealAnimationOrigin();
mNotificationGutsManager.openGuts(row, origin.x, origin.y, item);
- } else {
+ } else {
Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no "
+ "menu item in menuItemtoExposeOnSnap. Skipping.");
}
@@ -420,7 +422,7 @@ public class NotificationStackScrollLayoutController {
@Override
public void onSnooze(StatusBarNotification sbn,
- NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
+ NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
mCentralSurfaces.setNotificationSnoozed(sbn, snoozeOption);
}
@@ -544,7 +546,7 @@ public class NotificationStackScrollLayoutController {
@Override
public boolean updateSwipeProgress(View animView, boolean dismissable,
- float swipeProgress) {
+ float swipeProgress) {
// Returning true prevents alpha fading.
return !mFadeNotificationsOnDismiss;
}
@@ -584,16 +586,22 @@ public class NotificationStackScrollLayoutController {
@Override
public void onHeadsUpPinned(NotificationEntry entry) {
- mNotificationRoundnessManager.updateView(entry.getRow(), false /* animate */);
+ if (!mUseRoundnessSourceTypes) {
+ mNotificationRoundnessManager.updateView(
+ entry.getRow(),
+ /* animate = */ false);
+ }
}
@Override
public void onHeadsUpUnPinned(NotificationEntry entry) {
- ExpandableNotificationRow row = entry.getRow();
- // update the roundedness posted, because we might be animating away the
- // headsup soon, so no need to set the roundedness to 0 and then back to 1.
- row.post(() -> mNotificationRoundnessManager.updateView(row,
- true /* animate */));
+ if (!mUseRoundnessSourceTypes) {
+ ExpandableNotificationRow row = entry.getRow();
+ // update the roundedness posted, because we might be animating away the
+ // headsup soon, so no need to set the roundedness to 0 and then back to 1.
+ row.post(() -> mNotificationRoundnessManager.updateView(row,
+ true /* animate */));
+ }
}
@Override
@@ -603,8 +611,10 @@ public class NotificationStackScrollLayoutController {
mView.setNumHeadsUp(numEntries);
mView.setTopHeadsUpEntry(topEntry);
generateHeadsUpAnimation(entry, isHeadsUp);
- ExpandableNotificationRow row = entry.getRow();
- mNotificationRoundnessManager.updateView(row, true /* animate */);
+ if (!mUseRoundnessSourceTypes) {
+ ExpandableNotificationRow row = entry.getRow();
+ mNotificationRoundnessManager.updateView(row, true /* animate */);
+ }
}
};
@@ -697,6 +707,7 @@ public class NotificationStackScrollLayoutController {
mSeenNotificationsProvider = seenNotificationsProvider;
mShadeController = shadeController;
mFeatureFlags = featureFlags;
+ mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
mNotificationTargetsHelper = notificationTargetsHelper;
updateResources();
}
@@ -763,8 +774,10 @@ public class NotificationStackScrollLayoutController {
mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
mFadeNotificationsOnDismiss = mFeatureFlags.isEnabled(Flags.NOTIFICATION_DISMISSAL_FADE);
- mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate);
- mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded);
+ if (!mUseRoundnessSourceTypes) {
+ mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate);
+ mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded);
+ }
mVisibilityLocationProviderDelegator.setDelegate(this::isInVisibleLocation);
@@ -997,9 +1010,11 @@ public class NotificationStackScrollLayoutController {
Log.wtf(TAG, "isHistoryEnabled failed to initialize its value");
return false;
}
- mHistoryEnabled = historyEnabled =
- Settings.Secure.getIntForUser(mView.getContext().getContentResolver(),
- Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1;
+ mHistoryEnabled = historyEnabled = Settings.Secure.getIntForUser(
+ mView.getContext().getContentResolver(),
+ Settings.Secure.NOTIFICATION_HISTORY_ENABLED,
+ 0,
+ UserHandle.USER_CURRENT) == 1;
}
return historyEnabled;
}
@@ -1029,7 +1044,7 @@ public class NotificationStackScrollLayoutController {
}
public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
- boolean cancelAnimators) {
+ boolean cancelAnimators) {
mView.setOverScrollAmount(amount, onTop, animate, cancelAnimators);
}
@@ -1207,7 +1222,7 @@ public class NotificationStackScrollLayoutController {
/**
* Update whether we should show the empty shade view ("no notifications" in the shade).
- *
+ * <p>
* When in split mode, notifications are always visible regardless of the state of the
* QuickSettings panel. That being the case, empty view is always shown if the other conditions
* are true.
@@ -1233,7 +1248,7 @@ public class NotificationStackScrollLayoutController {
/**
* @return true if {@link StatusBarStateController} is in transition to the KEYGUARD
- * and false otherwise.
+ * and false otherwise.
*/
private boolean isInTransitionToKeyguard() {
final int currentState = mStatusBarStateController.getState();
@@ -1265,7 +1280,9 @@ public class NotificationStackScrollLayoutController {
mView.setExpandedHeight(expandedHeight);
}
- /** Sets the QS header. Used to check if a touch is within its bounds. */
+ /**
+ * Sets the QS header. Used to check if a touch is within its bounds.
+ */
public void setQsHeader(ViewGroup view) {
mView.setQsHeader(view);
}
@@ -1328,7 +1345,7 @@ public class NotificationStackScrollLayoutController {
public RemoteInputController.Delegate createDelegate() {
return new RemoteInputController.Delegate() {
public void setRemoteInputActive(NotificationEntry entry,
- boolean remoteInputActive) {
+ boolean remoteInputActive) {
mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
entry.notifyHeightChanged(true /* needsAnimation */);
updateFooter();
@@ -1459,7 +1476,7 @@ public class NotificationStackScrollLayoutController {
}
private void onAnimationEnd(List<ExpandableNotificationRow> viewsToRemove,
- @SelectedRows int selectedRows) {
+ @SelectedRows int selectedRows) {
if (selectedRows == ROWS_ALL) {
mNotifCollection.dismissAllNotifications(
mLockscreenUserManager.getCurrentUserId());
@@ -1502,8 +1519,8 @@ public class NotificationStackScrollLayoutController {
/**
* @return the inset during the full shade transition, that needs to be added to the position
- * of the quick settings edge. This is relevant for media, that is transitioning
- * from the keyguard host to the quick settings one.
+ * of the quick settings edge. This is relevant for media, that is transitioning
+ * from the keyguard host to the quick settings one.
*/
public int getFullShadeTransitionInset() {
MediaContainerView view = mKeyguardMediaController.getSinglePaneContainer();
@@ -1517,10 +1534,10 @@ public class NotificationStackScrollLayoutController {
/**
* @param fraction The fraction of lockscreen to shade transition.
* 0f for all other states.
- *
- * Once the lockscreen to shade transition completes and the shade is 100% open,
- * LockscreenShadeTransitionController resets amount and fraction to 0, where they remain
- * until the next lockscreen-to-shade transition.
+ * <p>
+ * Once the lockscreen to shade transition completes and the shade is 100% open,
+ * LockscreenShadeTransitionController resets amount and fraction to 0, where
+ * they remain until the next lockscreen-to-shade transition.
*/
public void setTransitionToFullShadeAmount(float fraction) {
mView.setFractionToShade(fraction);
@@ -1533,7 +1550,9 @@ public class NotificationStackScrollLayoutController {
mView.setExtraTopInsetForFullShadeTransition(overScrollAmount);
}
- /** */
+ /**
+ *
+ */
public void setWillExpand(boolean willExpand) {
mView.setWillExpand(willExpand);
}
@@ -1549,7 +1568,7 @@ public class NotificationStackScrollLayoutController {
* Set rounded rect clipping bounds on this view.
*/
public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
- int bottomRadius) {
+ int bottomRadius) {
mView.setRoundedClippingBounds(left, top, right, bottom, topRadius, bottomRadius);
}
@@ -1569,6 +1588,15 @@ public class NotificationStackScrollLayoutController {
}
/**
+ * Set the remove notification listener
+ * @param listener callback for notification removed
+ */
+ public void setOnNotificationRemovedListener(
+ NotificationStackScrollLayout.OnNotificationRemovedListener listener) {
+ mView.setOnNotificationRemovedListener(listener);
+ }
+
+ /**
* Enum for UiEvent logged from this class
*/
enum NotificationPanelEvent implements UiEventLogger.UiEventEnum {
@@ -1578,10 +1606,13 @@ public class NotificationStackScrollLayoutController {
@UiEvent(doc = "User dismissed all silent notifications from notification panel.")
DISMISS_SILENT_NOTIFICATIONS_PANEL(314);
private final int mId;
+
NotificationPanelEvent(int id) {
mId = id;
}
- @Override public int getId() {
+
+ @Override
+ public int getId() {
return mId;
}
@@ -1722,8 +1753,12 @@ public class NotificationStackScrollLayoutController {
@Override
public void bindRow(ExpandableNotificationRow row) {
row.setHeadsUpAnimatingAwayListener(animatingAway -> {
- mNotificationRoundnessManager.updateView(row, false);
- mHeadsUpAppearanceController.updateHeader(row.getEntry());
+ if (!mUseRoundnessSourceTypes) {
+ mNotificationRoundnessManager.updateView(row, false);
+ }
+ NotificationEntry entry = row.getEntry();
+ mHeadsUpAppearanceController.updateHeader(entry);
+ mHeadsUpAppearanceController.updateHeadsUpAndPulsingRoundness(entry);
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index ee57411cb495..aaf9300e7cc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -20,6 +20,7 @@ package com.android.systemui.statusbar.notification.stack;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_SWIPE;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -34,9 +35,11 @@ import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.SwipeHelper;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -49,6 +52,7 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
@VisibleForTesting
protected static final long COVER_MENU_DELAY = 4000;
private static final String TAG = "NotificationSwipeHelper";
+ private static final SourceType SWIPE_DISMISS = SourceType.from("SwipeDismiss");
private final Runnable mFalsingCheck;
private View mTranslatingParentView;
private View mMenuExposedView;
@@ -64,13 +68,21 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
private WeakReference<NotificationMenuRowPlugin> mCurrMenuRowRef;
private boolean mIsExpanded;
private boolean mPulsing;
+ private final NotificationRoundnessManager mNotificationRoundnessManager;
+ private final boolean mUseRoundnessSourceTypes;
NotificationSwipeHelper(
- Resources resources, ViewConfiguration viewConfiguration,
- FalsingManager falsingManager, FeatureFlags featureFlags,
- int swipeDirection, NotificationCallback callback,
- NotificationMenuRowPlugin.OnMenuEventListener menuListener) {
+ Resources resources,
+ ViewConfiguration viewConfiguration,
+ FalsingManager falsingManager,
+ FeatureFlags featureFlags,
+ int swipeDirection,
+ NotificationCallback callback,
+ NotificationMenuRowPlugin.OnMenuEventListener menuListener,
+ NotificationRoundnessManager notificationRoundnessManager) {
super(swipeDirection, callback, resources, viewConfiguration, falsingManager, featureFlags);
+ mNotificationRoundnessManager = notificationRoundnessManager;
+ mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
mMenuListener = menuListener;
mCallback = callback;
mFalsingCheck = () -> resetExposedMenuView(true /* animate */, true /* force */);
@@ -304,6 +316,33 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
handleMenuCoveredOrDismissed();
}
+ @Override
+ protected void prepareDismissAnimation(View view, Animator anim) {
+ super.prepareDismissAnimation(view, anim);
+
+ if (mUseRoundnessSourceTypes
+ && view instanceof ExpandableNotificationRow
+ && mNotificationRoundnessManager.isClearAllInProgress()) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ row.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, SWIPE_DISMISS);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ row.requestRoundnessReset(SWIPE_DISMISS);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ row.requestRoundnessReset(SWIPE_DISMISS);
+ }
+ });
+ }
+ }
+
@VisibleForTesting
protected void superDismissChild(final View view, float velocity, boolean useAccelerateInterpolator) {
super.dismissChild(view, velocity, useAccelerateInterpolator);
@@ -521,14 +560,17 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
private int mSwipeDirection;
private NotificationCallback mNotificationCallback;
private NotificationMenuRowPlugin.OnMenuEventListener mOnMenuEventListener;
+ private NotificationRoundnessManager mNotificationRoundnessManager;
@Inject
Builder(@Main Resources resources, ViewConfiguration viewConfiguration,
- FalsingManager falsingManager, FeatureFlags featureFlags) {
+ FalsingManager falsingManager, FeatureFlags featureFlags,
+ NotificationRoundnessManager notificationRoundnessManager) {
mResources = resources;
mViewConfiguration = viewConfiguration;
mFalsingManager = falsingManager;
mFeatureFlags = featureFlags;
+ mNotificationRoundnessManager = notificationRoundnessManager;
}
Builder setSwipeDirection(int swipeDirection) {
@@ -549,7 +591,8 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
NotificationSwipeHelper build() {
return new NotificationSwipeHelper(mResources, mViewConfiguration, mFalsingManager,
- mFeatureFlags, mSwipeDirection, mNotificationCallback, mOnMenuEventListener);
+ mFeatureFlags, mSwipeDirection, mNotificationCallback, mOnMenuEventListener,
+ mNotificationRoundnessManager);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt
index 991a14bb9c2a..548d1a135948 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt
@@ -20,8 +20,7 @@ class NotificationTargetsHelper
constructor(
featureFlags: FeatureFlags,
) {
- private val isNotificationGroupCornerEnabled =
- featureFlags.isEnabled(Flags.NOTIFICATION_GROUP_CORNER)
+ private val useRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES)
/**
* This method looks for views that can be rounded (and implement [Roundable]) during a
@@ -48,7 +47,7 @@ constructor(
if (notificationParent != null && childrenContainer != null) {
// We are inside a notification group
- if (!isNotificationGroupCornerEnabled) {
+ if (!useRoundnessSourceTypes) {
return RoundableTargets(null, null, null)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index d8c68780951a..aff7b4c6c515 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -31,6 +31,7 @@ import com.android.systemui.R;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.notification.LegacySourceType;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -51,6 +52,7 @@ public class StackScrollAlgorithm {
private static final String TAG = "StackScrollAlgorithm";
private static final Boolean DEBUG = false;
+ private static final SourceType STACK_SCROLL_ALGO = SourceType.from("StackScrollAlgorithm");
private final ViewGroup mHostView;
private float mPaddingBetweenElements;
@@ -70,6 +72,7 @@ public class StackScrollAlgorithm {
private float mQuickQsOffsetHeight;
private float mSmallCornerRadius;
private float mLargeCornerRadius;
+ private boolean mUseRoundnessSourceTypes;
public StackScrollAlgorithm(
Context context,
@@ -129,7 +132,7 @@ public class StackScrollAlgorithm {
}
private void updateAlphaState(StackScrollAlgorithmState algorithmState,
- AmbientState ambientState) {
+ AmbientState ambientState) {
for (ExpandableView view : algorithmState.visibleChildren) {
final ViewState viewState = view.getViewState();
@@ -229,7 +232,7 @@ public class StackScrollAlgorithm {
}
private void getNotificationChildrenStates(StackScrollAlgorithmState algorithmState,
- AmbientState ambientState) {
+ AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
ExpandableView v = algorithmState.visibleChildren.get(i);
@@ -241,7 +244,7 @@ public class StackScrollAlgorithm {
}
private void updateSpeedBumpState(StackScrollAlgorithmState algorithmState,
- int speedBumpIndex) {
+ int speedBumpIndex) {
int childCount = algorithmState.visibleChildren.size();
int belowSpeedBump = speedBumpIndex;
for (int i = 0; i < childCount; i++) {
@@ -268,7 +271,7 @@ public class StackScrollAlgorithm {
}
private void updateClipping(StackScrollAlgorithmState algorithmState,
- AmbientState ambientState) {
+ AmbientState ambientState) {
float drawStart = ambientState.isOnKeyguard() ? 0
: ambientState.getStackY() - ambientState.getScrollY();
float clipStart = 0;
@@ -314,7 +317,7 @@ public class StackScrollAlgorithm {
* Updates the dimmed, activated and hiding sensitive states of the children.
*/
private void updateDimmedActivatedHideSensitive(AmbientState ambientState,
- StackScrollAlgorithmState algorithmState) {
+ StackScrollAlgorithmState algorithmState) {
boolean dimmed = ambientState.isDimmed();
boolean hideSensitive = ambientState.isHideSensitive();
View activatedChild = ambientState.getActivatedChild();
@@ -408,7 +411,7 @@ public class StackScrollAlgorithm {
}
private int updateNotGoneIndex(StackScrollAlgorithmState state, int notGoneIndex,
- ExpandableView v) {
+ ExpandableView v) {
ExpandableViewState viewState = v.getViewState();
viewState.notGoneIndex = notGoneIndex;
state.visibleChildren.add(v);
@@ -434,7 +437,7 @@ public class StackScrollAlgorithm {
* @param ambientState The current ambient state
*/
protected void updatePositionsForState(StackScrollAlgorithmState algorithmState,
- AmbientState ambientState) {
+ AmbientState ambientState) {
if (!ambientState.isOnKeyguard()
|| (ambientState.isBypassEnabled() && ambientState.isPulseExpanding())) {
algorithmState.mCurrentYPosition += mNotificationScrimPadding;
@@ -448,7 +451,7 @@ public class StackScrollAlgorithm {
}
private void setLocation(ExpandableViewState expandableViewState, float currentYPosition,
- int i) {
+ int i) {
expandableViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
if (currentYPosition <= 0) {
expandableViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
@@ -496,9 +499,13 @@ public class StackScrollAlgorithm {
}
@VisibleForTesting
- void maybeUpdateHeadsUpIsVisible(ExpandableViewState viewState, boolean isShadeExpanded,
- boolean mustStayOnScreen, boolean topVisible, float viewEnd, float hunMax) {
-
+ void maybeUpdateHeadsUpIsVisible(
+ ExpandableViewState viewState,
+ boolean isShadeExpanded,
+ boolean mustStayOnScreen,
+ boolean topVisible,
+ float viewEnd,
+ float hunMax) {
if (isShadeExpanded && mustStayOnScreen && topVisible) {
viewState.headsUpIsVisible = viewEnd < hunMax;
}
@@ -676,7 +683,7 @@ public class StackScrollAlgorithm {
}
private void updatePulsingStates(StackScrollAlgorithmState algorithmState,
- AmbientState ambientState) {
+ AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
View child = algorithmState.visibleChildren.get(i);
@@ -693,7 +700,7 @@ public class StackScrollAlgorithm {
}
private void updateHeadsUpStates(StackScrollAlgorithmState algorithmState,
- AmbientState ambientState) {
+ AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
// Move the tracked heads up into position during the appear animation, by interpolating
@@ -777,7 +784,7 @@ public class StackScrollAlgorithm {
*/
@VisibleForTesting
void clampHunToTop(float quickQsOffsetHeight, float stackTranslation, float collapsedHeight,
- ExpandableViewState viewState) {
+ ExpandableViewState viewState) {
final float newTranslation = Math.max(quickQsOffsetHeight + stackTranslation,
viewState.getYTranslation());
@@ -792,7 +799,7 @@ public class StackScrollAlgorithm {
// Pin HUN to bottom of expanded QS
// while the rest of notifications are scrolled offscreen.
private void clampHunToMaxTranslation(AmbientState ambientState, ExpandableNotificationRow row,
- ExpandableViewState childState) {
+ ExpandableViewState childState) {
float maxHeadsUpTranslation = ambientState.getMaxHeadsUpTranslation();
final float maxShelfPosition = ambientState.getInnerHeight() + ambientState.getTopPadding()
+ ambientState.getStackTranslation();
@@ -807,14 +814,19 @@ public class StackScrollAlgorithm {
// Animate pinned HUN bottom corners to and from original roundness.
final float originalCornerRadius =
row.isLastInSection() ? 1f : (mSmallCornerRadius / mLargeCornerRadius);
- final float roundness = computeCornerRoundnessForPinnedHun(mHostView.getHeight(),
+ final float bottomValue = computeCornerRoundnessForPinnedHun(mHostView.getHeight(),
ambientState.getStackY(), getMaxAllowedChildHeight(row), originalCornerRadius);
- row.requestBottomRoundness(roundness, /* animate = */ false, SourceType.OnScroll);
+ if (mUseRoundnessSourceTypes) {
+ row.requestBottomRoundness(bottomValue, STACK_SCROLL_ALGO);
+ row.addOnDetachResetRoundness(STACK_SCROLL_ALGO);
+ } else {
+ row.requestBottomRoundness(bottomValue, LegacySourceType.OnScroll);
+ }
}
@VisibleForTesting
float computeCornerRoundnessForPinnedHun(float hostViewHeight, float stackY,
- float viewMaxHeight, float originalCornerRadius) {
+ float viewMaxHeight, float originalCornerRadius) {
// Compute y where corner roundness should be in its original unpinned state.
// We use view max height because the pinned collapsed HUN expands to max height
@@ -844,7 +856,7 @@ public class StackScrollAlgorithm {
* @param ambientState The ambient state of the algorithm
*/
private void updateZValuesForState(StackScrollAlgorithmState algorithmState,
- AmbientState ambientState) {
+ AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
float childrenOnTop = 0.0f;
@@ -876,9 +888,9 @@ public class StackScrollAlgorithm {
* previous HUNs whose Z positions are greater than 0.
*/
protected float updateChildZValue(int i, float childrenOnTop,
- StackScrollAlgorithmState algorithmState,
- AmbientState ambientState,
- boolean isTopHun) {
+ StackScrollAlgorithmState algorithmState,
+ AmbientState ambientState,
+ boolean isTopHun) {
ExpandableView child = algorithmState.visibleChildren.get(i);
ExpandableViewState childViewState = child.getViewState();
float baseZ = ambientState.getBaseZHeight();
@@ -950,6 +962,14 @@ public class StackScrollAlgorithm {
this.mIsExpanded = isExpanded;
}
+ /**
+ * Enable the support for rounded corner based on the SourceType
+ * @param enabled true if is supported
+ */
+ public void useRoundnessSourceTypes(boolean enabled) {
+ mUseRoundnessSourceTypes = enabled;
+ }
+
public static class StackScrollAlgorithmState {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 484441a1e76b..c217ab3b42e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -19,11 +19,14 @@ package com.android.systemui.statusbar.phone;
import static com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.OPERATOR_NAME_FRAME_VIEW;
import android.graphics.Rect;
+import android.util.MathUtils;
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.R;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.NotificationPanelViewController;
@@ -32,8 +35,10 @@ import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope;
import com.android.systemui.statusbar.policy.Clock;
@@ -51,6 +56,7 @@ import javax.inject.Named;
/**
* Controls the appearance of heads up notifications in the icon area and the header itself.
+ * It also controls the roundness of the heads up notifications and the pulsing notifications.
*/
@StatusBarFragmentScope
public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBarView>
@@ -59,12 +65,17 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
NotificationWakeUpCoordinator.WakeUpListener {
public static final int CONTENT_FADE_DURATION = 110;
public static final int CONTENT_FADE_DELAY = 100;
+
+ private static final SourceType HEADS_UP = SourceType.from("HeadsUp");
+ private static final SourceType PULSING = SourceType.from("Pulsing");
private final NotificationIconAreaController mNotificationIconAreaController;
private final HeadsUpManagerPhone mHeadsUpManager;
private final NotificationStackScrollLayoutController mStackScrollerController;
private final DarkIconDispatcher mDarkIconDispatcher;
private final NotificationPanelViewController mNotificationPanelViewController;
+ private final NotificationRoundnessManager mNotificationRoundnessManager;
+ private final boolean mUseRoundnessSourceTypes;
private final Consumer<ExpandableNotificationRow>
mSetTrackingHeadsUp = this::setTrackingHeadsUp;
private final BiConsumer<Float, Float> mSetExpandedHeight = this::setAppearFraction;
@@ -105,11 +116,15 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
CommandQueue commandQueue,
NotificationStackScrollLayoutController stackScrollerController,
NotificationPanelViewController notificationPanelViewController,
+ NotificationRoundnessManager notificationRoundnessManager,
+ FeatureFlags featureFlags,
HeadsUpStatusBarView headsUpStatusBarView,
Clock clockView,
@Named(OPERATOR_NAME_FRAME_VIEW) Optional<View> operatorNameViewOptional) {
super(headsUpStatusBarView);
mNotificationIconAreaController = notificationIconAreaController;
+ mNotificationRoundnessManager = notificationRoundnessManager;
+ mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
mHeadsUpManager = headsUpManager;
// We may be mid-HUN-expansion when this controller is re-created (for example, if the user
@@ -179,6 +194,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
public void onHeadsUpPinned(NotificationEntry entry) {
updateTopEntry();
updateHeader(entry);
+ updateHeadsUpAndPulsingRoundness(entry);
}
private void updateTopEntry() {
@@ -316,6 +332,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
public void onHeadsUpUnPinned(NotificationEntry entry) {
updateTopEntry();
updateHeader(entry);
+ updateHeadsUpAndPulsingRoundness(entry);
}
public void setAppearFraction(float expandedHeight, float appearFraction) {
@@ -346,7 +363,9 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
ExpandableNotificationRow previousTracked = mTrackedChild;
mTrackedChild = trackedChild;
if (previousTracked != null) {
- updateHeader(previousTracked.getEntry());
+ NotificationEntry entry = previousTracked.getEntry();
+ updateHeader(entry);
+ updateHeadsUpAndPulsingRoundness(entry);
}
}
@@ -357,6 +376,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
private void updateHeadsUpHeaders() {
mHeadsUpManager.getAllEntries().forEach(entry -> {
updateHeader(entry);
+ updateHeadsUpAndPulsingRoundness(entry);
});
}
@@ -370,6 +390,31 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
row.setHeaderVisibleAmount(headerVisibleAmount);
}
+ /**
+ * Update the HeadsUp and the Pulsing roundness based on current state
+ * @param entry target notification
+ */
+ public void updateHeadsUpAndPulsingRoundness(NotificationEntry entry) {
+ if (mUseRoundnessSourceTypes) {
+ ExpandableNotificationRow row = entry.getRow();
+ boolean isTrackedChild = row == mTrackedChild;
+ if (row.isPinned() || row.isHeadsUpAnimatingAway() || isTrackedChild) {
+ float roundness = MathUtils.saturate(1f - mAppearFraction);
+ row.requestRoundness(roundness, roundness, HEADS_UP);
+ } else {
+ row.requestRoundnessReset(HEADS_UP);
+ }
+ if (mNotificationRoundnessManager.shouldRoundNotificationPulsing()) {
+ if (row.showingPulsing()) {
+ row.requestRoundness(/* top = */ 1f, /* bottom = */ 1f, PULSING);
+ } else {
+ row.requestRoundnessReset(PULSING);
+ }
+ }
+ }
+ }
+
+
@Override
public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) {
mView.onDarkChanged(areas, darkIntensity, tint);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index fb67f1a1bf50..e7afc50dbc2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.dagger
+import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.TableLogBufferFactory
@@ -29,6 +30,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupR
import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepositoryImpl
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxyImpl
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
@@ -40,6 +42,8 @@ import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiIntera
import dagger.Binds
import dagger.Module
import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
@Module
abstract class StatusBarPipelineModule {
@@ -52,8 +56,7 @@ abstract class StatusBarPipelineModule {
@Binds
abstract fun connectivityRepository(impl: ConnectivityRepositoryImpl): ConnectivityRepository
- @Binds
- abstract fun wifiRepository(impl: WifiRepositoryImpl): WifiRepository
+ @Binds abstract fun wifiRepository(impl: WifiRepositoryImpl): WifiRepository
@Binds
abstract fun wifiInteractor(impl: WifiInteractorImpl): WifiInteractor
@@ -63,15 +66,18 @@ abstract class StatusBarPipelineModule {
impl: MobileConnectionsRepositoryImpl
): MobileConnectionsRepository
- @Binds
- abstract fun userSetupRepository(impl: UserSetupRepositoryImpl): UserSetupRepository
+ @Binds abstract fun userSetupRepository(impl: UserSetupRepositoryImpl): UserSetupRepository
- @Binds
- abstract fun mobileMappingsProxy(impl: MobileMappingsProxyImpl): MobileMappingsProxy
+ @Binds abstract fun mobileMappingsProxy(impl: MobileMappingsProxyImpl): MobileMappingsProxy
@Binds
abstract fun mobileIconsInteractor(impl: MobileIconsInteractorImpl): MobileIconsInteractor
+ @Binds
+ @IntoMap
+ @ClassKey(MobileUiAdapter::class)
+ abstract fun bindFeature(impl: MobileUiAdapter): CoreStartable
+
@Module
companion object {
@JvmStatic
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/DataConnectionState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/DataConnectionState.kt
index da87f7306e60..5479b92edd22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/DataConnectionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/DataConnectionState.kt
@@ -20,6 +20,7 @@ import android.telephony.TelephonyManager.DATA_CONNECTED
import android.telephony.TelephonyManager.DATA_CONNECTING
import android.telephony.TelephonyManager.DATA_DISCONNECTED
import android.telephony.TelephonyManager.DATA_DISCONNECTING
+import android.telephony.TelephonyManager.DATA_UNKNOWN
import android.telephony.TelephonyManager.DataState
/** Internal enum representation of the telephony data connection states */
@@ -28,6 +29,7 @@ enum class DataConnectionState(@DataState val dataState: Int) {
Connecting(DATA_CONNECTING),
Disconnected(DATA_DISCONNECTED),
Disconnecting(DATA_DISCONNECTING),
+ Unknown(DATA_UNKNOWN),
}
fun @receiver:DataState Int.toDataConnectionType(): DataConnectionState =
@@ -36,5 +38,6 @@ fun @receiver:DataState Int.toDataConnectionType(): DataConnectionState =
DATA_CONNECTING -> DataConnectionState.Connecting
DATA_DISCONNECTED -> DataConnectionState.Disconnected
DATA_DISCONNECTING -> DataConnectionState.Disconnecting
- else -> throw IllegalArgumentException("unknown data state received")
+ DATA_UNKNOWN -> DataConnectionState.Unknown
+ else -> throw IllegalArgumentException("unknown data state received $this")
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
index c7e0ce173ece..d9487bf92260 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.mobile.ui
+import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.phone.StatusBarIconController
@@ -29,9 +30,10 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
/**
* This class is intended to provide a context to collect on the
@@ -50,9 +52,9 @@ constructor(
interactor: MobileIconsInteractor,
private val iconController: StatusBarIconController,
private val iconsViewModelFactory: MobileIconsViewModel.Factory,
- @Application scope: CoroutineScope,
+ @Application private val scope: CoroutineScope,
private val statusBarPipelineFlags: StatusBarPipelineFlags,
-) {
+) : CoreStartable {
private val mobileSubIds: Flow<List<Int>> =
interactor.filteredSubscriptions.mapLatest { infos ->
infos.map { subscriptionInfo -> subscriptionInfo.subscriptionId }
@@ -66,18 +68,19 @@ constructor(
* NOTE: this should go away as the view presenter learns more about this data pipeline
*/
private val mobileSubIdsState: StateFlow<List<Int>> =
- mobileSubIds
- .onEach {
- // Only notify the icon controller if we want to *render* the new icons.
- // Note that this flow may still run if
- // [statusBarPipelineFlags.runNewMobileIconsBackend] is true because we may want to
- // get the logging data without rendering.
- if (statusBarPipelineFlags.useNewMobileIcons()) {
- // Notify the icon controller here so that it knows to add icons
- iconController.setNewMobileIconSubIds(it)
- }
+ mobileSubIds.stateIn(scope, SharingStarted.WhileSubscribed(), listOf())
+
+ override fun start() {
+ // Only notify the icon controller if we want to *render* the new icons.
+ // Note that this flow may still run if
+ // [statusBarPipelineFlags.runNewMobileIconsBackend] is true because we may want to
+ // get the logging data without rendering.
+ if (statusBarPipelineFlags.useNewMobileIcons()) {
+ scope.launch {
+ mobileSubIds.collectLatest { iconController.setNewMobileIconSubIds(it) }
}
- .stateIn(scope, SharingStarted.WhileSubscribed(), listOf())
+ }
+ }
/**
* Create a MobileIconsViewModel for a given [IconManager], and bind it to to the manager's
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt
index b816364ed4cf..52237605caf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt
@@ -24,6 +24,7 @@ import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
+import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
import javax.inject.Inject
@@ -73,7 +74,9 @@ constructor(
// Note that this flow may still run if
// [statusBarPipelineFlags.runNewWifiIconBackend] is true because we may
// want to get the logging data without rendering.
- if (wifiIcon != null && statusBarPipelineFlags.useNewWifiIcon()) {
+ if (
+ wifiIcon is WifiIcon.Visible && statusBarPipelineFlags.useNewWifiIcon()
+ ) {
iconController.setNewWifiIcon()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
index 345f8cb75660..f5b5950d33a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
@@ -30,6 +30,7 @@ import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON
+import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
@@ -92,8 +93,10 @@ object WifiViewBinder {
launch {
viewModel.wifiIcon.collect { wifiIcon ->
- view.isVisible = wifiIcon != null
- wifiIcon?.let { IconViewBinder.bind(wifiIcon, iconView) }
+ view.isVisible = wifiIcon is WifiIcon.Visible
+ if (wifiIcon is WifiIcon.Visible) {
+ IconViewBinder.bind(wifiIcon.icon, iconView)
+ }
}
}
@@ -135,7 +138,7 @@ object WifiViewBinder {
return object : Binding {
override fun getShouldIconBeVisible(): Boolean {
- return viewModel.wifiIcon.value != null
+ return viewModel.wifiIcon.value is WifiIcon.Visible
}
override fun onVisibilityStateChanged(@StatusBarIconView.VisibleState state: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
new file mode 100644
index 000000000000..e491d2bbf0d6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.wifi.ui.model
+
+import android.annotation.DrawableRes
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
+
+/** Represents the various states of the wifi icon. */
+sealed interface WifiIcon : Diffable<WifiIcon> {
+ /** Represents a wifi icon that should be hidden (not visible). */
+ object Hidden : WifiIcon {
+ override fun toString() = "hidden"
+ }
+
+ /**
+ * Represents a visible wifi icon that uses [res] as its image and [contentDescription] as its
+ * description.
+ */
+ class Visible(
+ @DrawableRes res: Int,
+ val contentDescription: ContentDescription.Loaded,
+ ) : WifiIcon {
+ val icon = Icon.Resource(res, contentDescription)
+
+ override fun toString() = contentDescription.description.toString()
+ }
+
+ override fun logDiffs(prevVal: WifiIcon, row: TableRowLogger) {
+ if (prevVal.toString() != toString()) {
+ row.logChange(COL_ICON, toString())
+ }
+ }
+
+ override fun logFull(row: TableRowLogger) {
+ row.logChange(COL_ICON, toString())
+ }
+}
+
+private const val COL_ICON = "wifiIcon"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/HomeWifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/HomeWifiViewModel.kt
index 95ab251422b2..a29c9b94e6b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/HomeWifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/HomeWifiViewModel.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
import android.graphics.Color
-import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
+import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -28,7 +28,7 @@ import kotlinx.coroutines.flow.StateFlow
*/
class HomeWifiViewModel(
statusBarPipelineFlags: StatusBarPipelineFlags,
- wifiIcon: StateFlow<Icon.Resource?>,
+ wifiIcon: StateFlow<WifiIcon>,
isActivityInViewVisible: Flow<Boolean>,
isActivityOutViewVisible: Flow<Boolean>,
isActivityContainerVisible: Flow<Boolean>,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/KeyguardWifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/KeyguardWifiViewModel.kt
index 86535d63f84f..1e190fb898ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/KeyguardWifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/KeyguardWifiViewModel.kt
@@ -17,15 +17,15 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
import android.graphics.Color
-import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
+import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
/** A view model for the wifi icon shown on keyguard (lockscreen). */
class KeyguardWifiViewModel(
statusBarPipelineFlags: StatusBarPipelineFlags,
- wifiIcon: StateFlow<Icon.Resource?>,
+ wifiIcon: StateFlow<WifiIcon>,
isActivityInViewVisible: Flow<Boolean>,
isActivityOutViewVisible: Flow<Boolean>,
isActivityContainerVisible: Flow<Boolean>,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt
index 7cbdf5dbdf2d..e35a8fef4528 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
import android.graphics.Color
-import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
+import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flowOf
@@ -33,8 +33,8 @@ abstract class LocationBasedWifiViewModel(
statusBarPipelineFlags: StatusBarPipelineFlags,
debugTint: Int,
- /** The wifi icon that should be displayed. Null if we shouldn't display any icon. */
- val wifiIcon: StateFlow<Icon.Resource?>,
+ /** The wifi icon that should be displayed. */
+ val wifiIcon: StateFlow<WifiIcon>,
/** True if the activity in view should be visible. */
val isActivityInViewVisible: Flow<Boolean>,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/QsWifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/QsWifiViewModel.kt
index fd54c5f5062e..18e62b284cb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/QsWifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/QsWifiViewModel.kt
@@ -17,15 +17,15 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
import android.graphics.Color
-import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
+import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
/** A view model for the wifi icon shown in quick settings (when the shade is pulled down). */
class QsWifiViewModel(
statusBarPipelineFlags: StatusBarPipelineFlags,
- wifiIcon: StateFlow<Icon.Resource?>,
+ wifiIcon: StateFlow<WifiIcon>,
isActivityInViewVisible: Flow<Boolean>,
isActivityOutViewVisible: Flow<Boolean>,
isActivityContainerVisible: Flow<Boolean>,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
index 0782bbb774eb..ec7ba653cac7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
@@ -17,16 +17,18 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
import android.content.Context
-import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.annotation.VisibleForTesting
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
import com.android.systemui.R
import com.android.systemui.common.shared.model.ContentDescription
-import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
+import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
@@ -71,50 +73,39 @@ constructor(
connectivityConstants: ConnectivityConstants,
private val context: Context,
logger: ConnectivityPipelineLogger,
+ @WifiTableLog wifiTableLogBuffer: TableLogBuffer,
interactor: WifiInteractor,
@Application private val scope: CoroutineScope,
statusBarPipelineFlags: StatusBarPipelineFlags,
wifiConstants: WifiConstants,
) {
- /**
- * Returns the drawable resource ID to use for the wifi icon based on the given network.
- * Null if we can't compute the icon.
- */
- @DrawableRes
- private fun WifiNetworkModel.iconResId(): Int? {
+ /** Returns the icon to use based on the given network. */
+ private fun WifiNetworkModel.icon(): WifiIcon {
return when (this) {
- is WifiNetworkModel.CarrierMerged -> null
- is WifiNetworkModel.Inactive -> WIFI_NO_NETWORK
- is WifiNetworkModel.Active ->
- when {
- this.level == null -> null
- this.isValidated -> WIFI_FULL_ICONS[this.level]
- else -> WIFI_NO_INTERNET_ICONS[this.level]
- }
- }
- }
-
- /**
- * Returns the content description for the wifi icon based on the given network.
- * Null if we can't compute the content description.
- */
- private fun WifiNetworkModel.contentDescription(): ContentDescription? {
- return when (this) {
- is WifiNetworkModel.CarrierMerged -> null
- is WifiNetworkModel.Inactive ->
+ is WifiNetworkModel.CarrierMerged -> WifiIcon.Hidden
+ is WifiNetworkModel.Inactive -> WifiIcon.Visible(
+ res = WIFI_NO_NETWORK,
ContentDescription.Loaded(
"${context.getString(WIFI_NO_CONNECTION)},${context.getString(NO_INTERNET)}"
)
+ )
is WifiNetworkModel.Active ->
when (this.level) {
- null -> null
+ null -> WifiIcon.Hidden
else -> {
val levelDesc = context.getString(WIFI_CONNECTION_STRENGTH[this.level])
when {
- this.isValidated -> ContentDescription.Loaded(levelDesc)
+ this.isValidated ->
+ WifiIcon.Visible(
+ WIFI_FULL_ICONS[this.level],
+ ContentDescription.Loaded(levelDesc)
+ )
else ->
- ContentDescription.Loaded(
- "$levelDesc,${context.getString(NO_INTERNET)}"
+ WifiIcon.Visible(
+ WIFI_NO_INTERNET_ICONS[this.level],
+ ContentDescription.Loaded(
+ "$levelDesc,${context.getString(NO_INTERNET)}"
+ )
)
}
}
@@ -122,8 +113,8 @@ constructor(
}
}
- /** The wifi icon that should be displayed. Null if we shouldn't display any icon. */
- private val wifiIcon: StateFlow<Icon.Resource?> =
+ /** The wifi icon that should be displayed. */
+ private val wifiIcon: StateFlow<WifiIcon> =
combine(
interactor.isEnabled,
interactor.isDefault,
@@ -131,22 +122,29 @@ constructor(
interactor.wifiNetwork,
) { isEnabled, isDefault, isForceHidden, wifiNetwork ->
if (!isEnabled || isForceHidden || wifiNetwork is WifiNetworkModel.CarrierMerged) {
- return@combine null
+ return@combine WifiIcon.Hidden
}
- val iconResId = wifiNetwork.iconResId() ?: return@combine null
- val icon = Icon.Resource(iconResId, wifiNetwork.contentDescription())
+ val icon = wifiNetwork.icon()
return@combine when {
isDefault -> icon
wifiConstants.alwaysShowIconIfEnabled -> icon
!connectivityConstants.hasDataCapabilities -> icon
wifiNetwork is WifiNetworkModel.Active && wifiNetwork.isValidated -> icon
- else -> null
+ else -> WifiIcon.Hidden
}
}
- .logOutputChange(logger, "icon") { icon -> icon?.contentDescription.toString() }
- .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = null)
+ .logDiffsForTable(
+ wifiTableLogBuffer,
+ columnPrefix = "",
+ initialValue = WifiIcon.Hidden,
+ )
+ .stateIn(
+ scope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = WifiIcon.Hidden
+ )
/** The wifi activity status. Null if we shouldn't display the activity status. */
private val activity: Flow<WifiActivityModel?> =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
index 761773b1a345..fdef34449adf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
@@ -702,7 +702,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
- fun bind_seekBarDisabled_noActions_seekBarVisibilityIsSetToGone() {
+ fun bind_seekBarDisabled_noActions_seekBarVisibilityIsSetToInvisible() {
useRealConstraintSets()
val state = mediaData.copy(semanticActions = MediaButton())
@@ -711,7 +711,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
player.bindPlayer(state, PACKAGE)
- assertThat(expandedSet.getVisibility(seekBar.id)).isEqualTo(ConstraintSet.GONE)
+ assertThat(expandedSet.getVisibility(seekBar.id)).isEqualTo(ConstraintSet.INVISIBLE)
}
@Test
@@ -741,7 +741,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
- fun seekBarChangesToDisabledAfterBind_noActions_seekBarChangesToGone() {
+ fun seekBarChangesToDisabledAfterBind_noActions_seekBarChangesToInvisible() {
useRealConstraintSets()
val state = mediaData.copy(semanticActions = MediaButton())
@@ -752,7 +752,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
getEnabledChangeListener().onEnabledChanged(enabled = false)
- assertThat(expandedSet.getVisibility(seekBar.id)).isEqualTo(ConstraintSet.GONE)
+ assertThat(expandedSet.getVisibility(seekBar.id)).isEqualTo(ConstraintSet.INVISIBLE)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 9758842d1e35..4a9c7508b1b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -16,16 +16,21 @@
package com.android.systemui.notetask
import android.app.KeyguardManager
+import android.content.ComponentName
import android.content.Context
import android.content.Intent
+import android.content.pm.PackageManager
import android.os.UserManager
import android.test.suitebuilder.annotation.SmallTest
-import android.view.KeyEvent
import androidx.test.runner.AndroidJUnit4
import com.android.systemui.SysuiTestCase
import com.android.systemui.notetask.NoteTaskIntentResolver.Companion.NOTES_ACTION
+import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.android.wm.shell.bubbles.Bubbles
+import com.google.common.truth.Truth.assertThat
import java.util.Optional
import org.junit.Before
import org.junit.Test
@@ -48,6 +53,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
private val notesIntent = Intent(NOTES_ACTION)
@Mock lateinit var context: Context
+ @Mock lateinit var packageManager: PackageManager
@Mock lateinit var noteTaskIntentResolver: NoteTaskIntentResolver
@Mock lateinit var bubbles: Bubbles
@Mock lateinit var optionalBubbles: Optional<Bubbles>
@@ -60,6 +66,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
+ whenever(context.packageManager).thenReturn(packageManager)
whenever(noteTaskIntentResolver.resolveIntent()).thenReturn(notesIntent)
whenever(optionalBubbles.orElse(null)).thenReturn(bubbles)
whenever(optionalKeyguardManager.orElse(null)).thenReturn(keyguardManager)
@@ -78,89 +85,125 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
)
}
+ // region showNoteTask
@Test
- fun handleSystemKey_keyguardIsLocked_shouldStartActivity() {
+ fun showNoteTask_keyguardIsLocked_shouldStartActivity() {
whenever(keyguardManager.isKeyguardLocked).thenReturn(true)
- createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
+ createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
verify(context).startActivity(notesIntent)
verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
- fun handleSystemKey_keyguardIsUnlocked_shouldStartBubbles() {
+ fun showNoteTask_keyguardIsUnlocked_shouldStartBubbles() {
whenever(keyguardManager.isKeyguardLocked).thenReturn(false)
- createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
+ createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
verify(bubbles).showAppBubble(notesIntent)
verify(context, never()).startActivity(notesIntent)
}
@Test
- fun handleSystemKey_receiveInvalidSystemKey_shouldDoNothing() {
- createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_UNKNOWN)
+ fun showNoteTask_isInMultiWindowMode_shouldStartActivity() {
+ whenever(keyguardManager.isKeyguardLocked).thenReturn(false)
- verify(context, never()).startActivity(notesIntent)
+ createNoteTaskController().showNoteTask(isInMultiWindowMode = true)
+
+ verify(context).startActivity(notesIntent)
verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
- fun handleSystemKey_bubblesIsNull_shouldDoNothing() {
+ fun showNoteTask_bubblesIsNull_shouldDoNothing() {
whenever(optionalBubbles.orElse(null)).thenReturn(null)
- createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
+ createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
verify(context, never()).startActivity(notesIntent)
verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
- fun handleSystemKey_keyguardManagerIsNull_shouldDoNothing() {
+ fun showNoteTask_keyguardManagerIsNull_shouldDoNothing() {
whenever(optionalKeyguardManager.orElse(null)).thenReturn(null)
- createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
+ createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
verify(context, never()).startActivity(notesIntent)
verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
- fun handleSystemKey_userManagerIsNull_shouldDoNothing() {
+ fun showNoteTask_userManagerIsNull_shouldDoNothing() {
whenever(optionalUserManager.orElse(null)).thenReturn(null)
- createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
+ createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
verify(context, never()).startActivity(notesIntent)
verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
- fun handleSystemKey_intentResolverReturnsNull_shouldDoNothing() {
+ fun showNoteTask_intentResolverReturnsNull_shouldDoNothing() {
whenever(noteTaskIntentResolver.resolveIntent()).thenReturn(null)
- createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
+ createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
verify(context, never()).startActivity(notesIntent)
verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
- fun handleSystemKey_flagDisabled_shouldDoNothing() {
- createNoteTaskController(isEnabled = false).handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
+ fun showNoteTask_flagDisabled_shouldDoNothing() {
+ createNoteTaskController(isEnabled = false).showNoteTask()
verify(context, never()).startActivity(notesIntent)
verify(bubbles, never()).showAppBubble(notesIntent)
}
@Test
- fun handleSystemKey_userIsLocked_shouldDoNothing() {
+ fun showNoteTask_userIsLocked_shouldDoNothing() {
whenever(userManager.isUserUnlocked).thenReturn(false)
- createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
+ createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
verify(context, never()).startActivity(notesIntent)
verify(bubbles, never()).showAppBubble(notesIntent)
}
+ // endregion
+
+ // region setNoteTaskShortcutEnabled
+ @Test
+ fun setNoteTaskShortcutEnabled_setTrue() {
+ createNoteTaskController().setNoteTaskShortcutEnabled(value = true)
+
+ val argument = argumentCaptor<ComponentName>()
+ verify(context.packageManager)
+ .setComponentEnabledSetting(
+ argument.capture(),
+ eq(PackageManager.COMPONENT_ENABLED_STATE_ENABLED),
+ eq(PackageManager.DONT_KILL_APP),
+ )
+ val expected = ComponentName(context, CreateNoteTaskShortcutActivity::class.java)
+ assertThat(argument.value.flattenToString()).isEqualTo(expected.flattenToString())
+ }
+
+ @Test
+ fun setNoteTaskShortcutEnabled_setFalse() {
+ createNoteTaskController().setNoteTaskShortcutEnabled(value = false)
+
+ val argument = argumentCaptor<ComponentName>()
+ verify(context.packageManager)
+ .setComponentEnabledSetting(
+ argument.capture(),
+ eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED),
+ eq(PackageManager.DONT_KILL_APP),
+ )
+ val expected = ComponentName(context, CreateNoteTaskShortcutActivity::class.java)
+ assertThat(argument.value.flattenToString()).isEqualTo(expected.flattenToString())
+ }
+ // endregion
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index 334089c43e27..538131a4dd73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -16,10 +16,10 @@
package com.android.systemui.notetask
import android.test.suitebuilder.annotation.SmallTest
+import android.view.KeyEvent
import androidx.test.runner.AndroidJUnit4
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.wm.shell.bubbles.Bubbles
import java.util.Optional
@@ -45,6 +45,7 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
@Mock lateinit var commandQueue: CommandQueue
@Mock lateinit var bubbles: Bubbles
@Mock lateinit var optionalBubbles: Optional<Bubbles>
+ @Mock lateinit var noteTaskController: NoteTaskController
@Before
fun setUp() {
@@ -57,12 +58,13 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
private fun createNoteTaskInitializer(isEnabled: Boolean = true): NoteTaskInitializer {
return NoteTaskInitializer(
optionalBubbles = optionalBubbles,
- lazyNoteTaskController = mock(),
+ noteTaskController = noteTaskController,
commandQueue = commandQueue,
isEnabled = isEnabled,
)
}
+ // region initializer
@Test
fun initialize_shouldAddCallbacks() {
createNoteTaskInitializer().initialize()
@@ -85,4 +87,35 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
verify(commandQueue, never()).addCallback(any())
}
+
+ @Test
+ fun initialize_flagEnabled_shouldEnableShortcut() {
+ createNoteTaskInitializer().initialize()
+
+ verify(noteTaskController).setNoteTaskShortcutEnabled(true)
+ }
+
+ @Test
+ fun initialize_flagDisabled_shouldDisableShortcut() {
+ createNoteTaskInitializer(isEnabled = false).initialize()
+
+ verify(noteTaskController).setNoteTaskShortcutEnabled(false)
+ }
+ // endregion
+
+ // region handleSystemKey
+ @Test
+ fun handleSystemKey_receiveValidSystemKey_shouldShowNoteTask() {
+ createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
+
+ verify(noteTaskController).showNoteTask()
+ }
+
+ @Test
+ fun handleSystemKey_receiveInvalidSystemKey_shouldDoNothing() {
+ createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_UNKNOWN)
+
+ verify(noteTaskController, never()).showNoteTask()
+ }
+ // endregion
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index 5e9c1aaad309..906c20b1d032 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -703,28 +703,32 @@ public class QSSecurityFooterTest extends SysuiTestCase {
public void testParentalControls() {
// Make sure the security footer is visible, so that the images are updated.
when(mSecurityController.isProfileOwnerOfOrganizationOwnedDevice()).thenReturn(true);
-
when(mSecurityController.isParentalControlsEnabled()).thenReturn(true);
+ // We use the default icon when there is no admin icon.
+ when(mSecurityController.getIcon(any())).thenReturn(null);
+ mFooter.refreshState();
+ TestableLooper.get(this).processAllMessages();
+ assertEquals(mContext.getString(R.string.quick_settings_disclosure_parental_controls),
+ mFooterText.getText());
+ assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource());
+
Drawable testDrawable = new VectorDrawable();
when(mSecurityController.getIcon(any())).thenReturn(testDrawable);
assertNotNull(mSecurityController.getIcon(null));
mFooter.refreshState();
-
TestableLooper.get(this).processAllMessages();
assertEquals(mContext.getString(R.string.quick_settings_disclosure_parental_controls),
mFooterText.getText());
assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility());
-
assertEquals(testDrawable, mPrimaryFooterIcon.getDrawable());
// Ensure the primary icon is back to default after parental controls are gone
when(mSecurityController.isParentalControlsEnabled()).thenReturn(false);
mFooter.refreshState();
TestableLooper.get(this).processAllMessages();
-
assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index faf4592d26e3..5431eba8441c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -72,6 +72,7 @@ import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.log.LogBuffer;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -245,6 +246,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
mFakeExecutor,
mCallbackHandler,
mock(AccessPointControllerImpl.class),
+ mock(StatusBarPipelineFlags.class),
mock(DataUsageController.class),
mMockSubDefaults,
mMockProvisionController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
index ca75a40300cb..9441d49d454a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
@@ -49,6 +49,7 @@ import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.log.LogBuffer;
+import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.CarrierConfigTracker;
@@ -150,6 +151,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
mFakeExecutor,
mCallbackHandler,
mock(AccessPointControllerImpl.class),
+ mock(StatusBarPipelineFlags.class),
mock(DataUsageController.class),
mMockSubDefaults,
mock(DeviceProvisionedController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
index 84c242cda459..4c1f0a8a1066 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
@@ -44,6 +44,7 @@ import com.android.settingslib.net.DataUsageController;
import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.log.LogBuffer;
+import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.CarrierConfigTracker;
@@ -78,6 +79,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mFakeExecutor,
mCallbackHandler,
mock(AccessPointControllerImpl.class),
+ mock(StatusBarPipelineFlags.class),
mock(DataUsageController.class),
mMockSubDefaults,
mMockProvisionController,
@@ -115,6 +117,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mFakeExecutor,
mCallbackHandler,
mock(AccessPointControllerImpl.class),
+ mock(StatusBarPipelineFlags.class),
mock(DataUsageController.class),
mMockSubDefaults,
mMockProvisionController,
@@ -150,6 +153,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mFakeExecutor,
mCallbackHandler,
mock(AccessPointControllerImpl.class),
+ mock(StatusBarPipelineFlags.class),
mock(DataUsageController.class),
mMockSubDefaults,
mock(DeviceProvisionedController.class),
@@ -188,6 +192,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mFakeExecutor,
mCallbackHandler,
mock(AccessPointControllerImpl.class),
+ mock(StatusBarPipelineFlags.class),
mock(DataUsageController.class),
mMockSubDefaults,
mock(DeviceProvisionedController.class),
@@ -274,6 +279,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mFakeExecutor,
mCallbackHandler,
mock(AccessPointControllerImpl.class),
+ mock(StatusBarPipelineFlags.class),
mock(DataUsageController.class),
mMockSubDefaults,
mock(DeviceProvisionedController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt
new file mode 100644
index 000000000000..89faa239c5a2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt
@@ -0,0 +1,164 @@
+package com.android.systemui.statusbar.notification
+
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(JUnit4::class)
+class RoundableTest : SysuiTestCase() {
+ val targetView: View = mock()
+ val roundable = FakeRoundable(targetView)
+
+ @Test
+ fun defaultConfig_shouldNotHaveRoundedCorner() {
+ // the expected default value for the roundness is top = 0f, bottom = 0f
+ assertEquals(0f, roundable.roundableState.topRoundness)
+ assertEquals(0f, roundable.roundableState.bottomRoundness)
+ assertEquals(false, roundable.hasRoundedCorner())
+ }
+
+ @Test
+ fun applyRoundnessAndInvalidate_should_invalidate_targetView() {
+ roundable.applyRoundnessAndInvalidate()
+
+ verify(targetView, times(1)).invalidate()
+ }
+
+ @Test
+ fun requestTopRoundness_update_and_invalidate_targetView() {
+ roundable.requestTopRoundness(value = 1f, sourceType = SOURCE1)
+
+ assertEquals(1f, roundable.roundableState.topRoundness)
+ verify(targetView, times(1)).invalidate()
+ }
+
+ @Test
+ fun requestBottomRoundness_update_and_invalidate_targetView() {
+ roundable.requestBottomRoundness(value = 1f, sourceType = SOURCE1)
+
+ assertEquals(1f, roundable.roundableState.bottomRoundness)
+ verify(targetView, times(1)).invalidate()
+ }
+
+ @Test
+ fun requestRoundness_update_and_invalidate_targetView() {
+ roundable.requestRoundness(top = 1f, bottom = 1f, sourceType = SOURCE1)
+
+ assertEquals(1f, roundable.roundableState.topRoundness)
+ assertEquals(1f, roundable.roundableState.bottomRoundness)
+ verify(targetView, atLeastOnce()).invalidate()
+ }
+
+ @Test
+ fun requestRoundnessReset_update_and_invalidate_targetView() {
+ roundable.requestRoundness(1f, 1f, SOURCE1)
+ assertEquals(1f, roundable.roundableState.topRoundness)
+ assertEquals(1f, roundable.roundableState.bottomRoundness)
+
+ roundable.requestRoundnessReset(sourceType = SOURCE1)
+
+ assertEquals(0f, roundable.roundableState.topRoundness)
+ assertEquals(0f, roundable.roundableState.bottomRoundness)
+ verify(targetView, atLeastOnce()).invalidate()
+ }
+
+ @Test
+ fun hasRoundedCorner_return_true_ifRoundnessIsGreaterThenZero() {
+ roundable.requestRoundness(top = 1f, bottom = 1f, sourceType = SOURCE1)
+ assertEquals(true, roundable.hasRoundedCorner())
+
+ roundable.requestRoundness(top = 1f, bottom = 0f, sourceType = SOURCE1)
+ assertEquals(true, roundable.hasRoundedCorner())
+
+ roundable.requestRoundness(top = 0f, bottom = 1f, sourceType = SOURCE1)
+ assertEquals(true, roundable.hasRoundedCorner())
+
+ roundable.requestRoundness(top = 0f, bottom = 0f, sourceType = SOURCE1)
+ assertEquals(false, roundable.hasRoundedCorner())
+ }
+
+ @Test
+ fun roundness_take_maxValue_onMultipleSources_first_lower() {
+ roundable.requestRoundness(0.1f, 0.1f, SOURCE1)
+ assertEquals(0.1f, roundable.roundableState.topRoundness)
+ assertEquals(0.1f, roundable.roundableState.bottomRoundness)
+
+ roundable.requestRoundness(0.2f, 0.2f, SOURCE2)
+ // SOURCE1 has 0.1f - SOURCE2 has 0.2f
+ assertEquals(0.2f, roundable.roundableState.topRoundness)
+ assertEquals(0.2f, roundable.roundableState.bottomRoundness)
+ }
+
+ @Test
+ fun roundness_take_maxValue_onMultipleSources_first_higher() {
+ roundable.requestRoundness(0.5f, 0.5f, SOURCE1)
+ assertEquals(0.5f, roundable.roundableState.topRoundness)
+ assertEquals(0.5f, roundable.roundableState.bottomRoundness)
+
+ roundable.requestRoundness(0.1f, 0.1f, SOURCE2)
+ // SOURCE1 has 0.5f - SOURCE2 has 0.1f
+ assertEquals(0.5f, roundable.roundableState.topRoundness)
+ assertEquals(0.5f, roundable.roundableState.bottomRoundness)
+ }
+
+ @Test
+ fun roundness_take_maxValue_onMultipleSources_first_higher_second_step() {
+ roundable.requestRoundness(0.1f, 0.1f, SOURCE1)
+ assertEquals(0.1f, roundable.roundableState.topRoundness)
+ assertEquals(0.1f, roundable.roundableState.bottomRoundness)
+
+ roundable.requestRoundness(0.2f, 0.2f, SOURCE2)
+ // SOURCE1 has 0.1f - SOURCE2 has 0.2f
+ assertEquals(0.2f, roundable.roundableState.topRoundness)
+ assertEquals(0.2f, roundable.roundableState.bottomRoundness)
+
+ roundable.requestRoundness(0.3f, 0.3f, SOURCE1)
+ // SOURCE1 has 0.3f - SOURCE2 has 0.2f
+ assertEquals(0.3f, roundable.roundableState.topRoundness)
+ assertEquals(0.3f, roundable.roundableState.bottomRoundness)
+ }
+
+ @Test
+ fun roundness_take_maxValue_onMultipleSources_first_lower_second_step() {
+ roundable.requestRoundness(0.5f, 0.5f, SOURCE1)
+ assertEquals(0.5f, roundable.roundableState.topRoundness)
+ assertEquals(0.5f, roundable.roundableState.bottomRoundness)
+
+ roundable.requestRoundness(0.2f, 0.2f, SOURCE2)
+ // SOURCE1 has 0.5f - SOURCE2 has 0.2f
+ assertEquals(0.5f, roundable.roundableState.topRoundness)
+ assertEquals(0.5f, roundable.roundableState.bottomRoundness)
+
+ roundable.requestRoundness(0.1f, 0.1f, SOURCE1)
+ // SOURCE1 has 0.1f - SOURCE2 has 0.2f
+ assertEquals(0.2f, roundable.roundableState.topRoundness)
+ assertEquals(0.2f, roundable.roundableState.bottomRoundness)
+ }
+
+ class FakeRoundable(
+ targetView: View,
+ radius: Float = MAX_RADIUS,
+ ) : Roundable {
+ override val roundableState =
+ RoundableState(
+ targetView = targetView,
+ roundable = this,
+ maxRadius = radius,
+ )
+ }
+
+ companion object {
+ private const val MAX_RADIUS = 10f
+ private val SOURCE1 = SourceType.from("Source1")
+ private val SOURCE2 = SourceType.from("Source2")
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index fb31bef909e0..728e0265c729 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -215,8 +215,7 @@ public class NotificationTestHelper {
SourceType sourceType
) throws Exception {
ExpandableNotificationRow row = createRow();
- row.requestTopRoundness(topRoundness, false, sourceType);
- row.requestBottomRoundness(bottomRoundness, /*animate = */ false, sourceType);
+ row.requestRoundness(topRoundness, bottomRoundness, sourceType, /*animate = */ false);
assertEquals(topRoundness, row.getTopRoundness(), /* delta = */ 0f);
assertEquals(bottomRoundness, row.getBottomRoundness(), /* delta = */ 0f);
return row;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index 438b528944be..fd1944e7478d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -25,7 +25,7 @@ import android.view.View;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.SourceType;
+import com.android.systemui.statusbar.notification.LegacySourceType;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -158,7 +158,7 @@ public class NotificationChildrenContainerTest extends SysuiTestCase {
ExpandableNotificationRow row = mNotificationTestHelper.createRowWithRoundness(
/* topRoundness = */ 1f,
/* bottomRoundness = */ 1f,
- /* sourceType = */ SourceType.OnScroll);
+ /* sourceType = */ LegacySourceType.OnScroll);
mChildrenContainer.addNotification(row, 0);
@@ -171,11 +171,11 @@ public class NotificationChildrenContainerTest extends SysuiTestCase {
ExpandableNotificationRow row1 = mNotificationTestHelper.createRowWithRoundness(
/* topRoundness = */ 1f,
/* bottomRoundness = */ 1f,
- /* sourceType = */ SourceType.DefaultValue);
+ /* sourceType = */ LegacySourceType.DefaultValue);
ExpandableNotificationRow row2 = mNotificationTestHelper.createRowWithRoundness(
/* topRoundness = */ 1f,
/* bottomRoundness = */ 1f,
- /* sourceType = */ SourceType.OnDismissAnimation);
+ /* sourceType = */ LegacySourceType.OnDismissAnimation);
mChildrenContainer.addNotification(row1, 0);
mChildrenContainer.addNotification(row2, 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index 8c8b64424814..bd0a556ac670 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -35,6 +35,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationRoundnessLogger;
@@ -73,7 +74,8 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
mRoundnessManager = new NotificationRoundnessManager(
new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext),
mLogger,
- mock(DumpManager.class));
+ mock(DumpManager.class),
+ mock(FeatureFlags.class));
allowTestableLooperAsMainThread();
NotificationTestHelper testHelper = new NotificationTestHelper(
mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index ecc02246ea1d..30da08ebfe7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -30,6 +30,7 @@ import android.view.ViewGroup;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.controls.ui.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
@@ -59,10 +60,12 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
@Mock private KeyguardMediaController mKeyguardMediaController;
@Mock private NotificationSectionsFeatureManager mSectionsFeatureManager;
@Mock private MediaContainerController mMediaContainerController;
+ @Mock private NotificationRoundnessManager mNotificationRoundnessManager;
@Mock private SectionHeaderController mIncomingHeaderController;
@Mock private SectionHeaderController mPeopleHeaderController;
@Mock private SectionHeaderController mAlertingHeaderController;
@Mock private SectionHeaderController mSilentHeaderController;
+ @Mock private FeatureFlags mFeatureFlag;
private NotificationSectionsManager mSectionsManager;
@@ -89,10 +92,12 @@ public class NotificationSectionsManagerTest extends SysuiTestCase {
mKeyguardMediaController,
mSectionsFeatureManager,
mMediaContainerController,
+ mNotificationRoundnessManager,
mIncomingHeaderController,
mPeopleHeaderController,
mAlertingHeaderController,
- mSilentHeaderController
+ mSilentHeaderController,
+ mFeatureFlag
);
// Required in order for the header inflation to work properly
when(mNssl.generateLayoutParams(any(AttributeSet.class)))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index bda233611158..9d759c4b6016 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -9,7 +9,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.notification.SourceType
+import com.android.systemui.statusbar.notification.LegacySourceType
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
@@ -314,9 +314,9 @@ class NotificationShelfTest : SysuiTestCase() {
val row: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness(
/* topRoundness = */ 1f,
/* bottomRoundness = */ 1f,
- /* sourceType = */ SourceType.OnScroll)
+ /* sourceType = */ LegacySourceType.OnScroll)
- NotificationShelf.resetOnScrollRoundness(row)
+ NotificationShelf.resetLegacyOnScrollRoundness(row)
assertEquals(0f, row.topRoundness)
assertEquals(0f, row.bottomRoundness)
@@ -327,14 +327,14 @@ class NotificationShelfTest : SysuiTestCase() {
val row1: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness(
/* topRoundness = */ 1f,
/* bottomRoundness = */ 1f,
- /* sourceType = */ SourceType.DefaultValue)
+ /* sourceType = */ LegacySourceType.DefaultValue)
val row2: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness(
/* topRoundness = */ 1f,
/* bottomRoundness = */ 1f,
- /* sourceType = */ SourceType.OnDismissAnimation)
+ /* sourceType = */ LegacySourceType.OnDismissAnimation)
- NotificationShelf.resetOnScrollRoundness(row1)
- NotificationShelf.resetOnScrollRoundness(row2)
+ NotificationShelf.resetLegacyOnScrollRoundness(row1)
+ NotificationShelf.resetLegacyOnScrollRoundness(row2)
assertEquals(1f, row1.topRoundness)
assertEquals(1f, row1.bottomRoundness)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 4ea1c7100557..680a32375988 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -74,6 +74,7 @@ public class NotificationSwipeHelperTest extends SysuiTestCase {
private NotificationSwipeHelper mSwipeHelper;
private NotificationSwipeHelper.NotificationCallback mCallback;
private NotificationMenuRowPlugin.OnMenuEventListener mListener;
+ private NotificationRoundnessManager mNotificationRoundnessManager;
private View mView;
private MotionEvent mEvent;
private NotificationMenuRowPlugin mMenuRow;
@@ -92,10 +93,17 @@ public class NotificationSwipeHelperTest extends SysuiTestCase {
public void setUp() throws Exception {
mCallback = mock(NotificationSwipeHelper.NotificationCallback.class);
mListener = mock(NotificationMenuRowPlugin.OnMenuEventListener.class);
+ mNotificationRoundnessManager = mock(NotificationRoundnessManager.class);
mFeatureFlags = mock(FeatureFlags.class);
mSwipeHelper = spy(new NotificationSwipeHelper(
- mContext.getResources(), ViewConfiguration.get(mContext),
- new FalsingManagerFake(), mFeatureFlags, SwipeHelper.X, mCallback, mListener));
+ mContext.getResources(),
+ ViewConfiguration.get(mContext),
+ new FalsingManagerFake(),
+ mFeatureFlags,
+ SwipeHelper.X,
+ mCallback,
+ mListener,
+ mNotificationRoundnessManager));
mView = mock(View.class);
mEvent = mock(MotionEvent.class);
mMenuRow = mock(NotificationMenuRowPlugin.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
index a2e92305bf27..81a3f1245efe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
@@ -35,7 +35,7 @@ class NotificationTargetsHelperTest : SysuiTestCase() {
) =
NotificationTargetsHelper(
FakeFeatureFlags().apply {
- set(Flags.NOTIFICATION_GROUP_CORNER, notificationGroupCorner)
+ set(Flags.USE_ROUNDNESS_SOURCETYPES, notificationGroupCorner)
}
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 103b7b4268de..9727b6c5eb83 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -32,6 +32,7 @@ import android.widget.TextView;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.NotificationPanelViewController;
@@ -40,6 +41,7 @@ import com.android.systemui.statusbar.HeadsUpStatusBarView;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -71,6 +73,8 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
private NotificationWakeUpCoordinator mWakeUpCoordinator;
private KeyguardStateController mKeyguardStateController;
private CommandQueue mCommandQueue;
+ private NotificationRoundnessManager mNotificationRoundnessManager;
+ private FeatureFlags mFeatureFlag;
@Before
public void setUp() throws Exception {
@@ -89,6 +93,8 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
mWakeUpCoordinator = mock(NotificationWakeUpCoordinator.class);
mKeyguardStateController = mock(KeyguardStateController.class);
mCommandQueue = mock(CommandQueue.class);
+ mNotificationRoundnessManager = mock(NotificationRoundnessManager.class);
+ mFeatureFlag = mock(FeatureFlags.class);
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mock(NotificationIconAreaController.class),
mHeadsUpManager,
@@ -100,6 +106,8 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
mCommandQueue,
mStackScrollerController,
mPanelView,
+ mNotificationRoundnessManager,
+ mFeatureFlag,
mHeadsUpStatusBarView,
new Clock(mContext, null),
Optional.of(mOperatorNameView));
@@ -182,6 +190,8 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
mCommandQueue,
mStackScrollerController,
mPanelView,
+ mNotificationRoundnessManager,
+ mFeatureFlag,
mHeadsUpStatusBarView,
new Clock(mContext, null),
Optional.empty());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepositoryTest.kt
index 5ce51bb62c78..aa7ab0df2ea3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepositoryTest.kt
@@ -31,6 +31,7 @@ import android.telephony.TelephonyManager.DATA_CONNECTED
import android.telephony.TelephonyManager.DATA_CONNECTING
import android.telephony.TelephonyManager.DATA_DISCONNECTED
import android.telephony.TelephonyManager.DATA_DISCONNECTING
+import android.telephony.TelephonyManager.DATA_UNKNOWN
import android.telephony.TelephonyManager.NETWORK_TYPE_LTE
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
import androidx.test.filters.SmallTest
@@ -221,6 +222,21 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
}
@Test
+ fun testFlowForSubId_dataConnectionState_unknown() =
+ runBlocking(IMMEDIATE) {
+ var latest: MobileSubscriptionModel? = null
+ val job = underTest.subscriptionModelFlow.onEach { latest = it }.launchIn(this)
+
+ val callback =
+ getTelephonyCallbackForType<TelephonyCallback.DataConnectionStateListener>()
+ callback.onDataConnectionStateChanged(DATA_UNKNOWN, 200 /* unused */)
+
+ assertThat(latest?.dataConnectionState).isEqualTo(DataConnectionState.Unknown)
+
+ job.cancel()
+ }
+
+ @Test
fun testFlowForSubId_dataActivity() =
runBlocking(IMMEDIATE) {
var latest: MobileSubscriptionModel? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index 5c16e1295b65..3d9fd961222f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -25,6 +25,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.lifecycle.InstantTaskExecutorRule
+import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON
@@ -64,6 +65,7 @@ class ModernStatusBarWifiViewTest : SysuiTestCase() {
private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@Mock
private lateinit var logger: ConnectivityPipelineLogger
+ @Mock private lateinit var tableLogBuffer: TableLogBuffer
@Mock
private lateinit var connectivityConstants: ConnectivityConstants
@Mock
@@ -103,6 +105,7 @@ class ModernStatusBarWifiViewTest : SysuiTestCase() {
connectivityConstants,
context,
logger,
+ tableLogBuffer,
interactor,
scope,
statusBarPipelineFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
index 3001b8162185..12b93819fc5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
@@ -23,6 +23,7 @@ import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_
import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
+import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
@@ -40,6 +41,7 @@ import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepo
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
+import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel.Companion.NO_INTERNET
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
@@ -67,6 +69,7 @@ internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase
@Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@Mock private lateinit var logger: ConnectivityPipelineLogger
+ @Mock private lateinit var tableLogBuffer: TableLogBuffer
@Mock private lateinit var connectivityConstants: ConnectivityConstants
@Mock private lateinit var wifiConstants: WifiConstants
private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
@@ -123,6 +126,7 @@ internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase
connectivityConstants,
context,
logger,
+ tableLogBuffer,
interactor,
scope,
statusBarPipelineFlags,
@@ -137,15 +141,21 @@ internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase
yield()
// THEN we get the expected icon
- assertThat(iconFlow.value?.res).isEqualTo(testCase.expected?.iconResource)
- val expectedContentDescription =
- if (testCase.expected == null) {
- null
- } else {
- testCase.expected.contentDescription.invoke(context)
+ val actualIcon = iconFlow.value
+ when (testCase.expected) {
+ null -> {
+ assertThat(actualIcon).isInstanceOf(WifiIcon.Hidden::class.java)
+ }
+ else -> {
+ assertThat(actualIcon).isInstanceOf(WifiIcon.Visible::class.java)
+ val actualIconVisible = actualIcon as WifiIcon.Visible
+ assertThat(actualIconVisible.icon.res).isEqualTo(testCase.expected.iconResource)
+ val expectedContentDescription =
+ testCase.expected.contentDescription.invoke(context)
+ assertThat(actualIconVisible.contentDescription.loadContentDescription(context))
+ .isEqualTo(expectedContentDescription)
}
- assertThat(iconFlow.value?.contentDescription?.loadContentDescription(context))
- .isEqualTo(expectedContentDescription)
+ }
job.cancel()
}
@@ -174,7 +184,7 @@ internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase
val isDefault: Boolean = false,
val network: WifiNetworkModel,
- /** The expected output. Null if we expect the output to be null. */
+ /** The expected output. Null if we expect the output to be hidden. */
val expected: Expected?
) {
override fun toString(): String {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 6a6b2a801ab0..7502020286f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
@@ -34,6 +34,7 @@ import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiIntera
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiActivityModel
+import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -59,6 +60,7 @@ class WifiViewModelTest : SysuiTestCase() {
@Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@Mock private lateinit var logger: ConnectivityPipelineLogger
+ @Mock private lateinit var tableLogBuffer: TableLogBuffer
@Mock private lateinit var connectivityConstants: ConnectivityConstants
@Mock private lateinit var wifiConstants: WifiConstants
private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
@@ -103,21 +105,21 @@ class WifiViewModelTest : SysuiTestCase() {
@Test
fun wifiIcon_allLocationViewModelsReceiveSameData() = runBlocking(IMMEDIATE) {
- var latestHome: Icon? = null
+ var latestHome: WifiIcon? = null
val jobHome = underTest
.home
.wifiIcon
.onEach { latestHome = it }
.launchIn(this)
- var latestKeyguard: Icon? = null
+ var latestKeyguard: WifiIcon? = null
val jobKeyguard = underTest
.keyguard
.wifiIcon
.onEach { latestKeyguard = it }
.launchIn(this)
- var latestQs: Icon? = null
+ var latestQs: WifiIcon? = null
val jobQs = underTest
.qs
.wifiIcon
@@ -133,7 +135,7 @@ class WifiViewModelTest : SysuiTestCase() {
)
yield()
- assertThat(latestHome).isInstanceOf(Icon.Resource::class.java)
+ assertThat(latestHome).isInstanceOf(WifiIcon.Visible::class.java)
assertThat(latestHome).isEqualTo(latestKeyguard)
assertThat(latestKeyguard).isEqualTo(latestQs)
@@ -541,6 +543,7 @@ class WifiViewModelTest : SysuiTestCase() {
connectivityConstants,
context,
logger,
+ tableLogBuffer,
interactor,
scope,
statusBarPipelineFlags,
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 40e28dadb4da..c0e7ab8a28a5 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -602,6 +602,14 @@ class AutomaticBrightnessController {
mAmbientBrightnessThresholdsIdle.dump(pw);
}
+ public float[] getLastSensorValues() {
+ return mAmbientLightRingBuffer.getAllLuxValues();
+ }
+
+ public long[] getLastSensorTimestamps() {
+ return mAmbientLightRingBuffer.getAllTimestamps();
+ }
+
private String configStateToString(int state) {
switch (state) {
case AUTO_BRIGHTNESS_ENABLED:
@@ -1231,10 +1239,42 @@ class AutomaticBrightnessController {
return mRingLux[offsetOf(index)];
}
+ public float[] getAllLuxValues() {
+ float[] values = new float[mCount];
+ if (mCount == 0) {
+ return values;
+ }
+
+ if (mStart < mEnd) {
+ System.arraycopy(mRingLux, mStart, values, 0, mCount);
+ } else {
+ System.arraycopy(mRingLux, mStart, values, 0, mCapacity - mStart);
+ System.arraycopy(mRingLux, 0, values, mCapacity - mStart, mEnd);
+ }
+
+ return values;
+ }
+
public long getTime(int index) {
return mRingTime[offsetOf(index)];
}
+ public long[] getAllTimestamps() {
+ long[] values = new long[mCount];
+ if (mCount == 0) {
+ return values;
+ }
+
+ if (mStart < mEnd) {
+ System.arraycopy(mRingTime, mStart, values, 0, mCount);
+ } else {
+ System.arraycopy(mRingTime, mStart, values, 0, mCapacity - mStart);
+ System.arraycopy(mRingTime, 0, values, mCapacity - mStart, mEnd);
+ }
+
+ return values;
+ }
+
public void push(long time, float lux) {
int next = mEnd;
if (mCount == mCapacity) {
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 8f59ffd30bba..fc8c6da6ad8d 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -79,10 +79,8 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
-import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Date;
-import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -101,8 +99,6 @@ public class BrightnessTracker {
private static final int MAX_EVENTS = 100;
// Discard events when reading or writing that are older than this.
private static final long MAX_EVENT_AGE = TimeUnit.DAYS.toMillis(30);
- // Time over which we keep lux sensor readings.
- private static final long LUX_EVENT_HORIZON = TimeUnit.SECONDS.toNanos(10);
private static final String TAG_EVENTS = "events";
private static final String TAG_EVENT = "event";
@@ -174,8 +170,6 @@ public class BrightnessTracker {
// Lock held while collecting data related to brightness changes.
private final Object mDataCollectionLock = new Object();
@GuardedBy("mDataCollectionLock")
- private Deque<LightData> mLastSensorReadings = new ArrayDeque<>();
- @GuardedBy("mDataCollectionLock")
private float mLastBatteryLevel = Float.NaN;
@GuardedBy("mDataCollectionLock")
private float mLastBrightness = -1;
@@ -327,7 +321,8 @@ public class BrightnessTracker {
*/
public void notifyBrightnessChanged(float brightness, boolean userInitiated,
float powerBrightnessFactor, boolean isUserSetBrightness,
- boolean isDefaultBrightnessConfig, String uniqueDisplayId) {
+ boolean isDefaultBrightnessConfig, String uniqueDisplayId, float[] luxValues,
+ long[] luxTimestamps) {
if (DEBUG) {
Slog.d(TAG, String.format("notifyBrightnessChanged(brightness=%f, userInitiated=%b)",
brightness, userInitiated));
@@ -335,7 +330,7 @@ public class BrightnessTracker {
Message m = mBgHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED,
userInitiated ? 1 : 0, 0 /*unused*/, new BrightnessChangeValues(brightness,
powerBrightnessFactor, isUserSetBrightness, isDefaultBrightnessConfig,
- mInjector.currentTimeMillis(), uniqueDisplayId));
+ mInjector.currentTimeMillis(), uniqueDisplayId, luxValues, luxTimestamps));
m.sendToTarget();
}
@@ -349,7 +344,8 @@ public class BrightnessTracker {
private void handleBrightnessChanged(float brightness, boolean userInitiated,
float powerBrightnessFactor, boolean isUserSetBrightness,
- boolean isDefaultBrightnessConfig, long timestamp, String uniqueDisplayId) {
+ boolean isDefaultBrightnessConfig, long timestamp, String uniqueDisplayId,
+ float[] luxValues, long[] luxTimestamps) {
BrightnessChangeEvent.Builder builder;
synchronized (mDataCollectionLock) {
@@ -376,28 +372,22 @@ public class BrightnessTracker {
builder.setIsDefaultBrightnessConfig(isDefaultBrightnessConfig);
builder.setUniqueDisplayId(uniqueDisplayId);
- final int readingCount = mLastSensorReadings.size();
- if (readingCount == 0) {
+ if (luxValues.length == 0) {
// No sensor data so ignore this.
return;
}
- float[] luxValues = new float[readingCount];
- long[] luxTimestamps = new long[readingCount];
-
- int pos = 0;
+ long[] luxTimestampsMillis = new long[luxTimestamps.length];
- // Convert sensor timestamp in elapsed time nanos to current time millis.
+ // Convert lux timestamp in elapsed time to current time.
long currentTimeMillis = mInjector.currentTimeMillis();
long elapsedTimeNanos = mInjector.elapsedRealtimeNanos();
- for (LightData reading : mLastSensorReadings) {
- luxValues[pos] = reading.lux;
- luxTimestamps[pos] = currentTimeMillis -
- TimeUnit.NANOSECONDS.toMillis(elapsedTimeNanos - reading.timestamp);
- ++pos;
+ for (int i = 0; i < luxTimestamps.length; i++) {
+ luxTimestampsMillis[i] = currentTimeMillis - (TimeUnit.NANOSECONDS.toMillis(
+ elapsedTimeNanos) - luxTimestamps[i]);
}
builder.setLuxValues(luxValues);
- builder.setLuxTimestamps(luxTimestamps);
+ builder.setLuxTimestamps(luxTimestampsMillis);
builder.setBatteryLevel(mLastBatteryLevel);
builder.setLastBrightness(previousBrightness);
@@ -452,9 +442,6 @@ public class BrightnessTracker {
if (mLightSensor != lightSensor) {
mLightSensor = lightSensor;
stopSensorListener();
- synchronized (mDataCollectionLock) {
- mLastSensorReadings.clear();
- }
// Attempt to restart the sensor listener. It will check to see if it should be running
// so there is no need to also check here.
startSensorListener();
@@ -774,12 +761,6 @@ public class BrightnessTracker {
pw.println(" mLightSensor=" + mLightSensor);
pw.println(" mLastBatteryLevel=" + mLastBatteryLevel);
pw.println(" mLastBrightness=" + mLastBrightness);
- pw.println(" mLastSensorReadings.size=" + mLastSensorReadings.size());
- if (!mLastSensorReadings.isEmpty()) {
- pw.println(" mLastSensorReadings time span "
- + mLastSensorReadings.peekFirst().timestamp + "->"
- + mLastSensorReadings.peekLast().timestamp);
- }
}
synchronized (mEventsLock) {
pw.println(" mEventsDirty=" + mEventsDirty);
@@ -895,43 +876,6 @@ public class BrightnessTracker {
return ParceledListSlice.emptyList();
}
- // Not allowed to keep the SensorEvent so used to copy the data we care about.
- private static class LightData {
- public float lux;
- // Time in elapsedRealtimeNanos
- public long timestamp;
- }
-
- private void recordSensorEvent(SensorEvent event) {
- long horizon = mInjector.elapsedRealtimeNanos() - LUX_EVENT_HORIZON;
- synchronized (mDataCollectionLock) {
- if (DEBUG) {
- Slog.v(TAG, "Sensor event " + event);
- }
- if (!mLastSensorReadings.isEmpty()
- && event.timestamp < mLastSensorReadings.getLast().timestamp) {
- // Ignore event that came out of order.
- return;
- }
- LightData data = null;
- while (!mLastSensorReadings.isEmpty()
- && mLastSensorReadings.getFirst().timestamp < horizon) {
- // Remove data that has fallen out of the window.
- data = mLastSensorReadings.removeFirst();
- }
- // We put back the last one we removed so we know how long
- // the first sensor reading was valid for.
- if (data != null) {
- mLastSensorReadings.addFirst(data);
- }
-
- data = new LightData();
- data.timestamp = event.timestamp;
- data.lux = event.values[0];
- mLastSensorReadings.addLast(data);
- }
- }
-
private void recordAmbientBrightnessStats(SensorEvent event) {
mAmbientBrightnessStatsTracker.add(mCurrentUserId, event.values[0]);
}
@@ -945,7 +889,6 @@ public class BrightnessTracker {
private final class SensorListener implements SensorEventListener {
@Override
public void onSensorChanged(SensorEvent event) {
- recordSensorEvent(event);
recordAmbientBrightnessStats(event);
}
@@ -1032,7 +975,7 @@ public class BrightnessTracker {
handleBrightnessChanged(values.brightness, userInitiatedChange,
values.powerBrightnessFactor, values.isUserSetBrightness,
values.isDefaultBrightnessConfig, values.timestamp,
- values.uniqueDisplayId);
+ values.uniqueDisplayId, values.luxValues, values.luxTimestamps);
break;
case MSG_START_SENSOR_LISTENER:
startSensorListener();
@@ -1068,16 +1011,20 @@ public class BrightnessTracker {
public final boolean isDefaultBrightnessConfig;
public final long timestamp;
public final String uniqueDisplayId;
+ public final float[] luxValues;
+ public final long[] luxTimestamps;
BrightnessChangeValues(float brightness, float powerBrightnessFactor,
boolean isUserSetBrightness, boolean isDefaultBrightnessConfig,
- long timestamp, String uniqueDisplayId) {
+ long timestamp, String uniqueDisplayId, float[] luxValues, long[] luxTimestamps) {
this.brightness = brightness;
this.powerBrightnessFactor = powerBrightnessFactor;
this.isUserSetBrightness = isUserSetBrightness;
this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
this.timestamp = timestamp;
this.uniqueDisplayId = uniqueDisplayId;
+ this.luxValues = luxValues;
+ this.luxTimestamps = luxTimestamps;
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index d7bbb109b6f7..bb27651ed0ed 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -2471,7 +2471,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
: 1.0f;
mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated,
powerFactor, hadUserDataPoint,
- mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId);
+ mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId,
+ mAutomaticBrightnessController.getLastSensorValues(),
+ mAutomaticBrightnessController.getLastSensorTimestamps());
}
}
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index a11f1721a4b9..73131a1dc220 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -619,7 +619,7 @@ final class PersistentDataStore {
private static final class DisplayState {
private int mColorMode;
- private float mBrightness;
+ private float mBrightness = Float.NaN;
private int mWidth;
private int mHeight;
private float mRefreshRate;
@@ -700,7 +700,11 @@ final class PersistentDataStore {
break;
case TAG_BRIGHTNESS_VALUE:
String brightness = parser.nextText();
- mBrightness = Float.parseFloat(brightness);
+ try {
+ mBrightness = Float.parseFloat(brightness);
+ } catch (NumberFormatException e) {
+ mBrightness = Float.NaN;
+ }
break;
case TAG_BRIGHTNESS_CONFIGURATIONS:
mDisplayBrightnessConfigurations.loadFromXml(parser);
@@ -727,7 +731,9 @@ final class PersistentDataStore {
serializer.endTag(null, TAG_COLOR_MODE);
serializer.startTag(null, TAG_BRIGHTNESS_VALUE);
- serializer.text(Float.toString(mBrightness));
+ if (!Float.isNaN(mBrightness)) {
+ serializer.text(Float.toString(mBrightness));
+ }
serializer.endTag(null, TAG_BRIGHTNESS_VALUE);
serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 61936df4a124..babe4eafd8f3 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -59,6 +59,9 @@ public final class SnoozeHelper {
static final int CONCURRENT_SNOOZE_LIMIT = 500;
+ // A safe size for strings to be put in persistent storage, to avoid breaking the XML write.
+ static final int MAX_STRING_LENGTH = 1000;
+
protected static final String XML_TAG_NAME = "snoozed-notifications";
private static final String XML_SNOOZED_NOTIFICATION = "notification";
@@ -200,7 +203,7 @@ public final class SnoozeHelper {
scheduleRepost(key, duration);
Long activateAt = System.currentTimeMillis() + duration;
synchronized (mLock) {
- mPersistedSnoozedNotifications.put(key, activateAt);
+ mPersistedSnoozedNotifications.put(getTrimmedString(key), activateAt);
}
}
@@ -210,7 +213,10 @@ public final class SnoozeHelper {
protected void snooze(NotificationRecord record, String contextId) {
if (contextId != null) {
synchronized (mLock) {
- mPersistedSnoozedNotificationsWithContext.put(record.getKey(), contextId);
+ mPersistedSnoozedNotificationsWithContext.put(
+ getTrimmedString(record.getKey()),
+ getTrimmedString(contextId)
+ );
}
}
snooze(record);
@@ -225,6 +231,13 @@ public final class SnoozeHelper {
}
}
+ private String getTrimmedString(String key) {
+ if (key != null && key.length() > MAX_STRING_LENGTH) {
+ return key.substring(0, MAX_STRING_LENGTH);
+ }
+ return key;
+ }
+
protected boolean cancel(int userId, String pkg, String tag, int id) {
synchronized (mLock) {
final Set<Map.Entry<String, NotificationRecord>> records =
@@ -293,10 +306,12 @@ public final class SnoozeHelper {
}
protected void repost(String key, int userId, boolean muteOnReturn) {
+ final String trimmedKey = getTrimmedString(key);
+
NotificationRecord record;
synchronized (mLock) {
- mPersistedSnoozedNotifications.remove(key);
- mPersistedSnoozedNotificationsWithContext.remove(key);
+ mPersistedSnoozedNotifications.remove(trimmedKey);
+ mPersistedSnoozedNotificationsWithContext.remove(trimmedKey);
record = mSnoozedNotifications.remove(key);
}
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 2a65ea2552b3..ced3a45935f8 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -378,13 +378,14 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
try {
conditionSatisfied = mStateConditions.get(state).getAsBoolean();
} catch (IllegalStateException e) {
- // Failed to compute the current state based on current available data. Return
+ // Failed to compute the current state based on current available data. Continue
// with the expectation that notifyDeviceStateChangedIfNeeded() will be called
- // when a callback with the missing data is triggered.
+ // when a callback with the missing data is triggered. May trigger another state
+ // change if another state is satisfied currently.
if (DEBUG) {
Slog.d(TAG, "Unable to check current state", e);
}
- return;
+ continue;
}
if (conditionSatisfied) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e776e68f108c..e19e921852ff 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5443,25 +5443,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/
private void postApplyAnimation(boolean visible, boolean fromTransition) {
final boolean usingShellTransitions = mTransitionController.isShellTransitionsEnabled();
- final boolean delayed = isAnimating(PARENTS | CHILDREN,
+ final boolean delayed = !usingShellTransitions && isAnimating(PARENTS | CHILDREN,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION
| ANIMATION_TYPE_RECENTS);
- if (!delayed) {
+ if (!delayed && !usingShellTransitions) {
// We aren't delayed anything, but exiting windows rely on the animation finished
// callback being called in case the ActivityRecord was pretending to be delayed,
// which we might have done because we were in closing/opening apps list.
- if (!usingShellTransitions) {
- onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION, null /* AnimationAdapter */);
- if (visible) {
- // The token was made immediately visible, there will be no entrance animation.
- // We need to inform the client the enter animation was finished.
- mEnteringAnimation = true;
- mWmService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(
- token);
- }
- } else {
- // update wallpaper target
- setAppLayoutChanges(FINISH_LAYOUT_REDO_WALLPAPER, "ActivityRecord");
+ onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION, null /* AnimationAdapter */);
+ if (visible) {
+ // The token was made immediately visible, there will be no entrance animation.
+ // We need to inform the client the enter animation was finished.
+ mEnteringAnimation = true;
+ mWmService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(
+ token);
}
}
@@ -5470,8 +5465,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// updated.
// If we're becoming invisible, update the client visibility if we are not running an
// animation. Otherwise, we'll update client visibility in onAnimationFinished.
- if (visible || !isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)
- || usingShellTransitions) {
+ if (visible || usingShellTransitions
+ || !isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)) {
setClientVisible(visible);
}
@@ -7452,8 +7447,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
} else if (!show && mLastSurfaceShowing) {
getSyncTransaction().hide(mSurfaceControl);
}
- if (show) {
- mActivityRecordInputSink.applyChangesToSurfaceIfChanged(getSyncTransaction());
+ // Input sink surface is not a part of animation, so just apply in a steady state
+ // (non-sync) with pending transaction.
+ if (show && mSyncState == SYNC_STATE_NONE) {
+ mActivityRecordInputSink.applyChangesToSurfaceIfChanged(getPendingTransaction());
}
}
if (mThumbnail != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index eaa08fd5eb0b..185e06eecabb 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -1537,6 +1537,7 @@ public class DisplayRotation {
private int mHalfFoldSavedRotation = -1; // No saved rotation
private DeviceStateController.FoldState mFoldState =
DeviceStateController.FoldState.UNKNOWN;
+ private boolean mInHalfFoldTransition = false;
boolean overrideFrozenRotation() {
return mFoldState == DeviceStateController.FoldState.HALF_FOLDED;
@@ -1544,6 +1545,7 @@ public class DisplayRotation {
boolean shouldRevertOverriddenRotation() {
return mFoldState == DeviceStateController.FoldState.OPEN // When transitioning to open.
+ && mInHalfFoldTransition
&& mHalfFoldSavedRotation != -1 // Ignore if we've already reverted.
&& mUserRotationMode
== WindowManagerPolicy.USER_ROTATION_LOCKED; // Ignore if we're unlocked.
@@ -1552,6 +1554,7 @@ public class DisplayRotation {
int revertOverriddenRotation() {
int savedRotation = mHalfFoldSavedRotation;
mHalfFoldSavedRotation = -1;
+ mInHalfFoldTransition = false;
return savedRotation;
}
@@ -1577,16 +1580,11 @@ public class DisplayRotation {
mService.updateRotation(false /* alwaysSendConfiguration */,
false /* forceRelayout */);
} else {
- // Revert the rotation to our saved value if we transition from HALF_FOLDED.
- if (mHalfFoldSavedRotation != -1) {
- mRotation = mHalfFoldSavedRotation;
- }
- // Tell the device to update its orientation (mFoldState is still HALF_FOLDED here
- // so we will override USER_ROTATION_LOCKED and allow a rotation).
+ mInHalfFoldTransition = true;
+ mFoldState = newState;
+ // Tell the device to update its orientation.
mService.updateRotation(false /* alwaysSendConfiguration */,
false /* forceRelayout */);
- // Once we are rotated, set mFoldstate, effectively removing the lock override.
- mFoldState = newState;
}
}
}
@@ -1683,6 +1681,7 @@ public class DisplayRotation {
private static class RotationHistory {
private static final int MAX_SIZE = 8;
+ private static final int NO_FOLD_CONTROLLER = -2;
private static class Record {
final @Surface.Rotation int mFromRotation;
final @Surface.Rotation int mToRotation;
@@ -1694,6 +1693,9 @@ public class DisplayRotation {
final String mLastOrientationSource;
final @ActivityInfo.ScreenOrientation int mSourceOrientation;
final long mTimestamp = System.currentTimeMillis();
+ final int mHalfFoldSavedRotation;
+ final boolean mInHalfFoldTransition;
+ final DeviceStateController.FoldState mFoldState;
Record(DisplayRotation dr, int fromRotation, int toRotation) {
mFromRotation = fromRotation;
@@ -1719,6 +1721,15 @@ public class DisplayRotation {
mLastOrientationSource = null;
mSourceOrientation = SCREEN_ORIENTATION_UNSET;
}
+ if (dr.mFoldController != null) {
+ mHalfFoldSavedRotation = dr.mFoldController.mHalfFoldSavedRotation;
+ mInHalfFoldTransition = dr.mFoldController.mInHalfFoldTransition;
+ mFoldState = dr.mFoldController.mFoldState;
+ } else {
+ mHalfFoldSavedRotation = NO_FOLD_CONTROLLER;
+ mInHalfFoldTransition = false;
+ mFoldState = DeviceStateController.FoldState.UNKNOWN;
+ }
}
void dump(String prefix, PrintWriter pw) {
@@ -1735,6 +1746,12 @@ public class DisplayRotation {
if (mNonDefaultRequestingTaskDisplayArea != null) {
pw.println(prefix + " requestingTda=" + mNonDefaultRequestingTaskDisplayArea);
}
+ if (mHalfFoldSavedRotation != NO_FOLD_CONTROLLER) {
+ pw.println(prefix + " halfFoldSavedRotation="
+ + mHalfFoldSavedRotation
+ + " mInHalfFoldTransition=" + mInHalfFoldTransition
+ + " mFoldState=" + mFoldState);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index e6a0f4d5d77d..48258a11d13a 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -37,6 +37,7 @@ import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
@@ -651,6 +652,12 @@ class KeyguardController {
mRequestDismissKeyguard = lastDismissKeyguardActivity != mDismissingKeyguardActivity
&& !mOccluded && !mKeyguardGoingAway
&& mDismissingKeyguardActivity != null;
+ if (mOccluded && mKeyguardShowing && !display.isSleeping() && !top.fillsParent()
+ && display.mWallpaperController.getWallpaperTarget() == null) {
+ // The occluding activity may be translucent or not fill screen. Then let wallpaper
+ // to check whether it should set itself as target to avoid blank background.
+ display.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ }
if (mTopTurnScreenOnActivity != lastTurnScreenOnActivity
&& mTopTurnScreenOnActivity != null
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 84e30142f983..51eec03855a5 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -499,6 +499,12 @@ class Task extends TaskFragment {
*/
boolean mInRemoveTask;
+ /**
+ * When set, disassociate the leaf task if relaunched and reparented it to TDA as root task if
+ * possible.
+ */
+ boolean mReparentLeafTaskIfRelaunch;
+
private final AnimatingActivityRegistry mAnimatingActivityRegistry =
new AnimatingActivityRegistry();
@@ -2177,7 +2183,7 @@ class Task extends TaskFragment {
}
private boolean shouldStartChangeTransition(int prevWinMode, @NonNull Rect prevBounds) {
- if (!isLeafTask() || !canStartChangeTransition()) {
+ if (!(isLeafTask() || mCreatedByOrganizer) || !canStartChangeTransition()) {
return false;
}
final int newWinMode = getWindowingMode();
@@ -6142,6 +6148,12 @@ class Task extends TaskFragment {
}
}
+ void setReparentLeafTaskIfRelaunch(boolean reparentLeafTaskIfRelaunch) {
+ if (isOrganized()) {
+ mReparentLeafTaskIfRelaunch = reparentLeafTaskIfRelaunch;
+ }
+ }
+
@Override
public void dumpDebug(ProtoOutputStream proto, long fieldId,
@WindowTraceLogLevel int logLevel) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 1088a2e543eb..e0ed356fbe59 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -899,15 +899,16 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
}
} else if (candidateTask != null) {
final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
- final Task launchRootTask = getLaunchRootTask(resolvedWindowingMode, activityType,
+ final Task launchParentTask = getLaunchRootTask(resolvedWindowingMode, activityType,
options, sourceTask, launchFlags, candidateTask);
- if (launchRootTask != null) {
+ if (launchParentTask != null) {
if (candidateTask.getParent() == null) {
- launchRootTask.addChild(candidateTask, position);
- } else if (candidateTask.getParent() != launchRootTask) {
- candidateTask.reparent(launchRootTask, position);
+ launchParentTask.addChild(candidateTask, position);
+ } else if (candidateTask.getParent() != launchParentTask) {
+ candidateTask.reparent(launchParentTask, position);
}
- } else if (candidateTask.getDisplayArea() != this) {
+ } else if (candidateTask.getDisplayArea() != this
+ || candidateTask.getRootTask().mReparentLeafTaskIfRelaunch) {
if (candidateTask.getParent() == null) {
addChild(candidateTask, position);
} else {
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 14a2d0383019..0b90a7ce664c 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -164,9 +164,11 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
}
// If the launch windowing mode is still undefined, inherit from the target task if the
// task is already on the right display area (otherwise, the task may be on a different
- // display area that has incompatible windowing mode).
+ // display area that has incompatible windowing mode or the task organizer request to
+ // disassociate the leaf task if relaunched and reparented it to TDA as root task).
if (launchMode == WINDOWING_MODE_UNDEFINED
- && task != null && task.getTaskDisplayArea() == suggestedDisplayArea) {
+ && task != null && task.getTaskDisplayArea() == suggestedDisplayArea
+ && !task.getRootTask().mReparentLeafTaskIfRelaunch) {
launchMode = task.getWindowingMode();
if (DEBUG) {
appendLog("inherit-from-task="
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index ec3962c282f6..6b14dba6eb81 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -646,8 +646,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
if (target.asDisplayContent() != null || target.asActivityRecord() != null) {
t.setCrop(targetLeash, null /* crop */);
} else {
- // Crop to the requested bounds.
- final Rect clipRect = target.getRequestedOverrideBounds();
+ // Crop to the resolved override bounds.
+ final Rect clipRect = target.getResolvedOverrideBounds();
t.setWindowCrop(targetLeash, clipRect.width(), clipRect.height());
}
t.setCornerRadius(targetLeash, 0);
@@ -1645,8 +1645,11 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
WindowContainer<?> ancestor = findCommonAncestor(sortedTargets, changes, topApp);
- // make leash based on highest (z-order) direct child of ancestor with a participant.
- WindowContainer leashReference = sortedTargets.get(0);
+ // Make leash based on highest (z-order) direct child of ancestor with a participant.
+ // TODO(b/261418859): Handle the case when the target contains window containers which
+ // belong to a different display. As a workaround we use topApp, from which wallpaper
+ // window container is removed, instead of sortedTargets here.
+ WindowContainer leashReference = topApp;
while (leashReference.getParent() != ancestor) {
leashReference = leashReference.getParent();
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index d3d1c163aa19..971b619e671f 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -38,6 +38,7 @@ import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.ITransitionMetricsReporter;
import android.window.ITransitionPlayer;
@@ -116,6 +117,8 @@ class TransitionController {
*/
boolean mBuildingFinishLayers = false;
+ private final SurfaceControl.Transaction mWakeT = new SurfaceControl.Transaction();
+
TransitionController(ActivityTaskManagerService atm,
TaskSnapshotController taskSnapshotController,
TransitionTracer transitionTracer) {
@@ -619,8 +622,16 @@ class TransitionController {
private void updateRunningRemoteAnimation(Transition transition, boolean isPlaying) {
if (mTransitionPlayerProc == null) return;
if (isPlaying) {
+ mWakeT.setEarlyWakeupStart();
+ mWakeT.apply();
+ // Usually transitions put quite a load onto the system already (with all the things
+ // happening in app), so pause task snapshot persisting to not increase the load.
+ mAtm.mWindowManager.mTaskSnapshotController.setPersisterPaused(true);
mTransitionPlayerProc.setRunningRemoteAnimation(true);
} else if (mPlayingTransitions.isEmpty()) {
+ mWakeT.setEarlyWakeupEnd();
+ mWakeT.apply();
+ mAtm.mWindowManager.mTaskSnapshotController.setPersisterPaused(false);
mTransitionPlayerProc.setRunningRemoteAnimation(false);
mRemotePlayer.clear();
return;
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index d85bd830cc11..69c4297ce717 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -42,6 +42,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_COMPANION_TASK_FRAGMENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_SHORTCUT;
@@ -1236,7 +1237,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
WindowContainer.fromBinder(hop.getContainer())
.removeLocalInsetsSourceProvider(hop.getInsetsTypes());
break;
- case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP:
+ case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: {
final WindowContainer container = WindowContainer.fromBinder(hop.getContainer());
if (container == null || container.asDisplayArea() == null
|| !container.isAttached()) {
@@ -1247,7 +1248,26 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
container.setAlwaysOnTop(hop.isAlwaysOnTop());
effects |= TRANSACT_EFFECTS_LIFECYCLE;
break;
-
+ }
+ case HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH: {
+ final WindowContainer container = WindowContainer.fromBinder(hop.getContainer());
+ final Task task = container != null ? container.asTask() : null;
+ if (task == null || !task.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on unknown or detached container: "
+ + container);
+ break;
+ }
+ if (!task.mCreatedByOrganizer) {
+ throw new UnsupportedOperationException(
+ "Cannot set reparent leaf task flag on non-organized task : " + task);
+ }
+ if (!task.isRootTask()) {
+ throw new UnsupportedOperationException(
+ "Cannot set reparent leaf task flag on non-root task : " + task);
+ }
+ task.setReparentLeafTaskIfRelaunch(hop.isReparentLeafTaskIfRelaunch());
+ break;
+ }
}
return effects;
}
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 4f2b613d7aed..bb08ef756a14 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -18,6 +18,7 @@ package com.android.server.display;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
@@ -178,7 +179,7 @@ public class AutomaticBrightnessControllerTest {
// Send new sensor value and verify
listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux1));
- assertEquals(normalizedBrightness1, mController.getAutomaticScreenBrightness(), 0.001f);
+ assertEquals(normalizedBrightness1, mController.getAutomaticScreenBrightness(), EPSILON);
// Set up system to return 0.0f (minimum possible brightness) as a brightness value
float lux2 = 10.0f;
@@ -192,7 +193,7 @@ public class AutomaticBrightnessControllerTest {
// Send new sensor value and verify
listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux2));
- assertEquals(normalizedBrightness2, mController.getAutomaticScreenBrightness(), 0.001f);
+ assertEquals(normalizedBrightness2, mController.getAutomaticScreenBrightness(), EPSILON);
}
@Test
@@ -221,7 +222,7 @@ public class AutomaticBrightnessControllerTest {
// Send new sensor value and verify
listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux1));
- assertEquals(normalizedBrightness1, mController.getAutomaticScreenBrightness(), 0.001f);
+ assertEquals(normalizedBrightness1, mController.getAutomaticScreenBrightness(), EPSILON);
// Set up system to return 1.0f as a brightness value (brightness_max)
@@ -236,7 +237,7 @@ public class AutomaticBrightnessControllerTest {
// Send new sensor value and verify
listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux2));
- assertEquals(normalizedBrightness2, mController.getAutomaticScreenBrightness(), 0.001f);
+ assertEquals(normalizedBrightness2, mController.getAutomaticScreenBrightness(), EPSILON);
}
@Test
@@ -418,6 +419,12 @@ public class AutomaticBrightnessControllerTest {
// ambient lux goes to 0
listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 0));
assertEquals(0.0f, mController.getAmbientLux(), EPSILON);
+
+ // only the values within the horizon should be kept
+ assertArrayEquals(new float[] {10000, 10000, 0, 0, 0}, mController.getLastSensorValues(),
+ EPSILON);
+ assertArrayEquals(new long[] {4000, 4500, 5000, 5500, 6000},
+ mController.getLastSensorTimestamps());
}
@Test
@@ -489,4 +496,92 @@ public class AutomaticBrightnessControllerTest {
0 /* adjustment */, false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f);
}
+
+ @Test
+ public void testGetSensorReadings() throws Exception {
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // Choose values such that the ring buffer's capacity is extended and the buffer is pruned
+ int increment = 11;
+ int lux = 5000;
+ for (int i = 0; i < 1000; i++) {
+ lux += increment;
+ mClock.fastForward(increment);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux));
+ }
+
+ int valuesCount = (int) Math.ceil((double) AMBIENT_LIGHT_HORIZON_LONG / increment + 1);
+ float[] sensorValues = mController.getLastSensorValues();
+ long[] sensorTimestamps = mController.getLastSensorTimestamps();
+
+ // Only the values within the horizon should be kept
+ assertEquals(valuesCount, sensorValues.length);
+ assertEquals(valuesCount, sensorTimestamps.length);
+
+ long sensorTimestamp = mClock.now();
+ for (int i = valuesCount - 1; i >= 1; i--) {
+ assertEquals(lux, sensorValues[i], EPSILON);
+ assertEquals(sensorTimestamp, sensorTimestamps[i]);
+ lux -= increment;
+ sensorTimestamp -= increment;
+ }
+ assertEquals(lux, sensorValues[0], EPSILON);
+ assertEquals(mClock.now() - AMBIENT_LIGHT_HORIZON_LONG, sensorTimestamps[0]);
+ }
+
+ @Test
+ public void testGetSensorReadingsFullBuffer() throws Exception {
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+ int initialCapacity = 150;
+
+ // Choose values such that the ring buffer is pruned
+ int increment1 = 200;
+ int lux = 5000;
+ for (int i = 0; i < 20; i++) {
+ lux += increment1;
+ mClock.fastForward(increment1);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux));
+ }
+
+ int valuesCount = (int) Math.ceil((double) AMBIENT_LIGHT_HORIZON_LONG / increment1 + 1);
+
+ // Choose values such that the buffer becomes full
+ int increment2 = 1;
+ for (int i = 0; i < initialCapacity - valuesCount; i++) {
+ lux += increment2;
+ mClock.fastForward(increment2);
+ listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, lux));
+ }
+
+ float[] sensorValues = mController.getLastSensorValues();
+ long[] sensorTimestamps = mController.getLastSensorTimestamps();
+
+ // The buffer should be full
+ assertEquals(initialCapacity, sensorValues.length);
+ assertEquals(initialCapacity, sensorTimestamps.length);
+
+ long sensorTimestamp = mClock.now();
+ for (int i = initialCapacity - 1; i >= 1; i--) {
+ assertEquals(lux, sensorValues[i], EPSILON);
+ assertEquals(sensorTimestamp, sensorTimestamps[i]);
+
+ if (i >= valuesCount) {
+ lux -= increment2;
+ sensorTimestamp -= increment2;
+ } else {
+ lux -= increment1;
+ sensorTimestamp -= increment1;
+ }
+ }
+ assertEquals(lux, sensorValues[0], EPSILON);
+ assertEquals(mClock.now() - AMBIENT_LIGHT_HORIZON_LONG, sensorTimestamps[0]);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index 06422281ab25..5780ef3edc67 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -136,28 +136,28 @@ public class BrightnessTrackerTest {
assertNull(mInjector.mSensorListener);
assertNotNull(mInjector.mBroadcastReceiver);
assertTrue(mInjector.mIdleScheduled);
- mInjector.sendScreenChange(/*screen on */ true);
+ mInjector.sendScreenChange(/* screenOn= */ true);
assertNotNull(mInjector.mSensorListener);
assertTrue(mInjector.mColorSamplingEnabled);
- mInjector.sendScreenChange(/*screen on */ false);
+ mInjector.sendScreenChange(/* screenOn= */ false);
assertNull(mInjector.mSensorListener);
assertFalse(mInjector.mColorSamplingEnabled);
// Turn screen on while brightness mode is manual
- mInjector.setBrightnessMode(/* isBrightnessModeAutomatic */ false);
- mInjector.sendScreenChange(/*screen on */ true);
+ mInjector.setBrightnessMode(/* isBrightnessModeAutomatic= */ false);
+ mInjector.sendScreenChange(/* screenOn= */ true);
assertNull(mInjector.mSensorListener);
assertFalse(mInjector.mColorSamplingEnabled);
// Set brightness mode to automatic while screen is off.
- mInjector.sendScreenChange(/*screen on */ false);
- mInjector.setBrightnessMode(/* isBrightnessModeAutomatic */ true);
+ mInjector.sendScreenChange(/* screenOn= */ false);
+ mInjector.setBrightnessMode(/* isBrightnessModeAutomatic= */ true);
assertNull(mInjector.mSensorListener);
assertFalse(mInjector.mColorSamplingEnabled);
// Turn on screen while brightness mode is automatic.
- mInjector.sendScreenChange(/*screen on */ true);
+ mInjector.sendScreenChange(/* screenOn= */ true);
assertNotNull(mInjector.mSensorListener);
assertTrue(mInjector.mColorSamplingEnabled);
@@ -188,14 +188,14 @@ public class BrightnessTrackerTest {
assertFalse(mInjector.mColorSamplingEnabled);
// Pretend screen is off, update config to turn on color sampling.
- mInjector.sendScreenChange(/*screen on */ false);
+ mInjector.sendScreenChange(/* screenOn= */ false);
mTracker.setBrightnessConfiguration(buildBrightnessConfiguration(
/* collectColorSamples= */ true));
mInjector.waitForHandler();
assertFalse(mInjector.mColorSamplingEnabled);
// Pretend screen is on.
- mInjector.sendScreenChange(/*screen on */ true);
+ mInjector.sendScreenChange(/* screenOn= */ true);
assertTrue(mInjector.mColorSamplingEnabled);
mTracker.stop();
@@ -261,7 +261,7 @@ public class BrightnessTrackerTest {
assertFalse(mInjector.mColorSamplingEnabled);
assertNull(mInjector.mDisplayListener);
- mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ true);
+ mInjector.setBrightnessMode(/* isBrightnessModeAutomatic= */ true);
assertNotNull(mInjector.mSensorListener);
assertTrue(mInjector.mColorSamplingEnabled);
assertNotNull(mInjector.mDisplayListener);
@@ -272,16 +272,15 @@ public class BrightnessTrackerTest {
mInjector.mColorSamplingEnabled = false;
mInjector.mDisplayListener = null;
// Duplicate notification
- mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ true);
+ mInjector.setBrightnessMode(/* isBrightnessModeAutomatic= */ true);
// Sensor shouldn't have been registered as it was already registered.
assertNull(mInjector.mSensorListener);
assertFalse(mInjector.mColorSamplingEnabled);
assertNull(mInjector.mDisplayListener);
- mInjector.mSensorListener = listener;
mInjector.mDisplayListener = displayListener;
mInjector.mColorSamplingEnabled = true;
- mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ false);
+ mInjector.setBrightnessMode(/* isBrightnessModeAutomatic= */ false);
assertNull(mInjector.mSensorListener);
assertFalse(mInjector.mColorSamplingEnabled);
assertNull(mInjector.mDisplayListener);
@@ -301,19 +300,21 @@ public class BrightnessTrackerTest {
final String displayId = "1234";
startTracker(mTracker);
- mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f));
+ final long sensorTime = TimeUnit.NANOSECONDS.toMillis(mInjector.elapsedRealtimeNanos());
mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
- notifyBrightnessChanged(mTracker, brightness, displayId);
+ final long currentTime = mInjector.currentTimeMillis();
+ notifyBrightnessChanged(mTracker, brightness, displayId, new float[] {1.0f},
+ new long[] {sensorTime});
List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
mTracker.stop();
assertEquals(1, events.size());
BrightnessChangeEvent event = events.get(0);
- assertEquals(mInjector.currentTimeMillis(), event.timeStamp);
+ assertEquals(currentTime, event.timeStamp);
assertEquals(displayId, event.uniqueDisplayId);
assertEquals(1, event.luxValues.length);
assertEquals(1.0f, event.luxValues[0], FLOAT_DELTA);
- assertEquals(mInjector.currentTimeMillis() - TimeUnit.SECONDS.toMillis(2),
+ assertEquals(currentTime - TimeUnit.SECONDS.toMillis(2),
event.luxTimestamps[0]);
assertEquals(brightness, event.brightness, FLOAT_DELTA);
assertEquals(DEFAULT_INITIAL_BRIGHTNESS, event.lastBrightness, FLOAT_DELTA);
@@ -339,9 +340,9 @@ public class BrightnessTrackerTest {
startTracker(mTracker, initialBrightness, DEFAULT_COLOR_SAMPLING_ENABLED);
mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
batteryChangeEvent(30, 60));
- mInjector.mSensorListener.onSensorChanged(createSensorEvent(1000.0f));
- final long sensorTime = mInjector.currentTimeMillis();
- notifyBrightnessChanged(mTracker, brightness, displayId);
+ final long currentTime = mInjector.currentTimeMillis();
+ notifyBrightnessChanged(mTracker, brightness, displayId, new float[] {1000.0f},
+ new long[] {TimeUnit.NANOSECONDS.toMillis(mInjector.elapsedRealtimeNanos())});
List<BrightnessChangeEvent> eventsNoPackage
= mTracker.getEvents(0, false).getList();
List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
@@ -349,10 +350,10 @@ public class BrightnessTrackerTest {
assertEquals(1, events.size());
BrightnessChangeEvent event = events.get(0);
- assertEquals(event.timeStamp, mInjector.currentTimeMillis());
+ assertEquals(event.timeStamp, currentTime);
assertEquals(displayId, event.uniqueDisplayId);
- assertArrayEquals(new float[] {1000.0f}, event.luxValues, 0.01f);
- assertArrayEquals(new long[] {sensorTime}, event.luxTimestamps);
+ assertArrayEquals(new float[] {1000.0f}, event.luxValues, FLOAT_DELTA);
+ assertArrayEquals(new long[] {currentTime}, event.luxTimestamps);
assertEquals(brightness, event.brightness, FLOAT_DELTA);
assertEquals(initialBrightness, event.lastBrightness, FLOAT_DELTA);
assertEquals(0.5, event.batteryLevel, FLOAT_DELTA);
@@ -374,13 +375,12 @@ public class BrightnessTrackerTest {
public void testIgnoreAutomaticBrightnessChange() {
final int initialBrightness = 30;
startTracker(mTracker, initialBrightness, DEFAULT_COLOR_SAMPLING_ENABLED);
- mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f));
mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1));
final int systemUpdatedBrightness = 20;
- notifyBrightnessChanged(mTracker, systemUpdatedBrightness, false /*userInitiated*/,
- 0.5f /*powerBrightnessFactor(*/, false /*isUserSetBrightness*/,
- false /*isDefaultBrightnessConfig*/, DEFAULT_DISPLAY_ID);
+ notifyBrightnessChanged(mTracker, systemUpdatedBrightness, /* userInitiated= */ false,
+ /* powerBrightnessFactor= */ 0.5f, /* isUserSetBrightness= */ false,
+ /* isDefaultBrightnessConfig= */ false, DEFAULT_DISPLAY_ID);
List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
// No events because we filtered out our change.
assertEquals(0, events.size());
@@ -408,10 +408,8 @@ public class BrightnessTrackerTest {
@Test
public void testLimitedBufferSize() {
startTracker(mTracker);
- mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f));
for (int brightness = 0; brightness <= 255; ++brightness) {
- mInjector.mSensorListener.onSensorChanged(createSensorEvent(1.0f));
mInjector.incrementTime(TimeUnit.SECONDS.toNanos(1));
notifyBrightnessChanged(mTracker, brightness);
}
@@ -427,33 +425,6 @@ public class BrightnessTrackerTest {
}
@Test
- public void testLimitedSensorEvents() {
- final int brightness = 20;
-
- startTracker(mTracker);
- // 20 Sensor events 1 second apart.
- for (int i = 0; i < 20; ++i) {
- mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1));
- mInjector.mSensorListener.onSensorChanged(createSensorEvent(i + 1.0f));
- }
- notifyBrightnessChanged(mTracker, 20);
- List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
- mTracker.stop();
-
- assertEquals(1, events.size());
- BrightnessChangeEvent event = events.get(0);
- assertEquals(mInjector.currentTimeMillis(), event.timeStamp);
-
- // 12 sensor events, 11 for 0->10 seconds + 1 previous event.
- assertEquals(12, event.luxValues.length);
- for (int i = 0; i < 12; ++i) {
- assertEquals(event.luxTimestamps[11 - i],
- mInjector.currentTimeMillis() - i * TimeUnit.SECONDS.toMillis(1));
- }
- assertEquals(brightness, event.brightness, FLOAT_DELTA);
- }
-
- @Test
public void testReadEvents() throws Exception {
BrightnessTracker tracker = new BrightnessTracker(InstrumentationRegistry.getContext(),
mInjector);
@@ -607,15 +578,16 @@ public class BrightnessTrackerTest {
startTracker(mTracker);
mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
batteryChangeEvent(30, 100));
- mInjector.mSensorListener.onSensorChanged(createSensorEvent(2000.0f));
- final long firstSensorTime = mInjector.currentTimeMillis();
+ final long elapsedTime1 = TimeUnit.NANOSECONDS.toMillis(mInjector.elapsedRealtimeNanos());
+ final long currentTime1 = mInjector.currentTimeMillis();
mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
- mInjector.mSensorListener.onSensorChanged(createSensorEvent(3000.0f));
- final long secondSensorTime = mInjector.currentTimeMillis();
+ final long elapsedTime2 = TimeUnit.NANOSECONDS.toMillis(mInjector.elapsedRealtimeNanos());
+ final long currentTime2 = mInjector.currentTimeMillis();
mInjector.incrementTime(TimeUnit.SECONDS.toMillis(3));
- notifyBrightnessChanged(mTracker, brightness, true /*userInitiated*/,
- 0.5f /*powerBrightnessFactor*/, true /*hasUserBrightnessPoints*/,
- false /*isDefaultBrightnessConfig*/, displayId);
+ notifyBrightnessChanged(mTracker, brightness, /* userInitiated= */ true,
+ /* powerBrightnessFactor= */ 0.5f, /* isUserSetBrightness= */ true,
+ /* isDefaultBrightnessConfig= */ false, displayId, new float[] {2000.0f, 3000.0f},
+ new long[] {elapsedTime1, elapsedTime2});
ByteArrayOutputStream baos = new ByteArrayOutputStream();
mTracker.writeEventsLocked(baos);
mTracker.stop();
@@ -631,7 +603,7 @@ public class BrightnessTrackerTest {
BrightnessChangeEvent event = events.get(0);
assertEquals(displayId, event.uniqueDisplayId);
assertArrayEquals(new float[] {2000.0f, 3000.0f}, event.luxValues, FLOAT_DELTA);
- assertArrayEquals(new long[] {firstSensorTime, secondSensorTime}, event.luxTimestamps);
+ assertArrayEquals(new long[] {currentTime1, currentTime2}, event.luxTimestamps);
assertEquals(brightness, event.brightness, FLOAT_DELTA);
assertEquals(0.3, event.batteryLevel, FLOAT_DELTA);
assertTrue(event.nightMode);
@@ -647,53 +619,6 @@ public class BrightnessTrackerTest {
}
@Test
- public void testWritePrunesOldEvents() throws Exception {
- final int brightness = 20;
-
- mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 1);
- mInjector.mSecureIntSettings.put(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 3339);
-
- mInjector.mSecureIntSettings.put(Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
- mInjector.mSecureIntSettings.put(Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 40);
-
- startTracker(mTracker);
- mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
- batteryChangeEvent(30, 100));
- mInjector.mSensorListener.onSensorChanged(createSensorEvent(1000.0f));
- mInjector.incrementTime(TimeUnit.SECONDS.toMillis(1));
- mInjector.mSensorListener.onSensorChanged(createSensorEvent(2000.0f));
- final long sensorTime = mInjector.currentTimeMillis();
- notifyBrightnessChanged(mTracker, brightness);
-
- // 31 days later
- mInjector.incrementTime(TimeUnit.DAYS.toMillis(31));
- mInjector.mSensorListener.onSensorChanged(createSensorEvent(3000.0f));
- notifyBrightnessChanged(mTracker, brightness);
- final long eventTime = mInjector.currentTimeMillis();
-
- List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
- assertEquals(2, events.size());
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- mTracker.writeEventsLocked(baos);
- events = mTracker.getEvents(0, true).getList();
- mTracker.stop();
-
- assertEquals(1, events.size());
- BrightnessChangeEvent event = events.get(0);
- assertEquals(eventTime, event.timeStamp);
-
- // We will keep one of the old sensor events because we keep 1 event outside the window.
- assertArrayEquals(new float[] {2000.0f, 3000.0f}, event.luxValues, FLOAT_DELTA);
- assertArrayEquals(new long[] {sensorTime, eventTime}, event.luxTimestamps);
- assertEquals(brightness, event.brightness, FLOAT_DELTA);
- assertEquals(0.3, event.batteryLevel, FLOAT_DELTA);
- assertTrue(event.nightMode);
- assertTrue(event.reduceBrightColors);
- assertEquals(3339, event.colorTemperature);
- }
-
- @Test
public void testParcelUnParcel() {
Parcel parcel = Parcel.obtain();
BrightnessChangeEvent.Builder builder = new BrightnessChangeEvent.Builder();
@@ -796,9 +721,10 @@ public class BrightnessTrackerTest {
// Send an event.
long eventTime = mInjector.currentTimeMillis();
- mTracker.notifyBrightnessChanged(brightness, true /*userInitiated*/,
- 1.0f /*powerBrightnessFactor*/, false /*isUserSetBrightness*/,
- false /*isDefaultBrightnessConfig*/, DEFAULT_DISPLAY_ID);
+ mTracker.notifyBrightnessChanged(brightness, /* userInitiated= */ true,
+ /* powerBrightnessFactor= */ 1.0f, /* isUserSetBrightness= */ false,
+ /* isDefaultBrightnessConfig= */ false, DEFAULT_DISPLAY_ID, new float[10],
+ new long[10]);
// Time passes before handler can run.
mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
@@ -890,20 +816,33 @@ public class BrightnessTrackerTest {
public void testOnlyOneReceiverRegistered() {
assertNull(mInjector.mLightSensor);
assertNull(mInjector.mSensorListener);
+ assertNull(mInjector.mContentObserver);
+ assertNull(mInjector.mBroadcastReceiver);
+ assertFalse(mInjector.mIdleScheduled);
startTracker(mTracker, 0.3f, false);
assertNotNull(mInjector.mLightSensor);
assertNotNull(mInjector.mSensorListener);
+ assertNotNull(mInjector.mContentObserver);
+ assertNotNull(mInjector.mBroadcastReceiver);
+ assertTrue(mInjector.mIdleScheduled);
Sensor registeredLightSensor = mInjector.mLightSensor;
SensorEventListener registeredSensorListener = mInjector.mSensorListener;
+ ContentObserver registeredContentObserver = mInjector.mContentObserver;
+ BroadcastReceiver registeredBroadcastReceiver = mInjector.mBroadcastReceiver;
mTracker.start(0.3f);
assertSame(registeredLightSensor, mInjector.mLightSensor);
assertSame(registeredSensorListener, mInjector.mSensorListener);
+ assertSame(registeredContentObserver, mInjector.mContentObserver);
+ assertSame(registeredBroadcastReceiver, mInjector.mBroadcastReceiver);
mTracker.stop();
assertNull(mInjector.mLightSensor);
assertNull(mInjector.mSensorListener);
+ assertNull(mInjector.mContentObserver);
+ assertNull(mInjector.mBroadcastReceiver);
+ assertFalse(mInjector.mIdleScheduled);
// mInjector asserts that we aren't removing a null receiver
mTracker.stop();
@@ -954,23 +893,41 @@ public class BrightnessTrackerTest {
private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness,
String displayId) {
- notifyBrightnessChanged(tracker, brightness, true /*userInitiated*/,
- 1.0f /*powerBrightnessFactor*/, false /*isUserSetBrightness*/,
- false /*isDefaultBrightnessConfig*/, displayId);
+ notifyBrightnessChanged(tracker, brightness, /* userInitiated= */ true,
+ /* powerBrightnessFactor= */ 1.0f, /* isUserSetBrightness= */ false,
+ /* isDefaultBrightnessConfig= */ false, displayId, new float[10], new long[10]);
+ }
+
+ private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness,
+ String displayId, float[] luxValues, long[] luxTimestamps) {
+ notifyBrightnessChanged(tracker, brightness, /* userInitiated= */ true,
+ /* powerBrightnessFactor= */ 1.0f, /* isUserSetBrightness= */ false,
+ /* isDefaultBrightnessConfig= */ false, displayId, luxValues, luxTimestamps);
}
private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness,
boolean userInitiated, float powerBrightnessFactor, boolean isUserSetBrightness,
boolean isDefaultBrightnessConfig, String displayId) {
tracker.notifyBrightnessChanged(brightness, userInitiated, powerBrightnessFactor,
- isUserSetBrightness, isDefaultBrightnessConfig, displayId);
+ isUserSetBrightness, isDefaultBrightnessConfig, displayId, new float[10],
+ new long[10]);
+ mInjector.waitForHandler();
+ }
+
+ private void notifyBrightnessChanged(BrightnessTracker tracker, float brightness,
+ boolean userInitiated, float powerBrightnessFactor, boolean isUserSetBrightness,
+ boolean isDefaultBrightnessConfig, String displayId, float[] luxValues,
+ long[] luxTimestamps) {
+ tracker.notifyBrightnessChanged(brightness, userInitiated, powerBrightnessFactor,
+ isUserSetBrightness, isDefaultBrightnessConfig, displayId, luxValues,
+ luxTimestamps);
mInjector.waitForHandler();
}
private BrightnessConfiguration buildBrightnessConfiguration(boolean collectColorSamples) {
BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(
- /* lux = */ new float[] {0f, 10f, 100f},
- /* nits = */ new float[] {1f, 90f, 100f});
+ /* lux= */ new float[] {0f, 10f, 100f},
+ /* nits= */ new float[] {1f, 90f, 100f});
builder.setShouldCollectColorSamples(collectColorSamples);
return builder.build();
}
diff --git a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
index 3b0a22f80c30..35a677e0f816 100644
--- a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
@@ -344,6 +344,40 @@ public class PersistentDataStoreTest {
assertEquals(85.3f, newDataStore.getUserPreferredRefreshRate(testDisplayDevice), 0.1f);
}
+ @Test
+ public void testBrightnessInitialisesWithInvalidFloat() {
+ final String uniqueDisplayId = "test:123";
+ DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+ @Override
+ public boolean hasStableUniqueId() {
+ return true;
+ }
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ return null;
+ }
+ };
+
+ // Set any value which initialises Display state
+ float refreshRate = 85.3f;
+ mDataStore.loadIfNeeded();
+ mDataStore.setUserPreferredRefreshRate(testDisplayDevice, refreshRate);
+
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ mInjector.setWriteStream(baos);
+ mDataStore.saveIfNeeded();
+ mTestLooper.dispatchAll();
+ assertTrue(mInjector.wasWriteSuccessful());
+ TestInjector newInjector = new TestInjector();
+ PersistentDataStore newDataStore = new PersistentDataStore(newInjector);
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ newInjector.setReadStream(bais);
+ newDataStore.loadIfNeeded();
+ assertTrue(Float.isNaN(mDataStore.getBrightness(testDisplayDevice)));
+ }
+
+
public class TestInjector extends PersistentDataStore.Injector {
private InputStream mReadStream;
private OutputStream mWriteStream;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 7817e8176e76..1f117b3697c9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -234,6 +234,37 @@ public class SnoozeHelperTest extends UiServiceTestCase {
}
@Test
+ public void testScheduleRepostsForLongTagPersistedNotification() throws Exception {
+ String longTag = "A".repeat(66000);
+ NotificationRecord r = getNotificationRecord("pkg", 1, longTag, UserHandle.SYSTEM);
+ mSnoozeHelper.snooze(r, 0);
+
+ // We store the full key in temp storage.
+ ArgumentCaptor<PendingIntent> captor = ArgumentCaptor.forClass(PendingIntent.class);
+ verify(mAm).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture());
+ assertEquals(66010, captor.getValue().getIntent().getStringExtra(EXTRA_KEY).length());
+
+ TypedXmlSerializer serializer = Xml.newFastSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ serializer.startDocument(null, true);
+ mSnoozeHelper.writeXml(serializer);
+ serializer.endDocument();
+ serializer.flush();
+
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), "utf-8");
+ mSnoozeHelper.readXml(parser, 4);
+
+ mSnoozeHelper.scheduleRepostsForPersistedNotifications(5);
+
+ // We trim the key in persistent storage.
+ verify(mAm, times(2)).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture());
+ assertEquals(1000, captor.getValue().getIntent().getStringExtra(EXTRA_KEY).length());
+ }
+
+ @Test
public void testSnoozeForTime() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
mSnoozeHelper.snooze(r, 1000);