summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/ImeInsetsSourceConsumer.java7
-rw-r--r--core/java/android/view/InsetsController.java5
-rw-r--r--core/java/android/view/InsetsResizeAnimationRunner.java3
-rw-r--r--core/java/android/view/ViewRootImpl.java145
-rw-r--r--core/java/android/view/WindowManagerImpl.java35
-rw-r--r--core/jni/android_graphics_BLASTBufferQueue.cpp8
-rw-r--r--core/jni/android_os_Debug.cpp22
-rw-r--r--data/etc/services.core.protolog.json24
-rw-r--r--graphics/java/android/graphics/BLASTBufferQueue.java4
-rw-r--r--graphics/java/android/graphics/HardwareRenderer.java33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java49
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java17
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java6
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRenderer.cpp54
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp12
-rw-r--r--libs/hwui/renderthread/CanvasContext.h6
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp18
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.h9
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp6
-rw-r--r--libs/hwui/renderthread/RenderProxy.h3
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt173
-rw-r--r--packages/SystemUI/res/layout/internet_connectivity_dialog.xml6
-rw-r--r--packages/SystemUI/res/layout/internet_list_item.xml4
-rw-r--r--packages/SystemUI/res/layout/keyguard_bottom_area.xml4
-rw-r--r--packages/SystemUI/res/layout/screen_record_dialog.xml35
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml2
-rw-r--r--packages/SystemUI/res/values/styles.xml8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java10
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java124
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java16
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconView.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java248
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java272
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java123
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java90
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java61
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFadeAware.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java104
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java105
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java182
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java73
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java64
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java187
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java31
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java3
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java12
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java8
-rw-r--r--services/core/java/com/android/server/location/injector/LocationAttributionHelper.java99
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java4
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java3
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java71
-rw-r--r--services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java9
-rw-r--r--services/core/java/com/android/server/wm/Task.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java14
-rw-r--r--services/core/java/com/android/server/wm/WindowContextListenerController.java43
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java42
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java134
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java42
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java54
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java23
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java85
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java7
121 files changed, 2630 insertions, 1175 deletions
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 02b2c5d5db84..d609fb8eb234 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -124,7 +124,12 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
public void setControl(@Nullable InsetsSourceControl control, int[] showTypes,
int[] hideTypes) {
super.setControl(control, showTypes, hideTypes);
- if (control == null && !isRequestedVisibleAwaitingControl()) {
+ // TODO(b/204524304): clean-up how to deal with the timing issues of hiding IME:
+ // 1) Already requested show IME, in the meantime of WM callback the control but got null
+ // control when relayout comes first
+ // 2) Make sure no regression on some implicit request IME visibility calls (e.g.
+ // toggleSoftInput)
+ if (control == null && !mIsRequestedVisibleAwaitingControl) {
hide();
removeSurface();
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index adda721e00a4..9bf71ec80998 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1266,6 +1266,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
cancelAnimation(runner, false /* invokeCallback */);
if (DEBUG) Log.d(TAG, "notifyFinished. shown: " + shown);
+ if (runner.getAnimationType() == ANIMATION_TYPE_RESIZE) {
+ // The resize animation doesn't show or hide the insets. We shouldn't change the
+ // requested visibility.
+ return;
+ }
if (shown) {
showDirectly(runner.getTypes(), true /* fromIme */);
} else {
diff --git a/core/java/android/view/InsetsResizeAnimationRunner.java b/core/java/android/view/InsetsResizeAnimationRunner.java
index e1352dd8dd4f..edcfc95fe4e4 100644
--- a/core/java/android/view/InsetsResizeAnimationRunner.java
+++ b/core/java/android/view/InsetsResizeAnimationRunner.java
@@ -131,6 +131,9 @@ public class InsetsResizeAnimationRunner implements InsetsAnimationControlRunner
@Override
public boolean applyChangeInsets(InsetsState outState) {
+ if (mCancelled) {
+ return false;
+ }
final float fraction = mAnimation.getInterpolatedFraction();
for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
final InsetsSource fromSource = mFromState.peekSource(type);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index cdfd7bea1a82..d053def6abe2 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -768,6 +768,12 @@ public final class ViewRootImpl implements ViewParent,
private boolean mWaitForBlastSyncComplete = false;
/**
+ * Keeps track of the last frame number that was attempted to draw. Should only be accessed on
+ * the RenderThread.
+ */
+ private long mRtLastAttemptedDrawFrameNum = 0;
+
+ /**
* Keeps track of whether a traverse was triggered while the UI thread was paused. This can
* occur when the client is waiting on another process to submit the transaction that
* contains the buffer. The UI thread needs to wait on the callback before it can submit
@@ -4051,73 +4057,112 @@ public final class ViewRootImpl implements ViewParent,
}
/**
- * The callback will run on the render thread.
+ * Only call this on the UI Thread.
*/
- private HardwareRenderer.FrameCompleteCallback createFrameCompleteCallback(Handler handler,
- boolean reportNextDraw, ArrayList<Runnable> commitCallbacks) {
- final Consumer<SurfaceControl.Transaction> blastSyncConsumer = mBLASTDrawConsumer;
- mBLASTDrawConsumer = null;
- return frameNr -> {
+ void clearBlastSync() {
+ mNextDrawUseBlastSync = false;
+ mWaitForBlastSyncComplete = false;
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Scheduling a traversal=" + mRequestedTraverseWhilePaused
+ + " due to a previous skipped traversal.");
+ }
+ if (mRequestedTraverseWhilePaused) {
+ mRequestedTraverseWhilePaused = false;
+ scheduleTraversals();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isHardwareEnabled() {
+ return mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled();
+ }
+
+ private boolean addFrameCompleteCallbackIfNeeded(boolean reportNextDraw) {
+ if (!isHardwareEnabled()) {
+ return false;
+ }
+
+ if (!mNextDrawUseBlastSync && !reportNextDraw) {
+ return false;
+ }
+
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Creating frameCompleteCallback");
+ }
+
+ mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(() -> {
+ long frameNr = mBlastBufferQueue.getLastAcquiredFrameNum();
if (DEBUG_BLAST) {
- Log.d(mTag, "Received frameCompleteCallback frameNum=" + frameNr);
+ Log.d(mTag, "Received frameCompleteCallback "
+ + " lastAcquiredFrameNum=" + frameNr
+ + " lastAttemptedDrawFrameNum=" + mRtLastAttemptedDrawFrameNum);
}
- handler.postAtFrontOfQueue(() -> {
+ boolean frameWasNotDrawn = frameNr != mRtLastAttemptedDrawFrameNum;
+ // If frame wasn't drawn, clear out the next transaction so it doesn't affect the next
+ // draw attempt. The next transaction and transaction complete callback were only set
+ // for the current draw attempt.
+ if (frameWasNotDrawn) {
+ mBlastBufferQueue.setNextTransaction(null);
+ mBlastBufferQueue.setTransactionCompleteCallback(mRtLastAttemptedDrawFrameNum,
+ null);
+ }
+
+ mHandler.postAtFrontOfQueue(() -> {
if (mNextDrawUseBlastSync) {
// We don't need to synchronize mRtBLASTSyncTransaction here since we're
// guaranteed that this is called after onFrameDraw and mNextDrawUseBlastSync
// is only true when the UI thread is paused. Therefore, no one should be
// modifying this object until the next vsync.
mSurfaceChangedTransaction.merge(mRtBLASTSyncTransaction);
- if (blastSyncConsumer != null) {
- blastSyncConsumer.accept(mSurfaceChangedTransaction);
+ if (mBLASTDrawConsumer != null) {
+ mBLASTDrawConsumer.accept(mSurfaceChangedTransaction);
}
+ mBLASTDrawConsumer = null;
}
if (reportNextDraw) {
- // TODO: Use the frame number
pendingDrawFinished();
}
- if (commitCallbacks != null) {
- for (int i = 0; i < commitCallbacks.size(); i++) {
- commitCallbacks.get(i).run();
- }
+
+ if (frameWasNotDrawn) {
+ clearBlastSync();
}
});
- };
- }
-
- /**
- * @hide
- */
- public boolean isHardwareEnabled() {
- return mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled();
+ });
+ return true;
}
- private boolean addFrameCompleteCallbackIfNeeded() {
+ private void addFrameCommitCallbackIfNeeded() {
if (!isHardwareEnabled()) {
- return false;
+ return;
}
ArrayList<Runnable> commitCallbacks = mAttachInfo.mTreeObserver
.captureFrameCommitCallbacks();
- final boolean needFrameCompleteCallback =
- mNextDrawUseBlastSync || mReportNextDraw
- || (commitCallbacks != null && commitCallbacks.size() > 0);
- if (needFrameCompleteCallback) {
- if (DEBUG_BLAST) {
- Log.d(mTag, "Creating frameCompleteCallback"
- + " mNextDrawUseBlastSync=" + mNextDrawUseBlastSync
- + " mReportNextDraw=" + mReportNextDraw
- + " commitCallbacks size="
- + (commitCallbacks == null ? 0 : commitCallbacks.size()));
- }
- mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(
- createFrameCompleteCallback(mAttachInfo.mHandler, mReportNextDraw,
- commitCallbacks));
- return true;
+ final boolean needFrameCommitCallback =
+ (commitCallbacks != null && commitCallbacks.size() > 0);
+ if (!needFrameCommitCallback) {
+ return;
}
- return false;
+
+ if (DEBUG_DRAW) {
+ Log.d(mTag, "Creating frameCommitCallback"
+ + " commitCallbacks size=" + commitCallbacks.size());
+ }
+ mAttachInfo.mThreadedRenderer.setFrameCommitCallback(didProduceBuffer -> {
+ if (DEBUG_DRAW) {
+ Log.d(mTag, "Received frameCommitCallback didProduceBuffer=" + didProduceBuffer);
+ }
+
+ mHandler.postAtFrontOfQueue(() -> {
+ for (int i = 0; i < commitCallbacks.size(); i++) {
+ commitCallbacks.get(i).run();
+ }
+ });
+ });
}
private void addFrameCallbackIfNeeded() {
@@ -4147,6 +4192,8 @@ public final class ViewRootImpl implements ViewParent,
+ " Creating transactionCompleteCallback=" + nextDrawUseBlastSync);
}
+ mRtLastAttemptedDrawFrameNum = frame;
+
if (needsCallbackForBlur) {
mBlurRegionAggregator
.dispatchBlurTransactionIfNeeded(frame, blurRegionsForFrame, hasBlurUpdates);
@@ -4169,18 +4216,7 @@ public final class ViewRootImpl implements ViewParent,
if (DEBUG_BLAST) {
Log.d(mTag, "Received transactionCompleteCallback frameNum=" + frame);
}
- mHandler.postAtFrontOfQueue(() -> {
- mNextDrawUseBlastSync = false;
- mWaitForBlastSyncComplete = false;
- if (DEBUG_BLAST) {
- Log.d(mTag, "Scheduling a traversal=" + mRequestedTraverseWhilePaused
- + " due to a previous skipped traversal.");
- }
- if (mRequestedTraverseWhilePaused) {
- mRequestedTraverseWhilePaused = false;
- scheduleTraversals();
- }
- });
+ mHandler.postAtFrontOfQueue(this::clearBlastSync);
});
}
};
@@ -4201,8 +4237,9 @@ public final class ViewRootImpl implements ViewParent,
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
- boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded();
addFrameCallbackIfNeeded();
+ addFrameCommitCallbackIfNeeded();
+ boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded(mReportNextDraw);
try {
boolean canUseAsync = draw(fullRedrawNeeded);
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 1c915cb016d4..7631269d9c1c 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -352,30 +352,33 @@ public final class WindowManagerImpl implements WindowManager {
throw e.rethrowFromSystemServer();
}
- Set<WindowMetrics> maxMetrics = new HashSet<>();
- WindowInsets windowInsets;
+ int size = possibleDisplayInfos.size();
DisplayInfo currentDisplayInfo;
- final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
- for (int i = 0; i < possibleDisplayInfos.size(); i++) {
- currentDisplayInfo = possibleDisplayInfos.get(i);
+ WindowInsets windowInsets = null;
+ if (size > 0) {
+ currentDisplayInfo = possibleDisplayInfos.get(0);
- // Calculate max bounds for this rotation and state.
- Rect maxBounds = new Rect(0, 0, currentDisplayInfo.logicalWidth,
- currentDisplayInfo.logicalHeight);
-
- // Calculate insets for the rotated max bounds.
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
final boolean isScreenRound = (currentDisplayInfo.flags & Display.FLAG_ROUND) != 0;
- // Initialize insets based upon display rotation. Note any window-provided insets
- // will not be set.
+ // TODO(181127261) not computing insets correctly - need to have underlying
+ // frame reflect the faked orientation.
windowInsets = getWindowInsetsFromServerForDisplay(
currentDisplayInfo.displayId, params,
new Rect(0, 0, currentDisplayInfo.getNaturalWidth(),
currentDisplayInfo.getNaturalHeight()), isScreenRound,
WINDOWING_MODE_FULLSCREEN);
- // Set the hardware-provided insets.
- windowInsets = new WindowInsets.Builder(windowInsets).setRoundedCorners(
- currentDisplayInfo.roundedCorners)
- .setDisplayCutout(currentDisplayInfo.displayCutout).build();
+ }
+
+ Set<WindowMetrics> maxMetrics = new HashSet<>();
+ for (int i = 0; i < size; i++) {
+ currentDisplayInfo = possibleDisplayInfos.get(i);
+
+ // Calculate max bounds for this rotation and state.
+ Rect maxBounds = new Rect(0, 0, currentDisplayInfo.logicalWidth,
+ currentDisplayInfo.logicalHeight);
+
+ // Calculate insets for the rotated max bounds.
+ // TODO(181127261) calculate insets for each display rotation and state.
maxMetrics.add(new WindowMetrics(maxBounds, windowInsets));
}
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index c1587eb2c4f4..3b7328e72913 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -134,6 +134,11 @@ static void nativeSetTransactionCompleteCallback(JNIEnv* env, jclass clazz, jlon
}
}
+static jlong nativeGetLastAcquiredFrameNum(JNIEnv* env, jclass clazz, jlong ptr) {
+ sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+ return queue->getLastAcquiredFrameNum();
+}
+
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
// clang-format off
@@ -145,7 +150,8 @@ static const JNINativeMethod gMethods[] = {
{"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
{"nativeSetTransactionCompleteCallback",
"(JJLandroid/graphics/BLASTBufferQueue$TransactionCompleteCallback;)V",
- (void*)nativeSetTransactionCompleteCallback}
+ (void*)nativeSetTransactionCompleteCallback},
+ {"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
// clang-format on
};
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 4c2b114c724a..5e0d9b32380c 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -34,6 +34,7 @@
#include <vector>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <bionic/malloc.h>
#include <debuggerd/client.h>
#include <log/log.h>
@@ -859,7 +860,22 @@ static jlong android_os_Debug_getDmabufHeapPoolsSizeKb(JNIEnv* env, jobject claz
return poolsSizeKb;
}
+static bool halSupportsGpuPrivateMemory() {
+ int productApiLevel =
+ android::base::GetIntProperty("ro.product.first_api_level",
+ android::base::GetIntProperty("ro.build.version.sdk",
+ __ANDROID_API_FUTURE__));
+ int boardApiLevel =
+ android::base::GetIntProperty("ro.board.api_level",
+ android::base::GetIntProperty("ro.board.first_api_level",
+ __ANDROID_API_FUTURE__));
+
+ return std::min(productApiLevel, boardApiLevel) >= __ANDROID_API_S__;
+}
+
static jlong android_os_Debug_getGpuPrivateMemoryKb(JNIEnv* env, jobject clazz) {
+ static bool gpuPrivateMemorySupported = halSupportsGpuPrivateMemory();
+
struct memtrack_proc* p = memtrack_proc_new();
if (p == nullptr) {
LOG(ERROR) << "getGpuPrivateMemoryKb: Failed to create memtrack_proc";
@@ -876,6 +892,12 @@ static jlong android_os_Debug_getGpuPrivateMemoryKb(JNIEnv* env, jobject clazz)
ssize_t gpuPrivateMem = memtrack_proc_gl_pss(p);
memtrack_proc_destroy(p);
+
+ // Old HAL implementations may return 0 for GPU private memory if not supported
+ if (gpuPrivateMem == 0 && !gpuPrivateMemorySupported) {
+ return -1;
+ }
+
return gpuPrivateMem / 1024;
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 8ccf02caf8c0..6bfbd8d55ab0 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -889,6 +889,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/NonAppWindowAnimationAdapter.java"
},
+ "-1145384901": {
+ "message": "shouldWaitAnimatingExit: isTransition: %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-1142279614": {
"message": "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s",
"level": "VERBOSE",
@@ -1273,6 +1279,12 @@
"group": "WM_DEBUG_SCREEN_ON",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-743856570": {
+ "message": "shouldWaitAnimatingExit: isAnimating: %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-743431900": {
"message": "Configuration no differences in %s",
"level": "VERBOSE",
@@ -1777,6 +1789,12 @@
"group": "WM_DEBUG_SYNC_ENGINE",
"at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
},
+ "-208825711": {
+ "message": "shouldWaitAnimatingExit: isWallpaperTarget: %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-198463978": {
"message": "updateRotationUnchecked: alwaysSendConfiguration=%b forceRelayout=%b",
"level": "VERBOSE",
@@ -2989,6 +3007,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
},
+ "1087494661": {
+ "message": "Clear window stuck on animatingExit status: %s",
+ "level": "WARN",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"1088929964": {
"message": "onLockTaskPackagesUpdated: starting new locktask task=%s",
"level": "DEBUG",
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index fca4de91a352..2393eaf32e9a 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -38,6 +38,7 @@ public final class BLASTBufferQueue {
long frameNumber);
private static native void nativeSetTransactionCompleteCallback(long ptr, long frameNumber,
TransactionCompleteCallback callback);
+ private static native long nativeGetLastAcquiredFrameNum(long ptr);
/**
* Callback sent to {@link #setTransactionCompleteCallback(long, TransactionCompleteCallback)}
@@ -140,4 +141,7 @@ public final class BLASTBufferQueue {
nativeMergeWithNextTransaction(mNativeObject, nativeTransaction, frameNumber);
}
+ public long getLastAcquiredFrameNum() {
+ return nativeGetLastAcquiredFrameNum(mNativeObject);
+ }
}
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index c3b1cd74d29b..14ad74e12618 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -388,7 +388,8 @@ public class HardwareRenderer {
*/
public @NonNull FrameRenderRequest setFrameCommitCallback(@NonNull Executor executor,
@NonNull Runnable frameCommitCallback) {
- setFrameCompleteCallback(frameNr -> executor.execute(frameCommitCallback));
+ nSetFrameCommitCallback(mNativeProxy,
+ didProduceBuffer -> executor.execute(frameCommitCallback));
return this;
}
@@ -609,6 +610,11 @@ public class HardwareRenderer {
}
/** @hide */
+ public void setFrameCommitCallback(FrameCommitCallback callback) {
+ nSetFrameCommitCallback(mNativeProxy, callback);
+ }
+
+ /** @hide */
public void setFrameCompleteCallback(FrameCompleteCallback callback) {
nSetFrameCompleteCallback(mNativeProxy, callback);
}
@@ -904,13 +910,27 @@ public class HardwareRenderer {
*
* @hide
*/
- public interface FrameCompleteCallback {
+ public interface FrameCommitCallback {
/**
- * Invoked after a frame draw
+ * Invoked after a new frame was drawn
*
- * @param frameNr The id of the frame that was drawn.
+ * @param didProduceBuffer The draw successfully produced a new buffer.
+ */
+ void onFrameCommit(boolean didProduceBuffer);
+ }
+
+ /**
+ * Interface used to be notified when RenderThread has finished an attempt to draw. This doesn't
+ * mean a new frame has drawn, specifically if there's nothing new to draw, but only that
+ * RenderThread had a chance to draw a frame.
+ *
+ * @hide
+ */
+ public interface FrameCompleteCallback {
+ /**
+ * Invoked after a frame draw was attempted.
*/
- void onFrameComplete(long frameNr);
+ void onFrameComplete();
}
/**
@@ -1362,6 +1382,9 @@ public class HardwareRenderer {
private static native void nSetFrameCallback(long nativeProxy, FrameDrawingCallback callback);
+ private static native void nSetFrameCommitCallback(long nativeProxy,
+ FrameCommitCallback callback);
+
private static native void nSetFrameCompleteCallback(long nativeProxy,
FrameCompleteCallback callback);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 75bc46125324..7f370367187c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -29,6 +29,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.TaskInfo;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.content.LocusId;
import android.content.pm.ActivityInfo;
@@ -122,6 +123,16 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
}
/**
+ * Callbacks for events in which the focus has changed.
+ */
+ public interface FocusListener {
+ /**
+ * Notifies when the task which is focused has changed.
+ */
+ void onFocusTaskChanged(RunningTaskInfo taskInfo);
+ }
+
+ /**
* Keys map from either a task id or {@link TaskListenerType}.
* @see #addListenerForTaskId
* @see #addListenerForType
@@ -142,6 +153,8 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
/** @see #addLocusIdListener */
private final ArraySet<LocusIdListener> mLocusIdListeners = new ArraySet<>();
+ private final ArraySet<FocusListener> mFocusListeners = new ArraySet<>();
+
private final Object mLock = new Object();
private StartingWindowController mStartingWindow;
@@ -155,6 +168,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
@Nullable
private final Optional<RecentTasksController> mRecentTasks;
+ @Nullable
+ private RunningTaskInfo mLastFocusedTaskInfo;
+
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */,
Optional.empty() /* recentTasksController */);
@@ -331,6 +347,27 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
}
}
+ /**
+ * Adds a listener to be notified for task focus changes.
+ */
+ public void addFocusListener(FocusListener listener) {
+ synchronized (mLock) {
+ mFocusListeners.add(listener);
+ if (mLastFocusedTaskInfo != null) {
+ listener.onFocusTaskChanged(mLastFocusedTaskInfo);
+ }
+ }
+ }
+
+ /**
+ * Removes listener.
+ */
+ public void removeLocusIdListener(FocusListener listener) {
+ synchronized (mLock) {
+ mFocusListeners.remove(listener);
+ }
+ }
+
@Override
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
if (mStartingWindow != null) {
@@ -422,6 +459,18 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
mRecentTasks.ifPresent(recentTasks ->
recentTasks.onTaskWindowingModeChanged(taskInfo));
}
+ // TODO (b/207687679): Remove check for HOME once bug is fixed
+ final boolean isFocusedOrHome = taskInfo.isFocused
+ || (taskInfo.topActivityType == WindowConfiguration.ACTIVITY_TYPE_HOME
+ && taskInfo.isVisible);
+ final boolean focusTaskChanged = (mLastFocusedTaskInfo == null
+ || mLastFocusedTaskInfo.taskId != taskInfo.taskId) && isFocusedOrHome;
+ if (focusTaskChanged) {
+ for (int i = 0; i < mFocusListeners.size(); i++) {
+ mFocusListeners.valueAt(i).onFocusTaskChanged(taskInfo);
+ }
+ mLastFocusedTaskInfo = taskInfo;
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 300319a2f78f..b40021ec82a7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -32,6 +32,7 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
+import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -529,9 +530,10 @@ public class BubbleStackView extends FrameLayout
// Otherwise, we either tapped the stack (which means we're collapsed
// and should expand) or the currently selected bubble (we're expanded
// and should collapse).
- if (!maybeShowStackEdu()) {
+ if (!maybeShowStackEdu() && !mShowedUserEducationInTouchListenerActive) {
mBubbleData.setExpanded(!mBubbleData.isExpanded());
}
+ mShowedUserEducationInTouchListenerActive = false;
}
}
};
@@ -549,6 +551,14 @@ public class BubbleStackView extends FrameLayout
return true;
}
+ mShowedUserEducationInTouchListenerActive = false;
+ if (maybeShowStackEdu()) {
+ mShowedUserEducationInTouchListenerActive = true;
+ return true;
+ } else if (isStackEduShowing()) {
+ mStackEduView.hide(false /* fromExpansion */);
+ }
+
// If the manage menu is visible, just hide it.
if (mShowingManage) {
showManageMenu(false /* show */);
@@ -607,7 +617,8 @@ public class BubbleStackView extends FrameLayout
// If we're expanding or collapsing, ignore all touch events.
if (mIsExpansionAnimating
// Also ignore events if we shouldn't be draggable.
- || (mPositioner.showingInTaskbar() && !mIsExpanded)) {
+ || (mPositioner.showingInTaskbar() && !mIsExpanded)
+ || mShowedUserEducationInTouchListenerActive) {
return;
}
@@ -628,7 +639,7 @@ public class BubbleStackView extends FrameLayout
mExpandedAnimationController.dragBubbleOut(
v, viewInitialX + dx, viewInitialY + dy);
} else {
- if (mStackEduView != null) {
+ if (isStackEduShowing()) {
mStackEduView.hide(false /* fromExpansion */);
}
mStackAnimationController.moveStackFromTouch(
@@ -646,6 +657,10 @@ public class BubbleStackView extends FrameLayout
|| (mPositioner.showingInTaskbar() && !mIsExpanded)) {
return;
}
+ if (mShowedUserEducationInTouchListenerActive) {
+ mShowedUserEducationInTouchListenerActive = false;
+ return;
+ }
// First, see if the magnetized object consumes the event - if so, the bubble was
// released in the target or flung out of it, and we should ignore the event.
@@ -738,6 +753,7 @@ public class BubbleStackView extends FrameLayout
private ImageView mManageSettingsIcon;
private TextView mManageSettingsText;
private boolean mShowingManage = false;
+ private boolean mShowedUserEducationInTouchListenerActive = false;
private PhysicsAnimator.SpringConfig mManageSpringConfig = new PhysicsAnimator.SpringConfig(
SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
private BubblePositioner mPositioner;
@@ -929,10 +945,12 @@ public class BubbleStackView extends FrameLayout
showManageMenu(false /* show */);
} else if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
mManageEduView.hide();
- } else if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
+ } else if (isStackEduShowing()) {
mStackEduView.hide(false /* isExpanding */);
} else if (mBubbleData.isExpanded()) {
mBubbleData.setExpanded(false);
+ } else {
+ maybeShowStackEdu();
}
});
@@ -1116,6 +1134,9 @@ public class BubbleStackView extends FrameLayout
* Whether the educational view should show for the expanded view "manage" menu.
*/
private boolean shouldShowManageEdu() {
+ if (ActivityManager.isRunningInTestHarness()) {
+ return false;
+ }
final boolean seen = getPrefBoolean(ManageEducationViewKt.PREF_MANAGED_EDUCATION);
final boolean shouldShow = (!seen || BubbleDebugConfig.forceShowUserEducation(mContext))
&& mExpandedBubble != null;
@@ -1140,6 +1161,9 @@ public class BubbleStackView extends FrameLayout
* Whether education view should show for the collapsed stack.
*/
private boolean shouldShowStackEdu() {
+ if (ActivityManager.isRunningInTestHarness()) {
+ return false;
+ }
final boolean seen = getPrefBoolean(StackEducationViewKt.PREF_STACK_EDUCATION);
final boolean shouldShow = !seen || BubbleDebugConfig.forceShowUserEducation(mContext);
if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
@@ -1157,7 +1181,7 @@ public class BubbleStackView extends FrameLayout
* @return true if education view for collapsed stack should show and was not showing before.
*/
private boolean maybeShowStackEdu() {
- if (!shouldShowStackEdu()) {
+ if (!shouldShowStackEdu() || isExpanded()) {
return false;
}
if (mStackEduView == null) {
@@ -1168,9 +1192,13 @@ public class BubbleStackView extends FrameLayout
return mStackEduView.show(mPositioner.getDefaultStartPosition());
}
+ private boolean isStackEduShowing() {
+ return mStackEduView != null && mStackEduView.getVisibility() == VISIBLE;
+ }
+
// Recreates & shows the education views. Call when a theme/config change happens.
private void updateUserEdu() {
- if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
+ if (isStackEduShowing()) {
removeView(mStackEduView);
mStackEduView = new StackEducationView(mContext, mPositioner, mBubbleController);
addView(mStackEduView);
@@ -1852,7 +1880,7 @@ public class BubbleStackView extends FrameLayout
cancelDelayedExpandCollapseSwitchAnimations();
final boolean showVertically = mPositioner.showBubblesVertically();
mIsExpanded = true;
- if (mStackEduView != null) {
+ if (isStackEduShowing()) {
mStackEduView.hide(true /* fromExpansion */);
}
beforeExpandedViewAnimation();
@@ -2390,7 +2418,7 @@ public class BubbleStackView extends FrameLayout
if (flyoutMessage == null
|| flyoutMessage.message == null
|| !bubble.showFlyout()
- || (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE)
+ || isStackEduShowing()
|| isExpanded()
|| mIsExpansionAnimating
|| mIsGestureInProgress
@@ -2512,7 +2540,7 @@ public class BubbleStackView extends FrameLayout
* them.
*/
public void getTouchableRegion(Rect outRect) {
- if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
+ if (isStackEduShowing()) {
// When user education shows then capture all touches
outRect.set(0, 0, getWidth(), getHeight());
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
index f6a90b7a76cd..3846de73842d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
@@ -125,6 +125,7 @@ class StackEducationView constructor(
* @return true if user education was shown, false otherwise.
*/
fun show(stackPosition: PointF): Boolean {
+ isHiding = false
if (visibility == VISIBLE) return false
controller.updateWindowFlagsForBackpress(true /* interceptBack */)
@@ -164,6 +165,7 @@ class StackEducationView constructor(
*/
fun hide(isExpanding: Boolean) {
if (visibility != VISIBLE || isHiding) return
+ isHiding = true
controller.updateWindowFlagsForBackpress(false /* interceptBack */)
animate()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index d07fff3cce79..3579bf441390 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -321,6 +321,16 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mSplitLayoutHandler.onLayoutSizeChanged(this);
}
+ /** Sets divide position base on the ratio within root bounds. */
+ public void setDivideRatio(float ratio) {
+ final int position = isLandscape()
+ ? mRootBounds.left + (int) (mRootBounds.width() * ratio)
+ : mRootBounds.top + (int) (mRootBounds.height() * ratio);
+ DividerSnapAlgorithm.SnapTarget snapTarget =
+ mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(position);
+ setDividePosition(snapTarget.position);
+ }
+
/** Resets divider position. */
public void resetDividerPosition() {
mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position;
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 46c7b508d6e8..f562fd9cf1af 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
@@ -241,10 +241,11 @@ public class WMShellModule {
static PhonePipMenuController providesPipPhoneMenuController(Context context,
PipBoundsState pipBoundsState, PipMediaController pipMediaController,
SystemWindows systemWindows,
+ Optional<SplitScreenController> splitScreenOptional,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler) {
return new PhonePipMenuController(context, pipBoundsState, pipMediaController,
- systemWindows, mainExecutor, mainHandler);
+ systemWindows, splitScreenOptional, mainExecutor, mainHandler);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
index 8d9ad4d1b96c..caa1f017082b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
@@ -90,6 +90,11 @@ public interface PipMenuController {
default void updateMenuBounds(Rect destinationBounds) {}
/**
+ * Update when the current focused task changes.
+ */
+ default void onFocusTaskChanged(RunningTaskInfo taskInfo) {}
+
+ /**
* Returns a default LayoutParams for the PIP Menu.
* @param width the PIP stack width.
* @param height the PIP stack height.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 90135f2a3c69..c2ebc3031b93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -98,7 +98,7 @@ import java.util.function.IntConsumer;
* see also {@link PipMotionHelper}.
*/
public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
- DisplayController.OnDisplaysChangedListener {
+ DisplayController.OnDisplaysChangedListener, ShellTaskOrganizer.FocusListener {
private static final String TAG = PipTaskOrganizer.class.getSimpleName();
private static final boolean DEBUG = false;
/**
@@ -286,6 +286,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mMainExecutor.execute(() -> {
mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_PIP);
});
+ mTaskOrganizer.addFocusListener(this);
mPipTransitionController.setPipOrganizer(this);
displayController.addDisplayWindowListener(this);
}
@@ -772,6 +773,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
@Override
+ public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ mPipMenuController.onFocusTaskChanged(taskInfo);
+ }
+
+ @Override
public boolean supportSizeCompatUI() {
// PIP doesn't support size compat.
return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 5687f4d62444..eb512afa644d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.pip.phone;
import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.RemoteAction;
import android.content.Context;
import android.content.pm.ParceledListSlice;
@@ -43,10 +44,12 @@ import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipMediaController.ActionListener;
import com.android.wm.shell.pip.PipMenuController;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
/**
* Manages the PiP menu view which can show menu options or a scrim.
@@ -114,6 +117,7 @@ public class PhonePipMenuController implements PipMenuController {
private final ArrayList<Listener> mListeners = new ArrayList<>();
private final SystemWindows mSystemWindows;
+ private final Optional<SplitScreenController> mSplitScreenController;
private ParceledListSlice<RemoteAction> mAppActions;
private ParceledListSlice<RemoteAction> mMediaActions;
private SyncRtSurfaceTransactionApplier mApplier;
@@ -145,6 +149,7 @@ public class PhonePipMenuController implements PipMenuController {
public PhonePipMenuController(Context context, PipBoundsState pipBoundsState,
PipMediaController mediaController, SystemWindows systemWindows,
+ Optional<SplitScreenController> splitScreenOptional,
ShellExecutor mainExecutor, Handler mainHandler) {
mContext = context;
mPipBoundsState = pipBoundsState;
@@ -152,6 +157,7 @@ public class PhonePipMenuController implements PipMenuController {
mSystemWindows = systemWindows;
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
+ mSplitScreenController = splitScreenOptional;
}
public boolean isMenuVisible() {
@@ -180,7 +186,8 @@ public class PhonePipMenuController implements PipMenuController {
if (mPipMenuView != null) {
detachPipMenuView();
}
- mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler);
+ mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler,
+ mSplitScreenController);
mSystemWindows.addView(mPipMenuView,
getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
0, SHELL_ROOT_LAYER_PIP);
@@ -209,6 +216,13 @@ public class PhonePipMenuController implements PipMenuController {
updateMenuLayout(destinationBounds);
}
+ @Override
+ public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ if (mPipMenuView != null) {
+ mPipMenuView.onFocusTaskChanged(taskInfo);
+ }
+ }
+
/**
* Tries to grab a surface control from {@link PipMenuView}. If this isn't available for some
* reason (ie. the window isn't ready yet, thus {@link android.view.ViewRootImpl} is
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index b209699c1a19..82e827398bb7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.pip.phone;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS;
@@ -32,8 +33,10 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
+import android.app.ActivityManager;
import android.app.PendingIntent.CanceledException;
import android.app.RemoteAction;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -61,11 +64,13 @@ import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
/**
* Translucent window that gets started on top of a task in PIP to allow the user to control it.
@@ -105,6 +110,7 @@ public class PipMenuView extends FrameLayout {
private boolean mAllowMenuTimeout = true;
private boolean mAllowTouches = true;
private int mDismissFadeOutDurationMs;
+ private boolean mFocusedTaskAllowSplitScreen;
private final List<RemoteAction> mActions = new ArrayList<>();
@@ -116,6 +122,7 @@ public class PipMenuView extends FrameLayout {
private AnimatorSet mMenuContainerAnimator;
private PhonePipMenuController mController;
+ private Optional<SplitScreenController> mSplitScreenControllerOptional;
private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
new ValueAnimator.AnimatorUpdateListener() {
@@ -144,12 +151,14 @@ public class PipMenuView extends FrameLayout {
protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
public PipMenuView(Context context, PhonePipMenuController controller,
- ShellExecutor mainExecutor, Handler mainHandler) {
+ ShellExecutor mainExecutor, Handler mainHandler,
+ Optional<SplitScreenController> splitScreenController) {
super(context, null, 0);
mContext = context;
mController = controller;
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
+ mSplitScreenControllerOptional = splitScreenController;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
inflate(context, R.layout.pip_menu, this);
@@ -255,6 +264,15 @@ public class PipMenuView extends FrameLayout {
return super.dispatchGenericMotionEvent(event);
}
+ public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ final boolean isSplitScreen = mSplitScreenControllerOptional.isPresent()
+ && mSplitScreenControllerOptional.get().isTaskInSplitScreen(taskInfo.taskId);
+ mFocusedTaskAllowSplitScreen = isSplitScreen
+ || (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+ && taskInfo.supportsSplitScreenMultiWindow
+ && taskInfo.topActivityType != WindowConfiguration.ACTIVITY_TYPE_HOME);
+ }
+
void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout,
boolean resizeMenuOnShow, boolean withDelay, boolean showResizeHandle) {
mAllowMenuTimeout = allowMenuTimeout;
@@ -278,7 +296,8 @@ public class PipMenuView extends FrameLayout {
ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
mDismissButton.getAlpha(), 1f);
ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
- mEnterSplitButton.getAlpha(), ENABLE_ENTER_SPLIT ? 1f : 0f);
+ mEnterSplitButton.getAlpha(),
+ ENABLE_ENTER_SPLIT && mFocusedTaskAllowSplitScreen ? 1f : 0f);
if (menuState == MENU_STATE_FULL) {
mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
enterSplitAnim);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 3d3a63057dde..4a990975be0d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -83,14 +83,15 @@ interface ISplitScreen {
* Starts tasks simultaneously in one transition.
*/
oneway void startTasks(int mainTaskId, in Bundle mainOptions, int sideTaskId,
- in Bundle sideOptions, int sidePosition, in RemoteTransition remoteTransition) = 10;
+ in Bundle sideOptions, int sidePosition, float splitRatio,
+ in RemoteTransition remoteTransition) = 10;
/**
* Version of startTasks using legacy transition system.
*/
oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
int sideTaskId, in Bundle sideOptions, int sidePosition,
- in RemoteAnimationAdapter adapter) = 11;
+ float splitRatio, in RemoteAnimationAdapter adapter) = 11;
/**
* Blocking call that notifies and gets additional split-screen targets when entering
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 6b42ed775fb7..262a9a1ba2be 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -183,6 +183,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
return mStageCoordinator.isSplitScreenVisible();
}
+ public boolean isTaskInSplitScreen(int taskId) {
+ return isSplitScreenVisible()
+ && mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED;
+ }
+
public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) {
return moveToStage(taskId, STAGE_TYPE_SIDE, sideStagePosition,
new WindowContainerTransaction());
@@ -605,21 +610,21 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
@Override
public void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
- RemoteAnimationAdapter adapter) {
+ float splitRatio, RemoteAnimationAdapter adapter) {
executeRemoteCallWithTaskPermission(mController, "startTasks",
(controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition,
- adapter));
+ splitRatio, adapter));
}
@Override
public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions,
- @SplitPosition int sidePosition,
+ @SplitPosition int sidePosition, float splitRatio,
@Nullable RemoteTransition remoteTransition) {
executeRemoteCallWithTaskPermission(mController, "startTasks",
(controller) -> controller.mStageCoordinator.startTasks(mainTaskId, mainOptions,
- sideTaskId, sideOptions, sidePosition, remoteTransition));
+ sideTaskId, sideOptions, sidePosition, splitRatio, remoteTransition));
}
@Override
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 d095ba9b0de8..a5579ae7dc25 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
@@ -274,6 +274,17 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return mSideStageListener.mVisible && mMainStageListener.mVisible;
}
+ @SplitScreen.StageType
+ int getStageOfTask(int taskId) {
+ if (mMainStage.containsTask(taskId)) {
+ return STAGE_TYPE_MAIN;
+ } else if (mSideStage.containsTask(taskId)) {
+ return STAGE_TYPE_SIDE;
+ }
+
+ return STAGE_TYPE_UNDEFINED;
+ }
+
boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitScreen.StageType int stageType,
@SplitPosition int stagePosition, WindowContainerTransaction wct) {
StageTaskListener targetStage;
@@ -322,7 +333,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
/** Starts 2 tasks in one transition. */
void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
- @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
+ @Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio,
@Nullable RemoteTransition remoteTransition) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mainOptions = mainOptions != null ? mainOptions : new Bundle();
@@ -334,6 +345,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
mSideStage.setBounds(getSideStageBounds(), wct);
+ mSplitLayout.setDivideRatio(splitRatio);
// Make sure the launch options will put tasks in the corresponding split roots
addActivityOptions(mainOptions, mMainStage);
addActivityOptions(sideOptions, mSideStage);
@@ -349,7 +361,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
/** Starts 2 tasks in one legacy transition. */
void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
- RemoteAnimationAdapter adapter) {
+ float splitRatio, RemoteAnimationAdapter adapter) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
// Need to add another wrapper here in shell so that we can inject the divider bar
// and also manage the process elevation via setRunningRemote
@@ -404,6 +416,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
sideOptions = sideOptions != null ? sideOptions : new Bundle();
setSideStagePosition(sidePosition, wct);
+ mSplitLayout.setDivideRatio(splitRatio);
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 73eebad040d8..453050fcfab4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -106,6 +106,12 @@ public class SplitLayoutTests extends ShellTestCase {
}
@Test
+ public void testSetDivideRatio() {
+ mSplitLayout.setDivideRatio(0.5f);
+ verify(mSplitLayoutHandler).onLayoutSizeChanged(any(SplitLayout.class));
+ }
+
+ @Test
public void testOnDoubleTappedDivider() {
mSplitLayout.onDoubleTappedDivider();
verify(mSplitLayoutHandler).onDoubleTappedDivider();
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 54367b8334cb..b5536ad4830d 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -73,6 +73,10 @@ struct {
} gFrameDrawingCallback;
struct {
+ jmethodID onFrameCommit;
+} gFrameCommitCallback;
+
+struct {
jmethodID onFrameComplete;
} gFrameCompleteCallback;
@@ -101,22 +105,21 @@ private:
JavaVM* mVm;
};
-class FrameCompleteWrapper : public LightRefBase<FrameCompleteWrapper> {
+class FrameCommitWrapper : public LightRefBase<FrameCommitWrapper> {
public:
- explicit FrameCompleteWrapper(JNIEnv* env, jobject jobject) {
+ explicit FrameCommitWrapper(JNIEnv* env, jobject jobject) {
env->GetJavaVM(&mVm);
mObject = env->NewGlobalRef(jobject);
LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref");
}
- ~FrameCompleteWrapper() {
- releaseObject();
- }
+ ~FrameCommitWrapper() { releaseObject(); }
- void onFrameComplete(int64_t frameNr) {
+ void onFrameCommit(bool didProduceBuffer) {
if (mObject) {
- ATRACE_FORMAT("frameComplete %" PRId64, frameNr);
- getenv(mVm)->CallVoidMethod(mObject, gFrameCompleteCallback.onFrameComplete, frameNr);
+ ATRACE_FORMAT("frameCommit success=%d", didProduceBuffer);
+ getenv(mVm)->CallVoidMethod(mObject, gFrameCommitCallback.onFrameCommit,
+ didProduceBuffer);
releaseObject();
}
}
@@ -637,15 +640,33 @@ static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env,
}
}
+static void android_view_ThreadedRenderer_setFrameCommitCallback(JNIEnv* env, jobject clazz,
+ jlong proxyPtr, jobject callback) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ if (!callback) {
+ proxy->setFrameCommitCallback(nullptr);
+ } else {
+ sp<FrameCommitWrapper> wrapper = new FrameCommitWrapper{env, callback};
+ proxy->setFrameCommitCallback(
+ [wrapper](bool didProduceBuffer) { wrapper->onFrameCommit(didProduceBuffer); });
+ }
+}
+
static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env,
jobject clazz, jlong proxyPtr, jobject callback) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
if (!callback) {
proxy->setFrameCompleteCallback(nullptr);
} else {
- sp<FrameCompleteWrapper> wrapper = new FrameCompleteWrapper{env, callback};
- proxy->setFrameCompleteCallback([wrapper](int64_t frameNr) {
- wrapper->onFrameComplete(frameNr);
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ JavaVM* vm = nullptr;
+ LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
+ auto globalCallbackRef =
+ std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback));
+ proxy->setFrameCompleteCallback([globalCallbackRef]() {
+ JNIEnv* env = getenv(globalCallbackRef->vm());
+ env->CallVoidMethod(globalCallbackRef->object(),
+ gFrameCompleteCallback.onFrameComplete);
});
}
}
@@ -929,6 +950,8 @@ static const JNINativeMethod gMethods[] = {
(void*)android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCallback},
{"nSetFrameCallback", "(JLandroid/graphics/HardwareRenderer$FrameDrawingCallback;)V",
(void*)android_view_ThreadedRenderer_setFrameCallback},
+ {"nSetFrameCommitCallback", "(JLandroid/graphics/HardwareRenderer$FrameCommitCallback;)V",
+ (void*)android_view_ThreadedRenderer_setFrameCommitCallback},
{"nSetFrameCompleteCallback",
"(JLandroid/graphics/HardwareRenderer$FrameCompleteCallback;)V",
(void*)android_view_ThreadedRenderer_setFrameCompleteCallback},
@@ -994,10 +1017,15 @@ int register_android_view_ThreadedRenderer(JNIEnv* env) {
gFrameDrawingCallback.onFrameDraw = GetMethodIDOrDie(env, frameCallbackClass,
"onFrameDraw", "(J)V");
+ jclass frameCommitClass =
+ FindClassOrDie(env, "android/graphics/HardwareRenderer$FrameCommitCallback");
+ gFrameCommitCallback.onFrameCommit =
+ GetMethodIDOrDie(env, frameCommitClass, "onFrameCommit", "(Z)V");
+
jclass frameCompleteClass = FindClassOrDie(env,
"android/graphics/HardwareRenderer$FrameCompleteCallback");
- gFrameCompleteCallback.onFrameComplete = GetMethodIDOrDie(env, frameCompleteClass,
- "onFrameComplete", "(J)V");
+ gFrameCompleteCallback.onFrameComplete =
+ GetMethodIDOrDie(env, frameCompleteClass, "onFrameComplete", "()V");
void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
fromSurface = (ANW_fromSurface)dlsym(handle_, "ANativeWindow_fromSurface");
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 2f3a509831d1..a066e6f7c693 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -491,10 +491,10 @@ nsecs_t CanvasContext::draw() {
// Notify the callbacks, even if there's nothing to draw so they aren't waiting
// indefinitely
waitOnFences();
- for (auto& func : mFrameCompleteCallbacks) {
- std::invoke(func, mFrameNumber);
+ for (auto& func : mFrameCommitCallbacks) {
+ std::invoke(func, false /* didProduceBuffer */);
}
- mFrameCompleteCallbacks.clear();
+ mFrameCommitCallbacks.clear();
return 0;
}
@@ -603,10 +603,10 @@ nsecs_t CanvasContext::draw() {
#endif
if (didSwap) {
- for (auto& func : mFrameCompleteCallbacks) {
- std::invoke(func, frameCompleteNr);
+ for (auto& func : mFrameCommitCallbacks) {
+ std::invoke(func, true /* didProduceBuffer */);
}
- mFrameCompleteCallbacks.clear();
+ mFrameCommitCallbacks.clear();
}
if (requireSwap) {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 6dbfcc349d50..9df429badd5e 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -187,8 +187,8 @@ public:
IRenderPipeline* getRenderPipeline() { return mRenderPipeline.get(); }
- void addFrameCompleteListener(std::function<void(int64_t)>&& func) {
- mFrameCompleteCallbacks.push_back(std::move(func));
+ void addFrameCommitListener(std::function<void(bool)>&& func) {
+ mFrameCommitCallbacks.push_back(std::move(func));
}
void setPictureCapturedCallback(const std::function<void(sk_sp<SkPicture>&&)>& callback) {
@@ -320,7 +320,7 @@ private:
std::vector<std::future<void>> mFrameFences;
std::unique_ptr<IRenderPipeline> mRenderPipeline;
- std::vector<std::function<void(int64_t)>> mFrameCompleteCallbacks;
+ std::vector<std::function<void(bool)>> mFrameCommitCallbacks;
// If set to true, we expect that callbacks into onSurfaceStatsAvailable
bool mExpectSurfaceStats = false;
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index e7081df2b558..94aedd0f43be 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -150,16 +150,18 @@ void DrawFrameTask::run() {
canUnblockUiThread = syncFrameState(info);
canDrawThisFrame = info.out.canDrawThisFrame;
- if (mFrameCompleteCallback) {
- mContext->addFrameCompleteListener(std::move(mFrameCompleteCallback));
- mFrameCompleteCallback = nullptr;
+ if (mFrameCommitCallback) {
+ mContext->addFrameCommitListener(std::move(mFrameCommitCallback));
+ mFrameCommitCallback = nullptr;
}
}
// Grab a copy of everything we need
CanvasContext* context = mContext;
- std::function<void(int64_t)> callback = std::move(mFrameCallback);
+ std::function<void(int64_t)> frameCallback = std::move(mFrameCallback);
+ std::function<void()> frameCompleteCallback = std::move(mFrameCompleteCallback);
mFrameCallback = nullptr;
+ mFrameCompleteCallback = nullptr;
int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
int64_t frameDeadline = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameDeadline)];
int64_t frameStartTime = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameStartTime)];
@@ -170,9 +172,9 @@ void DrawFrameTask::run() {
}
// Even if we aren't drawing this vsync pulse the next frame number will still be accurate
- if (CC_UNLIKELY(callback)) {
+ if (CC_UNLIKELY(frameCallback)) {
context->enqueueFrameWork(
- [callback, frameNr = context->getFrameNumber()]() { callback(frameNr); });
+ [frameCallback, frameNr = context->getFrameNumber()]() { frameCallback(frameNr); });
}
nsecs_t dequeueBufferDuration = 0;
@@ -189,6 +191,10 @@ void DrawFrameTask::run() {
context->waitOnFences();
}
+ if (CC_UNLIKELY(frameCompleteCallback)) {
+ std::invoke(frameCompleteCallback);
+ }
+
if (!canUnblockUiThread) {
unblockUiThread();
}
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 6a61a2bb645f..e3ea802b07b9 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -81,7 +81,11 @@ public:
mFrameCallback = std::move(callback);
}
- void setFrameCompleteCallback(std::function<void(int64_t)>&& callback) {
+ void setFrameCommitCallback(std::function<void(bool)>&& callback) {
+ mFrameCommitCallback = std::move(callback);
+ }
+
+ void setFrameCompleteCallback(std::function<void()>&& callback) {
mFrameCompleteCallback = std::move(callback);
}
@@ -123,7 +127,8 @@ private:
int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE];
std::function<void(int64_t)> mFrameCallback;
- std::function<void(int64_t)> mFrameCompleteCallback;
+ std::function<void(bool)> mFrameCommitCallback;
+ std::function<void()> mFrameCompleteCallback;
nsecs_t mLastDequeueBufferDuration = 0;
nsecs_t mLastTargetWorkDuration = 0;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index c485ce2781e5..72d4ac5081e6 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -326,7 +326,11 @@ void RenderProxy::setFrameCallback(std::function<void(int64_t)>&& callback) {
mDrawFrameTask.setFrameCallback(std::move(callback));
}
-void RenderProxy::setFrameCompleteCallback(std::function<void(int64_t)>&& callback) {
+void RenderProxy::setFrameCommitCallback(std::function<void(bool)>&& callback) {
+ mDrawFrameTask.setFrameCommitCallback(std::move(callback));
+}
+
+void RenderProxy::setFrameCompleteCallback(std::function<void()>&& callback) {
mDrawFrameTask.setFrameCompleteCallback(std::move(callback));
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 2b5405c82563..6417b38df064 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -124,7 +124,8 @@ public:
const std::function<bool(int64_t, int64_t, int64_t)>& callback);
void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback);
void setFrameCallback(std::function<void(int64_t)>&& callback);
- void setFrameCompleteCallback(std::function<void(int64_t)>&& callback);
+ void setFrameCommitCallback(std::function<void(bool)>&& callback);
+ void setFrameCompleteCallback(std::function<void()>&& callback);
void addFrameMetricsObserver(FrameMetricsObserver* observer);
void removeFrameMetricsObserver(FrameMetricsObserver* observer);
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 9aad2783ba2d..e5726b08aff4 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -27,7 +27,6 @@ import android.os.Looper
import android.util.Log
import android.util.MathUtils
import android.view.GhostView
-import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver.OnPreDrawListener
@@ -87,10 +86,11 @@ class DialogLaunchAnimator(
// If the parent of the view we are launching from is the background of some other animated
// dialog, then this means the caller intent is to launch a dialog from another dialog. In
// this case, we also animate the parent (which is the dialog background).
- val animatedParent = openedDialogs
- .firstOrNull { it.dialogContentParent == view.parent }
- val parentHostDialog = animatedParent?.hostDialog
- val animateFrom = animatedParent?.dialogContentParent ?: view
+ val animatedParent = openedDialogs.firstOrNull {
+ it.dialogContentWithBackground == view || it.dialogContentWithBackground == view.parent
+ }
+ val dialogContentWithBackground = animatedParent?.dialogContentWithBackground
+ val animateFrom = dialogContentWithBackground ?: view
// Make sure we don't run the launch animation from the same view twice at the same time.
if (animateFrom.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) {
@@ -109,7 +109,7 @@ class DialogLaunchAnimator(
onDialogDismissed = { openedDialogs.remove(it) },
originalDialog = dialog,
animateBackgroundBoundsChange,
- openedDialogs.firstOrNull { it.hostDialog == parentHostDialog }
+ animatedParent
)
val hostDialog = animatedDialog.hostDialog
openedDialogs.add(animatedDialog)
@@ -288,13 +288,12 @@ private class AnimatedDialog(
private val hostDialogRoot = FrameLayout(context)
/**
- * The parent of the original dialog content view, that serves as a fake window that will have
- * the same size as the original dialog window and to which we will set the original dialog
- * window background.
+ * The dialog content with its background. When animating a fullscreen dialog, this is just the
+ * first ViewGroup of the dialog that has a background. When animating a normal (not fullscreen)
+ * dialog, this is an additional view that serves as a fake window that will have the same size
+ * as the original dialog window and to which we will set the original dialog window background.
*/
- val dialogContentParent = FrameLayout(context).apply {
- id = DIALOG_CONTENT_PARENT_ID
- }
+ var dialogContentWithBackground: ViewGroup? = null
/**
* The background color of [originalDialogView], taking into consideration the [originalDialog]
@@ -451,59 +450,87 @@ private class AnimatedDialog(
hostDialogRoot.setOnClickListener { hostDialog.dismiss() }
dialogView.isClickable = true
- // Set the background of the window dialog to the dialog itself.
- // TODO(b/193634619): Support dialog windows without background.
- // TODO(b/193634619): Support dialog whose background comes from the content view instead of
- // the window.
- val typedArray =
- originalDialog.context.obtainStyledAttributes(com.android.internal.R.styleable.Window)
- val backgroundRes =
- typedArray.getResourceId(com.android.internal.R.styleable.Window_windowBackground, 0)
- typedArray.recycle()
- if (backgroundRes == 0) {
- throw IllegalStateException("Dialogs with no backgrounds on window are not supported")
- }
+ // Remove the original dialog view from its parent.
+ (dialogView.parent as? ViewGroup)?.removeView(dialogView)
- // Add a parent view to the original dialog view to which we will set the original dialog
- // window background. This View serves as a fake window with background, so that we are sure
- // that we don't override the dialog view paddings with the window background that usually
- // has insets.
- dialogContentParent.setBackgroundResource(backgroundRes)
- hostDialogRoot.addView(
- dialogContentParent,
-
- // We give it the size of its original dialog window.
- FrameLayout.LayoutParams(
- originalDialog.window.attributes.width,
- originalDialog.window.attributes.height,
- Gravity.CENTER
+ val originalDialogWindow = originalDialog.window!!
+ val isOriginalWindowFullScreen =
+ originalDialogWindow.attributes.width == ViewGroup.LayoutParams.MATCH_PARENT &&
+ originalDialogWindow.attributes.height == ViewGroup.LayoutParams.MATCH_PARENT
+ if (isOriginalWindowFullScreen) {
+ // If the original dialog window is fullscreen, then we look for the first ViewGroup
+ // that has a background and animate towards that ViewGroup given that this is probably
+ // what represents the actual dialog view.
+ dialogContentWithBackground = findFirstViewGroupWithBackground(dialogView)
+ ?: throw IllegalStateException("Unable to find ViewGroup with background")
+
+ hostDialogRoot.addView(
+ dialogView,
+
+ FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
)
- )
+ } else {
+ // Add a parent view to the original dialog view to which we will set the original
+ // dialog window background. This View serves as a fake window with background, so that
+ // we are sure that we don't override the original dialog content view paddings with the
+ // window background that usually has insets.
+ dialogContentWithBackground = FrameLayout(context).apply {
+ id = DIALOG_CONTENT_PARENT_ID
+
+ // TODO(b/193634619): Support dialog windows without background.
+ background = originalDialogWindow.decorView?.background
+ ?: throw IllegalStateException(
+ "Dialogs with no backgrounds on window are not supported")
+
+ addView(
+ dialogView,
+
+ // It should match its parent size, which is sized the same as the original
+ // dialog window.
+ FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ )
+ }
+
+ // Add the parent (that has the background) to the host window.
+ hostDialogRoot.addView(
+ dialogContentWithBackground,
- // Make the dialog view parent invisible for now, to make sure it's not drawn yet.
- dialogContentParent.visibility = View.INVISIBLE
+ // We give it the size and gravity of its original dialog window.
+ FrameLayout.LayoutParams(
+ originalDialogWindow.attributes.width,
+ originalDialogWindow.attributes.height,
+ originalDialogWindow.attributes.gravity
+ )
+ )
+ }
+
+ val dialogContentWithBackground = this.dialogContentWithBackground!!
+
+ // Make the dialog and its background invisible for now, to make sure it's not drawn yet.
+ dialogContentWithBackground.visibility = View.INVISIBLE
- val background = dialogContentParent.background!!
+ val background = dialogContentWithBackground.background!!
originalDialogBackgroundColor =
GhostedViewLaunchAnimatorController.findGradientDrawable(background)
?.color
?.defaultColor ?: Color.BLACK
- // Add the dialog view to its parent (that has the original window background).
- (dialogView.parent as? ViewGroup)?.removeView(dialogView)
- dialogContentParent.addView(
- dialogView,
-
- // It should match its parent size, which is sized the same as the original dialog
- // window.
- FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT
- )
- )
+ if (isOriginalWindowFullScreen) {
+ // If the original window is full screen, the ViewGroup with background might already be
+ // correctly laid out. Make sure we relayout and that the layout listener below is still
+ // called.
+ dialogContentWithBackground.layout(0, 0, 0, 0)
+ dialogContentWithBackground.requestLayout()
+ }
// Start the animation when the dialog is laid out in the center of the host dialog.
- dialogContentParent.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
+ dialogContentWithBackground.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
override fun onLayoutChange(
view: View,
left: Int,
@@ -515,7 +542,7 @@ private class AnimatedDialog(
oldRight: Int,
oldBottom: Int
) {
- dialogContentParent.removeOnLayoutChangeListener(this)
+ dialogContentWithBackground.removeOnLayoutChangeListener(this)
isOriginalDialogViewLaidOut = true
maybeStartLaunchAnimation()
@@ -523,6 +550,25 @@ private class AnimatedDialog(
})
}
+ private fun findFirstViewGroupWithBackground(view: View): ViewGroup? {
+ if (view !is ViewGroup) {
+ return null
+ }
+
+ if (view.background != null) {
+ return view
+ }
+
+ for (i in 0 until view.childCount) {
+ val match = findFirstViewGroupWithBackground(view.getChildAt(i))
+ if (match != null) {
+ return match
+ }
+ }
+
+ return null
+ }
+
fun onOriginalDialogSizeChanged() {
// The dialog is the single child of the root.
if (hostDialogRoot.childCount != 1) {
@@ -571,7 +617,8 @@ private class AnimatedDialog(
// at the end of the launch animation, because the lauch animation already correctly
// handles bounds changes.
if (backgroundLayoutListener != null) {
- dialogContentParent.addOnLayoutChangeListener(backgroundLayoutListener)
+ dialogContentWithBackground!!
+ .addOnLayoutChangeListener(backgroundLayoutListener)
}
}
)
@@ -638,10 +685,12 @@ private class AnimatedDialog(
(touchSurface as? LaunchableView)?.setShouldBlockVisibilityChanges(false)
touchSurface.visibility = View.VISIBLE
- dialogContentParent.visibility = View.INVISIBLE
+ val dialogContentWithBackground = this.dialogContentWithBackground!!
+ dialogContentWithBackground.visibility = View.INVISIBLE
if (backgroundLayoutListener != null) {
- dialogContentParent.removeOnLayoutChangeListener(backgroundLayoutListener)
+ dialogContentWithBackground
+ .removeOnLayoutChangeListener(backgroundLayoutListener)
}
// The animated ghost was just removed. We create a temporary ghost that will be
@@ -674,8 +723,8 @@ private class AnimatedDialog(
) {
// Create 2 ghost controllers to animate both the dialog and the touch surface in the host
// dialog.
- val startView = if (isLaunching) touchSurface else dialogContentParent
- val endView = if (isLaunching) dialogContentParent else touchSurface
+ val startView = if (isLaunching) touchSurface else dialogContentWithBackground!!
+ val endView = if (isLaunching) dialogContentWithBackground!! else touchSurface
val startViewController = GhostedViewLaunchAnimatorController(startView)
val endViewController = GhostedViewLaunchAnimatorController(endView)
startViewController.launchContainer = hostDialogRoot
@@ -736,7 +785,9 @@ private class AnimatedDialog(
}
private fun shouldAnimateDialogIntoView(): Boolean {
- if (exitAnimationDisabled) {
+ // Don't animate if the dialog was previously hidden using hide() (either on the host dialog
+ // or on the original dialog) or if we disabled the exit animation.
+ if (exitAnimationDisabled || !hostDialog.isShowing) {
return false
}
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 98518c2bbf97..4a5b637a05d9 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -185,7 +185,7 @@
<LinearLayout
android:id="@+id/turn_on_wifi_layout"
style="@style/InternetDialog.Network"
- android:layout_height="72dp"
+ android:layout_height="@dimen/internet_dialog_wifi_network_height"
android:gravity="center"
android:clickable="false"
android:focusable="false">
@@ -227,7 +227,7 @@
<LinearLayout
android:id="@+id/wifi_connected_layout"
style="@style/InternetDialog.Network"
- android:layout_height="72dp"
+ android:layout_height="@dimen/internet_dialog_wifi_network_height"
android:paddingStart="20dp"
android:paddingEnd="24dp"
android:background="@drawable/settingslib_switch_bar_bg_on"
@@ -249,7 +249,7 @@
android:orientation="vertical"
android:clickable="false"
android:layout_width="wrap_content"
- android:layout_height="72dp"
+ android:layout_height="@dimen/internet_dialog_wifi_network_height"
android:layout_marginEnd="30dp"
android:layout_weight="1"
android:gravity="start|center_vertical">
diff --git a/packages/SystemUI/res/layout/internet_list_item.xml b/packages/SystemUI/res/layout/internet_list_item.xml
index 868331ec830f..f6a213662a18 100644
--- a/packages/SystemUI/res/layout/internet_list_item.xml
+++ b/packages/SystemUI/res/layout/internet_list_item.xml
@@ -25,7 +25,7 @@
<LinearLayout
android:id="@+id/wifi_list"
style="@style/InternetDialog.Network"
- android:layout_height="72dp"
+ android:layout_height="@dimen/internet_dialog_wifi_network_height"
android:paddingStart="20dp"
android:paddingEnd="24dp">
<FrameLayout
@@ -45,7 +45,7 @@
android:orientation="vertical"
android:clickable="false"
android:layout_width="wrap_content"
- android:layout_height="72dp"
+ android:layout_height="@dimen/internet_dialog_wifi_network_height"
android:layout_marginEnd="30dp"
android:layout_weight="1"
android:gravity="start|center_vertical">
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 8dbd59d42ab7..759670e01e71 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -17,7 +17,6 @@
<com.android.systemui.statusbar.phone.KeyguardBottomAreaView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
android:id="@+id/keyguard_bottom_area"
android:layout_height="match_parent"
android:layout_width="match_parent"
@@ -114,7 +113,8 @@
android:layout_height="match_parent">
<include layout="@layout/keyguard_bottom_area_overlay" />
-
</FrameLayout>
+ <include layout="@layout/ambient_indication"
+ android:id="@+id/ambient_indication_container" />
</com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
index e43a149a6cd9..6c5ad5060495 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog.xml
@@ -93,41 +93,6 @@
android:id="@+id/screenrecord_audio_switch"
style="@style/ScreenRecord.Switch"/>
</LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_marginTop="@dimen/screenrecord_option_padding">
- <ImageView
- android:layout_width="@dimen/screenrecord_option_icon_size"
- android:layout_height="@dimen/screenrecord_option_icon_size"
- android:layout_weight="0"
- android:src="@drawable/ic_touch"
- android:tint="?android:attr/textColorSecondary"
- android:layout_gravity="center"
- android:layout_marginRight="@dimen/screenrecord_option_padding"/>
- <TextView
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:minHeight="48dp"
- android:layout_weight="1"
- android:layout_gravity="fill_vertical"
- android:gravity="center_vertical"
- android:text="@string/screenrecord_taps_label"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:textColor="?android:attr/textColorPrimary"
- android:importantForAccessibility="no"/>
- <Switch
- android:layout_width="wrap_content"
- android:minWidth="48dp"
- android:layout_height="48dp"
- android:layout_weight="0"
- android:id="@+id/screenrecord_taps_switch"
- android:contentDescription="@string/screenrecord_taps_label"
- style="@style/ScreenRecord.Switch"/>
- </LinearLayout>
</LinearLayout>
<!-- Buttons -->
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 82186c13394e..591d8f52c4be 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -119,9 +119,6 @@
systemui:layout_constraintEnd_toEndOf="parent"
/>
- <include layout="@layout/ambient_indication"
- android:id="@+id/ambient_indication_container" />
-
<include layout="@layout/photo_preview_overlay" />
<include
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a2fd669c69fd..1938e48e859a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1243,6 +1243,8 @@
<!-- Internet panel related dimensions -->
<dimen name="internet_dialog_list_max_height">662dp</dimen>
+ <!-- The height of the WiFi network in Internet panel. -->
+ <dimen name="internet_dialog_wifi_network_height">72dp</dimen>
<!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
<dimen name="large_dialog_width">@dimen/match_parent</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 3c7b53a8e6db..61591bd0a450 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -373,11 +373,11 @@
<item name="android:windowIsFloating">true</item>
</style>
- <style name="Theme.SystemUI.Dialog.GlobalActionsLite" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar.Fullscreen">
- <item name="android:windowIsFloating">true</item>
+ <style name="Theme.SystemUI.Dialog.GlobalActionsLite" parent="Theme.SystemUI.Dialog">
+ <!-- Settings windowFullscreen: true is necessary to be able to intercept touch events -->
+ <!-- that would otherwise be intercepted by the Shade. -->
+ <item name="android:windowFullscreen">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
- <item name="android:backgroundDimEnabled">true</item>
- <item name="android:windowCloseOnTouchOutside">true</item>
</style>
<style name="QSBorderlessButton">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java b/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
index 07ad0c8a5120..8aa3abac831f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/navigationbar/buttons/KeyButtonRipple.java
@@ -51,6 +51,9 @@ public class KeyButtonRipple extends Drawable {
private static final Interpolator ALPHA_OUT_INTERPOLATOR =
new PathInterpolator(0f, 0f, 0.8f, 1f);
+ @DimenRes
+ private final int mMaxWidthResource;
+
private Paint mRipplePaint;
private CanvasProperty<Float> mLeftProp;
private CanvasProperty<Float> mTopProp;
@@ -90,10 +93,17 @@ public class KeyButtonRipple extends Drawable {
private Type mType = Type.ROUNDED_RECT;
public KeyButtonRipple(Context ctx, View targetView, @DimenRes int maxWidthResource) {
+ mMaxWidthResource = maxWidthResource;
mMaxWidth = ctx.getResources().getDimensionPixelSize(maxWidthResource);
mTargetView = targetView;
}
+ public void updateResources() {
+ mMaxWidth = mTargetView.getContext().getResources()
+ .getDimensionPixelSize(mMaxWidthResource);
+ invalidateSelf();
+ }
+
public void setDarkIntensity(float darkIntensity) {
mDark = darkIntensity >= 0.5f;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
index cbf739732361..857cc4620ebd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
@@ -21,6 +21,8 @@ import android.annotation.IdRes;
import android.annotation.LayoutRes;
import android.annotation.StringRes;
import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.Config;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.drawable.AnimatedVectorDrawable;
@@ -29,12 +31,12 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
import androidx.core.view.OneShotPreDrawListener;
-import com.android.systemui.shared.R;
import com.android.systemui.shared.rotation.FloatingRotationButtonPositionCalculator.Position;
/**
@@ -48,7 +50,21 @@ public class FloatingRotationButton implements RotationButton {
private final ViewGroup mKeyButtonContainer;
private final FloatingRotationButtonView mKeyButtonView;
- private final int mContainerSize;
+ private int mContainerSize;
+ private final Context mContext;
+
+ @StringRes
+ private final int mContentDescriptionResource;
+ @DimenRes
+ private final int mMinMarginResource;
+ @DimenRes
+ private final int mRoundedContentPaddingResource;
+ @DimenRes
+ private final int mTaskbarLeftMarginResource;
+ @DimenRes
+ private final int mTaskbarBottomMarginResource;
+ @DimenRes
+ private final int mButtonDiameterResource;
private AnimatedVectorDrawable mAnimatedDrawable;
private boolean mIsShowing;
@@ -58,13 +74,13 @@ public class FloatingRotationButton implements RotationButton {
private boolean mIsTaskbarVisible = false;
private boolean mIsTaskbarStashed = false;
- private final FloatingRotationButtonPositionCalculator mPositionCalculator;
+ private FloatingRotationButtonPositionCalculator mPositionCalculator;
private RotationButtonController mRotationButtonController;
private RotationButtonUpdatesCallback mUpdatesCallback;
private Position mPosition;
- public FloatingRotationButton(Context context, @StringRes int contentDescription,
+ public FloatingRotationButton(Context context, @StringRes int contentDescriptionResource,
@LayoutRes int layout, @IdRes int keyButtonId, @DimenRes int minMargin,
@DimenRes int roundedContentPadding, @DimenRes int taskbarLeftMargin,
@DimenRes int taskbarBottomMargin, @DimenRes int buttonDiameter,
@@ -73,24 +89,37 @@ public class FloatingRotationButton implements RotationButton {
mKeyButtonContainer = (ViewGroup) LayoutInflater.from(context).inflate(layout, null);
mKeyButtonView = mKeyButtonContainer.findViewById(keyButtonId);
mKeyButtonView.setVisibility(View.VISIBLE);
- mKeyButtonView.setContentDescription(context.getString(contentDescription));
+ mKeyButtonView.setContentDescription(context.getString(contentDescriptionResource));
mKeyButtonView.setRipple(rippleMaxWidth);
- Resources res = context.getResources();
+ mContext = context;
+
+ mContentDescriptionResource = contentDescriptionResource;
+ mMinMarginResource = minMargin;
+ mRoundedContentPaddingResource = roundedContentPadding;
+ mTaskbarLeftMarginResource = taskbarLeftMargin;
+ mTaskbarBottomMarginResource = taskbarBottomMargin;
+ mButtonDiameterResource = buttonDiameter;
+
+ updateDimensionResources();
+ }
+
+ private void updateDimensionResources() {
+ Resources res = mContext.getResources();
int defaultMargin = Math.max(
- res.getDimensionPixelSize(minMargin),
- res.getDimensionPixelSize(roundedContentPadding));
+ res.getDimensionPixelSize(mMinMarginResource),
+ res.getDimensionPixelSize(mRoundedContentPaddingResource));
int taskbarMarginLeft =
- res.getDimensionPixelSize(taskbarLeftMargin);
+ res.getDimensionPixelSize(mTaskbarLeftMarginResource);
int taskbarMarginBottom =
- res.getDimensionPixelSize(taskbarBottomMargin);
+ res.getDimensionPixelSize(mTaskbarBottomMarginResource);
mPositionCalculator = new FloatingRotationButtonPositionCalculator(defaultMargin,
taskbarMarginLeft, taskbarMarginBottom);
- final int diameter = res.getDimensionPixelSize(buttonDiameter);
+ final int diameter = res.getDimensionPixelSize(mButtonDiameterResource);
mContainerSize = diameter + Math.max(defaultMargin, Math.max(taskbarMarginLeft,
taskbarMarginBottom));
}
@@ -119,32 +148,10 @@ public class FloatingRotationButton implements RotationButton {
}
mIsShowing = true;
- int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-
- // TODO(b/200103245): add new window type that has z-index above
- // TYPE_NAVIGATION_BAR_PANEL as currently it could be below the taskbar which has
- // the same window type
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- mContainerSize,
- mContainerSize,
- 0, 0, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags,
- PixelFormat.TRANSLUCENT);
-
- lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
- lp.setTitle("FloatingRotationButton");
- lp.setFitInsetsTypes(0 /*types */);
-
- mDisplayRotation = mWindowManager.getDefaultDisplay().getRotation();
- mPosition = mPositionCalculator
- .calculatePosition(mDisplayRotation, mIsTaskbarVisible, mIsTaskbarStashed);
- lp.gravity = mPosition.getGravity();
- ((FrameLayout.LayoutParams) mKeyButtonView.getLayoutParams()).gravity =
- mPosition.getGravity();
+ final LayoutParams layoutParams = adjustViewPositionAndCreateLayoutParams();
+ mWindowManager.addView(mKeyButtonContainer, layoutParams);
- updateTranslation(mPosition, /* animate */ false);
-
- mWindowManager.addView(mKeyButtonContainer, lp);
if (mAnimatedDrawable != null) {
mAnimatedDrawable.reset();
mAnimatedDrawable.start();
@@ -232,6 +239,53 @@ public class FloatingRotationButton implements RotationButton {
}
}
+ /**
+ * Updates resources that could be changed in runtime, should be called on configuration
+ * change with changes diff integer mask
+ * @param configurationChanges - configuration changes with flags from ActivityInfo e.g.
+ * {@link android.content.pm.ActivityInfo#CONFIG_DENSITY}
+ */
+ public void onConfigurationChanged(@Config int configurationChanges) {
+ if ((configurationChanges & ActivityInfo.CONFIG_DENSITY) != 0
+ || (configurationChanges & ActivityInfo.CONFIG_SCREEN_SIZE) != 0) {
+ updateDimensionResources();
+
+ if (mIsShowing) {
+ final LayoutParams layoutParams = adjustViewPositionAndCreateLayoutParams();
+ mWindowManager.updateViewLayout(mKeyButtonContainer, layoutParams);
+ }
+ }
+
+ if ((configurationChanges & ActivityInfo.CONFIG_LOCALE) != 0) {
+ mKeyButtonView.setContentDescription(mContext.getString(mContentDescriptionResource));
+ }
+ }
+
+ private LayoutParams adjustViewPositionAndCreateLayoutParams() {
+ final LayoutParams lp = new LayoutParams(
+ mContainerSize,
+ mContainerSize,
+ /* xpos */ 0, /* ypos */ 0, LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
+
+ lp.privateFlags |= LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ lp.setTitle("FloatingRotationButton");
+ lp.setFitInsetsTypes(/* types */ 0);
+
+ mDisplayRotation = mWindowManager.getDefaultDisplay().getRotation();
+ mPosition = mPositionCalculator
+ .calculatePosition(mDisplayRotation, mIsTaskbarVisible, mIsTaskbarStashed);
+
+ lp.gravity = mPosition.getGravity();
+ ((FrameLayout.LayoutParams) mKeyButtonView.getLayoutParams()).gravity =
+ mPosition.getGravity();
+
+ updateTranslation(mPosition, /* animate */ false);
+
+ return lp;
+ }
+
private void updateTranslation(Position position, boolean animate) {
final int translationX = position.getTranslationX();
final int translationY = position.getTranslationY();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
index c5f8fc15b3b7..a4b6451caaea 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
@@ -17,6 +17,8 @@
package com.android.systemui.shared.rotation;
import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
@@ -37,12 +39,15 @@ public class FloatingRotationButtonView extends ImageView {
private KeyButtonRipple mRipple;
private final Paint mOvalBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+ private final Configuration mLastConfiguration;
+
public FloatingRotationButtonView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FloatingRotationButtonView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ mLastConfiguration = getResources().getConfiguration();
setClickable(true);
@@ -63,6 +68,17 @@ public class FloatingRotationButtonView extends ImageView {
}
}
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ final int changes = mLastConfiguration.updateFrom(newConfig);
+ if ((changes & ActivityInfo.CONFIG_SCREEN_SIZE) != 0
+ || ((changes & ActivityInfo.CONFIG_DENSITY) != 0)) {
+ if (mRipple != null) {
+ mRipple.updateResources();
+ }
+ }
+ }
+
public void setColors(int lightColor, int darkColor) {
getDrawable().setColorFilter(new PorterDuffColorFilter(lightColor, PorterDuff.Mode.SRC_IN));
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 2dbd5dee76aa..78867f7220af 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -481,7 +481,9 @@ public class RotationButtonController {
* orientation overview.
*/
public void setSkipOverrideUserLockPrefsOnce() {
- mSkipOverrideUserLockPrefsOnce = true;
+ // If live-tile is enabled (recents animation keeps running in overview), there is no
+ // activity switch so the display rotation is not changed, then it is no need to skip.
+ mSkipOverrideUserLockPrefsOnce = !mIsRecentsAnimationRunning;
}
private boolean shouldOverrideUserLockPrefs(final int rotation) {
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index 68132f4c598b..b2ecc6140460 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -18,6 +18,7 @@ package com.android.keyguard;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
@@ -30,6 +31,7 @@ import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
@@ -82,14 +84,18 @@ public class LockIconView extends FrameLayout implements Dumpable {
void updateColorAndBackgroundVisibility() {
if (mUseBackground && mLockIcon.getDrawable() != null) {
- mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
- android.R.attr.textColorPrimary);
+ mLockIconColor = ColorUtils.blendARGB(
+ Utils.getColorAttrDefaultColor(getContext(), android.R.attr.textColorPrimary),
+ Color.WHITE,
+ mDozeAmount);
mBgView.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
mBgView.setAlpha(1f - mDozeAmount);
mBgView.setVisibility(View.VISIBLE);
} else {
- mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
- R.attr.wallpaperTextColorAccent);
+ mLockIconColor = ColorUtils.blendARGB(
+ Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColorAccent),
+ Color.WHITE,
+ mDozeAmount);
mBgView.setVisibility(View.GONE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index fd3fcdd1d58a..12786f278a16 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -66,7 +66,6 @@ import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfC
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
-import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -125,7 +124,6 @@ import dagger.Provides;
},
subcomponents = {
StatusBarComponent.class,
- StatusBarFragmentComponent.class,
NotificationRowComponent.class,
DozeComponent.class,
ExpandableNotificationRowComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index ad472326555e..ff14064834d7 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -31,8 +31,6 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_G
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.Dialog;
@@ -56,6 +54,7 @@ import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -80,8 +79,6 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -112,7 +109,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.MultiListLayout;
import com.android.systemui.MultiListLayout.MultiListAdapter;
-import com.android.systemui.animation.Interpolators;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Background;
@@ -123,6 +120,7 @@ import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.scrim.ScrimDrawable;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -239,6 +237,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private int mSmallestScreenWidthDp;
private final Optional<StatusBar> mStatusBarOptional;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
@VisibleForTesting
public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -343,10 +342,11 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
UiEventLogger uiEventLogger,
RingerModeTracker ringerModeTracker,
SysUiState sysUiState,
- @Main Handler handler,
+ @Main Handler handler,
PackageManager packageManager,
Optional<StatusBar> statusBarOptional,
- KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ DialogLaunchAnimator dialogLaunchAnimator) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -377,6 +377,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
mStatusBarOptional = statusBarOptional;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -435,11 +436,14 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
}
/**
- * Show the global actions dialog (creating if necessary)
+ * Show the global actions dialog (creating if necessary) or hide it if it's already showing.
*
- * @param keyguardShowing True if keyguard is showing
+ * @param keyguardShowing True if keyguard is showing
+ * @param isDeviceProvisioned True if device is provisioned
+ * @param view The view from which we should animate the dialog when showing it
*/
- public void showOrHideDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
+ public void showOrHideDialog(boolean keyguardShowing, boolean isDeviceProvisioned,
+ @Nullable View view) {
mKeyguardShowing = keyguardShowing;
mDeviceProvisioned = isDeviceProvisioned;
if (mDialog != null && mDialog.isShowing()) {
@@ -451,7 +455,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mDialog.dismiss();
mDialog = null;
} else {
- handleShow();
+ handleShow(view);
}
}
@@ -483,7 +487,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
}
}
- protected void handleShow() {
+ protected void handleShow(@Nullable View view) {
awakenIfNecessary();
mDialog = createDialog();
prepareDialog();
@@ -493,8 +497,13 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mDialog.getWindow().setAttributes(attrs);
// Don't acquire soft keyboard focus, to avoid destroying state when capturing bugreports
- mDialog.getWindow().setFlags(FLAG_ALT_FOCUSABLE_IM, FLAG_ALT_FOCUSABLE_IM);
- mDialog.show();
+ mDialog.getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
+
+ if (view != null) {
+ mDialogLaunchAnimator.showFromView(mDialog, view);
+ } else {
+ mDialog.show();
+ }
mWindowManagerFuncs.onGlobalActionsShown();
}
@@ -643,7 +652,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
}
}
- protected void onRotate() {
+ protected void onRefresh() {
// re-allocate actions between main and overflow lists
this.createActionItems();
}
@@ -667,7 +676,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite,
mAdapter, mOverflowAdapter, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
- mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
+ mSysUiState, this::onRefresh, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils);
dialog.setOnDismissListener(this);
@@ -702,14 +711,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
}
@Override
- public void onUiModeChanged() {
- mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
- if (mDialog != null && mDialog.isShowing()) {
- mDialog.refreshDialog();
- }
- }
-
- @Override
public void onConfigChanged(Configuration newConfig) {
if (mDialog != null && mDialog.isShowing()
&& (newConfig.smallestScreenWidthDp != mSmallestScreenWidthDp)) {
@@ -717,6 +718,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mDialog.refreshDialog();
}
}
+
/**
* Implements {@link GlobalActionsPanelPlugin.Callbacks#dismissGlobalActionsMenu()}, which is
* called when the quick access wallet requests dismissal.
@@ -1363,6 +1365,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
final Action action = mAdapter.getItem(position);
if (action instanceof LongPressAction) {
if (mDialog != null) {
+ // Usually clicking an item shuts down the phone, locks, or starts an activity.
+ // We don't want to animate back into the power button when that happens, so we
+ // disable the dialog animation before dismissing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
} else {
Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1379,6 +1385,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
if (mDialog != null) {
// don't dismiss the dialog if we're opening the power options menu
if (!(item instanceof PowerOptionsAction)) {
+ // Usually clicking an item shuts down the phone, locks, or starts an
+ // activity. We don't want to animate back into the power button when that
+ // happens, so we disable the dialog animation before dismissing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
}
} else {
@@ -1446,6 +1456,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
final Action action = getItem(position);
if (action instanceof LongPressAction) {
if (mDialog != null) {
+ // Usually clicking an item shuts down the phone, locks, or starts an activity.
+ // We don't want to animate back into the power button when that happens, so we
+ // disable the dialog animation before dismissing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
} else {
Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1459,6 +1473,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
Action item = getItem(position);
if (!(item instanceof SilentModeTriStateAction)) {
if (mDialog != null) {
+ // Usually clicking an item shuts down the phone, locks, or starts an activity.
+ // We don't want to animate back into the power button when that happens, so we
+ // disable the dialog animation before dismissing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
} else {
Log.w(TAG, "Action clicked while mDialog is null.");
@@ -1510,6 +1528,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
final Action action = getItem(position);
if (action instanceof LongPressAction) {
if (mDialog != null) {
+ // Usually clicking an item shuts down the phone, locks, or starts an activity.
+ // We don't want to animate back into the power button when that happens, so we
+ // disable the dialog animation before dismissing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
} else {
Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1523,6 +1545,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
Action item = getItem(position);
if (!(item instanceof SilentModeTriStateAction)) {
if (mDialog != null) {
+ // Usually clicking an item shuts down the phone, locks, or starts an activity.
+ // We don't want to animate back into the power button when that happens, so we
+ // disable the dialog animation before dismissing.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mDialog.dismiss();
} else {
Log.w(TAG, "Action clicked while mDialog is null.");
@@ -2066,7 +2092,9 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
case MESSAGE_DISMISS:
if (mDialog != null) {
if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
- mDialog.completeDismiss();
+ // Hide instantly.
+ mDialog.hide();
+ mDialog.dismiss();
} else {
mDialog.dismiss();
}
@@ -2113,7 +2141,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
}
@VisibleForTesting
- static class ActionsDialogLite extends Dialog implements DialogInterface,
+ static class ActionsDialogLite extends SystemUIDialog implements DialogInterface,
ColorExtractor.OnColorsChangedListener {
protected final Context mContext;
@@ -2126,13 +2154,12 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
protected Drawable mBackgroundDrawable;
protected final SysuiColorExtractor mColorExtractor;
private boolean mKeyguardShowing;
- protected boolean mShowing;
protected float mScrimAlpha;
protected final NotificationShadeWindowController mNotificationShadeWindowController;
protected final SysUiState mSysUiState;
private ListPopupWindow mOverflowPopup;
private Dialog mPowerOptionsDialog;
- protected final Runnable mOnRotateCallback;
+ protected final Runnable mOnRefreshCallback;
private UiEventLogger mUiEventLogger;
private GestureDetector mGestureDetector;
private Optional<StatusBar> mStatusBarOptional;
@@ -2151,7 +2178,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
}
@Override
- public boolean onSingleTapConfirmed(MotionEvent e) {
+ public boolean onSingleTapUp(MotionEvent e) {
// Close without opening shade
mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
cancel();
@@ -2189,11 +2216,13 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
MyOverflowAdapter overflowAdapter,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
- SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
+ SysUiState sysuiState, Runnable onRefreshCallback, boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
Optional<StatusBar> statusBarOptional,
KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) {
- super(context, themeRes);
+ // We set dismissOnDeviceLock to false because we have a custom broadcast receiver to
+ // dismiss this dialog when the device is locked.
+ super(context, themeRes, false /* dismissOnDeviceLock */);
mContext = context;
mAdapter = adapter;
mOverflowAdapter = overflowAdapter;
@@ -2202,36 +2231,32 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
mSysUiState = sysuiState;
- mOnRotateCallback = onRotateCallback;
+ mOnRefreshCallback = onRefreshCallback;
mKeyguardShowing = keyguardShowing;
mUiEventLogger = uiEventLogger;
mStatusBarOptional = statusBarOptional;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
-
mGestureDetector = new GestureDetector(mContext, mGestureListener);
+ }
- // Window initialization
- Window window = getWindow();
- window.requestFeature(Window.FEATURE_NO_TITLE);
- // Inflate the decor view, so the attributes below are not overwritten by the theme.
- window.getDecorView();
- window.getAttributes().systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
- window.setLayout(MATCH_PARENT, MATCH_PARENT);
- window.addFlags(
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
- window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
- window.getAttributes().setFitInsetsTypes(0 /* types */);
- setTitle(R.string.global_actions);
-
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
initializeLayout();
}
@Override
+ protected int getWidth() {
+ return MATCH_PARENT;
+ }
+
+ @Override
+ protected int getHeight() {
+ return MATCH_PARENT;
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent event) {
return mGestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
}
@@ -2371,7 +2396,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
message.animate()
.alpha(0f)
.setDuration(TOAST_FADE_TIME)
- .setStartDelay(visibleTime);
+ .setStartDelay(visibleTime)
+ .setListener(null);
}
});
}
@@ -2423,122 +2449,32 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
@Override
public void show() {
super.show();
- // split this up so we can override but still call Dialog.show
- showDialog();
- }
-
- protected void showDialog() {
- mShowing = true;
mNotificationShadeWindowController.setRequestTopUi(true, TAG);
mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, true)
.commitUpdate(mContext.getDisplayId());
-
- ViewGroup root = (ViewGroup) mGlobalActionsLayout.getRootView();
- root.setOnApplyWindowInsetsListener((v, windowInsets) -> {
- root.setPadding(windowInsets.getStableInsetLeft(),
- windowInsets.getStableInsetTop(),
- windowInsets.getStableInsetRight(),
- windowInsets.getStableInsetBottom());
- return WindowInsets.CONSUMED;
- });
-
- mBackgroundDrawable.setAlpha(0);
- float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
- ObjectAnimator alphaAnimator =
- ObjectAnimator.ofFloat(mContainer, "alpha", 0f, 1f);
- alphaAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- alphaAnimator.setDuration(183);
- alphaAnimator.addUpdateListener((animation) -> {
- float animatedValue = animation.getAnimatedFraction();
- int alpha = (int) (animatedValue * mScrimAlpha * 255);
- mBackgroundDrawable.setAlpha(alpha);
- });
-
- ObjectAnimator xAnimator =
- ObjectAnimator.ofFloat(mContainer, "translationX", xOffset, 0f);
- xAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- xAnimator.setDuration(350);
-
- AnimatorSet animatorSet = new AnimatorSet();
- animatorSet.playTogether(alphaAnimator, xAnimator);
- animatorSet.start();
}
@Override
public void dismiss() {
- dismissWithAnimation(() -> {
- dismissInternal();
- });
- }
-
- protected void dismissInternal() {
- mContainer.setTranslationX(0);
- ObjectAnimator alphaAnimator =
- ObjectAnimator.ofFloat(mContainer, "alpha", 1f, 0f);
- alphaAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
- alphaAnimator.setDuration(233);
- alphaAnimator.addUpdateListener((animation) -> {
- float animatedValue = 1f - animation.getAnimatedFraction();
- int alpha = (int) (animatedValue * mScrimAlpha * 255);
- mBackgroundDrawable.setAlpha(alpha);
- });
-
- float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
- ObjectAnimator xAnimator =
- ObjectAnimator.ofFloat(mContainer, "translationX", 0f, xOffset);
- xAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
- xAnimator.setDuration(350);
-
- AnimatorSet animatorSet = new AnimatorSet();
- animatorSet.playTogether(alphaAnimator, xAnimator);
- animatorSet.addListener(new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animation) {
- completeDismiss();
- }
- });
-
- animatorSet.start();
-
- // close first, as popup windows will not fade during the animation
- dismissOverflow(false);
- dismissPowerOptions(false);
- }
+ dismissOverflow();
+ dismissPowerOptions();
- void dismissWithAnimation(Runnable animation) {
- if (!mShowing) {
- return;
- }
- mShowing = false;
- animation.run();
- }
-
- protected void completeDismiss() {
- mShowing = false;
- dismissOverflow(true);
- dismissPowerOptions(true);
mNotificationShadeWindowController.setRequestTopUi(false, TAG);
mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, false)
.commitUpdate(mContext.getDisplayId());
+
super.dismiss();
}
- protected final void dismissOverflow(boolean immediate) {
+ protected final void dismissOverflow() {
if (mOverflowPopup != null) {
- if (immediate) {
- mOverflowPopup.dismissImmediate();
- } else {
- mOverflowPopup.dismiss();
- }
+ mOverflowPopup.dismiss();
}
}
- protected final void dismissPowerOptions(boolean immediate) {
+ protected final void dismissPowerOptions() {
if (mPowerOptionsDialog != null) {
- if (immediate) {
- mPowerOptionsDialog.dismiss();
- } else {
- mPowerOptionsDialog.dismiss();
- }
+ mPowerOptionsDialog.dismiss();
}
}
@@ -2574,20 +2510,18 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
}
public void refreshDialog() {
- // ensure dropdown menus are dismissed before re-initializing the dialog
- dismissOverflow(true);
- dismissPowerOptions(true);
+ mOnRefreshCallback.run();
- // re-create dialog
- initializeLayout();
+ // Dismiss the dropdown menus.
+ dismissOverflow();
+ dismissPowerOptions();
+
+ // Update the list as the max number of items per row has probably changed.
mGlobalActionsLayout.updateList();
}
public void onRotate(int from, int to) {
- if (mShowing) {
- mOnRotateCallback.run();
- refreshDialog();
- }
+ refreshDialog();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index c4508e043c7d..96ae646ac7f9 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -82,7 +82,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks
if (mDisabled) return;
mGlobalActionsDialog = mGlobalActionsDialogLazy.get();
mGlobalActionsDialog.showOrHideDialog(mKeyguardStateController.isShowing(),
- mDeviceProvisionedController.isDeviceProvisioned());
+ mDeviceProvisionedController.isDeviceProvisioned(), null /* view */);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 868193b44704..54e40f1429be 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -289,6 +289,9 @@ public abstract class MediaOutputBaseAdapter extends
public void onAnimationEnd(Animator animation) {
to.requireViewById(R.id.volume_indeterminate_progress).setVisibility(
View.VISIBLE);
+ // Unset the listener, otherwise this may persist for another view
+ // property animation
+ toTitleText.animate().setListener(null);
}
});
// Animation for seek bar
@@ -312,8 +315,14 @@ public abstract class MediaOutputBaseAdapter extends
public void onAnimationEnd(Animator animation) {
mIsAnimating = false;
notifyDataSetChanged();
+ // Unset the listener, otherwise this may persist for
+ // another view property animation
+ fromTitleText.animate().setListener(null);
}
});
+ // Unset the listener, otherwise this may persist for another view
+ // property animation
+ fromSeekBar.animate().setListener(null);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
new file mode 100644
index 000000000000..25337b6d52a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.inputmethodservice.InputMethodService;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import dagger.Lazy;
+
+/**
+ * Extracts shared elements between navbar and taskbar delegate to de-dupe logic and help them
+ * experience the joys of friendship.
+ * The events are then passed through
+ *
+ * Currently consolidates
+ * * A11y
+ * * Assistant
+ */
+@SysUISingleton
+public final class NavBarHelper implements
+ AccessibilityButtonModeObserver.ModeChangedListener,
+ OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
+ Dumpable {
+ private final AccessibilityManager mAccessibilityManager;
+ private final Lazy<AssistManager> mAssistManagerLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final UserTracker mUserTracker;
+ private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
+ private final List<NavbarTaskbarStateUpdater> mA11yEventListeners = new ArrayList<>();
+ private final Context mContext;
+ private ContentResolver mContentResolver;
+ private boolean mAssistantAvailable;
+ private boolean mLongPressHomeEnabled;
+ private boolean mAssistantTouchGestureEnabled;
+ private int mNavBarMode;
+
+ private final ContentObserver mAssistContentObserver = new ContentObserver(
+ new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ updateAssitantAvailability();
+ }
+ };
+
+ /**
+ * @param context This is not display specific, then again neither is any of the code in
+ * this class. Once there's display specific code, we may want to create an
+ * instance of this class per navbar vs having it be a singleton.
+ */
+ @Inject
+ public NavBarHelper(Context context, AccessibilityManager accessibilityManager,
+ AccessibilityManagerWrapper accessibilityManagerWrapper,
+ AccessibilityButtonModeObserver accessibilityButtonModeObserver,
+ OverviewProxyService overviewProxyService,
+ Lazy<AssistManager> assistManagerLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ NavigationModeController navigationModeController,
+ UserTracker userTracker,
+ DumpManager dumpManager) {
+ mContext = context;
+ mAccessibilityManager = accessibilityManager;
+ mAssistManagerLazy = assistManagerLazy;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mUserTracker = userTracker;
+ accessibilityManagerWrapper.addCallback(
+ accessibilityManager1 -> NavBarHelper.this.dispatchA11yEventUpdate());
+ mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
+
+ mAccessibilityButtonModeObserver.addListener(this);
+ mNavBarMode = navigationModeController.addListener(this);
+ overviewProxyService.addCallback(this);
+ dumpManager.registerDumpable(this);
+ }
+
+ public void init() {
+ mContentResolver = mContext.getContentResolver();
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
+ false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
+ false, mAssistContentObserver, UserHandle.USER_ALL);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
+ false, mAssistContentObserver, UserHandle.USER_ALL);
+ updateAssitantAvailability();
+ }
+
+ public void destroy() {
+ mContentResolver.unregisterContentObserver(mAssistContentObserver);
+ }
+
+ /**
+ * @param listener Will immediately get callbacks based on current state
+ */
+ public void registerNavTaskStateUpdater(NavbarTaskbarStateUpdater listener) {
+ mA11yEventListeners.add(listener);
+ listener.updateAccessibilityServicesState();
+ listener.updateAssistantAvailable(mAssistantAvailable);
+ }
+
+ public void removeNavTaskStateUpdater(NavbarTaskbarStateUpdater listener) {
+ mA11yEventListeners.remove(listener);
+ }
+
+ private void dispatchA11yEventUpdate() {
+ for (NavbarTaskbarStateUpdater listener : mA11yEventListeners) {
+ listener.updateAccessibilityServicesState();
+ }
+ }
+
+ private void dispatchAssistantEventUpdate(boolean assistantAvailable) {
+ for (NavbarTaskbarStateUpdater listener : mA11yEventListeners) {
+ listener.updateAssistantAvailable(assistantAvailable);
+ }
+ }
+
+ @Override
+ public void onAccessibilityButtonModeChanged(int mode) {
+ dispatchA11yEventUpdate();
+ }
+
+ /**
+ * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
+ * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
+ *
+ * @return the a11y button clickable and long_clickable states, or 0 if there is no
+ * a11y button in the navbar
+ */
+ public int getA11yButtonState() {
+ // AccessibilityManagerService resolves services for the current user since the local
+ // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
+ final List<String> a11yButtonTargets =
+ mAccessibilityManager.getAccessibilityShortcutTargets(
+ AccessibilityManager.ACCESSIBILITY_BUTTON);
+ final int requestingServices = a11yButtonTargets.size();
+
+ // If accessibility button is floating menu mode, click and long click state should be
+ // disabled.
+ if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
+ == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+ return 0;
+ }
+
+ return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
+ | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
+ }
+
+ @Override
+ public void onConnectionChanged(boolean isConnected) {
+ if (isConnected) {
+ updateAssitantAvailability();
+ }
+ }
+
+ private void updateAssitantAvailability() {
+ boolean assistantAvailableForUser = mAssistManagerLazy.get()
+ .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
+ boolean longPressDefault = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
+ mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
+ mUserTracker.getUserId()) != 0;
+ boolean gestureDefault = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_assistTouchGestureEnabledDefault);
+ mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0,
+ mUserTracker.getUserId()) != 0;
+
+ mAssistantAvailable = assistantAvailableForUser
+ && mAssistantTouchGestureEnabled
+ && QuickStepContract.isGesturalMode(mNavBarMode);
+ dispatchAssistantEventUpdate(mAssistantAvailable);
+ }
+
+ public boolean getLongPressHomeEnabled() {
+ return mLongPressHomeEnabled;
+ }
+
+ @Override
+ public void startAssistant(Bundle bundle) {
+ mAssistManagerLazy.get().startAssist(bundle);
+ }
+
+ @Override
+ public void onNavigationModeChanged(int mode) {
+ mNavBarMode = mode;
+ updateAssitantAvailability();
+ }
+
+ /**
+ * @return Whether the IME is shown on top of the screen given the {@code vis} flag of
+ * {@link InputMethodService} and the keyguard states.
+ */
+ public boolean isImeShown(int vis) {
+ View shadeWindowView = mStatusBarOptionalLazy.get().get().getNotificationShadeWindowView();
+ boolean isKeyguardShowing = mStatusBarOptionalLazy.get().get().isKeyguardShowing();
+ boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
+ && shadeWindowView.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
+ return imeVisibleOnShade
+ || (!isKeyguardShowing && (vis & InputMethodService.IME_VISIBLE) != 0);
+ }
+
+ /**
+ * Callbacks will get fired once immediately after registering via
+ * {@link #registerNavTaskStateUpdater(NavbarTaskbarStateUpdater)}
+ */
+ public interface NavbarTaskbarStateUpdater {
+ void updateAccessibilityServicesState();
+ void updateAssistantAvailable(boolean available);
+ }
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("NavbarTaskbarFriendster");
+ pw.println(" longPressHomeEnabled=" + mLongPressHomeEnabled);
+ pw.println(" mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled);
+ pw.println(" mAssistantAvailable=" + mAssistantAvailable);
+ pw.println(" mNavBarMode=" + mNavBarMode);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index bc023cc892a6..03bceacdfbdc 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -68,22 +68,17 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
-import android.database.ContentObserver;
import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.inputmethodservice.InputMethodService;
-import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.DeviceConfig;
-import android.provider.Settings;
import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.util.Log;
@@ -200,8 +195,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private final Handler mHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
- private final NavigationBarA11yHelper mNavigationBarA11yHelper;
- private final UserTracker mUserTracker;
+ private final NavBarHelper mNavBarHelper;
private final NotificationShadeDepthController mNotificationShadeDepthController;
private Bundle mSavedState;
@@ -213,9 +207,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private int mNavigationIconHints = 0;
private @TransitionMode int mNavigationBarMode;
private ContentResolver mContentResolver;
- private boolean mAssistantAvailable;
private boolean mLongPressHomeEnabled;
- private boolean mAssistantTouchGestureEnabled;
private int mDisabledFlags1;
private int mDisabledFlags2;
@@ -309,16 +301,31 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
};
+ private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater =
+ new NavBarHelper.NavbarTaskbarStateUpdater() {
+ @Override
+ public void updateAccessibilityServicesState() {
+ updateAcessibilityStateFlags();
+ }
+
+ @Override
+ public void updateAssistantAvailable(boolean available) {
+ // TODO(b/198002034): Content observers currently can still be called back after
+ // being unregistered, and in this case we can ignore the change if the nav bar
+ // has been destroyed already
+ if (mNavigationBarView == null) {
+ return;
+ }
+ mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
+ updateAssistantEntrypoints(available);
+ }
+ };
+
private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
@Override
public void onConnectionChanged(boolean isConnected) {
mNavigationBarView.updateStates();
updateScreenPinningGestures();
-
- // Send the assistant availability upon connection
- if (isConnected) {
- updateAssistantEntrypoints();
- }
}
@Override
@@ -421,20 +428,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
};
- private final ContentObserver mAssistContentObserver = new ContentObserver(
- new Handler(Looper.getMainLooper())) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- // TODO(b/198002034): Content observers currently can still be called back after being
- // unregistered, and in this case we can ignore the change if the nav bar has been
- // destroyed already
- if (mNavigationBarView == null) {
- return;
- }
- updateAssistantEntrypoints();
- }
- };
-
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
new DeviceConfig.OnPropertiesChangedListener() {
@Override
@@ -504,7 +497,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
@Main Handler mainHandler,
NavigationBarOverlayController navbarOverlayController,
UiEventLogger uiEventLogger,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
UserTracker userTracker,
LightBarController mainLightBarController,
LightBarController.Factory lightBarControllerFactory,
@@ -535,8 +528,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
- mNavigationBarA11yHelper = navigationBarA11yHelper;
- mUserTracker = userTracker;
+ mNavBarHelper = navBarHelper;
mNotificationShadeDepthController = notificationShadeDepthController;
mMainLightBarController = mainLightBarController;
mLightBarControllerFactory = lightBarControllerFactory;
@@ -568,18 +560,9 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mIsOnDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
mCommandQueue.addCallback(this);
- mAssistantAvailable = mAssistManagerLazy.get()
- .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
+ mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
mContentResolver = mContext.getContentResolver();
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
- false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED),
- false, mAssistContentObserver, UserHandle.USER_ALL);
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
- false, mAssistContentObserver, UserHandle.USER_ALL);
+ mNavBarHelper.init();
mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean(
R.bool.allow_force_nav_bar_handle_opaque);
mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
@@ -593,7 +576,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
)).filter(duration -> duration != 0);
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener);
- updateAssistantEntrypoints();
if (savedState != null) {
mDisabledFlags1 = savedState.getInt(EXTRA_DISABLE_STATE, 0);
@@ -620,8 +602,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mWindowManager.removeViewImmediate(mNavigationBarView.getRootView());
mNavigationModeController.removeListener(this);
- mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener);
- mContentResolver.unregisterContentObserver(mAssistContentObserver);
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ mNavBarHelper.destroy();
mDeviceProvisionedController.removeCallback(mUserSetupListener);
mNotificationShadeDepthController.removeListener(mDepthListener);
@@ -643,7 +625,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
mNavigationBarView.setBehavior(mBehavior);
- mNavigationBarA11yHelper.registerA11yEventListener(mAccessibilityListener);
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener);
@@ -716,7 +698,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mHandler.removeCallbacks(mAutoDim);
mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
mHandler.removeCallbacks(mEnableLayoutTransitions);
- mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener);
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mFrame = null;
mNavigationBarView = null;
mOrientationHandle = null;
@@ -885,7 +867,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
pw.println(" mCurrentRotation=" + mCurrentRotation);
pw.println(" mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs);
pw.println(" mLongPressHomeEnabled=" + mLongPressHomeEnabled);
- pw.println(" mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled);
pw.println(" mNavigationBarWindowState="
+ windowStateToString(mNavigationBarWindowState));
pw.println(" mNavigationBarMode="
@@ -902,7 +883,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
if (displayId != mDisplayId) {
return;
}
- boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
+ boolean imeShown = mNavBarHelper.isImeShown(vis);
+ showImeSwitcher = imeShown && showImeSwitcher;
int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
imeShown, showImeSwitcher);
if (hints == mNavigationIconHints) return;
@@ -1165,7 +1147,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
- updateAccessibilityServicesState();
+ updateAcessibilityStateFlags();
ButtonDispatcher imeSwitcherButton = mNavigationBarView.getImeSwitchButton();
imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick);
@@ -1390,8 +1372,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
return true;
}
- void updateAccessibilityServicesState() {
- int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+ void updateAcessibilityStateFlags() {
+ int a11yFlags = mNavBarHelper.getA11yButtonState();
if (mNavigationBarView != null) {
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
@@ -1403,7 +1385,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
public void updateSystemUiStateFlags(int a11yFlags) {
if (a11yFlags < 0) {
- a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+ a11yFlags = mNavBarHelper.getA11yButtonState();
}
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -1430,24 +1412,10 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
}
- private void updateAssistantEntrypoints() {
- mAssistantAvailable = mAssistManagerLazy.get()
- .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
- boolean longPressDefault = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
- mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
- mUserTracker.getUserId()) != 0;
- boolean gestureDefault = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_assistTouchGestureEnabledDefault);
- mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0,
- mUserTracker.getUserId()) != 0;
+ private void updateAssistantEntrypoints(boolean assistantAvailable) {
if (mOverviewProxyService.getProxy() != null) {
try {
- mOverviewProxyService.getProxy().onAssistantAvailable(mAssistantAvailable
- && mAssistantTouchGestureEnabled
- && QuickStepContract.isGesturalMode(mNavBarMode));
+ mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable);
} catch (RemoteException e) {
Log.w(TAG, "Unable to send assistant availability data to launcher");
}
@@ -1518,8 +1486,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
@Override
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
- // update assistant entry points on system navigation radio button click
- updateAssistantEntrypoints();
if (!QuickStepContract.isGesturalMode(mode)) {
// Reset the override alpha
@@ -1558,9 +1524,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mNavigationBarView.getBarTransitions().finishAnimations();
}
- private final NavigationBarA11yHelper.NavA11yEventListener mAccessibilityListener =
- this::updateAccessibilityServicesState;
-
private WindowManager.LayoutParams getBarLayoutParams(int rotation) {
WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation);
lp.paramsForRotation = new WindowManager.LayoutParams[4];
@@ -1668,7 +1631,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
// The accessibility settings may be different for the new user
- updateAccessibilityServicesState();
+ updateAcessibilityStateFlags();
}
}
};
@@ -1704,7 +1667,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private final Handler mMainHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
- private final NavigationBarA11yHelper mNavigationBarA11yHelper;
+ private final NavBarHelper mNavBarHelper;
private final UserTracker mUserTracker;
private final LightBarController mMainLightBarController;
private final LightBarController.Factory mLightBarControllerFactory;
@@ -1737,7 +1700,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
@Main Handler mainHandler,
NavigationBarOverlayController navbarOverlayController,
UiEventLogger uiEventLogger,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
UserTracker userTracker,
LightBarController mainLightBarController,
LightBarController.Factory lightBarControllerFactory,
@@ -1767,7 +1730,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mMainHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
- mNavigationBarA11yHelper = navigationBarA11yHelper;
+ mNavBarHelper = navBarHelper;
mUserTracker = userTracker;
mMainLightBarController = mainLightBarController;
mLightBarControllerFactory = lightBarControllerFactory;
@@ -1788,10 +1751,10 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mSplitScreenOptional, mRecentsOptional, mStatusBarOptionalLazy,
mShadeController, mNotificationRemoteInputManager,
mNotificationShadeDepthController, mSystemActions, mMainHandler,
- mNavbarOverlayController, mUiEventLogger, mNavigationBarA11yHelper,
+ mNavbarOverlayController, mUiEventLogger, mNavBarHelper,
mUserTracker, mMainLightBarController, mLightBarControllerFactory,
mMainAutoHideController, mAutoHideControllerFactory, mTelecomManagerOptional,
mInputMethodManager);
}
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java
deleted file mode 100644
index 13e6d8b410d6..000000000000
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.android.systemui.navigationbar;
-
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
-
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
-
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * Extracts shared elements of a11y necessary between navbar and taskbar delegate
- */
-@SysUISingleton
-public final class NavigationBarA11yHelper implements
- AccessibilityButtonModeObserver.ModeChangedListener {
- private final AccessibilityManager mAccessibilityManager;
- private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
- private final List<NavA11yEventListener> mA11yEventListeners = new ArrayList<>();
-
- @Inject
- public NavigationBarA11yHelper(AccessibilityManager accessibilityManager,
- AccessibilityManagerWrapper accessibilityManagerWrapper,
- AccessibilityButtonModeObserver accessibilityButtonModeObserver) {
- mAccessibilityManager = accessibilityManager;
- accessibilityManagerWrapper.addCallback(
- accessibilityManager1 -> NavigationBarA11yHelper.this.dispatchEventUpdate());
- mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
-
- mAccessibilityButtonModeObserver.addListener(this);
- }
-
- public void registerA11yEventListener(NavA11yEventListener listener) {
- mA11yEventListeners.add(listener);
- }
-
- public void removeA11yEventListener(NavA11yEventListener listener) {
- mA11yEventListeners.remove(listener);
- }
-
- private void dispatchEventUpdate() {
- for (NavA11yEventListener listener : mA11yEventListeners) {
- listener.updateAccessibilityServicesState();
- }
- }
-
- @Override
- public void onAccessibilityButtonModeChanged(int mode) {
- dispatchEventUpdate();
- }
-
- /**
- * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
- * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
- *
- * @return the a11y button clickable and long_clickable states, or 0 if there is no
- * a11y button in the navbar
- */
- public int getA11yButtonState() {
- // AccessibilityManagerService resolves services for the current user since the local
- // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
- final List<String> a11yButtonTargets =
- mAccessibilityManager.getAccessibilityShortcutTargets(
- AccessibilityManager.ACCESSIBILITY_BUTTON);
- final int requestingServices = a11yButtonTargets.size();
-
- // If accessibility button is floating menu mode, click and long click state should be
- // disabled.
- if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
- == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
- return 0;
- }
-
- return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
- | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
- }
-
- public interface NavA11yEventListener {
- void updateAccessibilityServicesState();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 472f1f1245e1..0429c022234d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -101,7 +101,7 @@ public class NavigationBarController implements
CommandQueue commandQueue,
@Main Handler mainHandler,
ConfigurationController configurationController,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
TaskbarDelegate taskbarDelegate,
NavigationBar.Factory navigationBarFactory,
DumpManager dumpManager,
@@ -117,7 +117,7 @@ public class NavigationBarController implements
mNavMode = navigationModeController.addListener(this);
mTaskbarDelegate = taskbarDelegate;
mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
- navigationBarA11yHelper, navigationModeController, sysUiFlagsContainer,
+ navBarHelper, navigationModeController, sysUiFlagsContainer,
dumpManager, autoHideController, lightBarController);
mIsTablet = isTablet(mContext);
dumpManager.registerDumpable(this);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 680cc5cb5cba..7c8c3e0dce44 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -110,6 +110,7 @@ public class NavigationBarView extends FrameLayout implements
private final int mNavColorSampleMargin;
private final SysUiState mSysUiFlagContainer;
+ // The current view is one of mHorizontal or mVertical depending on the current configuration
View mCurrentView = null;
private View mVertical;
private View mHorizontal;
@@ -397,12 +398,6 @@ public class NavigationBarView extends FrameLayout implements
}
}
- @Override
- protected boolean onSetAlpha(int alpha) {
- Log.e(TAG, "onSetAlpha", new Throwable());
- return super.onSetAlpha(alpha);
- }
-
public void setAutoHideController(AutoHideController autoHideController) {
mAutoHideController = autoHideController;
}
@@ -505,6 +500,18 @@ public class NavigationBarView extends FrameLayout implements
return mCurrentView;
}
+ /**
+ * Applies {@param consumer} to each of the nav bar views.
+ */
+ public void forEachView(Consumer<View> consumer) {
+ if (mVertical != null) {
+ consumer.accept(mVertical);
+ }
+ if (mHorizontal != null) {
+ consumer.accept(mHorizontal);
+ }
+ }
+
public RotationButtonController getRotationButtonController() {
return mRotationButtonController;
}
@@ -1205,7 +1212,9 @@ public class NavigationBarView extends FrameLayout implements
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mTmpLastConfiguration.updateFrom(mConfiguration);
- mConfiguration.updateFrom(newConfig);
+ final int changes = mConfiguration.updateFrom(newConfig);
+ mFloatingRotationButton.onConfigurationChanged(changes);
+
boolean uiCarModeChanged = updateCarMode();
updateIcons(mTmpLastConfiguration);
updateRecentsIcon();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 089423fd7286..8fb394c06ba1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -41,8 +41,9 @@ import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
-import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
import android.view.Display;
import android.view.InsetsVisibilities;
import android.view.View;
@@ -76,12 +77,13 @@ import javax.inject.Singleton;
public class TaskbarDelegate implements CommandQueue.Callbacks,
OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
ComponentCallbacks, Dumpable {
+ private static final String TAG = TaskbarDelegate.class.getSimpleName();
private final EdgeBackGestureHandler mEdgeBackGestureHandler;
-
+ private boolean mInitialized;
private CommandQueue mCommandQueue;
private OverviewProxyService mOverviewProxyService;
- private NavigationBarA11yHelper mNavigationBarA11yHelper;
+ private NavBarHelper mNavBarHelper;
private NavigationModeController mNavigationModeController;
private SysUiState mSysUiState;
private AutoHideController mAutoHideController;
@@ -89,8 +91,18 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
private LightBarTransitionsController mLightBarTransitionsController;
private int mDisplayId;
private int mNavigationIconHints;
- private final NavigationBarA11yHelper.NavA11yEventListener mNavA11yEventListener =
- this::updateSysuiFlags;
+ private final NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater =
+ new NavBarHelper.NavbarTaskbarStateUpdater() {
+ @Override
+ public void updateAccessibilityServicesState() {
+ updateSysuiFlags();
+ }
+
+ @Override
+ public void updateAssistantAvailable(boolean available) {
+ updateAssistantAvailability(available);
+ }
+ };
private int mDisabledFlags;
private @WindowVisibleState int mTaskBarWindowState = WINDOW_STATE_SHOWING;
private @Behavior int mBehavior;
@@ -130,7 +142,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
public void setDependencies(CommandQueue commandQueue,
OverviewProxyService overviewProxyService,
- NavigationBarA11yHelper navigationBarA11yHelper,
+ NavBarHelper navBarHelper,
NavigationModeController navigationModeController,
SysUiState sysUiState, DumpManager dumpManager,
AutoHideController autoHideController,
@@ -138,7 +150,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
// TODO: adding this in the ctor results in a dagger dependency cycle :(
mCommandQueue = commandQueue;
mOverviewProxyService = overviewProxyService;
- mNavigationBarA11yHelper = navigationBarA11yHelper;
+ mNavBarHelper = navBarHelper;
mNavigationModeController = navigationModeController;
mSysUiState = sysUiState;
dumpManager.registerDumpable(this);
@@ -170,12 +182,16 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
}
public void init(int displayId) {
+ if (mInitialized) {
+ return;
+ }
mDisplayId = displayId;
mCommandQueue.addCallback(this);
mOverviewProxyService.addCallback(this);
mEdgeBackGestureHandler.onNavigationModeChanged(
mNavigationModeController.addListener(this));
- mNavigationBarA11yHelper.registerA11yEventListener(mNavA11yEventListener);
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ mNavBarHelper.init();
mEdgeBackGestureHandler.onNavBarAttached();
// Initialize component callback
Display display = mDisplayManager.getDisplay(displayId);
@@ -185,29 +201,31 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
updateSysuiFlags();
mAutoHideController.setNavigationBar(mAutoHideUiElement);
mLightBarController.setNavigationBar(mLightBarTransitionsController);
+ mInitialized = true;
}
public void destroy() {
+ if (!mInitialized) {
+ return;
+ }
mCommandQueue.removeCallback(this);
mOverviewProxyService.removeCallback(this);
mNavigationModeController.removeListener(this);
- mNavigationBarA11yHelper.removeA11yEventListener(mNavA11yEventListener);
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ mNavBarHelper.destroy();
mEdgeBackGestureHandler.onNavBarDetached();
if (mWindowContext != null) {
mWindowContext.unregisterComponentCallbacks(this);
mWindowContext = null;
}
mAutoHideController.setNavigationBar(null);
-//<<<<<<< HEAD
mLightBarTransitionsController.destroy(mContext);
mLightBarController.setNavigationBar(null);
-// mInitialized = false;
-//=======
-//>>>>>>> 2d145412fc44 (Revert "Extract assistant logic from NavigationBar to share with Taskbar")
+ mInitialized = false;
}
private void updateSysuiFlags() {
- int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+ int a11yFlags = mNavBarHelper.getA11yButtonState();
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -231,10 +249,23 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
.commitUpdate(mDisplayId);
}
+ private void updateAssistantAvailability(boolean assistantAvailable) {
+ if (mOverviewProxyService.getProxy() == null) {
+ return;
+ }
+
+ try {
+ mOverviewProxyService.getProxy().onAssistantAvailable(assistantAvailable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onAssistantAvailable() failed, available: " + assistantAvailable, e);
+ }
+ }
+
@Override
public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
- boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
+ boolean imeShown = mNavBarHelper.isImeShown(vis);
+ showImeSwitcher = imeShown && showImeSwitcher;
int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
imeShown, showImeSwitcher);
if (hints != mNavigationIconHints) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
index 98b914672112..bc3a4fdfd7ff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -116,7 +116,7 @@ class FooterActionsController @Inject constructor(
}
} else if (v === powerMenuLite) {
uiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
- globalActionsDialog.showOrHideDialog(false, true)
+ globalActionsDialog.showOrHideDialog(false, true, v)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index e42c47b1d3be..ee59ae6ab6d7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -770,6 +770,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
public void onAnimationEnd(Animator animation) {
mHeaderAnimating = false;
updateQsState();
+ // Unset the listener, otherwise this may persist for another view property animation
+ getView().animate().setListener(null);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 883552a1f7c3..033fe1e0e6a9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -130,6 +130,7 @@ public class InternetDialog extends SystemUIDialog implements
private boolean mCanConfigMobileData;
// Wi-Fi entries
+ private int mWifiNetworkHeight;
@VisibleForTesting
protected WifiEntry mConnectedWifiEntry;
@VisibleForTesting
@@ -187,6 +188,9 @@ public class InternetDialog extends SystemUIDialog implements
window.setWindowAnimations(R.style.Animation_InternetDialog);
+ mWifiNetworkHeight = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.internet_dialog_wifi_network_height);
+
mInternetDialogLayout = mDialogView.requireViewById(R.id.internet_connectivity_dialog);
mInternetDialogTitle = mDialogView.requireViewById(R.id.internet_dialog_title);
mInternetDialogSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
@@ -335,9 +339,6 @@ public class InternetDialog extends SystemUIDialog implements
mSeeAllLayout.setOnClickListener(v -> onClickSeeMoreButton());
mWiFiToggle.setOnCheckedChangeListener(
(buttonView, isChecked) -> {
- if (isChecked) {
- mWifiScanNotifyLayout.setVisibility(View.GONE);
- }
buttonView.setChecked(isChecked);
mWifiManager.setWifiEnabled(isChecked);
});
@@ -390,6 +391,8 @@ public class InternetDialog extends SystemUIDialog implements
array.recycle();
mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
+ mMobileToggleDivider.setVisibility(
+ mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
}
}
@@ -427,9 +430,26 @@ public class InternetDialog extends SystemUIDialog implements
mSeeAllLayout.setVisibility(View.GONE);
return;
}
- mWifiRecyclerView.setVisibility(mWifiEntriesCount > 0 ? View.VISIBLE : View.GONE);
- mSeeAllLayout.setVisibility(
- (mConnectedWifiEntry != null || mWifiEntriesCount > 0) ? View.VISIBLE : View.GONE);
+ mWifiRecyclerView.setMinimumHeight(mWifiNetworkHeight * getWifiListMaxCount());
+ mWifiRecyclerView.setVisibility(View.VISIBLE);
+ final boolean showSeeAll = mConnectedWifiEntry != null || mWifiEntriesCount > 0;
+ mSeeAllLayout.setVisibility(showSeeAll ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ @VisibleForTesting
+ @MainThread
+ int getWifiListMaxCount() {
+ int count = InternetDialogController.MAX_WIFI_ENTRY_COUNT;
+ if (mEthernetLayout.getVisibility() == View.VISIBLE) {
+ count -= 1;
+ }
+ if (mMobileNetworkLayout.getVisibility() == View.VISIBLE) {
+ count -= 1;
+ }
+ if (mConnectedWifListLayout.getVisibility() == View.VISIBLE) {
+ count -= 1;
+ }
+ return count;
}
@MainThread
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 5bb3413595ba..d64c05fadeb8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -32,7 +32,6 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;
@@ -62,7 +61,6 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
private static final String EXTRA_RESULT_CODE = "extra_resultCode";
private static final String EXTRA_PATH = "extra_path";
private static final String EXTRA_AUDIO_SOURCE = "extra_useAudio";
- private static final String EXTRA_SHOW_TAPS = "extra_showTaps";
private static final String ACTION_START = "com.android.systemui.screenrecord.START";
private static final String ACTION_STOP = "com.android.systemui.screenrecord.STOP";
@@ -74,8 +72,6 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
private final RecordingController mController;
private final KeyguardDismissUtil mKeyguardDismissUtil;
private ScreenRecordingAudioSource mAudioSource;
- private boolean mShowTaps;
- private boolean mOriginalShowTaps;
private ScreenMediaRecorder mRecorder;
private final Executor mLongExecutor;
private final UiEventLogger mUiEventLogger;
@@ -102,15 +98,12 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
* android.content.Intent)}
* @param audioSource The ordinal value of the audio source
* {@link com.android.systemui.screenrecord.ScreenRecordingAudioSource}
- * @param showTaps True to make touches visible while recording
*/
- public static Intent getStartIntent(Context context, int resultCode,
- int audioSource, boolean showTaps) {
+ public static Intent getStartIntent(Context context, int resultCode, int audioSource) {
return new Intent(context, RecordingService.class)
.setAction(ACTION_START)
.putExtra(EXTRA_RESULT_CODE, resultCode)
- .putExtra(EXTRA_AUDIO_SOURCE, audioSource)
- .putExtra(EXTRA_SHOW_TAPS, showTaps);
+ .putExtra(EXTRA_AUDIO_SOURCE, audioSource);
}
@Override
@@ -128,13 +121,6 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
mAudioSource = ScreenRecordingAudioSource
.values()[intent.getIntExtra(EXTRA_AUDIO_SOURCE, 0)];
Log.d(TAG, "recording with audio source" + mAudioSource);
- mShowTaps = intent.getBooleanExtra(EXTRA_SHOW_TAPS, false);
-
- mOriginalShowTaps = Settings.System.getInt(
- getApplicationContext().getContentResolver(),
- Settings.System.SHOW_TOUCHES, 0) != 0;
-
- setTapsVisible(mShowTaps);
mRecorder = new ScreenMediaRecorder(
mUserContextTracker.getUserContext(),
@@ -379,7 +365,6 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
}
private void stopRecording(int userId) {
- setTapsVisible(mOriginalShowTaps);
if (getRecorder() != null) {
getRecorder().end();
saveRecording(userId);
@@ -411,11 +396,6 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis
});
}
- private void setTapsVisible(boolean turnOn) {
- int value = turnOn ? 1 : 0;
- Settings.System.putInt(getContentResolver(), Settings.System.SHOW_TOUCHES, value);
- }
-
/**
* Get an intent to stop the recording service.
* @param context Context from the requesting activity
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index 1fb88dfe9b52..582cc762c1b7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -55,7 +55,6 @@ public class ScreenRecordDialog extends SystemUIDialog {
private final UserContextProvider mUserContextProvider;
@Nullable
private final Runnable mOnStartRecordingClicked;
- private Switch mTapsSwitch;
private Switch mAudioSwitch;
private Spinner mOptions;
@@ -96,7 +95,6 @@ public class ScreenRecordDialog extends SystemUIDialog {
});
mAudioSwitch = findViewById(R.id.screenrecord_audio_switch);
- mTapsSwitch = findViewById(R.id.screenrecord_taps_switch);
mOptions = findViewById(R.id.screen_recording_options);
ArrayAdapter a = new ScreenRecordingAdapter(getContext().getApplicationContext(),
android.R.layout.simple_spinner_dropdown_item,
@@ -110,7 +108,6 @@ public class ScreenRecordDialog extends SystemUIDialog {
private void requestScreenCapture() {
Context userContext = mUserContextProvider.getUserContext();
- boolean showTaps = mTapsSwitch.isChecked();
ScreenRecordingAudioSource audioMode = mAudioSwitch.isChecked()
? (ScreenRecordingAudioSource) mOptions.getSelectedItem()
: NONE;
@@ -118,7 +115,7 @@ public class ScreenRecordDialog extends SystemUIDialog {
RecordingService.REQUEST_CODE,
RecordingService.getStartIntent(
userContext, Activity.RESULT_OK,
- audioMode.ordinal(), showTaps),
+ audioMode.ordinal()),
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
PendingIntent stopIntent = PendingIntent.getService(userContext,
RecordingService.REQUEST_CODE,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 190c773ef422..f23a7cac7d2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -717,6 +717,9 @@ public class KeyguardIndicationController {
textView.setTranslationY(BOUNCE_ANIMATION_FINAL_Y);
ViewClippingUtil.setClippingDeactivated(textView, false,
mClippingParams);
+ // Unset the listener, otherwise this may persist for
+ // another view property animation
+ textView.animate().setListener(null);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFadeAware.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFadeAware.java
new file mode 100644
index 000000000000..8d2e3c92c92a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFadeAware.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Used to let views that have an alpha not apply the HARDWARE layer type directly, and instead
+ * delegate that to specific children. This is useful if we want to fake not having overlapping
+ * rendering to avoid layer trashing, when fading out a view that is also changing.
+ */
+public interface NotificationFadeAware {
+ /**
+ * Calls {@link View#setLayerType} with {@link View#LAYER_TYPE_HARDWARE} if faded and
+ * {@link View#LAYER_TYPE_NONE} otherwise.
+ */
+ static void setLayerTypeForFaded(@Nullable View view, boolean faded) {
+ if (view != null) {
+ int newLayerType = faded ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE;
+ view.setLayerType(newLayerType, null);
+ }
+ }
+
+ /**
+ * Used like {@link View#setLayerType} with {@link View#LAYER_TYPE_HARDWARE} or
+ * {@link View#LAYER_TYPE_NONE} except that instead of necessarily affecting this view
+ * specifically, this may delegate the call to child views.
+ *
+ * When set to <code>true</code>, the view has two possible paths:
+ * 1. If a hardware layer is required to ensure correct appearance of this view, then
+ * set that layer type.
+ * 2. Otherwise, delegate this call to children, who might make that call for themselves.
+ *
+ * When set to <code>false</code>, the view should undo the above, typically by calling
+ * {@link View#setLayerType} with {@link View#LAYER_TYPE_NONE} on itself and children, and
+ * delegating to this method on children where implemented.
+ *
+ * When this delegates to {@link View#setLayerType} on this view or a subview, `null` will be
+ * passed for the `paint` argument of that call.
+ */
+ void setNotificationFaded(boolean faded);
+
+ /**
+ * Interface for the top level notification view that fades and optimizes that through deep
+ * awareness of individual components.
+ */
+ interface FadeOptimizedNotification extends NotificationFadeAware {
+ /** Top-level feature switch */
+ boolean FADE_LAYER_OPTIMIZATION_ENABLED = true;
+
+ /** Determine if the notification is currently faded. */
+ boolean isNotificationFaded();
+ }
+}
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 a2d1040b8a4e..23a0a750561c 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
@@ -87,6 +87,7 @@ import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -125,7 +126,8 @@ import java.util.function.Consumer;
* the group summary (which contains 1 or more child notifications).
*/
public class ExpandableNotificationRow extends ActivatableNotificationView
- implements PluginListener<NotificationMenuRowPlugin>, SwipeableView {
+ implements PluginListener<NotificationMenuRowPlugin>, SwipeableView,
+ NotificationFadeAware.FadeOptimizedNotification {
private static final boolean DEBUG = false;
private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
@@ -138,6 +140,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private boolean mUpdateBackgroundOnUpdate;
private boolean mNotificationTranslationFinished = false;
private boolean mIsSnoozed;
+ private boolean mIsFaded;
/**
* Listener for when {@link ExpandableNotificationRow} is laid out.
@@ -2753,17 +2756,112 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
// alphas are reset
if (mChildrenContainer != null) {
mChildrenContainer.setAlpha(1.0f);
- mChildrenContainer.setLayerType(LAYER_TYPE_NONE, null);
}
for (NotificationContentView l : mLayouts) {
l.setAlpha(1.0f);
- l.setLayerType(LAYER_TYPE_NONE, null);
+ }
+ if (FADE_LAYER_OPTIMIZATION_ENABLED) {
+ setNotificationFaded(false);
+ } else {
+ setNotificationFadedOnChildren(false);
}
} else {
setHeadsUpAnimatingAway(false);
}
}
+ /** Gets the last value set with {@link #setNotificationFaded(boolean)} */
+ @Override
+ public boolean isNotificationFaded() {
+ return mIsFaded;
+ }
+
+ /**
+ * This class needs to delegate the faded state set on it by
+ * {@link com.android.systemui.statusbar.notification.stack.ViewState} to its children.
+ * Having each notification use layerType of HARDWARE anytime it fades in/out can result in
+ * extremely large layers (in the case of groups, it can even exceed the device height).
+ * Because these large renders can cause serious jank when rendering, we instead have
+ * notifications return false from {@link #hasOverlappingRendering()} and delegate the
+ * layerType to child views which really need it in order to render correctly, such as icon
+ * views or the conversation face pile.
+ *
+ * Another compounding factor for notifications is that we change clipping on each frame of the
+ * animation, so the hardware layer isn't able to do any caching at the top level, but the
+ * individual elements we render with hardware layers (e.g. icons) cache wonderfully because we
+ * never invalidate them.
+ */
+ @Override
+ public void setNotificationFaded(boolean faded) {
+ mIsFaded = faded;
+ if (childrenRequireOverlappingRendering()) {
+ // == Simple Scenario ==
+ // If a child (like remote input) needs this to have overlapping rendering, then set
+ // the layerType of this view and reset the children to render as if the notification is
+ // not fading.
+ NotificationFadeAware.setLayerTypeForFaded(this, faded);
+ setNotificationFadedOnChildren(false);
+ } else {
+ // == Delegating Scenario ==
+ // This is the new normal for alpha: Explicitly reset this view's layer type to NONE,
+ // and require that all children use their own hardware layer if they have bad
+ // overlapping rendering.
+ NotificationFadeAware.setLayerTypeForFaded(this, false);
+ setNotificationFadedOnChildren(faded);
+ }
+ }
+
+ /** Private helper for iterating over the layouts and children containers to set faded state */
+ private void setNotificationFadedOnChildren(boolean faded) {
+ delegateNotificationFaded(mChildrenContainer, faded);
+ for (NotificationContentView layout : mLayouts) {
+ delegateNotificationFaded(layout, faded);
+ }
+ }
+
+ private static void delegateNotificationFaded(@Nullable View view, boolean faded) {
+ if (FADE_LAYER_OPTIMIZATION_ENABLED && view instanceof NotificationFadeAware) {
+ ((NotificationFadeAware) view).setNotificationFaded(faded);
+ } else {
+ NotificationFadeAware.setLayerTypeForFaded(view, faded);
+ }
+ }
+
+ /**
+ * Only declare overlapping rendering if independent children of the view require it.
+ */
+ @Override
+ public boolean hasOverlappingRendering() {
+ return super.hasOverlappingRendering() && childrenRequireOverlappingRendering();
+ }
+
+ /**
+ * Because RemoteInputView is designed to be an opaque view that overlaps the Actions row, the
+ * row should require overlapping rendering to ensure that the overlapped view doesn't bleed
+ * through when alpha fading.
+ *
+ * Note that this currently works for top-level notifications which squish their height down
+ * while collapsing the shade, but does not work for children inside groups, because the
+ * accordion affect does not apply to those views, so super.hasOverlappingRendering() will
+ * always return false to avoid the clipping caused when the view's measured height is less than
+ * the 'actual height'.
+ */
+ private boolean childrenRequireOverlappingRendering() {
+ if (!FADE_LAYER_OPTIMIZATION_ENABLED) {
+ return true;
+ }
+ // The colorized background is another layer with which all other elements overlap
+ if (getEntry().getSbn().getNotification().isColorized()) {
+ return true;
+ }
+ // Check if the showing layout has a need for overlapping rendering.
+ // NOTE: We could check both public and private layouts here, but becuause these states
+ // don't animate well, there are bigger visual artifacts if we start changing the shown
+ // layout during shade expansion.
+ NotificationContentView showingLayout = getShowingLayout();
+ return showingLayout != null && showingLayout.requireRowToHaveOverlappingRendering();
+ }
+
@Override
public int getExtraBottomPadding() {
if (mIsSummaryWithChildren && isGroupExpanded()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
index caba3ac7e17b..c66140822d92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
@@ -28,6 +28,7 @@ import android.widget.TextView;
import com.android.internal.widget.ConversationLayout;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
/**
* A hybrid view which may contain information about one ore more conversations.
@@ -138,4 +139,14 @@ public class HybridConversationNotificationView extends HybridNotificationView {
lp.height = size;
view.setLayoutParams(lp);
}
+
+ /**
+ * Apply the faded state as a layer type change to the face pile view which needs to have
+ * overlapping contents render precisely.
+ */
+ @Override
+ public void setNotificationFaded(boolean faded) {
+ super.setNotificationFaded(faded);
+ NotificationFadeAware.setLayerTypeForFaded(mConversationFacePile, faded);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
index bc2adac31d07..c0d85a6a16ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
@@ -28,13 +28,14 @@ import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.TransformState;
/**
* A hybrid view which may contain information about one ore more notifications.
*/
public class HybridNotificationView extends AlphaOptimizedLinearLayout
- implements TransformableView {
+ implements TransformableView, NotificationFadeAware {
protected final ViewTransformationHelper mTransformationHelper = new ViewTransformationHelper();
protected TextView mTitleView;
@@ -148,4 +149,7 @@ public class HybridNotificationView extends AlphaOptimizedLinearLayout
setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
mTransformationHelper.setVisible(visible);
}
+
+ @Override
+ public void setNotificationFaded(boolean faded) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 0ffca30ed409..9cc484c02802 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -46,6 +46,7 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.TransformableView;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -71,7 +72,7 @@ import java.util.List;
* expanded and heads up layout. This class is responsible for clipping the content and and
* switching between the expanded, contracted and the heads up view depending on its clipped size.
*/
-public class NotificationContentView extends FrameLayout {
+public class NotificationContentView extends FrameLayout implements NotificationFadeAware {
private static final String TAG = "NotificationContentView";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -1994,4 +1995,39 @@ public class NotificationContentView extends FrameLayout {
}
return Notification.COLOR_INVALID;
}
+
+ /**
+ * Delegate the faded state to the notification content views which actually
+ * need to have overlapping contents render precisely.
+ */
+ @Override
+ public void setNotificationFaded(boolean faded) {
+ if (mContractedWrapper != null) {
+ mContractedWrapper.setNotificationFaded(faded);
+ }
+ if (mHeadsUpWrapper != null) {
+ mHeadsUpWrapper.setNotificationFaded(faded);
+ }
+ if (mExpandedWrapper != null) {
+ mExpandedWrapper.setNotificationFaded(faded);
+ }
+ if (mSingleLineView != null) {
+ mSingleLineView.setNotificationFaded(faded);
+ }
+ }
+
+ /**
+ * @return true if a visible view has a remote input active, as this requires that the entire
+ * row report that it has overlapping rendering.
+ */
+ public boolean requireRowToHaveOverlappingRendering() {
+ // This inexpensive check is done on both states to avoid state invalidating the result.
+ if (mHeadsUpRemoteInput != null && mHeadsUpRemoteInput.isActive()) {
+ return true;
+ }
+ if (mExpandedRemoteInput != null && mExpandedRemoteInput.isActive()) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
index 12e94cbc1ab9..bb43b95357db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
@@ -21,6 +21,7 @@ import android.view.View
import com.android.internal.widget.CachingIconView
import com.android.internal.widget.CallLayout
import com.android.systemui.R
+import com.android.systemui.statusbar.notification.NotificationFadeAware
import com.android.systemui.statusbar.notification.NotificationUtils
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -37,6 +38,7 @@ class NotificationCallTemplateViewWrapper constructor(
NotificationUtils.getFontScaledHeight(ctx, R.dimen.notification_max_height)
private val callLayout: CallLayout = view as CallLayout
+ private lateinit var conversationIconContainer: View
private lateinit var conversationIconView: CachingIconView
private lateinit var conversationBadgeBg: View
private lateinit var expandBtn: View
@@ -45,6 +47,8 @@ class NotificationCallTemplateViewWrapper constructor(
private fun resolveViews() {
with(callLayout) {
+ conversationIconContainer =
+ requireViewById(com.android.internal.R.id.conversation_icon_container)
conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon)
conversationBadgeBg =
requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
@@ -82,4 +86,14 @@ class NotificationCallTemplateViewWrapper constructor(
}
override fun getMinLayoutHeight(): Int = minHeightWithActions
+
+ /**
+ * Apply the faded state as a layer type change to the face pile view which needs to have
+ * overlapping contents render precisely.
+ */
+ override fun setNotificationFaded(faded: Boolean) {
+ // Do not call super
+ NotificationFadeAware.setLayerTypeForFaded(expandBtn, faded)
+ NotificationFadeAware.setLayerTypeForFaded(conversationIconContainer, faded)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index 3ef5b665d778..e136055b80b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -23,6 +23,7 @@ import com.android.internal.widget.CachingIconView
import com.android.internal.widget.ConversationLayout
import com.android.internal.widget.MessagingLinearLayout
import com.android.systemui.R
+import com.android.systemui.statusbar.notification.NotificationFadeAware
import com.android.systemui.statusbar.notification.NotificationUtils
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.wrapper.NotificationMessagingTemplateViewWrapper.setCustomImageMessageTransform
@@ -42,6 +43,7 @@ class NotificationConversationTemplateViewWrapper constructor(
)
private val conversationLayout: ConversationLayout = view as ConversationLayout
+ private lateinit var conversationIconContainer: View
private lateinit var conversationIconView: CachingIconView
private lateinit var conversationBadgeBg: View
private lateinit var expandBtn: View
@@ -59,6 +61,8 @@ class NotificationConversationTemplateViewWrapper constructor(
messagingLinearLayout = conversationLayout.messagingLinearLayout
imageMessageContainer = conversationLayout.imageMessageContainer
with(conversationLayout) {
+ conversationIconContainer =
+ requireViewById(com.android.internal.R.id.conversation_icon_container)
conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon)
conversationBadgeBg =
requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
@@ -136,4 +140,10 @@ class NotificationConversationTemplateViewWrapper constructor(
minHeightWithActions
else
super.getMinLayoutHeight()
+
+ override fun setNotificationFaded(faded: Boolean) {
+ // Do not call super
+ NotificationFadeAware.setLayerTypeForFaded(expandBtn, faded)
+ NotificationFadeAware.setLayerTypeForFaded(conversationIconContainer, faded)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
index 4c9c2f95b35c..fdff12d22354 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
@@ -22,6 +22,7 @@ import android.view.View;
import com.android.internal.graphics.ColorUtils;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
@@ -86,4 +87,14 @@ public class NotificationCustomViewWrapper extends NotificationViewWrapper {
public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) {
return true;
}
+
+ /**
+ * Apply the faded state as a layer type change to the custom view which needs to have
+ * overlapping contents render precisely.
+ */
+ @Override
+ public void setNotificationFaded(boolean faded) {
+ super.setNotificationFaded(faded);
+ NotificationFadeAware.setLayerTypeForFaded(mView, faded);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
index 8c6fa023d92b..31595397b9b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
@@ -67,4 +68,14 @@ public class NotificationDecoratedCustomViewWrapper extends NotificationTemplate
}
super.onContentUpdated(row);
}
+
+ /**
+ * Apply the faded state as a layer type change to the custom view which needs to have
+ * overlapping contents render precisely.
+ */
+ @Override
+ public void setNotificationFaded(boolean faded) {
+ super.setNotificationFaded(faded);
+ NotificationFadeAware.setLayerTypeForFaded(mWrappedView, faded);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 76301917b458..6c3e0d2f798b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -42,6 +42,7 @@ import com.android.internal.widget.CachingIconView;
import com.android.settingslib.Utils;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.TransformableView;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.TransformState;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -395,4 +396,13 @@ public abstract class NotificationViewWrapper implements TransformableView {
*/
public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) {
}
+
+ /**
+ * Apply the faded state as a layer type change to the views which need to have overlapping
+ * contents render precisely.
+ */
+ public void setNotificationFaded(boolean faded) {
+ NotificationFadeAware.setLayerTypeForFaded(getIcon(), faded);
+ NotificationFadeAware.setLayerTypeForFaded(getExpandButton(), faded);
+ }
}
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 c2716b949b71..7ece3d89ce6e 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
@@ -38,6 +38,7 @@ import com.android.internal.widget.NotificationExpandButton;
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.NotificationGroupingUtil;
+import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -51,7 +52,8 @@ import java.util.List;
/**
* A container containing child notifications
*/
-public class NotificationChildrenContainer extends ViewGroup {
+public class NotificationChildrenContainer extends ViewGroup
+ implements NotificationFadeAware {
@VisibleForTesting
static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2;
@@ -111,6 +113,7 @@ public class NotificationChildrenContainer extends ViewGroup {
private int mCurrentHeaderTranslation = 0;
private float mHeaderVisibleAmount = 1.0f;
private int mUntruncatedChildCount;
+ private boolean mContainingNotificationIsFaded = false;
public NotificationChildrenContainer(Context context) {
this(context, null);
@@ -277,6 +280,7 @@ public class NotificationChildrenContainer extends ViewGroup {
mDividers.add(newIndex, divider);
row.setContentTransformationAmount(0, false /* isLastChild */);
+ row.setNotificationFaded(mContainingNotificationIsFaded);
// It doesn't make sense to keep old animations around, lets cancel them!
ExpandableViewState viewState = row.getViewState();
if (viewState != null) {
@@ -301,6 +305,7 @@ public class NotificationChildrenContainer extends ViewGroup {
});
row.setSystemChildExpanded(false);
+ row.setNotificationFaded(false);
row.setUserLocked(false);
if (!row.isRemoved()) {
mGroupingUtil.restoreChildNotification(row);
@@ -1308,4 +1313,18 @@ public class NotificationChildrenContainer extends ViewGroup {
mNotificationHeaderWrapperLowPriority.setRecentlyAudiblyAlerted(audiblyAlertedRecently);
}
}
+
+ @Override
+ public void setNotificationFaded(boolean faded) {
+ mContainingNotificationIsFaded = faded;
+ if (mNotificationHeaderWrapper != null) {
+ mNotificationHeaderWrapper.setNotificationFaded(faded);
+ }
+ if (mNotificationHeaderWrapperLowPriority != null) {
+ mNotificationHeaderWrapperLowPriority.setNotificationFaded(faded);
+ }
+ for (ExpandableNotificationRow child : mAttachedChildren) {
+ child.setNotificationFaded(faded);
+ }
+ }
}
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 6a127102b726..ab08865c8bef 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
@@ -6092,6 +6092,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return mExpandHelperCallback;
}
+ float getAppearFraction() {
+ return mLastSentAppear;
+ }
+
+ float getExpandedHeight() {
+ return mLastSentExpandedHeight;
+ }
+
/** Enum for selecting some or all notification rows (does not included non-notif views). */
@Retention(SOURCE)
@IntDef({ROWS_ALL, ROWS_HIGH_PRIORITY, ROWS_GENTLE})
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 03fc76760133..f14cc93c2046 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
@@ -858,6 +858,14 @@ public class NotificationStackScrollLayoutController {
mView.setHeadsUpAppearanceController(controller);
}
+ public float getAppearFraction() {
+ return mView.getAppearFraction();
+ }
+
+ public float getExpandedHeight() {
+ return mView.getExpandedHeight();
+ }
+
public void requestLayout() {
mView.requestLayout();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
index 6d82a45313d1..83bea84c8d33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
@@ -29,6 +29,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.NotificationFadeAware.FadeOptimizedNotification;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
@@ -206,14 +207,26 @@ public class ViewState implements Dumpable {
} else if (view.getAlpha() != this.alpha) {
// apply layer type
boolean becomesFullyVisible = this.alpha == 1.0f;
- boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible
- && view.hasOverlappingRendering();
- int layerType = view.getLayerType();
- int newLayerType = newLayerTypeIsHardware
- ? View.LAYER_TYPE_HARDWARE
- : View.LAYER_TYPE_NONE;
- if (layerType != newLayerType) {
- view.setLayerType(newLayerType, null);
+ boolean becomesFaded = !becomesInvisible && !becomesFullyVisible;
+ if (FadeOptimizedNotification.FADE_LAYER_OPTIMIZATION_ENABLED
+ && view instanceof FadeOptimizedNotification) {
+ // NOTE: A view that's going to utilize this interface to avoid having a hardware
+ // layer will have to return false from hasOverlappingRendering(), so we
+ // intentionally do not check that value in this if, even though we do in the else.
+ FadeOptimizedNotification fadeOptimizedView = (FadeOptimizedNotification) view;
+ boolean isFaded = fadeOptimizedView.isNotificationFaded();
+ if (isFaded != becomesFaded) {
+ fadeOptimizedView.setNotificationFaded(becomesFaded);
+ }
+ } else {
+ boolean newLayerTypeIsHardware = becomesFaded && view.hasOverlappingRendering();
+ int layerType = view.getLayerType();
+ int newLayerType = newLayerTypeIsHardware
+ ? View.LAYER_TYPE_HARDWARE
+ : View.LAYER_TYPE_NONE;
+ if (layerType != newLayerType) {
+ view.setLayerType(newLayerType, null);
+ }
}
// apply alpha
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 927b4c8cc919..8a7cf360a254 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -24,6 +24,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
@@ -35,23 +36,29 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.util.ViewController;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import javax.inject.Inject;
+
/**
* Controls the appearance of heads up notifications in the icon area and the header itself.
*/
-public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
- DarkIconDispatcher.DarkReceiver, NotificationWakeUpCoordinator.WakeUpListener {
+@StatusBarFragmentScope
+public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBarView>
+ implements OnHeadsUpChangedListener,
+ DarkIconDispatcher.DarkReceiver,
+ NotificationWakeUpCoordinator.WakeUpListener {
public static final int CONTENT_FADE_DURATION = 110;
public static final int CONTENT_FADE_DELAY = 100;
private final NotificationIconAreaController mNotificationIconAreaController;
private final HeadsUpManagerPhone mHeadsUpManager;
private final NotificationStackScrollLayoutController mStackScrollerController;
- private final HeadsUpStatusBarView mHeadsUpStatusBarView;
private final View mCenteredIconView;
private final View mClockView;
private final View mOperatorNameView;
@@ -67,8 +74,6 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
@VisibleForTesting
float mExpandedHeight;
@VisibleForTesting
- boolean mIsExpanded;
- @VisibleForTesting
float mAppearFraction;
private ExpandableNotificationRow mTrackedChild;
private boolean mShown;
@@ -83,7 +88,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
Point mPoint;
private KeyguardStateController mKeyguardStateController;
-
+ @Inject
public HeadsUpAppearanceController(
NotificationIconAreaController notificationIconAreaController,
HeadsUpManagerPhone headsUpManager,
@@ -92,11 +97,15 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
KeyguardBypassController keyguardBypassController,
KeyguardStateController keyguardStateController,
NotificationWakeUpCoordinator wakeUpCoordinator, CommandQueue commandQueue,
- NotificationPanelViewController notificationPanelViewController, View statusBarView) {
+ NotificationPanelViewController notificationPanelViewController,
+ @RootView PhoneStatusBarView statusBarView) {
this(notificationIconAreaController, headsUpManager, statusBarStateController,
keyguardBypassController, wakeUpCoordinator, keyguardStateController,
commandQueue, notificationStackScrollLayoutController,
notificationPanelViewController,
+ // TODO(b/205609837): We should have the StatusBarFragmentComponent provide these
+ // four views, and then we can delete this constructor and just use the one below
+ // (which also removes the undesirable @VisibleForTesting).
statusBarView.findViewById(R.id.heads_up_status_bar_view),
statusBarView.findViewById(R.id.clock),
statusBarView.findViewById(R.id.operator_name_frame),
@@ -118,25 +127,27 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
View clockView,
View operatorNameView,
View centeredIconView) {
+ super(headsUpStatusBarView);
mNotificationIconAreaController = notificationIconAreaController;
mHeadsUpManager = headsUpManager;
- mHeadsUpManager.addListener(this);
- mHeadsUpStatusBarView = headsUpStatusBarView;
mCenteredIconView = centeredIconView;
- headsUpStatusBarView.setOnDrawingRectChangedListener(
- () -> updateIsolatedIconLocation(true /* requireUpdate */));
+
+ // We may be mid-HUN-expansion when this controller is re-created (for example, if the user
+ // has started pulling down the notification shade from the HUN and then the font size
+ // changes). We need to re-fetch these values since they're used to correctly display the
+ // HUN during this shade expansion.
+ mTrackedChild = notificationPanelViewController.getTrackedHeadsUpNotification();
+ mAppearFraction = stackScrollerController.getAppearFraction();
+ mExpandedHeight = stackScrollerController.getExpandedHeight();
+
mStackScrollerController = stackScrollerController;
mNotificationPanelViewController = notificationPanelViewController;
- notificationPanelViewController.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
- notificationPanelViewController.setHeadsUpAppearanceController(this);
- mStackScrollerController.addOnExpandedHeightChangedListener(mSetExpandedHeight);
mStackScrollerController.setHeadsUpAppearanceController(this);
mClockView = clockView;
mOperatorNameView = operatorNameView;
mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
- mDarkIconDispatcher.addDarkReceiver(this);
- mHeadsUpStatusBarView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ mView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
@@ -146,21 +157,32 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
// trigger scroller to notify the latest panel translation
mStackScrollerController.requestLayout();
}
- mHeadsUpStatusBarView.removeOnLayoutChangeListener(this);
+ mView.removeOnLayoutChangeListener(this);
}
});
mBypassController = bypassController;
mStatusBarStateController = stateController;
mWakeUpCoordinator = wakeUpCoordinator;
- wakeUpCoordinator.addListener(this);
mCommandQueue = commandQueue;
mKeyguardStateController = keyguardStateController;
}
+ @Override
+ protected void onViewAttached() {
+ mHeadsUpManager.addListener(this);
+ mView.setOnDrawingRectChangedListener(
+ () -> updateIsolatedIconLocation(true /* requireUpdate */));
+ mWakeUpCoordinator.addListener(this);
+ mNotificationPanelViewController.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
+ mNotificationPanelViewController.setHeadsUpAppearanceController(this);
+ mStackScrollerController.addOnExpandedHeightChangedListener(mSetExpandedHeight);
+ mDarkIconDispatcher.addDarkReceiver(this);
+ }
- public void destroy() {
+ @Override
+ protected void onViewDetached() {
mHeadsUpManager.removeListener(this);
- mHeadsUpStatusBarView.setOnDrawingRectChangedListener(null);
+ mView.setOnDrawingRectChangedListener(null);
mWakeUpCoordinator.removeListener(this);
mNotificationPanelViewController.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
mNotificationPanelViewController.setHeadsUpAppearanceController(null);
@@ -170,7 +192,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
private void updateIsolatedIconLocation(boolean requireStateUpdate) {
mNotificationIconAreaController.setIsolatedIconLocation(
- mHeadsUpStatusBarView.getIconDrawingRect(), requireStateUpdate);
+ mView.getIconDrawingRect(), requireStateUpdate);
}
@Override
@@ -184,20 +206,20 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
if (shouldBeVisible()) {
newEntry = mHeadsUpManager.getTopEntry();
}
- NotificationEntry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
- mHeadsUpStatusBarView.setEntry(newEntry);
+ NotificationEntry previousEntry = mView.getShowingEntry();
+ mView.setEntry(newEntry);
if (newEntry != previousEntry) {
boolean animateIsolation = false;
if (newEntry == null) {
// no heads up anymore, lets start the disappear animation
setShown(false);
- animateIsolation = !mIsExpanded;
+ animateIsolation = !isExpanded();
} else if (previousEntry == null) {
// We now have a headsUp and didn't have one before. Let's start the disappear
// animation
setShown(true);
- animateIsolation = !mIsExpanded;
+ animateIsolation = !isExpanded();
}
updateIsolatedIconLocation(false /* requireUpdate */);
mNotificationIconAreaController.showIconIsolated(newEntry == null ? null
@@ -210,8 +232,8 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
mShown = isShown;
if (isShown) {
updateParentClipping(false /* shouldClip */);
- mHeadsUpStatusBarView.setVisibility(View.VISIBLE);
- show(mHeadsUpStatusBarView);
+ mView.setVisibility(View.VISIBLE);
+ show(mView);
hide(mClockView, View.INVISIBLE);
if (mCenteredIconView.getVisibility() != View.GONE) {
hide(mCenteredIconView, View.INVISIBLE);
@@ -227,21 +249,21 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
if (mOperatorNameView != null) {
show(mOperatorNameView);
}
- hide(mHeadsUpStatusBarView, View.GONE, () -> {
+ hide(mView, View.GONE, () -> {
updateParentClipping(true /* shouldClip */);
});
}
// Show the status bar icons when the view gets shown / hidden
if (mStatusBarStateController.getState() != StatusBarState.SHADE) {
mCommandQueue.recomputeDisableFlags(
- mHeadsUpStatusBarView.getContext().getDisplayId(), false);
+ mView.getContext().getDisplayId(), false);
}
}
}
private void updateParentClipping(boolean shouldClip) {
ViewClippingUtil.setClippingDeactivated(
- mHeadsUpStatusBarView, !shouldClip, mParentClippingParams);
+ mView, !shouldClip, mParentClippingParams);
}
/**
@@ -310,7 +332,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
*/
public boolean shouldBeVisible() {
boolean notificationsShown = !mWakeUpCoordinator.getNotificationsFullyHidden();
- boolean canShow = !mIsExpanded && notificationsShown;
+ boolean canShow = !isExpanded() && notificationsShown;
if (mBypassController.getBypassEnabled() &&
(mStatusBarStateController.getState() == StatusBarState.KEYGUARD
|| mKeyguardStateController.isKeyguardGoingAway())
@@ -328,17 +350,17 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
public void setAppearFraction(float expandedHeight, float appearFraction) {
boolean changed = expandedHeight != mExpandedHeight;
+ boolean oldIsExpanded = isExpanded();
+
mExpandedHeight = expandedHeight;
mAppearFraction = appearFraction;
- boolean isExpanded = expandedHeight > 0;
// We only notify if the expandedHeight changed and not on the appearFraction, since
// otherwise we may run into an infinite loop where the panel and this are constantly
// updating themselves over just a small fraction
if (changed) {
updateHeadsUpHeaders();
}
- if (isExpanded != mIsExpanded) {
- mIsExpanded = isExpanded;
+ if (isExpanded() != oldIsExpanded) {
updateTopEntry();
}
}
@@ -358,6 +380,10 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
}
}
+ private boolean isExpanded() {
+ return mExpandedHeight > 0;
+ }
+
private void updateHeadsUpHeaders() {
mHeadsUpManager.getAllEntries().forEach(entry -> {
updateHeader(entry);
@@ -376,22 +402,13 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
@Override
public void onDarkChanged(Rect area, float darkIntensity, int tint) {
- mHeadsUpStatusBarView.onDarkChanged(area, darkIntensity, tint);
+ mView.onDarkChanged(area, darkIntensity, tint);
}
public void onStateChanged() {
updateTopEntry();
}
- void readFrom(HeadsUpAppearanceController oldController) {
- if (oldController != null) {
- mTrackedChild = oldController.mTrackedChild;
- mExpandedHeight = oldController.mExpandedHeight;
- mIsExpanded = oldController.mIsExpanded;
- mAppearFraction = oldController.mAppearFraction;
- }
- }
-
@Override
public void onFullyHiddenChanged(boolean isFullyHidden) {
updateTopEntry();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index e26f75f7ce40..d27b5fcf598a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -71,6 +71,7 @@ import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
@@ -150,6 +151,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private ControlsComponent mControlsComponent;
private boolean mControlServicesAvailable = false;
+ @Nullable private View mAmbientIndicationArea;
private ViewGroup mIndicationArea;
private TextView mIndicationText;
private TextView mIndicationTextBottom;
@@ -270,6 +272,29 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
public void initFrom(KeyguardBottomAreaView oldBottomArea) {
setStatusBar(oldBottomArea.mStatusBar);
+
+ // if it exists, continue to use the original ambient indication container
+ // instead of the newly inflated one
+ if (mAmbientIndicationArea != null) {
+ // remove old ambient indication from its parent
+ View originalAmbientIndicationView =
+ oldBottomArea.findViewById(R.id.ambient_indication_container);
+ ((ViewGroup) originalAmbientIndicationView.getParent())
+ .removeView(originalAmbientIndicationView);
+
+ // remove current ambient indication from its parent (discard)
+ ViewGroup ambientIndicationParent = (ViewGroup) mAmbientIndicationArea.getParent();
+ int ambientIndicationIndex =
+ ambientIndicationParent.indexOfChild(mAmbientIndicationArea);
+ ambientIndicationParent.removeView(mAmbientIndicationArea);
+
+ // add the old ambient indication to this view
+ ambientIndicationParent.addView(originalAmbientIndicationView, ambientIndicationIndex);
+ mAmbientIndicationArea = originalAmbientIndicationView;
+
+ // update burn-in offsets
+ dozeTimeTick();
+ }
}
@Override
@@ -283,6 +308,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
mWalletButton = findViewById(R.id.wallet_button);
mControlsButton = findViewById(R.id.controls_button);
mIndicationArea = findViewById(R.id.keyguard_indication_area);
+ mAmbientIndicationArea = findViewById(R.id.ambient_indication_container);
mIndicationText = findViewById(R.id.keyguard_indication_text);
mIndicationTextBottom = findViewById(R.id.keyguard_indication_text_bottom);
mIndicationBottomMargin = getResources().getDimensionPixelSize(
@@ -901,6 +927,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
int burnInYOffset = getBurnInOffset(mBurnInYOffset * 2, false /* xAxis */)
- mBurnInYOffset;
mIndicationArea.setTranslationY(burnInYOffset * mDarkAmount);
+ mAmbientIndicationArea.setTranslationY(burnInYOffset * mDarkAmount);
}
public void setAntiBurnInOffsetX(int burnInXOffset) {
@@ -909,6 +936,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
mBurnInXOffset = burnInXOffset;
mIndicationArea.setTranslationX(burnInXOffset);
+ mAmbientIndicationArea.setTranslationX(burnInXOffset);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
index 3f3328172e12..68ab07798520 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
@@ -124,6 +124,9 @@ public class LightsOutNotifController {
public void onAnimationEnd(Animator a) {
mLightsOutNotifView.setAlpha(showDot ? 1 : 0);
mLightsOutNotifView.setVisibility(showDot ? View.VISIBLE : View.GONE);
+ // Unset the listener, otherwise this may persist for
+ // another view property animation
+ mLightsOutNotifView.animate().setListener(null);
}
})
.start();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 9b5fa2a49ff1..0312c30c73ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -88,6 +88,7 @@ import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
+import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintSet;
import com.android.internal.annotations.VisibleForTesting;
@@ -451,9 +452,13 @@ public class NotificationPanelViewController extends PanelViewController {
private boolean mUserSetupComplete;
private boolean mHideIconsDuringLaunchAnimation = true;
private int mStackScrollerMeasuringPass;
- private ArrayList<Consumer<ExpandableNotificationRow>>
- mTrackingHeadsUpListeners =
- new ArrayList<>();
+ /**
+ * Non-null if there's a heads-up notification that we're currently tracking the position of.
+ */
+ @Nullable
+ private ExpandableNotificationRow mTrackedHeadsUpNotification;
+ private final ArrayList<Consumer<ExpandableNotificationRow>>
+ mTrackingHeadsUpListeners = new ArrayList<>();
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private int mPanelAlpha;
@@ -3050,18 +3055,24 @@ public class NotificationPanelViewController extends PanelViewController {
mQsExpandImmediate = false;
mNotificationStackScrollLayoutController.setShouldShowShelfOnly(false);
mTwoFingerQsExpandPossible = false;
- notifyListenersTrackingHeadsUp(null);
+ updateTrackingHeadsUp(null);
mExpandingFromHeadsUp = false;
setPanelScrimMinFraction(0.0f);
}
- private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) {
+ private void updateTrackingHeadsUp(@Nullable ExpandableNotificationRow pickedChild) {
+ mTrackedHeadsUpNotification = pickedChild;
for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) {
Consumer<ExpandableNotificationRow> listener = mTrackingHeadsUpListeners.get(i);
listener.accept(pickedChild);
}
}
+ @Nullable
+ public ExpandableNotificationRow getTrackedHeadsUpNotification() {
+ return mTrackedHeadsUpNotification;
+ }
+
private void setListening(boolean listening) {
mKeyguardStatusBarViewController.setBatteryListening(listening);
if (mQs == null) return;
@@ -3298,7 +3309,7 @@ public class NotificationPanelViewController extends PanelViewController {
public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
if (pickedChild != null) {
- notifyListenersTrackingHeadsUp(pickedChild);
+ updateTrackingHeadsUp(pickedChild);
mExpandingFromHeadsUp = true;
}
// otherwise we update the state when the expansion is finished
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
index 9cefded72365..bf5467716910 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
@@ -120,6 +120,9 @@ public class SettingsButton extends AlphaOptimizedImageButton {
setAlpha(1f);
setTranslationX(0);
cancelLongClick();
+ // Unset the listener, otherwise this may persist for
+ // another view property animation
+ animate().setListener(null);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 0f55f289ff27..d96fec5bf92e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -519,7 +519,6 @@ public class StatusBar extends SystemUI implements
private QSPanelController mQSPanelController;
private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
- private final PhoneStatusBarViewController.Factory mPhoneStatusBarViewControllerFactory;
KeyguardIndicationController mKeyguardIndicationController;
private View mReportRejectedTouch;
@@ -664,7 +663,6 @@ public class StatusBar extends SystemUI implements
private boolean mNoAnimationOnNextBarModeChange;
private final SysuiStatusBarStateController mStatusBarStateController;
- private HeadsUpAppearanceController mHeadsUpAppearanceController;
private final ActivityLaunchAnimator mActivityLaunchAnimator;
private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
protected StatusBarNotificationPresenter mPresenter;
@@ -765,7 +763,6 @@ public class StatusBar extends SystemUI implements
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
OperatorNameViewController.Factory operatorNameViewControllerFactory,
- PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
PhoneStatusBarPolicy phoneStatusBarPolicy,
KeyguardIndicationController keyguardIndicationController,
DemoModeController demoModeController,
@@ -804,7 +801,6 @@ public class StatusBar extends SystemUI implements
mKeyguardStateController = keyguardStateController;
mHeadsUpManager = headsUpManagerPhone;
mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
- mPhoneStatusBarViewControllerFactory = phoneStatusBarViewControllerFactory;
mKeyguardIndicationController = keyguardIndicationController;
mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
mDynamicPrivacyController = dynamicPrivacyController;
@@ -1149,12 +1145,8 @@ public class StatusBar extends SystemUI implements
}
mStatusBarView = statusBarFragmentComponent.getPhoneStatusBarView();
-
- // TODO(b/205609837): Migrate this to StatusBarFragmentComponent.
- mPhoneStatusBarViewController = mPhoneStatusBarViewControllerFactory
- .create(mStatusBarView, mNotificationPanelViewController
- .getStatusBarTouchEventHandler());
- mPhoneStatusBarViewController.init();
+ mPhoneStatusBarViewController =
+ statusBarFragmentComponent.getPhoneStatusBarViewController();
// Ensure we re-propagate panel expansion values to the panel controller and
// any listeners it may have, such as PanelBar. This will also ensure we
@@ -1164,21 +1156,6 @@ public class StatusBar extends SystemUI implements
mNotificationPanelViewController.updatePanelExpansionAndVisibility();
setBouncerShowingForStatusBarComponents(mBouncerShowing);
- HeadsUpAppearanceController oldController = mHeadsUpAppearanceController;
- if (mHeadsUpAppearanceController != null) {
- // This view is being recreated, let's destroy the old one
- mHeadsUpAppearanceController.destroy();
- }
- // TODO (b/136993073) Separate notification shade and status bar
- // TODO(b/205609837): Migrate this to StatusBarFragmentComponent.
- mHeadsUpAppearanceController = new HeadsUpAppearanceController(
- mNotificationIconAreaController, mHeadsUpManager,
- mStackScrollerController,
- mStatusBarStateController, mKeyguardBypassController,
- mKeyguardStateController, mWakeUpCoordinator, mCommandQueue,
- mNotificationPanelViewController, mStatusBarView);
- mHeadsUpAppearanceController.readFrom(oldController);
-
mLightsOutNotifController.setLightsOutNotifView(
mStatusBarView.findViewById(R.id.notification_lights_out));
mNotificationShadeWindowViewController.setStatusBarView(mStatusBarView);
@@ -1904,10 +1881,6 @@ public class StatusBar extends SystemUI implements
mScrimController.setKeyguardOccluded(occluded);
}
- public boolean headsUpShouldBeVisible() {
- return mHeadsUpAppearanceController.shouldBeVisible();
- }
-
/** A launch animation was cancelled. */
//TODO: These can / should probably be moved to NotificationPresenter or ShadeController
public void onLaunchAnimationCancelled(boolean isLaunchForActivity) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 0d23d663c51c..e2bf0db6eb1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -555,12 +555,13 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public void onStartedWakingUp() {
mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
.setAnimationsDisabled(false);
- View currentView = getCurrentNavBarView();
- if (currentView != null) {
- currentView.animate()
- .alpha(1f)
- .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
- .start();
+ NavigationBarView navBarView = mStatusBar.getNavigationBarView();
+ if (navBarView != null) {
+ navBarView.forEachView(view ->
+ view.animate()
+ .alpha(1f)
+ .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
+ .start());
}
}
@@ -568,12 +569,13 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public void onStartedGoingToSleep() {
mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
.setAnimationsDisabled(true);
- View currentView = getCurrentNavBarView();
- if (currentView != null) {
- currentView.animate()
- .alpha(0f)
- .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
- .start();
+ NavigationBarView navBarView = mStatusBar.getNavigationBarView();
+ if (navBarView != null) {
+ navBarView.forEachView(view ->
+ view.animate()
+ .alpha(0f)
+ .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
+ .start());
}
}
@@ -1015,17 +1017,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mStatusBar.onKeyguardViewManagerStatesUpdated();
}
- /**
- * Updates the visibility of the nav bar content views.
- */
- private void updateNavigationBarContentVisibility(boolean navBarContentVisible) {
- final NavigationBarView navBarView = mStatusBar.getNavigationBarView();
- if (navBarView != null && navBarView.getCurrentView() != null) {
- final View currentView = navBarView.getCurrentView();
- currentView.setVisibility(navBarContentVisible ? View.VISIBLE : View.INVISIBLE);
- }
- }
-
private View getCurrentNavBarView() {
final NavigationBarView navBarView = mStatusBar.getNavigationBarView();
return navBarView != null ? navBarView.getCurrentView() : null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 50e6151bd3a3..633be3c32d03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -88,7 +88,6 @@ import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
-import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -216,7 +215,6 @@ public interface StatusBarPhoneModule {
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
OperatorNameViewController.Factory operatorNameViewControllerFactory,
- PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
PhoneStatusBarPolicy phoneStatusBarPolicy,
KeyguardIndicationController keyguardIndicationController,
DemoModeController demoModeController,
@@ -317,7 +315,6 @@ public interface StatusBarPhoneModule {
extensionController,
userInfoControllerImpl,
operatorNameViewControllerFactory,
- phoneStatusBarViewControllerFactory,
phoneStatusBarPolicy,
keyguardIndicationController,
demoModeController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 76176df136b5..32f3ba4a6f5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -68,7 +68,7 @@ import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
-@Module
+@Module(subcomponents = StatusBarFragmentComponent.class)
public abstract class StatusBarViewModule {
public static final String SPLIT_SHADE_HEADER = "split_shade_header";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index b32acce51e84..d4d890773927 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -329,8 +329,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
protected int adjustDisableFlags(int state) {
- boolean headsUpVisible = mStatusBarOptionalLazy.get()
- .map(StatusBar::headsUpShouldBeVisible).orElse(false);
+ boolean headsUpVisible =
+ mStatusBarFragmentComponent.getHeadsUpAppearanceController().shouldBeVisible();
if (headsUpVisible) {
state |= DISABLE_CLOCK;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
index 47c187540740..3656ed116b32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
@@ -18,7 +18,9 @@ package com.android.systemui.statusbar.phone.fragment.dagger;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
+import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import dagger.BindsInstance;
@@ -56,6 +58,8 @@ public interface StatusBarFragmentComponent {
// No one accesses this controller, so we need to make sure we reference it here so it does
// get initialized.
getBatteryMeterViewController().init();
+ getHeadsUpAppearanceController().init();
+ getPhoneStatusBarViewController().init();
}
/** */
@@ -66,4 +70,12 @@ public interface StatusBarFragmentComponent {
@StatusBarFragmentScope
@RootView
PhoneStatusBarView getPhoneStatusBarView();
+
+ /** */
+ @StatusBarFragmentScope
+ PhoneStatusBarViewController getPhoneStatusBarViewController();
+
+ /** */
+ @StatusBarFragmentScope
+ HeadsUpAppearanceController getHeadsUpAppearanceController();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
index 969361bb6100..d2445580d56b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -19,7 +19,9 @@ package com.android.systemui.statusbar.phone.fragment.dagger;
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
+import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import dagger.Module;
@@ -43,4 +45,16 @@ public interface StatusBarFragmentModule {
static BatteryMeterView provideBatteryMeterView(@RootView PhoneStatusBarView view) {
return view.findViewById(R.id.battery);
}
+
+ /** */
+ @Provides
+ @StatusBarFragmentScope
+ static PhoneStatusBarViewController providePhoneStatusBarViewController(
+ PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
+ @RootView PhoneStatusBarView phoneStatusBarView,
+ NotificationPanelViewController notificationPanelViewController) {
+ return phoneStatusBarViewControllerFactory.create(
+ phoneStatusBarView,
+ notificationPanelViewController.getStatusBarTouchEventHandler());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 3b1c5f32a772..bf5522c50a78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -54,6 +54,7 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.model.SysUiState;
@@ -115,6 +116,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private UserContextProvider mUserContextProvider;
@Mock private StatusBar mStatusBar;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock private DialogLaunchAnimator mDialogLaunchAnimator;
private TestableLooper mTestableLooper;
@@ -159,8 +161,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mHandler,
mPackageManager,
Optional.of(mStatusBar),
- mKeyguardUpdateMonitor
- );
+ mKeyguardUpdateMonitor,
+ mDialogLaunchAnimator);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors();
@@ -218,7 +220,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener);
- gestureListener.onSingleTapConfirmed(null);
+ gestureListener.onSingleTapUp(null);
verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
}
@@ -444,12 +446,12 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
// When entering power menu from lockscreen, with smart lock enabled
when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
- mGlobalActionsDialogLite.showOrHideDialog(true, true);
+ mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
// Then smart lock will be disabled
verify(mLockPatternUtils).requireCredentialEntry(eq(user));
// hide dialog again
- mGlobalActionsDialogLite.showOrHideDialog(true, true);
+ mGlobalActionsDialogLite.showOrHideDialog(true, true, null /* view */);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
new file mode 100644
index 000000000000..a445d6f4e5c5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+import dagger.Lazy;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NavBarHelperTest extends SysuiTestCase {
+
+ @Mock
+ AccessibilityManager mAccessibilityManager;
+ @Mock
+ AccessibilityManagerWrapper mAccessibilityManagerWrapper;
+ @Mock
+ AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
+ @Mock
+ OverviewProxyService mOverviewProxyService;
+ @Mock
+ Lazy<AssistManager> mAssistManagerLazy;
+ @Mock
+ AssistManager mAssistManager;
+ @Mock
+ NavigationModeController mNavigationModeController;
+ @Mock
+ UserTracker mUserTracker;
+ @Mock
+ ComponentName mAssistantComponent;
+ @Mock
+ DumpManager mDumpManager;
+ @Mock
+ NavBarHelper.NavbarTaskbarStateUpdater mNavbarTaskbarStateUpdater;
+
+ private NavBarHelper mNavBarHelper;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mAssistManagerLazy.get()).thenReturn(mAssistManager);
+ when(mAssistManager.getAssistInfoForUser(anyInt())).thenReturn(mAssistantComponent);
+ when(mUserTracker.getUserId()).thenReturn(1);
+
+ mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
+ mAccessibilityManagerWrapper, mAccessibilityButtonModeObserver,
+ mOverviewProxyService, mAssistManagerLazy, () -> Optional.of(mock(StatusBar.class)),
+ mNavigationModeController, mUserTracker, mDumpManager);
+
+ }
+
+ @Test
+ public void registerListenersInCtor() {
+ verify(mAccessibilityButtonModeObserver, times(1)).addListener(mNavBarHelper);
+ verify(mNavigationModeController, times(1)).addListener(mNavBarHelper);
+ verify(mOverviewProxyService, times(1)).addCallback(mNavBarHelper);
+ }
+
+ @Test
+ public void registerAssistantContentObserver() {
+ mNavBarHelper.init();
+ verify(mAssistManager, times(1)).getAssistInfoForUser(anyInt());
+ }
+
+ @Test
+ public void callbacksFiredWhenRegistering() {
+ mNavBarHelper.init();
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void assistantCallbacksFiredAfterConnecting() {
+ mNavBarHelper.init();
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ mNavBarHelper.onConnectionChanged(false);
+ // assert no more callbacks fired
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+
+ mNavBarHelper.onConnectionChanged(true);
+ // assert no more callbacks fired
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(2))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void a11yCallbacksFiredAfterModeChange() {
+ mNavBarHelper.init();
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ mNavBarHelper.onAccessibilityButtonModeChanged(0);
+ verify(mNavbarTaskbarStateUpdater, times(2))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void assistantCallbacksFiredAfterNavModeChange() {
+ mNavBarHelper.init();
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ mNavBarHelper.onNavigationModeChanged(0);
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(2))
+ .updateAssistantAvailable(anyBoolean());
+ }
+
+ @Test
+ public void removeListenerNoCallbacksFired() {
+ mNavBarHelper.init();
+ // 1st set of callbacks get called when registering
+ mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ // Remove listener
+ mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
+
+ // Would have fired 2nd callback if not removed
+ mNavBarHelper.onAccessibilityButtonModeChanged(0);
+
+ // assert no more callbacks fired
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAccessibilityServicesState();
+ verify(mNavbarTaskbarStateUpdater, times(1))
+ .updateAssistantAvailable(anyBoolean());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 62832205a097..9d2541c0150f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -83,7 +83,7 @@ public class NavigationBarControllerTest extends SysuiTestCase {
mCommandQueue,
Dependency.get(Dependency.MAIN_HANDLER),
mock(ConfigurationController.class),
- mock(NavigationBarA11yHelper.class),
+ mock(NavBarHelper.class),
mock(TaskbarDelegate.class),
mNavigationBarFactory,
mock(DumpManager.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 223ffbd7bba5..5003013358be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -23,17 +23,20 @@ import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
import static android.inputmethodservice.InputMethodService.IME_VISIBLE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+import static android.view.WindowInsets.Type.ime;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
import static com.android.systemui.navigationbar.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -57,6 +60,7 @@ import android.view.Display;
import android.view.DisplayInfo;
import android.view.MotionEvent;
import android.view.View;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityManager;
@@ -72,6 +76,7 @@ import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -83,8 +88,10 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -98,6 +105,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
import java.util.Optional;
@@ -130,8 +138,7 @@ public class NavigationBarTest extends SysuiTestCase {
EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory;
@Mock
EdgeBackGestureHandler mEdgeBackGestureHandler;
- @Mock
- NavigationBarA11yHelper mNavigationBarA11yHelper;
+ NavBarHelper mNavBarHelper;
@Mock
private LightBarController mLightBarController;
@Mock
@@ -148,6 +155,8 @@ public class NavigationBarTest extends SysuiTestCase {
private InputMethodManager mInputMethodManager;
@Mock
private AssistManager mAssistManager;
+ @Mock
+ private StatusBar mStatusBar;
@Rule
public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
@@ -172,6 +181,12 @@ public class NavigationBarTest extends SysuiTestCase {
mDependency.injectTestDependency(OverviewProxyService.class, mOverviewProxyService);
mDependency.injectTestDependency(NavigationModeController.class, mNavigationModeController);
TestableLooper.get(this).runWithLooper(() -> {
+ mNavBarHelper = spy(new NavBarHelper(mContext, mock(AccessibilityManager.class),
+ mock(AccessibilityManagerWrapper.class),
+ mock(AccessibilityButtonModeObserver.class), mOverviewProxyService,
+ () -> mock(AssistManager.class), () -> Optional.of(mStatusBar),
+ mock(NavigationModeController.class), mock(UserTracker.class),
+ mock(DumpManager.class)));
mNavigationBar = createNavBar(mContext);
mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
});
@@ -220,6 +235,7 @@ public class NavigationBarTest extends SysuiTestCase {
new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_SYSTEMUI)
.setLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 100)
.build());
+ when(mNavBarHelper.getLongPressHomeEnabled()).thenReturn(true);
mNavigationBar.onViewAttachedToWindow(mNavigationBar.createView(null));
mNavigationBar.onHomeTouch(mNavigationBar.getView(), MotionEvent.obtain(
@@ -255,6 +271,11 @@ public class NavigationBarTest extends SysuiTestCase {
// Create default & external NavBar fragment.
NavigationBar defaultNavBar = mNavigationBar;
NavigationBar externalNavBar = mExternalDisplayNavigationBar;
+ NotificationShadeWindowView mockShadeWindowView = mock(NotificationShadeWindowView.class);
+ WindowInsets windowInsets = new WindowInsets.Builder().setVisible(ime(), false).build();
+ doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
+ doReturn(mockShadeWindowView).when(mStatusBar).getNotificationShadeWindowView();
+ doReturn(true).when(mockShadeWindowView).isAttachedToWindow();
doNothing().when(defaultNavBar).checkNavBarModes();
doNothing().when(externalNavBar).checkNavBarModes();
defaultNavBar.createView(null);
@@ -281,17 +302,51 @@ public class NavigationBarTest extends SysuiTestCase {
}
@Test
+ public void testSetImeWindowStatusWhenKeyguardLockingAndImeInsetsChange() {
+ NotificationShadeWindowView mockShadeWindowView = mock(NotificationShadeWindowView.class);
+ doReturn(mockShadeWindowView).when(mStatusBar).getNotificationShadeWindowView();
+ doReturn(true).when(mockShadeWindowView).isAttachedToWindow();
+ doNothing().when(mNavigationBar).checkNavBarModes();
+ mNavigationBar.createView(null);
+ WindowInsets windowInsets = new WindowInsets.Builder().setVisible(ime(), false).build();
+ doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
+
+ // Verify navbar altered back icon when an app is showing IME
+ mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
+ BACK_DISPOSITION_DEFAULT, true);
+ assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
+ assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+
+ // Verify navbar didn't alter and showing back icon when the keyguard is showing without
+ // requesting IME insets visible.
+ doReturn(true).when(mStatusBar).isKeyguardShowing();
+ mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
+ BACK_DISPOSITION_DEFAULT, true);
+ assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
+ assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+
+ // Verify navbar altered and showing back icon when the keyguard is showing and
+ // requesting IME insets visible.
+ windowInsets = new WindowInsets.Builder().setVisible(ime(), true).build();
+ doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
+ mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
+ BACK_DISPOSITION_DEFAULT, true);
+ assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
+ assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+ }
+
+ @Test
public void testA11yEventAfterDetach() {
View v = mNavigationBar.createView(null);
mNavigationBar.onViewAttachedToWindow(v);
- verify(mNavigationBarA11yHelper).registerA11yEventListener(any(
- NavigationBarA11yHelper.NavA11yEventListener.class));
+ verify(mNavBarHelper).registerNavTaskStateUpdater(any(
+ NavBarHelper.NavbarTaskbarStateUpdater.class));
mNavigationBar.onViewDetachedFromWindow(v);
- verify(mNavigationBarA11yHelper).removeA11yEventListener(any(
- NavigationBarA11yHelper.NavA11yEventListener.class));
+ verify(mNavBarHelper).removeNavTaskStateUpdater(any(
+ NavBarHelper.NavbarTaskbarStateUpdater.class));
// Should be safe even though the internal view is now null.
- mNavigationBar.updateAccessibilityServicesState();
+ mNavigationBar.updateAcessibilityStateFlags();
}
private NavigationBar createNavBar(Context context) {
@@ -313,7 +368,7 @@ public class NavigationBarTest extends SysuiTestCase {
Optional.of(mock(Pip.class)),
Optional.of(mock(LegacySplitScreen.class)),
Optional.of(mock(Recents.class)),
- () -> Optional.of(mock(StatusBar.class)),
+ () -> Optional.of(mStatusBar),
mock(ShadeController.class),
mock(NotificationRemoteInputManager.class),
mock(NotificationShadeDepthController.class),
@@ -321,7 +376,7 @@ public class NavigationBarTest extends SysuiTestCase {
mHandler,
mock(NavigationBarOverlayController.class),
mUiEventLogger,
- mNavigationBarA11yHelper,
+ mNavBarHelper,
mock(UserTracker.class),
mLightBarController,
mLightBarcontrollerFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index b32b4d4f3810..339d5bb04d74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -1,5 +1,7 @@
package com.android.systemui.qs.tiles.dialog;
+import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -219,7 +221,7 @@ public class InternetDialogTest extends SysuiTestCase {
}
@Test
- public void updateDialog_wifiOnAndNoWifiEntry_hideWifiEntryAndSeeAll() {
+ public void updateDialog_wifiOnAndNoWifiEntry_showWifiListAndSeeAllArea() {
// The precondition WiFi ON is already in setUp()
mInternetDialog.mConnectedWifiEntry = null;
mInternetDialog.mWifiEntriesCount = 0;
@@ -227,19 +229,21 @@ public class InternetDialogTest extends SysuiTestCase {
mInternetDialog.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
- assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
- assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+ // Show a blank block to fix the dialog height even if there is no WiFi list
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
}
@Test
- public void updateDialog_wifiOnAndHasConnectedWifi_showConnectedWifiAndSeeAll() {
+ public void updateDialog_wifiOnAndHasConnectedWifi_showAllWifiAndSeeAllArea() {
// The preconditions WiFi ON and WiFi entries are already in setUp()
mInternetDialog.mWifiEntriesCount = 0;
mInternetDialog.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+ // Show a blank block to fix the dialog height even if there is no WiFi list
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -412,4 +416,54 @@ public class InternetDialogTest extends SysuiTestCase {
assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
assertThat(mInternetDialog.mIsSearchingHidden).isTrue();
}
+
+ @Test
+ public void getWifiListMaxCount_returnCountCorrectly() {
+ // Ethernet, MobileData, ConnectedWiFi are all hidden.
+ // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
+ setNetworkVisible(false, false, false);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
+
+ // Only one of Ethernet, MobileData, ConnectedWiFi is displayed.
+ // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT - 1.
+ setNetworkVisible(true, false, false);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+ setNetworkVisible(false, true, false);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+ setNetworkVisible(false, false, true);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+ // Only one of Ethernet, MobileData, ConnectedWiFi is hidden.
+ // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT - 2.
+ setNetworkVisible(true, true, false);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+
+ setNetworkVisible(true, false, true);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+
+ setNetworkVisible(false, true, true);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+
+ // Ethernet, MobileData, ConnectedWiFi are all displayed.
+ // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT - 3.
+ setNetworkVisible(true, true, true);
+
+ assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 3);
+ }
+
+ private void setNetworkVisible(boolean ethernetVisible, boolean mobileDataVisible,
+ boolean connectedWifiVisible) {
+ mEthernet.setVisibility(ethernetVisible ? View.VISIBLE : View.GONE);
+ mMobileDataToggle.setVisibility(mobileDataVisible ? View.VISIBLE : View.GONE);
+ mConnectedWifi.setVisibility(connectedWifiVisible ? View.VISIBLE : View.GONE);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index 91cafead596c..4073bb336283 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -104,7 +104,7 @@ public class RecordingServiceTest extends SysuiTestCase {
@Test
public void testLogStartRecording() {
- Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0, false);
+ Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0);
mRecordingService.onStartCommand(startIntent, 0, 0);
verify(mUiEventLogger, times(1)).log(Events.ScreenRecordEvent.SCREEN_RECORD_START);
@@ -137,7 +137,7 @@ public class RecordingServiceTest extends SysuiTestCase {
// When the screen recording does not start properly
doThrow(new RuntimeException("fail")).when(mScreenMediaRecorder).start();
- Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0, false);
+ Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0);
mRecordingService.onStartCommand(startIntent, 0, 0);
// Then the state is set to not recording
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 bafbccdb87d2..db5fd262a1d1 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
@@ -162,8 +162,11 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
}
@Test
- public void testHeaderReadFromOldController() {
- mHeadsUpAppearanceController.setAppearFraction(1.0f, 1.0f);
+ public void constructor_animationValuesUpdated() {
+ float appearFraction = .75f;
+ float expandedHeight = 400f;
+ when(mStackScrollerController.getAppearFraction()).thenReturn(appearFraction);
+ when(mStackScrollerController.getExpandedHeight()).thenReturn(expandedHeight);
HeadsUpAppearanceController newController = new HeadsUpAppearanceController(
mock(NotificationIconAreaController.class),
@@ -179,14 +182,9 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
new View(mContext),
new View(mContext),
new View(mContext));
- newController.readFrom(mHeadsUpAppearanceController);
-
- Assert.assertEquals(mHeadsUpAppearanceController.mExpandedHeight,
- newController.mExpandedHeight, 0.0f);
- Assert.assertEquals(mHeadsUpAppearanceController.mAppearFraction,
- newController.mAppearFraction, 0.0f);
- Assert.assertEquals(mHeadsUpAppearanceController.mIsExpanded,
- newController.mIsExpanded);
+
+ Assert.assertEquals(expandedHeight, newController.mExpandedHeight, 0.0f);
+ Assert.assertEquals(appearFraction, newController.mAppearFraction, 0.0f);
}
@Test
@@ -195,7 +193,9 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
reset(mDarkIconDispatcher);
reset(mPanelView);
reset(mStackScrollerController);
- mHeadsUpAppearanceController.destroy();
+
+ mHeadsUpAppearanceController.onViewDetached();
+
verify(mHeadsUpManager).removeListener(any());
verify(mDarkIconDispatcher).removeDarkReceiver((DarkIconDispatcher.DarkReceiver) any());
verify(mPanelView).removeTrackingHeadsUpListener(any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
index 210744eb30cf..3257a84f14d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
@@ -47,8 +47,8 @@ class KeyguardBottomAreaTest : SysuiTestCase() {
@Test
fun initFrom_doesntCrash() {
- val other = LayoutInflater.from(mContext).inflate(
- R.layout.keyguard_bottom_area, null, false) as KeyguardBottomAreaView
+ val other = LayoutInflater.from(mContext).inflate(R.layout.keyguard_bottom_area,
+ null, false) as KeyguardBottomAreaView
other.initFrom(mKeyguardBottomArea)
other.launchVoiceAssist()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index d58e13cd8a64..b0933b51d1d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -275,7 +275,6 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private StartingSurface mStartingSurface;
@Mock private OperatorNameViewController mOperatorNameViewController;
@Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
- @Mock private PhoneStatusBarViewController.Factory mPhoneStatusBarViewControllerFactory;
@Mock private ActivityLaunchAnimator mActivityLaunchAnimator;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
@@ -433,7 +432,6 @@ public class StatusBarTest extends SysuiTestCase {
mExtensionController,
mUserInfoControllerImpl,
mOperatorNameViewControllerFactory,
- mPhoneStatusBarViewControllerFactory,
mPhoneStatusBarPolicy,
mKeyguardIndicationController,
mDemoModeController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 8b5989ff61a3..6818947bd8d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -21,7 +21,6 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.when;
import android.app.Fragment;
@@ -48,6 +47,7 @@ import com.android.systemui.statusbar.DisableFlagsLogger;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
+import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -62,10 +62,11 @@ import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
import java.util.Optional;
@@ -82,7 +83,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
// Set in instantiate()
private StatusBarIconController mStatusBarIconController;
private NetworkController mNetworkController;
- private StatusBarStateController mStatusBarStateController;
private KeyguardStateController mKeyguardStateController;
private final StatusBar mStatusBar = mock(StatusBar.class);
@@ -90,8 +90,16 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
private OperatorNameViewController mOperatorNameViewController;
+ @Mock
private StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
+ @Mock
private StatusBarFragmentComponent mStatusBarFragmentComponent;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private HeadsUpAppearanceController mHeadsUpAppearanceController;
+ @Mock
+ private NotificationPanelViewController mNotificationPanelViewController;
public CollapsedStatusBarFragmentTest() {
super(CollapsedStatusBarFragment.class);
@@ -99,49 +107,35 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Before
public void setup() {
- mStatusBarStateController = mDependency
- .injectMockDependency(StatusBarStateController.class);
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
- when(mStatusBar.getPanelController()).thenReturn(
- mock(NotificationPanelViewController.class));
}
@Test
- public void testDisableNone() throws Exception {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ public void testDisableNone() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
- .getVisibility());
- assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock)
- .getVisibility());
+ assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
}
@Test
- public void testDisableSystemInfo() throws Exception {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ public void testDisableSystemInfo() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
- assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
- .getVisibility());
+ assertEquals(View.INVISIBLE, getSystemIconAreaView().getVisibility());
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
- .getVisibility());
+ assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
}
@Test
- public void testDisableNotifications() throws Exception {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ public void testDisableNotifications() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
@@ -153,25 +147,21 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- public void testDisableClock() throws Exception {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ public void testDisableClock() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_CLOCK, 0, false);
- assertEquals(View.GONE, mFragment.getView().findViewById(R.id.clock).getVisibility());
+ assertEquals(View.GONE, getClockView().getVisibility());
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock).getVisibility());
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
}
@Test
public void disable_noOngoingCall_chipHidden() {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
@@ -183,9 +173,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
public void disable_hasOngoingCall_chipDisplayedAndNotificationIconsHidden() {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
@@ -199,9 +187,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
public void disable_hasOngoingCallButNotificationIconsDisabled_chipHidden() {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
@@ -214,9 +200,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
public void disable_ongoingCallEnded_chipHidden() {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
@@ -234,41 +218,95 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
}
- @Ignore("b/192618546")
@Test
- public void testOnDozingChanged() throws Exception {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ public void disable_isDozingButNoCustomClock_clockAndSystemInfoVisible() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mStatusBarStateController.isDozing()).thenReturn(true);
+ when(mNotificationPanelViewController.hasCustomClock()).thenReturn(false);
- fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
- Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
+ assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
+ }
+
+ @Test
+ public void disable_customClockButNotDozing_clockAndSystemInfoVisible() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mStatusBarStateController.isDozing()).thenReturn(false);
+ when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
- reset(mStatusBarStateController);
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
+ }
+
+ @Test
+ public void disable_dozingAndCustomClock_clockAndSystemInfoHidden() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
when(mStatusBarStateController.isDozing()).thenReturn(true);
+ when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
+
+ // Make sure they start out as visible
+ assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
+
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ assertEquals(View.INVISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.GONE, getClockView().getVisibility());
+ }
+
+ @Test
+ public void onDozingChanged_clockAndSystemInfoVisibilitiesUpdated() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mStatusBarStateController.isDozing()).thenReturn(true);
+ when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true);
+
+ // Make sure they start out as visible
+ assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
+
fragment.onDozingChanged(true);
- Mockito.verify(mStatusBarStateController).isDozing();
- Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
+ // When this callback is triggered, we want to make sure the clock and system info
+ // visibilities are recalculated. Since dozing=true, they shouldn't be visible.
+ assertEquals(View.INVISIBLE, getSystemIconAreaView().getVisibility());
+ assertEquals(View.GONE, getClockView().getVisibility());
+ }
+
+ @Test
+ public void disable_headsUpShouldBeVisibleTrue_clockDisabled() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
+
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ assertEquals(View.GONE, getClockView().getVisibility());
+ }
+
+ @Test
+ public void disable_headsUpShouldBeVisibleFalse_clockNotDisabled() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(false);
+
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ assertEquals(View.VISIBLE, getClockView().getVisibility());
}
@Test
public void setUp_fragmentCreatesDaggerComponent() {
- mFragments.dispatchResume();
- processAllMessages();
- CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
assertEquals(mStatusBarFragmentComponent, fragment.getStatusBarFragmentComponent());
}
@Override
protected Fragment instantiate(Context context, String className, Bundle arguments) {
- mStatusBarFragmentComponentFactory =
- mock(StatusBarFragmentComponent.Factory.class);
- mStatusBarFragmentComponent = mock(StatusBarFragmentComponent.class);
- when(mStatusBarFragmentComponentFactory.create(any()))
- .thenReturn(mStatusBarFragmentComponent);
+ MockitoAnnotations.initMocks(this);
+ setUpDaggerComponent();
mOngoingCallController = mock(OngoingCallController.class);
mAnimationScheduler = mock(SystemStatusAnimationScheduler.class);
mLocationPublisher = mock(StatusBarLocationPublisher.class);
@@ -295,7 +333,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
new StatusBarHideIconsForBouncerManager(
mCommandQueue, new FakeExecutor(new FakeSystemClock()), new DumpManager()),
mKeyguardStateController,
- mock(NotificationPanelViewController.class),
+ mNotificationPanelViewController,
mNetworkController,
mStatusBarStateController,
mCommandQueue,
@@ -306,6 +344,13 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mOperatorNameViewControllerFactory);
}
+ private void setUpDaggerComponent() {
+ when(mStatusBarFragmentComponentFactory.create(any()))
+ .thenReturn(mStatusBarFragmentComponent);
+ when(mStatusBarFragmentComponent.getHeadsUpAppearanceController())
+ .thenReturn(mHeadsUpAppearanceController);
+ }
+
private void setUpNotificationIconAreaController() {
mMockNotificationAreaController = mock(NotificationIconAreaController.class);
@@ -324,4 +369,18 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
when(mMockNotificationAreaController.getNotificationInnerAreaView()).thenReturn(
mNotificationAreaInner);
}
+
+ private CollapsedStatusBarFragment resumeAndGetFragment() {
+ mFragments.dispatchResume();
+ processAllMessages();
+ return (CollapsedStatusBarFragment) mFragment;
+ }
+
+ private View getClockView() {
+ return mFragment.getView().findViewById(R.id.clock);
+ }
+
+ private View getSystemIconAreaView() {
+ return mFragment.getView().findViewById(R.id.system_icon_area);
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 61b8ded60db7..7341e744dea3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -251,7 +251,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
"Successful background authentication!");
}
- mAlreadyDone = true;
+ markAlreadyDone();
if (mTaskStackListener != null) {
mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
@@ -327,7 +327,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
final @LockoutTracker.LockoutMode int lockoutMode =
handleFailedAttempt(getTargetUserId());
if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
- mAlreadyDone = true;
+ markAlreadyDone();
}
final CoexCoordinator coordinator = CoexCoordinator.getInstance();
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index 9764a167fbbf..b73e91173a43 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -114,7 +114,7 @@ public abstract class BaseClientMonitor extends LoggableMonitor
// Currently only used for authentication client. The cookie generated by BiometricService
// is never 0.
private final int mCookie;
- boolean mAlreadyDone;
+ private boolean mAlreadyDone = false;
// Use an empty callback by default since delayed operations can receive events
// before they are started and cause NPE in subclasses that access this field directly.
@@ -202,11 +202,9 @@ public abstract class BaseClientMonitor extends LoggableMonitor
return callback;
}
- public boolean isAlreadyDone() {
- return mAlreadyDone;
- }
-
- public void destroy() {
+ /** Signals this operation has completed its lifecycle and should no longer be used. */
+ void destroy() {
+ mAlreadyDone = true;
if (mToken != null) {
try {
mToken.unlinkToDeath(this, 0);
@@ -218,6 +216,20 @@ public abstract class BaseClientMonitor extends LoggableMonitor
}
}
+ /**
+ * Call while the operation is still active, but nearly done, to prevent any action
+ * upon client death (only needed for authentication clients).
+ */
+ void markAlreadyDone() {
+ Slog.d(TAG, "marking operation as done: " + this);
+ mAlreadyDone = true;
+ }
+
+ /** If this operation has been marked as completely done (or cancelled). */
+ public boolean isAlreadyDone() {
+ return mAlreadyDone;
+ }
+
@Override
public void binderDied() {
binderDiedInternal(true /* clearListener */);
@@ -225,10 +237,9 @@ public abstract class BaseClientMonitor extends LoggableMonitor
// TODO(b/157790417): Move this to the scheduler
void binderDiedInternal(boolean clearListener) {
- Slog.e(TAG, "Binder died, owner: " + getOwnerString()
- + ", operation: " + this.getClass().getName());
+ Slog.e(TAG, "Binder died, operation: " + this);
- if (isAlreadyDone()) {
+ if (mAlreadyDone) {
Slog.w(TAG, "Binder died but client is finished, ignoring");
return;
}
@@ -299,7 +310,7 @@ public abstract class BaseClientMonitor extends LoggableMonitor
@Override
public String toString() {
return "{[" + mSequentialId + "] "
- + this.getClass().getSimpleName()
+ + this.getClass().getName()
+ ", proto=" + getProtoEnum()
+ ", owner=" + getOwnerString()
+ ", cookie=" + getCookie()
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 361ec40f2877..a358bc2bad55 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -605,6 +605,9 @@ public class BiometricScheduler {
if (operation.mState == Operation.STATE_WAITING_FOR_COOKIE) {
Slog.w(getTag(), "Skipping cancellation for non-started operation: " + operation);
// We can set it to null immediately, since the HAL was never notified to start.
+ if (mCurrentOperation != null) {
+ mCurrentOperation.mClientMonitor.destroy();
+ }
mCurrentOperation = null;
startNextOperationIfIdle();
return;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 9d6678053533..51da107af283 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -542,9 +542,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
*/
private InputMethodSubtype mCurrentSubtype;
- // Was the keyguard locked when this client became current?
- private boolean mCurClientInKeyguard;
-
/**
* {@code true} if the IME has not been mostly hidden via {@link android.view.InsetsController}
*/
@@ -2363,14 +2360,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mImeHiddenByDisplayPolicy = false;
if (mCurClient != cs) {
- // Was the keyguard locked when switching over to the new client?
- mCurClientInKeyguard = isKeyguardLocked();
// If the client is changing, we need to switch over to the new
// one.
unbindCurrentClientLocked(UnbindReason.SWITCH_CLIENT);
- if (DEBUG) Slog.v(TAG, "switching to client: client="
- + cs.client.asBinder() + " keyguard=" + mCurClientInKeyguard);
-
// If the screen is on, inform the new client it is active
if (mIsInteractive) {
scheduleSetActiveToClient(cs, true /* active */, false /* fullscreen */,
@@ -2875,10 +2867,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// all updateSystemUi happens on system previlege.
final long ident = Binder.clearCallingIdentity();
try {
- // apply policy for binder calls
- if (vis != 0 && isKeyguardLocked() && !mCurClientInKeyguard) {
- vis = 0;
- }
if (!mCurPerceptible) {
vis &= ~InputMethodService.IME_VISIBLE;
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 8460d6797543..1781588b0ba2 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -52,8 +52,6 @@ public final class GnssMeasurementsProvider extends
private class GnssMeasurementListenerRegistration extends GnssListenerRegistration {
- private static final String GNSS_MEASUREMENTS_BUCKET = "gnss_measurement";
-
protected GnssMeasurementListenerRegistration(
@Nullable GnssMeasurementRequest request,
CallerIdentity callerIdentity,
@@ -70,15 +68,13 @@ public final class GnssMeasurementsProvider extends
@Nullable
@Override
protected void onActive() {
- mLocationAttributionHelper.reportHighPowerLocationStart(
- getIdentity(), GNSS_MEASUREMENTS_BUCKET, getKey());
+ mLocationAttributionHelper.reportHighPowerLocationStart(getIdentity());
}
@Nullable
@Override
protected void onInactive() {
- mLocationAttributionHelper.reportHighPowerLocationStop(
- getIdentity(), GNSS_MEASUREMENTS_BUCKET, getKey());
+ mLocationAttributionHelper.reportHighPowerLocationStop(getIdentity());
}
}
diff --git a/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java b/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java
index 5cb360be819a..483875230ac4 100644
--- a/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java
+++ b/services/core/java/com/android/server/location/injector/LocationAttributionHelper.java
@@ -24,55 +24,23 @@ import static com.android.server.location.LocationManagerService.TAG;
import android.location.util.identity.CallerIdentity;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
/**
* Helps manage appop monitoring for multiple location clients.
*/
public class LocationAttributionHelper {
- private static class BucketKey {
- private final String mBucket;
- private final Object mKey;
-
- private BucketKey(String bucket, Object key) {
- mBucket = Objects.requireNonNull(bucket);
- mKey = Objects.requireNonNull(key);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- BucketKey that = (BucketKey) o;
- return mBucket.equals(that.mBucket)
- && mKey.equals(that.mKey);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mBucket, mKey);
- }
- }
-
private final AppOpsHelper mAppOpsHelper;
@GuardedBy("this")
- private final Map<CallerIdentity, Set<BucketKey>> mAttributions;
+ private final Map<CallerIdentity, Integer> mAttributions;
@GuardedBy("this")
- private final Map<CallerIdentity, Set<BucketKey>> mHighPowerAttributions;
+ private final Map<CallerIdentity, Integer> mHighPowerAttributions;
public LocationAttributionHelper(AppOpsHelper appOpsHelper) {
mAppOpsHelper = appOpsHelper;
@@ -84,15 +52,16 @@ public class LocationAttributionHelper {
/**
* Report normal location usage for the given caller in the given bucket, with a unique key.
*/
- public synchronized void reportLocationStart(CallerIdentity identity, String bucket,
- Object key) {
- Set<BucketKey> keySet = mAttributions.computeIfAbsent(identity,
- i -> new ArraySet<>());
- boolean empty = keySet.isEmpty();
- if (keySet.add(new BucketKey(bucket, key)) && empty) {
- if (!mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
- mAttributions.remove(identity);
+ public synchronized void reportLocationStart(CallerIdentity identity) {
+ identity = CallerIdentity.forAggregation(identity);
+
+ int count = mAttributions.getOrDefault(identity, 0);
+ if (count == 0) {
+ if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
+ mAttributions.put(identity, 1);
}
+ } else {
+ mAttributions.put(identity, count + 1);
}
}
@@ -100,13 +69,15 @@ public class LocationAttributionHelper {
* Report normal location usage has stopped for the given caller in the given bucket, with a
* unique key.
*/
- public synchronized void reportLocationStop(CallerIdentity identity, String bucket,
- Object key) {
- Set<BucketKey> keySet = mAttributions.get(identity);
- if (keySet != null && keySet.remove(new BucketKey(bucket, key))
- && keySet.isEmpty()) {
+ public synchronized void reportLocationStop(CallerIdentity identity) {
+ identity = CallerIdentity.forAggregation(identity);
+
+ int count = mAttributions.getOrDefault(identity, 0);
+ if (count == 1) {
mAttributions.remove(identity);
mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, identity);
+ } else if (count > 1) {
+ mAttributions.put(identity, count - 1);
}
}
@@ -114,19 +85,19 @@ public class LocationAttributionHelper {
* Report high power location usage for the given caller in the given bucket, with a unique
* key.
*/
- public synchronized void reportHighPowerLocationStart(CallerIdentity identity, String bucket,
- Object key) {
- Set<BucketKey> keySet = mHighPowerAttributions.computeIfAbsent(identity,
- i -> new ArraySet<>());
- boolean empty = keySet.isEmpty();
- if (keySet.add(new BucketKey(bucket, key)) && empty) {
+ public synchronized void reportHighPowerLocationStart(CallerIdentity identity) {
+ identity = CallerIdentity.forAggregation(identity);
+
+ int count = mHighPowerAttributions.getOrDefault(identity, 0);
+ if (count == 0) {
+ if (D) {
+ Log.v(TAG, "starting high power location attribution for " + identity);
+ }
if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) {
- if (D) {
- Log.v(TAG, "starting high power location attribution for " + identity);
- }
- } else {
- mHighPowerAttributions.remove(identity);
+ mHighPowerAttributions.put(identity, 1);
}
+ } else {
+ mHighPowerAttributions.put(identity, count + 1);
}
}
@@ -134,16 +105,18 @@ public class LocationAttributionHelper {
* Report high power location usage has stopped for the given caller in the given bucket,
* with a unique key.
*/
- public synchronized void reportHighPowerLocationStop(CallerIdentity identity, String bucket,
- Object key) {
- Set<BucketKey> keySet = mHighPowerAttributions.get(identity);
- if (keySet != null && keySet.remove(new BucketKey(bucket, key))
- && keySet.isEmpty()) {
+ public synchronized void reportHighPowerLocationStop(CallerIdentity identity) {
+ identity = CallerIdentity.forAggregation(identity);
+
+ int count = mHighPowerAttributions.getOrDefault(identity, 0);
+ if (count == 1) {
if (D) {
Log.v(TAG, "stopping high power location attribution for " + identity);
}
mHighPowerAttributions.remove(identity);
mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
+ } else if (count > 1) {
+ mHighPowerAttributions.put(identity, count - 1);
}
}
}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index f7c4d03bfdf3..155b61891d12 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -398,7 +398,7 @@ public class LocationProviderManager extends
EVENT_LOG.logProviderClientActive(mName, getIdentity());
if (!getRequest().isHiddenFromAppOps()) {
- mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey());
+ mLocationAttributionHelper.reportLocationStart(getIdentity());
}
onHighPowerUsageChanged();
@@ -413,7 +413,7 @@ public class LocationProviderManager extends
onHighPowerUsageChanged();
if (!getRequest().isHiddenFromAppOps()) {
- mLocationAttributionHelper.reportLocationStop(getIdentity(), getName(), getKey());
+ mLocationAttributionHelper.reportLocationStop(getIdentity());
}
onProviderListenerInactive();
@@ -488,10 +488,10 @@ public class LocationProviderManager extends
if (!getRequest().isHiddenFromAppOps()) {
if (mIsUsingHighPower) {
mLocationAttributionHelper.reportHighPowerLocationStart(
- getIdentity(), getName(), getKey());
+ getIdentity());
} else {
mLocationAttributionHelper.reportHighPowerLocationStop(
- getIdentity(), getName(), getKey());
+ getIdentity());
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 95a286cf9047..d97c845ef7c4 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1387,9 +1387,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/** Whether we should prepare a transition for this {@link ActivityRecord} parent change. */
private boolean shouldStartChangeTransition(
@Nullable TaskFragment newParent, @Nullable TaskFragment oldParent) {
- if (mWmService.mDisableTransitionAnimation
- || mDisplayContent == null || newParent == null || oldParent == null
- || getSurfaceControl() == null || !isVisible() || !isVisibleRequested()) {
+ if (newParent == null || oldParent == null || !canStartChangeTransition()) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 878822522d08..721907c21904 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -176,6 +176,9 @@ public class AppTransitionController {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO");
+ // TODO(b/205335975): Remove window which stuck in animatingExit status. Find actual cause.
+ mDisplayContent.forAllWindows(WindowState::cleanupAnimatingExitWindow,
+ true /* traverseTopToBottom */);
// TODO(new-app-transition): Remove code using appTransition.getAppTransition()
final AppTransition appTransition = mDisplayContent.mAppTransition;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 36579d96d6c5..b7c992e073f3 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -44,6 +44,8 @@ import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
+import static android.view.Display.STATE_UNKNOWN;
+import static android.view.Display.isSuspendedState;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
@@ -322,11 +324,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
private Rect mLastMirroredDisplayAreaBounds = null;
- /**
- * The last state of the display.
- */
- private int mLastDisplayState;
-
// Contains all IME window containers. Note that the z-ordering of the IME windows will depend
// on the IME target. We mainly have this container grouping so we can keep track of all the IME
// window containers together and move them in-sync if/when needed. We use a subclass of
@@ -594,7 +591,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** Caches the value whether told display manager that we have content. */
private boolean mLastHasContent;
- private static DisplayRotationUtil sRotationUtil = new DisplayRotationUtil();
+ private DisplayRotationUtil mRotationUtil = new DisplayRotationUtil();
/**
* The input method window for this display.
@@ -1658,11 +1655,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// to cover the activity configuration change.
return false;
}
- if (r.attachedToProcess() && mayImeShowOnLaunchingActivity(r)) {
- // Currently it is unknown that when will IME window be ready. Reject the case to
- // avoid flickering by showing IME in inconsistent orientation.
- return false;
- }
if (checkOpening) {
if (!mAppTransition.isTransitionSet() || !mOpeningApps.contains(r)) {
// Apply normal rotation animation in case of the activity set different requested
@@ -2098,35 +2090,29 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return mDisplayCutoutCache.getOrCompute(mInitialDisplayCutout, rotation);
}
- static WmDisplayCutout calculateDisplayCutoutForRotationAndDisplaySizeUncached(
- DisplayCutout cutout, int rotation, int displayWidth, int displayHeight) {
+ private WmDisplayCutout calculateDisplayCutoutForRotationUncached(
+ DisplayCutout cutout, int rotation) {
if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) {
return WmDisplayCutout.NO_CUTOUT;
}
if (rotation == ROTATION_0) {
return WmDisplayCutout.computeSafeInsets(
- cutout, displayWidth, displayHeight);
+ cutout, mInitialDisplayWidth, mInitialDisplayHeight);
}
final Insets waterfallInsets =
RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
- final Rect[] newBounds = sRotationUtil.getRotatedBounds(
+ final Rect[] newBounds = mRotationUtil.getRotatedBounds(
cutout.getBoundingRectsAll(),
- rotation, displayWidth, displayHeight);
+ rotation, mInitialDisplayWidth, mInitialDisplayHeight);
final CutoutPathParserInfo info = cutout.getCutoutPathParserInfo();
final CutoutPathParserInfo newInfo = new CutoutPathParserInfo(
info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(),
info.getCutoutSpec(), rotation, info.getScale());
return WmDisplayCutout.computeSafeInsets(
DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo),
- rotated ? displayHeight : displayWidth,
- rotated ? displayWidth : displayHeight);
- }
-
- private WmDisplayCutout calculateDisplayCutoutForRotationUncached(
- DisplayCutout cutout, int rotation) {
- return calculateDisplayCutoutForRotationAndDisplaySizeUncached(cutout, rotation,
- mInitialDisplayWidth, mInitialDisplayHeight);
+ rotated ? mInitialDisplayHeight : mInitialDisplayWidth,
+ rotated ? mInitialDisplayWidth : mInitialDisplayHeight);
}
RoundedCorners calculateRoundedCornersForRotation(int rotation) {
@@ -4723,12 +4709,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mWmService.requestTraversal();
}
+ @Override
boolean okToDisplay() {
- return okToDisplay(false);
- }
-
- boolean okToDisplay(boolean ignoreFrozen) {
- return okToDisplay(ignoreFrozen, false /* ignoreScreenOn */);
+ return okToDisplay(false /* ignoreFrozen */, false /* ignoreScreenOn */);
}
boolean okToDisplay(boolean ignoreFrozen, boolean ignoreScreenOn) {
@@ -4740,18 +4723,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return mDisplayInfo.state == Display.STATE_ON;
}
- boolean okToAnimate() {
- return okToAnimate(false);
- }
-
- boolean okToAnimate(boolean ignoreFrozen) {
- return okToAnimate(ignoreFrozen, false /* ignoreScreenOn */);
- }
-
+ @Override
boolean okToAnimate(boolean ignoreFrozen, boolean ignoreScreenOn) {
return okToDisplay(ignoreFrozen, ignoreScreenOn)
&& (mDisplayId != DEFAULT_DISPLAY
- || mWmService.mPolicy.okToAnimate(ignoreScreenOn));
+ || mWmService.mPolicy.okToAnimate(ignoreScreenOn))
+ && getDisplayPolicy().isScreenOnFully();
}
static final class TaskForResizePointSearchResult {
@@ -4884,7 +4861,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// WindowState#applyImeWindowsIfNeeded} in case of any state mismatch.
return dc.mImeLayeringTarget != null
&& (!dc.getDefaultTaskDisplayArea().isSplitScreenModeActivated()
- || dc.mImeLayeringTarget.getTask() == null);
+ || dc.mImeLayeringTarget.getTask() == null)
+ // Make sure that the IME window won't be skipped to report that it has
+ // completed the orientation change.
+ && !dc.mWmService.mDisplayFrozen;
}
/** Like {@link #forAllWindows}, but ignores {@link #skipImeWindowsDuringTraversal} */
@@ -5623,12 +5603,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
void onDisplayChanged() {
mDisplay.getRealSize(mTmpDisplaySize);
setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
+ final int lastDisplayState = mDisplayInfo.state;
updateDisplayInfo();
// The window policy is responsible for stopping activities on the default display.
final int displayId = mDisplay.getDisplayId();
+ final int displayState = mDisplayInfo.state;
if (displayId != DEFAULT_DISPLAY) {
- final int displayState = mDisplay.getState();
if (displayState == Display.STATE_OFF) {
mOffTokenAcquirer.acquire(mDisplayId);
} else if (displayState == Display.STATE_ON) {
@@ -5637,12 +5618,18 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
ProtoLog.v(WM_DEBUG_LAYER_MIRRORING,
"Display %d state is now (%d), so update layer mirroring?",
mDisplayId, displayState);
- if (mLastDisplayState != displayState) {
+ if (lastDisplayState != displayState) {
// If state is on due to surface being added, then start layer mirroring.
// If state is off due to surface being removed, then stop layer mirroring.
updateMirroring();
}
- mLastDisplayState = displayState;
+ }
+ // Dispatch pending Configuration to WindowContext if the associated display changes to
+ // un-suspended state from suspended.
+ if (isSuspendedState(lastDisplayState)
+ && !isSuspendedState(displayState) && displayState != STATE_UNKNOWN) {
+ mWmService.mWindowContextListenerController
+ .dispatchPendingConfigurationIfNeeded(mDisplayId);
}
mWmService.requestTraversal();
}
diff --git a/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java b/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
index 11a27c593d9e..ef8dee401b05 100644
--- a/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
+++ b/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
@@ -120,15 +120,8 @@ public class PossibleDisplayInfoMapper {
@Surface.Rotation int rotation) {
DisplayInfo updatedDisplayInfo = new DisplayInfo();
updatedDisplayInfo.copyFrom(displayInfo);
- // Apply rotations before updating width and height
- updatedDisplayInfo.roundedCorners = updatedDisplayInfo.roundedCorners.rotate(rotation,
- updatedDisplayInfo.logicalWidth, updatedDisplayInfo.logicalHeight);
- updatedDisplayInfo.displayCutout =
- DisplayContent.calculateDisplayCutoutForRotationAndDisplaySizeUncached(
- updatedDisplayInfo.displayCutout, rotation, updatedDisplayInfo.logicalWidth,
- updatedDisplayInfo.logicalHeight).getDisplayCutout();
-
updatedDisplayInfo.rotation = rotation;
+
final int naturalWidth = updatedDisplayInfo.getNaturalWidth();
final int naturalHeight = updatedDisplayInfo.getNaturalHeight();
updatedDisplayInfo.logicalWidth = naturalWidth;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1b7a012094f6..d2eea76a29fc 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2163,10 +2163,7 @@ class Task extends TaskFragment {
}
private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
- if (mWmService.mDisableTransitionAnimation
- || !isVisible()
- || getSurfaceControl() == null
- || !isLeafTask()) {
+ if (!isLeafTask() || !canStartChangeTransition()) {
return false;
}
// Only do an animation into and out-of freeform mode for now. Other mode
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 51cede0c051d..f32ab1e53318 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -2125,13 +2125,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
/** Whether we should prepare a transition for this {@link TaskFragment} bounds change. */
private boolean shouldStartChangeTransition(Rect startBounds) {
- if (mWmService.mDisableTransitionAnimation
- || mDisplayContent == null
- || mTaskFragmentOrganizer == null
- || getSurfaceControl() == null
- // The change transition will be covered by display.
- || mDisplayContent.inTransition()
- || !isVisible()) {
+ if (mTaskFragmentOrganizer == null || !canStartChangeTransition()) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 58bc244e9250..5af9147cd56f 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2600,6 +2600,13 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mSurfaceFreezer.unfreeze(getPendingTransaction());
}
+ /** Whether we can start change transition with this window and current display status. */
+ boolean canStartChangeTransition() {
+ return !mWmService.mDisableTransitionAnimation && mDisplayContent != null
+ && getSurfaceControl() != null && !mDisplayContent.inTransition()
+ && isVisible() && isVisibleRequested() && okToAnimate();
+ }
+
/**
* Initializes a change transition. See {@link SurfaceFreezer} for more information.
*
@@ -2948,12 +2955,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
boolean okToAnimate() {
- return okToAnimate(false /* ignoreFrozen */);
- }
-
- boolean okToAnimate(boolean ignoreFrozen) {
- final DisplayContent dc = getDisplayContent();
- return dc != null && dc.okToAnimate(ignoreFrozen);
+ return okToAnimate(false /* ignoreFrozen */, false /* ignoreScreenOn */);
}
boolean okToAnimate(boolean ignoreFrozen, boolean ignoreScreenOn) {
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index 86e356a876b5..cc527136eb51 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -17,7 +17,9 @@
package com.android.server.wm;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.Display.isSuspendedState;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
+import static android.window.WindowProviderService.isWindowProviderService;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR;
@@ -80,7 +82,7 @@ class WindowContextListenerController {
* @param options a bundle used to pass window-related options.
*/
void registerWindowContainerListener(@NonNull IBinder clientToken,
- @NonNull WindowContainer container, int ownerUid, @WindowType int type,
+ @NonNull WindowContainer<?> container, int ownerUid, @WindowType int type,
@Nullable Bundle options) {
WindowContextListenerImpl listener = mListeners.get(clientToken);
if (listener == null) {
@@ -103,6 +105,16 @@ class WindowContextListenerController {
listener.unregister();
}
+ void dispatchPendingConfigurationIfNeeded(int displayId) {
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ final WindowContextListenerImpl listener = mListeners.valueAt(i);
+ if (listener.getWindowContainer().getDisplayContent().getDisplayId() == displayId
+ && listener.mHasPendingConfiguration) {
+ listener.reportConfigToWindowTokenClient();
+ }
+ }
+ }
+
/**
* Verifies if the caller is allowed to do the operation to the listener specified by
* {@code clientToken}.
@@ -138,7 +150,7 @@ class WindowContextListenerController {
return listener != null ? listener.mOptions : null;
}
- @Nullable WindowContainer getContainer(IBinder clientToken) {
+ @Nullable WindowContainer<?> getContainer(IBinder clientToken) {
final WindowContextListenerImpl listener = mListeners.get(clientToken);
return listener != null ? listener.mContainer : null;
}
@@ -163,7 +175,7 @@ class WindowContextListenerController {
class WindowContextListenerImpl implements WindowContainerListener {
@NonNull private final IBinder mClientToken;
private final int mOwnerUid;
- @NonNull private WindowContainer mContainer;
+ @NonNull private WindowContainer<?> mContainer;
/**
* The options from {@link Context#createWindowContext(int, Bundle)}.
* <p>It can be used for choosing the {@link DisplayArea} where the window context
@@ -177,7 +189,9 @@ class WindowContextListenerController {
private int mLastReportedDisplay = INVALID_DISPLAY;
private Configuration mLastReportedConfig;
- private WindowContextListenerImpl(IBinder clientToken, WindowContainer container,
+ private boolean mHasPendingConfiguration;
+
+ private WindowContextListenerImpl(IBinder clientToken, WindowContainer<?> container,
int ownerUid, @WindowType int type, @Nullable Bundle options) {
mClientToken = clientToken;
mContainer = Objects.requireNonNull(container);
@@ -197,11 +211,11 @@ class WindowContextListenerController {
/** TEST ONLY: returns the {@link WindowContainer} of the listener */
@VisibleForTesting
- WindowContainer getWindowContainer() {
+ WindowContainer<?> getWindowContainer() {
return mContainer;
}
- private void updateContainer(@NonNull WindowContainer newContainer) {
+ private void updateContainer(@NonNull WindowContainer<?> newContainer) {
Objects.requireNonNull(newContainer);
if (mContainer.equals(newContainer)) {
@@ -246,12 +260,20 @@ class WindowContextListenerController {
if (mDeathRecipient == null) {
throw new IllegalStateException("Invalid client token: " + mClientToken);
}
-
- if (mLastReportedConfig == null) {
- mLastReportedConfig = new Configuration();
+ // If the display of window context associated window container is suspended, don't
+ // report the configuration update. Note that we still dispatch the configuration update
+ // to WindowProviderService to make it compatible with Service#onConfigurationChanged.
+ // Service always receives #onConfigurationChanged callback regardless of display state.
+ if (!isWindowProviderService(mOptions)
+ && isSuspendedState(mContainer.getDisplayContent().getDisplayInfo().state)) {
+ mHasPendingConfiguration = true;
+ return;
}
final Configuration config = mContainer.getConfiguration();
final int displayId = mContainer.getDisplayContent().getDisplayId();
+ if (mLastReportedConfig == null) {
+ mLastReportedConfig = new Configuration();
+ }
if (config.equals(mLastReportedConfig) && displayId == mLastReportedDisplay) {
// No changes since last reported time.
return;
@@ -266,6 +288,7 @@ class WindowContextListenerController {
} catch (RemoteException e) {
ProtoLog.w(WM_ERROR, "Could not report config changes to the window token client.");
}
+ mHasPendingConfiguration = false;
}
@Override
@@ -283,7 +306,7 @@ class WindowContextListenerController {
// If we cannot obtain the DisplayContent, the DisplayContent may also be removed.
// We should proceed the removal process.
if (dc != null) {
- final DisplayArea da = dc.findAreaForToken(windowToken);
+ final DisplayArea<?> da = dc.findAreaForToken(windowToken);
updateContainer(da);
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 9128732db366..44d2a7f593d0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5064,6 +5064,48 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return false;
}
+ private boolean shouldFinishAnimatingExit() {
+ // Exit animation might be applied soon.
+ if (inTransition()) {
+ ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "shouldWaitAnimatingExit: isTransition: %s",
+ this);
+ return false;
+ }
+ if (!mDisplayContent.okToAnimate()) {
+ return true;
+ }
+ // Exit animation is running.
+ if (isAnimating(TRANSITION | PARENTS, EXIT_ANIMATING_TYPES)) {
+ ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "shouldWaitAnimatingExit: isAnimating: %s",
+ this);
+ return false;
+ }
+ // If the wallpaper is currently behind this app window, we need to change both of
+ // them inside of a transaction to avoid artifacts.
+ if (mDisplayContent.mWallpaperController.isWallpaperTarget(this)) {
+ ProtoLog.d(WM_DEBUG_APP_TRANSITIONS,
+ "shouldWaitAnimatingExit: isWallpaperTarget: %s", this);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * If this is window is stuck in the animatingExit status, resume clean up procedure blocked
+ * by the exit animation.
+ */
+ void cleanupAnimatingExitWindow() {
+ // TODO(b/205335975): WindowManagerService#tryStartExitingAnimation starts an exit animation
+ // and set #mAnimationExit. After the exit animation finishes, #onExitAnimationDone shall
+ // be called, but there seems to be a case that #onExitAnimationDone is not triggered, so
+ // a windows stuck in the animatingExit status.
+ if (mAnimatingExit && shouldFinishAnimatingExit()) {
+ ProtoLog.w(WM_DEBUG_APP_TRANSITIONS, "Clear window stuck on animatingExit status: %s",
+ this);
+ onExitAnimationDone();
+ }
+ }
+
void onExitAnimationDone() {
if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this
+ ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java
index e2e7f5dd7ba2..94dcdf92d9d4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/LocationAttributionHelperTest.java
@@ -58,72 +58,86 @@ public class LocationAttributionHelperTest {
@Test
public void testLocationMonitoring() {
CallerIdentity caller1 = CallerIdentity.forTest(1, 1, "test1", null);
- Object key1 = new Object();
- Object key2 = new Object();
CallerIdentity caller2 = CallerIdentity.forTest(2, 2, "test2", null);
- Object key3 = new Object();
- Object key4 = new Object();
-
- mHelper.reportLocationStart(caller1, "gps", key1);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
-
- mHelper.reportLocationStart(caller1, "gps", key2);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
-
- mHelper.reportLocationStart(caller2, "gps", key3);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
-
- mHelper.reportLocationStart(caller2, "gps", key4);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
-
- mHelper.reportLocationStop(caller1, "gps", key2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
- mHelper.reportLocationStop(caller1, "gps", key1);
- verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller1);
-
- mHelper.reportLocationStop(caller2, "gps", key3);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
- mHelper.reportLocationStop(caller2, "gps", key4);
- verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller2);
+
+ mHelper.reportLocationStart(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+
+ mHelper.reportLocationStart(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+
+ mHelper.reportLocationStart(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+
+ mHelper.reportLocationStart(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+
+ mHelper.reportLocationStop(caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+ mHelper.reportLocationStop(caller1);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, CallerIdentity.forAggregation(caller1));
+
+ mHelper.reportLocationStop(caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+ mHelper.reportLocationStop(caller2);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, CallerIdentity.forAggregation(caller2));
}
@Test
public void testHighPowerLocationMonitoring() {
CallerIdentity caller1 = CallerIdentity.forTest(1, 1, "test1", null);
- Object key1 = new Object();
- Object key2 = new Object();
CallerIdentity caller2 = CallerIdentity.forTest(2, 2, "test2", null);
- Object key3 = new Object();
- Object key4 = new Object();
-
- mHelper.reportHighPowerLocationStart(caller1, "gps", key1);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
-
- mHelper.reportHighPowerLocationStart(caller1, "gps", key2);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
-
- mHelper.reportHighPowerLocationStart(caller2, "gps", key3);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
-
- mHelper.reportHighPowerLocationStart(caller2, "gps", key4);
- verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
-
- mHelper.reportHighPowerLocationStop(caller1, "gps", key2);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
- mHelper.reportHighPowerLocationStop(caller1, "gps", key1);
- verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
-
- mHelper.reportHighPowerLocationStop(caller2, "gps", key3);
- verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
- mHelper.reportHighPowerLocationStop(caller2, "gps", key4);
- verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+
+ mHelper.reportHighPowerLocationStart(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+
+ mHelper.reportHighPowerLocationStart(caller1);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+
+ mHelper.reportHighPowerLocationStart(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+
+ mHelper.reportHighPowerLocationStart(caller2);
+ verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+
+ mHelper.reportHighPowerLocationStop(caller1);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+ mHelper.reportHighPowerLocationStop(caller1);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller1));
+
+ mHelper.reportHighPowerLocationStop(caller2);
+ verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller2));
+ mHelper.reportHighPowerLocationStop(caller2);
+ verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION,
+ CallerIdentity.forAggregation(caller2));
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index d0b2edadc714..890a5495ef16 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -845,6 +845,48 @@ public class LocationProviderManagerTest {
}
@Test
+ public void testLocationMonitoring_multipleIdentities() {
+ CallerIdentity identity1 = CallerIdentity.forTest(CURRENT_USER, 1,
+ "mypackage", "attribution", "listener1");
+ CallerIdentity identity2 = CallerIdentity.forTest(CURRENT_USER, 1,
+ "mypackage", "attribution", "listener2");
+
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+ IDENTITY.getPackageName())).isFalse();
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+ IDENTITY.getPackageName())).isFalse();
+
+ ILocationListener listener1 = createMockLocationListener();
+ LocationRequest request1 = new LocationRequest.Builder(0).setWorkSource(
+ WORK_SOURCE).build();
+ mManager.registerLocationRequest(request1, identity1, PERMISSION_FINE, listener1);
+
+ ILocationListener listener2 = createMockLocationListener();
+ LocationRequest request2 = new LocationRequest.Builder(0).setWorkSource(
+ WORK_SOURCE).build();
+ mManager.registerLocationRequest(request2, identity2, PERMISSION_FINE, listener2);
+
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+ "mypackage")).isTrue();
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+ "mypackage")).isTrue();
+
+ mManager.unregisterLocationRequest(listener2);
+
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+ "mypackage")).isTrue();
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+ "mypackage")).isTrue();
+
+ mManager.unregisterLocationRequest(listener1);
+
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
+ "mypackage")).isFalse();
+ assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_HIGH_POWER_LOCATION,
+ "mypackage")).isFalse();
+ }
+
+ @Test
public void testProviderRequest() {
assertThat(mProvider.getRequest().isActive()).isFalse();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index e3e3900c47e0..d192697827f6 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -143,8 +143,8 @@ public class BiometricSchedulerTest {
final ClientMonitorCallbackConverter listener1 = mock(ClientMonitorCallbackConverter.class);
- final BiometricPromptClientMonitor client1 =
- new BiometricPromptClientMonitor(mContext, mToken, lazyDaemon1, listener1);
+ final TestAuthenticationClient client1 =
+ new TestAuthenticationClient(mContext, lazyDaemon1, mToken, listener1);
final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2);
final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
@@ -180,8 +180,8 @@ public class BiometricSchedulerTest {
@Test
public void testCancelNotInvoked_whenOperationWaitingForCookie() {
final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> mock(Object.class);
- final BiometricPromptClientMonitor client1 = new BiometricPromptClientMonitor(mContext,
- mToken, lazyDaemon1, mock(ClientMonitorCallbackConverter.class));
+ final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
+ lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class));
final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
// Schedule a BiometricPrompt authentication request
@@ -195,6 +195,8 @@ public class BiometricSchedulerTest {
// should go back to idle, since in this case the framework has not even requested the HAL
// to authenticate yet.
mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */);
+ assertTrue(client1.isAlreadyDone());
+ assertTrue(client1.mDestroyed);
assertNull(mScheduler.mCurrentOperation);
}
@@ -316,6 +318,10 @@ public class BiometricSchedulerTest {
eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED),
eq(0) /* vendorCode */);
assertNull(mScheduler.getCurrentClient());
+ assertTrue(client1.isAlreadyDone());
+ assertTrue(client1.mDestroyed);
+ assertTrue(client2.isAlreadyDone());
+ assertTrue(client2.mDestroyed);
}
@Test
@@ -465,39 +471,9 @@ public class BiometricSchedulerTest {
return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer));
}
- private static class BiometricPromptClientMonitor extends AuthenticationClient<Object> {
-
- public BiometricPromptClientMonitor(@NonNull Context context, @NonNull IBinder token,
- @NonNull LazyDaemon<Object> lazyDaemon, ClientMonitorCallbackConverter listener) {
- super(context, lazyDaemon, token, listener, 0 /* targetUserId */, 0 /* operationId */,
- false /* restricted */, TAG, 1 /* cookie */, false /* requireConfirmation */,
- TEST_SENSOR_ID, true /* isStrongBiometric */, 0 /* statsModality */,
- 0 /* statsClient */, null /* taskStackListener */, mock(LockoutTracker.class),
- false /* isKeyguard */, true /* shouldVibrate */,
- false /* isKeyguardBypassEnabled */);
- }
-
- @Override
- protected void stopHalOperation() {
- }
-
- @Override
- protected void startHalOperation() {
- }
-
- @Override
- protected void handleLifecycleAfterAuth(boolean authenticated) {
-
- }
-
- @Override
- public boolean wasUserDetected() {
- return false;
- }
- }
-
private static class TestAuthenticationClient extends AuthenticationClient<Object> {
int mNumCancels = 0;
+ boolean mDestroyed = false;
public TestAuthenticationClient(@NonNull Context context,
@NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token,
@@ -530,6 +506,13 @@ public class BiometricSchedulerTest {
return false;
}
+ @Override
+ public void destroy() {
+ mDestroyed = true;
+ super.destroy();
+ }
+
+ @Override
public void cancel() {
mNumCancels++;
super.cancel();
@@ -595,6 +578,7 @@ public class BiometricSchedulerTest {
@Override
public void destroy() {
+ super.destroy();
mDestroyed = true;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index fc2c162d058b..707e463424ae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1779,11 +1779,6 @@ public class ActivityRecordTests extends WindowTestsBase {
anyInt() /* orientation */, anyInt() /* lastRotation */);
// Set to visible so the activity can freeze the screen.
activity.setVisibility(true);
- // Update the display policy to make the screen fully turned on so the freeze is allowed
- display.getDisplayPolicy().screenTurnedOn(null);
- display.getDisplayPolicy().finishKeyguardDrawn();
- display.getDisplayPolicy().finishWindowsDrawn();
- display.getDisplayPolicy().finishScreenTurningOn();
display.rotateInDifferentOrientationIfNeeded(activity);
display.setFixedRotationLaunchingAppUnchecked(activity);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 6fa306b004a2..506270657e42 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -334,6 +334,18 @@ public class AppTransitionControllerTest extends WindowTestsBase {
}
@Test
+ public void testExitAnimationDone_beforeAppTransition() {
+ final Task task = createTask(mDisplayContent);
+ final WindowState win = createAppWindow(task, ACTIVITY_TYPE_STANDARD, "Win");
+ spyOn(win);
+ win.mAnimatingExit = true;
+ mDisplayContent.mAppTransition.setTimeout();
+ mDisplayContent.mAppTransitionController.handleAppTransitionReady();
+
+ verify(win).onExitAnimationDone();
+ }
+
+ @Test
public void testGetAnimationTargets_windowsAreBeingReplaced() {
// [DisplayContent] -+- [Task1] - [ActivityRecord1] (opening, visible)
// +- [AppWindow1] (being-replaced)
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
index d7daa57cc9da..407f9cfdbe3e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.STATE_ON;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
@@ -70,7 +71,7 @@ public class InputMethodMenuControllerTest extends WindowTestsBase {
@Before
public void setUp() throws Exception {
- // Let the Display to be created with the DualDisplay policy.
+ // Let the Display be created with the DualDisplay policy.
final DisplayAreaPolicy.Provider policyProvider =
new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider();
Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
@@ -78,6 +79,7 @@ public class InputMethodMenuControllerTest extends WindowTestsBase {
mController = new InputMethodMenuController(mock(InputMethodManagerService.class));
mSecondaryDisplay = new DualDisplayAreaGroupPolicyTest.DualDisplayContent
.Builder(mAtm, 1000, 1000).build();
+ mSecondaryDisplay.getDisplayInfo().state = STATE_ON;
// Mock addWindowTokenWithOptions to create a test window token.
mIWindowManager = WindowManagerGlobal.getWindowManagerService();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 6737b1ade785..730275cde40b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -16,11 +16,14 @@
package com.android.server.wm;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.clearInvocations;
import android.graphics.Rect;
@@ -91,6 +94,7 @@ public class TaskFragmentTest extends WindowTestsBase {
final Rect endBounds = new Rect(500, 500, 1000, 1000);
mTaskFragment.setBounds(startBounds);
doReturn(true).when(mTaskFragment).isVisible();
+ doReturn(true).when(mTaskFragment).isVisibleRequested();
clearInvocations(mTransaction);
mTaskFragment.setBounds(endBounds);
@@ -108,6 +112,25 @@ public class TaskFragmentTest extends WindowTestsBase {
verify(mTransaction).setWindowCrop(mLeash, 500, 500);
}
+ @Test
+ public void testNotOkToAnimate_doNotStartChangeTransition() {
+ mockSurfaceFreezerSnapshot(mTaskFragment.mSurfaceFreezer);
+ final Rect startBounds = new Rect(0, 0, 1000, 1000);
+ final Rect endBounds = new Rect(500, 500, 1000, 1000);
+ mTaskFragment.setBounds(startBounds);
+ doReturn(true).when(mTaskFragment).isVisible();
+ doReturn(true).when(mTaskFragment).isVisibleRequested();
+
+ final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
+ displayPolicy.screenTurnedOff();
+
+ assertFalse(mTaskFragment.okToAnimate());
+
+ mTaskFragment.setBounds(endBounds);
+
+ verify(mTaskFragment, never()).initializeChangeTransition(any());
+ }
+
/**
* Tests that when a {@link TaskFragmentInfo} is generated from a {@link TaskFragment}, an
* activity that has not yet been attached to a process because it is being initialized but
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index 0e504d326280..e0f69d4f2eaf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -165,6 +165,11 @@ class TestDisplayContent extends DisplayContent {
doReturn(false).when(displayPolicy).hasStatusBar();
doReturn(false).when(newDisplay).supportsSystemDecorations();
}
+ // Update the display policy to make the screen fully turned on so animation is allowed
+ displayPolicy.screenTurnedOn(null /* screenOnListener */);
+ displayPolicy.finishKeyguardDrawn();
+ displayPolicy.finishWindowsDrawn();
+ displayPolicy.finishScreenTurningOn();
if (mStatusBarHeight > 0) {
doReturn(true).when(displayPolicy).hasStatusBar();
doAnswer(invocation -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
index e5eba57f223d..646647fcc4ca 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
@@ -17,22 +17,35 @@
package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.STATE_OFF;
+import static android.view.Display.STATE_ON;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.window.WindowProvider.KEY_IS_WINDOW_PROVIDER_SERVICE;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import android.app.IWindowToken;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+import android.view.DisplayInfo;
import androidx.test.filters.SmallTest;
@@ -55,12 +68,15 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
private static final int ANOTHER_UID = 1000;
private final IBinder mClientToken = new Binder();
- private WindowContainer mContainer;
+ private WindowContainer<?> mContainer;
@Before
public void setUp() {
mController = new WindowContextListenerController();
mContainer = createTestWindowToken(TYPE_APPLICATION_OVERLAY, mDisplayContent);
+ // Make display on to verify configuration propagation.
+ mDefaultDisplay.getDisplayInfo().state = STATE_ON;
+ mDisplayContent.getDisplayInfo().state = STATE_ON;
}
@Test
@@ -76,7 +92,7 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
assertEquals(2, mController.mListeners.size());
- final WindowContainer container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
+ final WindowContainer<?> container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
mDefaultDisplay);
mController.registerWindowContainerListener(mClientToken, container, -1,
TYPE_APPLICATION_OVERLAY, null /* options */);
@@ -89,6 +105,7 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
assertEquals(container, listener.getWindowContainer());
}
+ @UseTestDisplay
@Test
public void testRegisterWindowContextListenerClientConfigPropagation() {
final TestWindowTokenClient clientToken = new TestWindowTokenClient();
@@ -107,7 +124,7 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
assertEquals(mDisplayContent.mDisplayId, clientToken.mDisplayId);
// Update the WindowContainer.
- final WindowContainer container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
+ final WindowContainer<?> container = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
mDefaultDisplay);
final Configuration config2 = container.getConfiguration();
final Rect bounds2 = new Rect(0, 0, 20, 20);
@@ -174,7 +191,7 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
.setDisplayContent(mDefaultDisplay)
.setFromClientToken(true)
.build();
- final DisplayArea da = windowContextCreatedToken.getDisplayArea();
+ final DisplayArea<?> da = windowContextCreatedToken.getDisplayArea();
mController.registerWindowContainerListener(mClientToken, windowContextCreatedToken,
TEST_UID, TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */);
@@ -192,11 +209,12 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
// Let the Display to be created with the DualDisplay policy.
final DisplayAreaPolicy.Provider policyProvider =
new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider();
- Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
+ doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
// Create a DisplayContent with dual RootDisplayArea
DualDisplayAreaGroupPolicyTest.DualDisplayContent dualDisplayContent =
new DualDisplayAreaGroupPolicyTest.DualDisplayContent
.Builder(mAtm, 1000, 1000).build();
+ dualDisplayContent.getDisplayInfo().state = STATE_ON;
final DisplayArea.Tokens imeContainer = dualDisplayContent.getImeContainer();
// Put the ImeContainer to the first sub-RootDisplayArea
dualDisplayContent.mFirstRoot.placeImeContainer(imeContainer);
@@ -222,7 +240,62 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
assertThat(mController.getContainer(mClientToken)).isEqualTo(imeContainer);
}
- private class TestWindowTokenClient extends IWindowToken.Stub {
+ @Test
+ public void testConfigUpdateForSuspendedWindowContext() {
+ final TestWindowTokenClient mockToken = new TestWindowTokenClient();
+ spyOn(mockToken);
+
+ mContainer.getDisplayContent().getDisplayInfo().state = STATE_OFF;
+
+ final Configuration config1 = mContainer.getConfiguration();
+ final Rect bounds1 = new Rect(0, 0, 10, 10);
+ config1.windowConfiguration.setBounds(bounds1);
+ config1.densityDpi = 100;
+ mContainer.onRequestedOverrideConfigurationChanged(config1);
+
+ mController.registerWindowContainerListener(mockToken, mContainer, -1,
+ TYPE_APPLICATION_OVERLAY, null /* options */);
+
+ verify(mockToken, never()).onConfigurationChanged(any(), anyInt());
+
+ // Turn on the display and verify if the client receive the callback
+ Display display = mContainer.getDisplayContent().getDisplay();
+ spyOn(display);
+ Mockito.doAnswer(invocation -> {
+ final DisplayInfo info = mContainer.getDisplayContent().getDisplayInfo();
+ info.state = STATE_ON;
+ ((DisplayInfo) invocation.getArgument(0)).copyFrom(info);
+ return null;
+ }).when(display).getDisplayInfo(any(DisplayInfo.class));
+
+ mContainer.getDisplayContent().onDisplayChanged();
+
+ assertThat(mockToken.mConfiguration).isEqualTo(config1);
+ assertThat(mockToken.mDisplayId).isEqualTo(mContainer.getDisplayContent().getDisplayId());
+ }
+
+ @Test
+ public void testReportConfigUpdateForSuspendedWindowProviderService() {
+ final TestWindowTokenClient clientToken = new TestWindowTokenClient();
+ final Bundle options = new Bundle();
+ options.putBoolean(KEY_IS_WINDOW_PROVIDER_SERVICE, true);
+
+ mContainer.getDisplayContent().getDisplayInfo().state = STATE_OFF;
+
+ final Configuration config1 = mContainer.getConfiguration();
+ final Rect bounds1 = new Rect(0, 0, 10, 10);
+ config1.windowConfiguration.setBounds(bounds1);
+ config1.densityDpi = 100;
+ mContainer.onRequestedOverrideConfigurationChanged(config1);
+
+ mController.registerWindowContainerListener(clientToken, mContainer, -1,
+ TYPE_APPLICATION_OVERLAY, options);
+
+ assertThat(clientToken.mConfiguration).isEqualTo(config1);
+ assertThat(clientToken.mDisplayId).isEqualTo(mDisplayContent.mDisplayId);
+ }
+
+ private static class TestWindowTokenClient extends IWindowToken.Stub {
private Configuration mConfiguration;
private int mDisplayId;
private boolean mRemoved;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 88d8ba308b45..b997acfaadcf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -200,6 +200,13 @@ class WindowTestsBase extends SystemServiceTestsBase {
SystemServicesTestRule.checkHoldsLock(mWm.mGlobalLock);
mDefaultDisplay = mWm.mRoot.getDefaultDisplay();
+ // Update the display policy to make the screen fully turned on so animation is allowed
+ final DisplayPolicy displayPolicy = mDefaultDisplay.getDisplayPolicy();
+ displayPolicy.screenTurnedOn(null /* screenOnListener */);
+ displayPolicy.finishKeyguardDrawn();
+ displayPolicy.finishWindowsDrawn();
+ displayPolicy.finishScreenTurningOn();
+
mTransaction = mSystemServicesTestRule.mTransaction;
mMockSession = mock(Session.class);