diff options
47 files changed, 666 insertions, 362 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt new file mode 100644 index 000000000000..ae9f57f1f1b0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.accessibility.data.repository + +import android.view.accessibility.AccessibilityManager +import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import dagger.Module +import dagger.Provides +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged + +/** Exposes accessibility-related state. */ +interface AccessibilityRepository { + /** @see [AccessibilityManager.isTouchExplorationEnabled] */ + val isTouchExplorationEnabled: Flow<Boolean> + + companion object { + operator fun invoke(a11yManager: AccessibilityManager): AccessibilityRepository = + AccessibilityRepositoryImpl(a11yManager) + } +} + +private class AccessibilityRepositoryImpl( + manager: AccessibilityManager, +) : AccessibilityRepository { + override val isTouchExplorationEnabled: Flow<Boolean> = + conflatedCallbackFlow { + val listener = TouchExplorationStateChangeListener(::trySend) + manager.addTouchExplorationStateChangeListener(listener) + trySend(manager.isTouchExplorationEnabled) + awaitClose { manager.removeTouchExplorationStateChangeListener(listener) } + } + .distinctUntilChanged() +} + +@Module +object AccessibilityRepositoryModule { + @Provides fun provideRepo(manager: AccessibilityManager) = AccessibilityRepository(manager) +} diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/AccessibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/AccessibilityInteractor.kt new file mode 100644 index 000000000000..968ce0dc8cb0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/AccessibilityInteractor.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.accessibility.domain.interactor + +import com.android.systemui.accessibility.data.repository.AccessibilityRepository +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +@SysUISingleton +class AccessibilityInteractor +@Inject +constructor( + private val a11yRepo: AccessibilityRepository, +) { + /** @see [android.view.accessibility.AccessibilityManager.isTouchExplorationEnabled] */ + val isTouchExplorationEnabled: Flow<Boolean> + get() = a11yRepo.isTouchExplorationEnabled +} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java index 66701080ddfb..08e1e9a9a035 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java @@ -26,9 +26,6 @@ public interface FalsingCollector { void onSuccessfulUnlock(); /** */ - void onNotificationActive(); - - /** */ void setShowingAod(boolean showingAod); /** */ diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java index cc25368161eb..f335d1d0475f 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java @@ -25,10 +25,6 @@ public class FalsingCollectorFake implements FalsingCollector { } @Override - public void onNotificationActive() { - } - - @Override public void setShowingAod(boolean showingAod) { } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java index 8bdef1304fa4..6a021f6a82b6 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java @@ -174,10 +174,6 @@ class FalsingCollectorImpl implements FalsingCollector { } @Override - public void onNotificationActive() { - } - - @Override public void setShowingAod(boolean showingAod) { mShowingAod = showingAod; updateSessionActive(); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index ac916651b9f3..044dd6a47241 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -29,6 +29,7 @@ import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.BootCompleteCache; import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.accessibility.AccessibilityModule; +import com.android.systemui.accessibility.data.repository.AccessibilityRepositoryModule; import com.android.systemui.appops.dagger.AppOpsModule; import com.android.systemui.assist.AssistModule; import com.android.systemui.biometrics.AlternateUdfpsTouchProvider; @@ -140,6 +141,7 @@ import javax.inject.Named; */ @Module(includes = { AccessibilityModule.class, + AccessibilityRepositoryModule.class, AppOpsModule.class, AssistModule.class, BiometricsModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index a4a7d4cd76e5..f77be3a13a04 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -178,7 +178,6 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.ViewGroupFadeHelper; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -225,8 +224,6 @@ import com.android.systemui.util.Utils; import com.android.systemui.util.time.SystemClock; import com.android.wm.shell.animation.FlingAnimationUtils; -import kotlin.Unit; - import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; @@ -237,6 +234,7 @@ import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Provider; +import kotlin.Unit; import kotlinx.coroutines.CoroutineDispatcher; @CentralSurfacesComponent.CentralSurfacesScope @@ -1709,8 +1707,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump final float extraSpaceForShelf = lockIconPadding - noShelfOverlapBottomPadding; if (extraSpaceForShelf > 0f) { - return Math.min(mNotificationShelfController.getIntrinsicHeight(), - extraSpaceForShelf); + return Math.min(getShelfHeight(), extraSpaceForShelf); } return 0f; } @@ -1732,10 +1729,18 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mNotificationStackScrollLayoutController.getView(), getVerticalSpaceForLockscreenNotifications(), getVerticalSpaceForLockscreenShelf(), - mNotificationShelfController.getIntrinsicHeight() + getShelfHeight() ); } + private int getShelfHeight() { + if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { + return mNotificationStackScrollLayoutController.getShelfHeight(); + } else { + return mNotificationShelfController.getIntrinsicHeight(); + } + } + private void updateClock() { if (mIsOcclusionTransitionRunning) { return; @@ -3308,16 +3313,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump public boolean hasPulsingNotifications() { return mNotificationListContainer.hasPulsingNotifications(); } - - @Override - public ActivatableNotificationView getActivatedChild() { - return mNotificationStackScrollLayoutController.getActivatedChild(); - } - - @Override - public void setActivatedChild(ActivatableNotificationView o) { - mNotificationStackScrollLayoutController.setActivatedChild(o); - } } @Override @@ -3338,12 +3333,12 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mGestureRecorder = recorder; mHideExpandedRunnable = hideExpandedRunnable; + mNotificationShelfController = notificationShelfController; if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { mNotificationStackScrollLayoutController.setShelfController( notificationShelfController); + mLockscreenShadeTransitionController.bindController(notificationShelfController); } - mNotificationShelfController = notificationShelfController; - mLockscreenShadeTransitionController.bindController(notificationShelfController); updateMaxDisplayedNotifications(true); } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 2f4cc1467517..ebbf1b5ac7c9 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -288,7 +288,6 @@ public class NotificationShadeWindowViewController { mService.userActivity(); mService.wakeUpIfDozing( mClock.uptimeMillis(), - mView, "LOCK_ICON_TOUCH", PowerManager.WAKE_REASON_GESTURE); } diff --git a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt index b42bdaac9cdb..fd82e2fc01fc 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt @@ -90,7 +90,6 @@ class PulsingGestureListener @Inject constructor( shadeLogger.d("Single tap handled, requesting centralSurfaces.wakeUpIfDozing") centralSurfaces.wakeUpIfDozing( SystemClock.uptimeMillis(), - notificationShadeWindowView, "PULSING_SINGLE_TAP", PowerManager.WAKE_REASON_TAP ) @@ -116,7 +115,6 @@ class PulsingGestureListener @Inject constructor( ) { centralSurfaces.wakeUpIfDozing( SystemClock.uptimeMillis(), - notificationShadeWindowView, "PULSING_DOUBLE_TAP", PowerManager.WAKE_REASON_TAP ) diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt index 34c9f6d16fff..b6a2a8a45d68 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt @@ -18,7 +18,6 @@ package com.android.systemui.shade import android.view.MotionEvent import android.view.ViewGroup import com.android.systemui.statusbar.RemoteInputController -import com.android.systemui.statusbar.notification.row.ActivatableNotificationView import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.phone.HeadsUpAppearanceController @@ -254,7 +253,4 @@ interface ShadeNotificationPresenter { /** Returns whether the screen has temporarily woken up to display notifications. */ fun hasPulsingNotifications(): Boolean - - /** The current activated notification. */ - var activatedChild: ActivatableNotificationView? } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java index f7d37e6b1058..4ef20633ac21 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java @@ -20,7 +20,6 @@ import android.view.View; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; -import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController; import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope; import com.android.systemui.statusbar.notification.stack.AmbientState; @@ -107,11 +106,6 @@ public class LegacyNotificationShelfControllerImpl implements NotificationShelfC } @Override - public void setOnActivatedListener(ActivatableNotificationView.OnActivatedListener listener) { - mView.setOnActivatedListener(listener); - } - - @Override public void setOnClickListener(View.OnClickListener onClickListener) { mView.setOnClickListener(onClickListener); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index 63e29d105cd8..792939b7649b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -276,7 +276,6 @@ class LockscreenShadeTransitionController @Inject constructor( if (statusBarStateController.state == StatusBarState.KEYGUARD) { centralSurfaces.wakeUpIfDozing( SystemClock.uptimeMillis(), - it, "SHADE_CLICK", PowerManager.WAKE_REASON_GESTURE, ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java index cb414ba3550f..1c9bc60deefe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java @@ -15,7 +15,6 @@ */ package com.android.systemui.statusbar; -import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; /** @@ -25,8 +24,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow * for affecting the state of the system (e.g. starting an intent, given that the presenter may * want to perform some action before doing so). */ -public interface NotificationPresenter extends ExpandableNotificationRow.OnExpandClickListener, - ActivatableNotificationView.OnActivatedListener { +public interface NotificationPresenter extends ExpandableNotificationRow.OnExpandClickListener { /** * Returns true if the presenter is not visible. For example, it may not be necessary to do * animations if this returns true. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index 9e2a07ea256b..c5191154377a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -124,7 +124,7 @@ public class NotificationRemoteInputManager implements Dumpable { View view, PendingIntent pendingIntent, RemoteViews.RemoteResponse response) { mCentralSurfacesOptionalLazy.get().ifPresent( centralSurfaces -> centralSurfaces.wakeUpIfDozing( - SystemClock.uptimeMillis(), view, "NOTIFICATION_CLICK", + SystemClock.uptimeMillis(), "NOTIFICATION_CLICK", PowerManager.WAKE_REASON_GESTURE)); final NotificationEntry entry = getNotificationForParent(view.getParent()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt index 07cfd0d8019c..1619ddaac85c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt @@ -21,8 +21,6 @@ import android.view.View import android.view.View.OnClickListener import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.statusbar.notification.row.ActivatableNotificationView -import com.android.systemui.statusbar.notification.row.ActivatableNotificationView.OnActivatedListener import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout @@ -43,9 +41,6 @@ interface NotificationShelfController { /** Whether or not the shelf can modify the color of notifications in the shade. */ fun canModifyColorOfNotifications(): Boolean - /** @see ActivatableNotificationView.setOnActivatedListener */ - fun setOnActivatedListener(listener: OnActivatedListener) - /** Binds the shelf to the host [NotificationStackScrollLayout], via its Controller. */ fun bind( ambientState: AmbientState, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java index 705cf92ee869..bab553e18fdc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java @@ -71,7 +71,7 @@ public final class NotificationClicker implements View.OnClickListener { } mCentralSurfacesOptional.ifPresent(centralSurfaces -> centralSurfaces.wakeUpIfDozing( - SystemClock.uptimeMillis(), v, "NOTIFICATION_CLICK", + SystemClock.uptimeMillis(), "NOTIFICATION_CLICK", PowerManager.WAKE_REASON_GESTURE)); final ExpandableNotificationRow row = (ExpandableNotificationRow) v; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java index 611edf88bffb..bdb206beb123 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java @@ -177,7 +177,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { private void bindRow(NotificationEntry entry, ExpandableNotificationRow row) { mListContainer.bindRow(row); mNotificationRemoteInputManager.bindRow(row); - row.setOnActivatedListener(mPresenter); entry.setRow(row); mNotifBindPipeline.manageRow(entry, row); mBindRowCallback.onBindRow(row); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 68ad49befc54..766ad88f8a55 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -28,7 +28,6 @@ import android.util.MathUtils; import android.view.Choreographer; import android.view.MotionEvent; import android.view.View; -import android.view.accessibility.AccessibilityManager; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; @@ -57,30 +56,6 @@ import java.util.Set; public abstract class ActivatableNotificationView extends ExpandableOutlineView { /** - * The amount of width, which is kept in the end when performing a disappear animation (also - * the amount from which the horizontal appearing begins) - */ - private static final float HORIZONTAL_COLLAPSED_REST_PARTIAL = 0.05f; - - /** - * At which point from [0,1] does the horizontal collapse animation end (or start when - * expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated. - */ - private static final float HORIZONTAL_ANIMATION_END = 0.2f; - - /** - * At which point from [0,1] does the horizontal collapse animation start (or start when - * expanding)? 1.0 meaning that it starts immediately and 0.0 that it is animated at all. - */ - private static final float HORIZONTAL_ANIMATION_START = 1.0f; - - /** - * At which point from [0,1] does the vertical collapse animation start (or end when - * expanding) 1.0 meaning that it starts immediately and 0.0 that it is animated at all. - */ - private static final float VERTICAL_ANIMATION_START = 1.0f; - - /** * A sentinel value when no color should be used. Can be used with {@link #setTintColor(int)} * or {@link #setOverrideTintColor(int, float)}. */ @@ -95,10 +70,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView * The start of the animation is at #ALPHA_APPEAR_START_FRACTION */ private static final float ALPHA_APPEAR_END_FRACTION = 1; - private static final Interpolator ACTIVATE_INVERSE_INTERPOLATOR - = new PathInterpolator(0.6f, 0, 0.5f, 1); - private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR - = new PathInterpolator(0, 0, 0.5f, 1); private final Set<SourceType> mOnDetachResetRoundness = new HashSet<>(); private int mTintedRippleColor; private int mNormalRippleColor; @@ -112,10 +83,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView */ private boolean mActivated; - private OnActivatedListener mOnActivatedListener; - private final Interpolator mSlowOutFastInInterpolator; - private final Interpolator mSlowOutLinearInInterpolator; private Interpolator mCurrentAppearInterpolator; NotificationBackgroundView mBackgroundNormal; @@ -142,13 +110,11 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView protected Point mTargetPoint; private boolean mDismissed; private boolean mRefocusOnDismiss; - private AccessibilityManager mAccessibilityManager; protected boolean mUseRoundnessSourceTypes; public ActivatableNotificationView(Context context, AttributeSet attrs) { super(context, attrs); mSlowOutFastInInterpolator = new PathInterpolator(0.8f, 0.0f, 0.6f, 1.0f); - mSlowOutLinearInInterpolator = new PathInterpolator(0.8f, 0.0f, 1.0f, 1.0f); setClipChildren(false); setClipToPadding(false); updateColors(); @@ -230,7 +196,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView public void onTap() {} /** Sets the last action up time this view was touched. */ - void setLastActionUpTime(long eventTime) { + public void setLastActionUpTime(long eventTime) { mLastActionUpTime = eventTime; } @@ -249,10 +215,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView return false; } - protected boolean handleSlideBack() { - return false; - } - /** * @return whether this view is interactive and can be double tapped */ @@ -270,40 +232,12 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mBackgroundNormal.setPressedAllowed(allowed); } - void makeActive() { - mActivated = true; - if (mOnActivatedListener != null) { - mOnActivatedListener.onActivated(this); - } - } - - public boolean isActive() { - return mActivated; - } - - /** - * Cancels the hotspot and makes the notification inactive. - */ - public void makeInactive(boolean animate) { - if (mActivated) { - mActivated = false; - } - if (mOnActivatedListener != null) { - mOnActivatedListener.onActivationReset(this); - } - } - private void updateOutlineAlpha() { float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED; alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount); setOutlineAlpha(alpha); } - private void setNormalBackgroundVisibilityAmount(float normalBackgroundVisibilityAmount) { - mNormalBackgroundVisibilityAmount = normalBackgroundVisibilityAmount; - updateOutlineAlpha(); - } - @Override public void setBelowSpeedBump(boolean below) { super.setBelowSpeedBump(below); @@ -318,13 +252,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } /** - * @return whether we are below the speed bump - */ - public boolean isBelowSpeedBump() { - return mIsBelowSpeedBump; - } - - /** * Sets the tint color of the background */ protected void setTintColor(int color) { @@ -728,10 +655,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } } - public void setOnActivatedListener(OnActivatedListener onActivatedListener) { - mOnActivatedListener = onActivatedListener; - } - @Override public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd, int outlineTranslation) { @@ -782,14 +705,10 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView return mRefocusOnDismiss || isAccessibilityFocused(); } - void setTouchHandler(Gefingerpoken touchHandler) { + public void setTouchHandler(Gefingerpoken touchHandler) { mTouchHandler = touchHandler; } - public void setAccessibilityManager(AccessibilityManager accessibilityManager) { - mAccessibilityManager = accessibilityManager; - } - /** * Enable the support for rounded corner based on the SourceType * @param enabled true if is supported @@ -832,9 +751,4 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView }); } } - - public interface OnActivatedListener { - void onActivated(ActivatableNotificationView view); - void onActivationReset(ActivatableNotificationView view); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java index b084a765956d..028cd18147ab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java @@ -16,15 +16,12 @@ package com.android.systemui.statusbar.notification.row; -import android.os.SystemClock; import android.view.MotionEvent; import android.view.View; import android.view.accessibility.AccessibilityManager; import com.android.systemui.Gefingerpoken; -import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.statusbar.phone.NotificationTapHelper; import com.android.systemui.util.ViewController; import javax.inject.Inject; @@ -37,44 +34,16 @@ public class ActivatableNotificationViewController private final ExpandableOutlineViewController mExpandableOutlineViewController; private final AccessibilityManager mAccessibilityManager; private final FalsingManager mFalsingManager; - private final FalsingCollector mFalsingCollector; - private final NotificationTapHelper mNotificationTapHelper; private final TouchHandler mTouchHandler = new TouchHandler(); - private boolean mNeedsDimming; - @Inject public ActivatableNotificationViewController(ActivatableNotificationView view, - NotificationTapHelper.Factory notificationTapHelpFactory, ExpandableOutlineViewController expandableOutlineViewController, - AccessibilityManager accessibilityManager, FalsingManager falsingManager, - FalsingCollector falsingCollector) { + AccessibilityManager accessibilityManager, FalsingManager falsingManager) { super(view); mExpandableOutlineViewController = expandableOutlineViewController; mAccessibilityManager = accessibilityManager; mFalsingManager = falsingManager; - mFalsingCollector = falsingCollector; - - mNotificationTapHelper = notificationTapHelpFactory.create( - (active) -> { - if (active) { - mView.makeActive(); - mFalsingCollector.onNotificationActive(); - } else { - mView.makeInactive(true /* animate */); - } - }, mView::performClick, mView::handleSlideBack); - - mView.setOnActivatedListener(new ActivatableNotificationView.OnActivatedListener() { - @Override - public void onActivated(ActivatableNotificationView view) { - mFalsingCollector.onNotificationActive(); - } - - @Override - public void onActivationReset(ActivatableNotificationView view) { - } - }); } /** @@ -85,7 +54,6 @@ public class ActivatableNotificationViewController mExpandableOutlineViewController.init(); mView.setOnTouchListener(mTouchHandler); mView.setTouchHandler(mTouchHandler); - mView.setAccessibilityManager(mAccessibilityManager); } @Override @@ -99,15 +67,9 @@ public class ActivatableNotificationViewController } class TouchHandler implements Gefingerpoken, View.OnTouchListener { - private boolean mBlockNextTouch; - @Override public boolean onTouch(View v, MotionEvent ev) { boolean result = false; - if (mBlockNextTouch) { - mBlockNextTouch = false; - return true; - } if (ev.getAction() == MotionEvent.ACTION_UP) { mView.setLastActionUpTime(ev.getEventTime()); } 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 950ab5d2f5ef..97dcbb54e44f 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 @@ -969,15 +969,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @Override - protected boolean handleSlideBack() { - if (mMenuRow != null && mMenuRow.isMenuVisible()) { - animateResetTranslation(); - return true; - } - return false; - } - - @Override public boolean isSummaryWithChildren() { return mIsSummaryWithChildren; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt new file mode 100644 index 000000000000..54af1078741c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ActivatableNotificationViewBinder.kt @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.row.ui.viewbinder + +import android.view.MotionEvent +import android.view.View +import android.view.View.OnTouchListener +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.Gefingerpoken +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.statusbar.notification.row.ActivatableNotificationView +import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModel +import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.launch + +/** Binds an [ActivatableNotificationView] to its [view model][ActivatableNotificationViewModel]. */ +object ActivatableNotificationViewBinder { + + fun bind( + viewModel: ActivatableNotificationViewModel, + view: ActivatableNotificationView, + falsingManager: FalsingManager, + ) { + ExpandableOutlineViewBinder.bind(viewModel, view) + val touchHandler = TouchHandler(view, falsingManager) + view.repeatWhenAttached { + repeatOnLifecycle(Lifecycle.State.STARTED) { + launch { + viewModel.isTouchable.collect { isTouchable -> + touchHandler.isTouchEnabled = isTouchable + } + } + view.registerListenersWhileAttached(touchHandler) + } + } + } + + private suspend fun ActivatableNotificationView.registerListenersWhileAttached( + touchHandler: TouchHandler, + ): Unit = + try { + setOnTouchListener(touchHandler) + setTouchHandler(touchHandler) + awaitCancellation() + } finally { + setTouchHandler(null) + setOnTouchListener(null) + } +} + +private class TouchHandler( + private val view: ActivatableNotificationView, + private val falsingManager: FalsingManager, +) : Gefingerpoken, OnTouchListener { + + var isTouchEnabled = false + + override fun onTouch(v: View, ev: MotionEvent): Boolean { + val result = false + if (ev.action == MotionEvent.ACTION_UP) { + view.setLastActionUpTime(ev.eventTime) + } + // With a11y, just do nothing. + if (!isTouchEnabled) { + return false + } + if (ev.action == MotionEvent.ACTION_UP) { + // If this is a false tap, capture the even so it doesn't result in a click. + val falseTap: Boolean = falsingManager.isFalseTap(FalsingManager.LOW_PENALTY) + if (!falseTap && v is ActivatableNotificationView) { + v.onTap() + } + return falseTap + } + return result + } + + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean = false + + /** Use [onTouch] instead. */ + override fun onTouchEvent(ev: MotionEvent): Boolean = false +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ExpandableOutlineViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ExpandableOutlineViewBinder.kt new file mode 100644 index 000000000000..745ce7743fe9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ExpandableOutlineViewBinder.kt @@ -0,0 +1,13 @@ +package com.android.systemui.statusbar.notification.row.ui.viewbinder + +import com.android.systemui.statusbar.notification.row.ExpandableOutlineView +import com.android.systemui.statusbar.notification.row.ui.viewmodel.ExpandableOutlineViewModel as ViewModel + +object ExpandableOutlineViewBinder { + fun bind( + viewModel: ViewModel, + view: ExpandableOutlineView, + ) { + ExpandableViewBinder.bind(viewModel, view) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ExpandableViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ExpandableViewBinder.kt new file mode 100644 index 000000000000..49cfb5766ce8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/ExpandableViewBinder.kt @@ -0,0 +1,8 @@ +package com.android.systemui.statusbar.notification.row.ui.viewbinder + +import com.android.systemui.statusbar.notification.row.ExpandableView +import com.android.systemui.statusbar.notification.row.ui.viewmodel.ExpandableViewModel as ViewModel + +object ExpandableViewBinder { + fun bind(viewModel: ViewModel, view: ExpandableView) {} +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt new file mode 100644 index 000000000000..f46d42473863 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModel.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.row.ui.viewmodel + +import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor +import dagger.Module +import dagger.Provides +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** ViewModel for [com.android.systemui.statusbar.notification.row.ActivatableNotificationView]. */ +interface ActivatableNotificationViewModel : ExpandableOutlineViewModel { + /** Does the view react to touches? */ + val isTouchable: Flow<Boolean> + + companion object { + operator fun invoke( + a11yInteractor: AccessibilityInteractor, + ): ActivatableNotificationViewModel = ActivatableNotificationViewModelImpl(a11yInteractor) + } +} + +private class ActivatableNotificationViewModelImpl( + a11yInteractor: AccessibilityInteractor, +) : ActivatableNotificationViewModel { + override val isTouchable: Flow<Boolean> = + // If a11y touch exploration is enabled, then the activatable view should ignore touches + a11yInteractor.isTouchExplorationEnabled.map { !it } +} + +@Module +object ActivatableNotificationViewModelModule { + @Provides + fun provideViewModel(interactor: AccessibilityInteractor) = + ActivatableNotificationViewModel(interactor) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ExpandableOutlineViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ExpandableOutlineViewModel.kt new file mode 100644 index 000000000000..5904c77426a9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ExpandableOutlineViewModel.kt @@ -0,0 +1,4 @@ +package com.android.systemui.statusbar.notification.row.ui.viewmodel + +/** ViewModel for [com.android.systemui.statusbar.notification.row.ExpandableOutlineView]. */ +interface ExpandableOutlineViewModel : ExpandableViewModel diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ExpandableViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ExpandableViewModel.kt new file mode 100644 index 000000000000..5efaf04aab9b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ExpandableViewModel.kt @@ -0,0 +1,4 @@ +package com.android.systemui.statusbar.notification.row.ui.viewmodel + +/** ViewModel for [com.android.systemui.statusbar.notification.row.ExpandableView]. */ +interface ExpandableViewModel diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt index 8ba65f7a1418..014406fe49f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt @@ -16,10 +16,14 @@ package com.android.systemui.statusbar.notification.shelf.domain.interactor +import android.os.PowerManager import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.KeyguardRepository +import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.NotificationShelf +import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent +import com.android.systemui.util.time.SystemClock import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -31,6 +35,9 @@ class NotificationShelfInteractor constructor( private val keyguardRepository: KeyguardRepository, private val deviceEntryFaceAuthRepository: DeviceEntryFaceAuthRepository, + private val centralSurfaces: CentralSurfaces, + private val systemClock: SystemClock, + private val keyguardTransitionController: LockscreenShadeTransitionController, ) { /** Is the shelf showing on the keyguard? */ val isShowingOnKeyguard: Flow<Boolean> @@ -45,4 +52,14 @@ constructor( ) { isKeyguardShowing, isBypassEnabled -> isKeyguardShowing && isBypassEnabled } + + /** Transition keyguard to the locked shade, triggered by the shelf. */ + fun goToLockedShadeFromShelf() { + centralSurfaces.wakeUpIfDozing( + systemClock.uptimeMillis(), + "SHADE_CLICK", + PowerManager.WAKE_REASON_GESTURE, + ) + keyguardTransitionController.goToLockedShade(null) + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt index b190cf658fa8..c82318913ced 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt @@ -17,10 +17,8 @@ package com.android.systemui.statusbar.notification.shelf.ui.viewbinder import android.view.View -import android.view.accessibility.AccessibilityManager import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.systemui.classifier.FalsingCollector import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.lifecycle.repeatWhenAttached @@ -28,19 +26,17 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl import com.android.systemui.statusbar.NotificationShelf import com.android.systemui.statusbar.NotificationShelfController -import com.android.systemui.statusbar.notification.row.ActivatableNotificationView -import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController -import com.android.systemui.statusbar.notification.row.ExpandableOutlineViewController -import com.android.systemui.statusbar.notification.row.ExpandableViewController +import com.android.systemui.statusbar.notification.row.ui.viewbinder.ActivatableNotificationViewBinder import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController +import com.android.systemui.statusbar.phone.NotificationIconAreaController import com.android.systemui.statusbar.phone.NotificationIconContainer -import com.android.systemui.statusbar.phone.NotificationTapHelper import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope import com.android.systemui.util.kotlin.getValue import dagger.Lazy import javax.inject.Inject +import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -57,11 +53,9 @@ constructor( private val shelf: NotificationShelf, private val viewModel: NotificationShelfViewModel, featureFlags: FeatureFlags, - private val notifTapHelperFactory: NotificationTapHelper.Factory, - private val a11yManager: AccessibilityManager, private val falsingManager: FalsingManager, - private val falsingCollector: FalsingCollector, hostControllerLazy: Lazy<NotificationStackScrollLayoutController>, + private val notificationIconAreaController: NotificationIconAreaController, ) : NotificationShelfController { private val hostController: NotificationStackScrollLayoutController by hostControllerLazy @@ -78,43 +72,28 @@ constructor( } fun init() { - NotificationShelfViewBinder.bind(viewModel, shelf) - - ActivatableNotificationViewController( - shelf, - notifTapHelperFactory, - ExpandableOutlineViewController(shelf, ExpandableViewController(shelf)), - a11yManager, - falsingManager, - falsingCollector, - ) - .init() + NotificationShelfViewBinder.bind(viewModel, shelf, falsingManager) hostController.setShelf(shelf) hostController.setOnNotificationRemovedListener { child, _ -> view.requestRoundnessResetFor(child) } + notificationIconAreaController.setShelfIcons(shelf.shelfIcons) } override val intrinsicHeight: Int - get() = shelf.intrinsicHeight + get() = unsupported override val shelfIcons: NotificationIconContainer - get() = shelf.shelfIcons + get() = unsupported override fun canModifyColorOfNotifications(): Boolean = unsupported - override fun setOnActivatedListener(listener: ActivatableNotificationView.OnActivatedListener) { - shelf.setOnActivatedListener(listener) - } - override fun bind( ambientState: AmbientState, notificationStackScrollLayoutController: NotificationStackScrollLayoutController, ) = unsupported - override fun setOnClickListener(listener: View.OnClickListener) { - shelf.setOnClickListener(listener) - } + override fun setOnClickListener(listener: View.OnClickListener) = unsupported private val unsupported: Nothing get() = NotificationShelfController.throwIllegalFlagStateError(expected = true) @@ -122,14 +101,32 @@ constructor( /** Binds a [NotificationShelf] to its backend. */ object NotificationShelfViewBinder { - fun bind(viewModel: NotificationShelfViewModel, shelf: NotificationShelf) { + fun bind( + viewModel: NotificationShelfViewModel, + shelf: NotificationShelf, + falsingManager: FalsingManager, + ) { + ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager) shelf.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.canModifyColorOfNotifications .onEach(shelf::setCanModifyColorOfNotifications) .launchIn(this) viewModel.isClickable.onEach(shelf::setCanInteract).launchIn(this) + registerViewListenersWhileAttached(shelf, viewModel) } } } + + private suspend fun registerViewListenersWhileAttached( + shelf: NotificationShelf, + viewModel: NotificationShelfViewModel, + ) { + try { + shelf.setOnClickListener { viewModel.onShelfClicked() } + awaitCancellation() + } finally { + shelf.setOnClickListener(null) + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt index 5e297c89dd2f..fb1944315e10 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.shelf.ui.viewmodel import com.android.systemui.statusbar.NotificationShelf +import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModel import com.android.systemui.statusbar.notification.shelf.domain.interactor.NotificationShelfInteractor import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope import javax.inject.Inject @@ -29,7 +30,8 @@ class NotificationShelfViewModel @Inject constructor( private val interactor: NotificationShelfInteractor, -) { + activatableViewModel: ActivatableNotificationViewModel, +) : ActivatableNotificationViewModel by activatableViewModel { /** Is the shelf allowed to be clickable when it has content? */ val isClickable: Flow<Boolean> get() = interactor.isShowingOnKeyguard @@ -37,4 +39,9 @@ constructor( /** Is the shelf allowed to modify the color of notifications in the host layout? */ val canModifyColorOfNotifications: Flow<Boolean> get() = interactor.isShelfStatic.map { static -> !static } + + /** Notifies that the user has clicked the shelf. */ + fun onShelfClicked() { + interactor.goToLockedShadeFromShelf() + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index 77ede0471603..ae7c216adcbb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -34,7 +34,6 @@ import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.BypassController; @@ -65,7 +64,6 @@ public class AmbientState implements Dumpable { private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private int mScrollY; private boolean mDimmed; - private ActivatableNotificationView mActivatedChild; private float mOverScrollTopAmount; private float mOverScrollBottomAmount; private boolean mDozing; @@ -360,14 +358,6 @@ public class AmbientState implements Dumpable { mHideSensitive = hideSensitive; } - /** - * In dimmed mode, a child can be activated, which happens on the first tap of the double-tap - * interaction. This child is then scaled normally and its background is fully opaque. - */ - public void setActivatedChild(ActivatableNotificationView activatedChild) { - mActivatedChild = activatedChild; - } - public boolean isDimmed() { // While we are expanding from pulse, we want the notifications not to be dimmed, otherwise // you'd see the difference to the pulsing notification @@ -382,10 +372,6 @@ public class AmbientState implements Dumpable { return mHideSensitive; } - public ActivatableNotificationView getActivatedChild() { - return mActivatedChild; - } - public void setOverScrollAmount(float amount, boolean onTop) { if (onTop) { mOverScrollTopAmount = amount; 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 af608a7f3a64..555d5027c7b6 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 @@ -4469,24 +4469,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } - /** - * See {@link AmbientState#setActivatedChild}. - */ - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - void setActivatedChild(ActivatableNotificationView activatedChild) { - mAmbientState.setActivatedChild(activatedChild); - if (mAnimationsEnabled) { - mActivateNeedsAnimation = true; - mNeedsAnimation = true; - } - requestChildrenUpdate(); - } - - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public ActivatableNotificationView getActivatedChild() { - return mAmbientState.getActivatedChild(); - } - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private void applyCurrentState() { int numChildren = getChildCount(); @@ -5137,6 +5119,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable requestChildrenUpdate(); } + @Nullable + public ExpandableView getShelf() { + if (NotificationShelfController.checkRefactorFlagEnabled(mAmbientState.getFeatureFlags())) { + return mShelf; + } else { + return null; + } + } + public void setShelf(NotificationShelf shelf) { if (!NotificationShelfController.checkRefactorFlagEnabled( mAmbientState.getFeatureFlags())) { @@ -5236,7 +5227,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable void onStatePostChange(boolean fromShadeLocked) { boolean onKeyguard = onKeyguard(); - mAmbientState.setActivatedChild(null); mAmbientState.setDimmed(onKeyguard); if (mHeadsUpAppearanceController != null) { @@ -5245,11 +5235,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable setDimmed(onKeyguard, fromShadeLocked); setExpandingEnabled(!onKeyguard); - ActivatableNotificationView activatedChild = getActivatedChild(); - setActivatedChild(null); - if (activatedChild != null) { - activatedChild.makeInactive(false /* animate */); - } updateFooter(); requestChildrenUpdate(); onUpdateRowStates(); 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 1c8727f1b092..b81fb7f022b4 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 @@ -1370,14 +1370,6 @@ public class NotificationStackScrollLayoutController { mView.onUpdateRowStates(); } - public ActivatableNotificationView getActivatedChild() { - return mView.getActivatedChild(); - } - - public void setActivatedChild(ActivatableNotificationView view) { - mView.setActivatedChild(view); - } - public void runAfterAnimationFinished(Runnable r) { mView.runAfterAnimationFinished(r); } @@ -1600,6 +1592,14 @@ public class NotificationStackScrollLayoutController { mView.setShelf(shelf); } + public int getShelfHeight() { + if (!NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) { + return 0; + } + ExpandableView shelf = mView.getShelf(); + return shelf == null ? 0 : shelf.getIntrinsicHeight(); + } + /** * Enum for UiEvent logged from this class */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index b1fb13e0e89f..306047388381 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -126,7 +126,7 @@ public class StackScrollAlgorithm { updateHeadsUpStates(algorithmState, ambientState); updatePulsingStates(algorithmState, ambientState); - updateDimmedActivatedHideSensitive(ambientState, algorithmState); + updateDimmedAndHideSensitive(ambientState, algorithmState); updateClipping(algorithmState, ambientState); updateSpeedBumpState(algorithmState, speedBumpIndex); updateShelfState(algorithmState, ambientState); @@ -341,25 +341,17 @@ public class StackScrollAlgorithm { } } - /** - * Updates the dimmed, activated and hiding sensitive states of the children. - */ - private void updateDimmedActivatedHideSensitive(AmbientState ambientState, - StackScrollAlgorithmState algorithmState) { + /** Updates the dimmed and hiding sensitive states of the children. */ + private void updateDimmedAndHideSensitive(AmbientState ambientState, + StackScrollAlgorithmState algorithmState) { boolean dimmed = ambientState.isDimmed(); boolean hideSensitive = ambientState.isHideSensitive(); - View activatedChild = ambientState.getActivatedChild(); int childCount = algorithmState.visibleChildren.size(); for (int i = 0; i < childCount; i++) { ExpandableView child = algorithmState.visibleChildren.get(i); ExpandableViewState childViewState = child.getViewState(); childViewState.dimmed = dimmed; childViewState.hideSensitive = hideSensitive; - boolean isActivatedChild = activatedChild == child; - if (dimmed && isActivatedChild) { - childViewState.setZTranslation(childViewState.getZTranslation() - + 2.0f * ambientState.getZDistanceBetweenElements()); - } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index 3a1272fa32e8..a6c0435921d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -212,7 +212,7 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn /** * Wakes up the device if the device was dozing. */ - void wakeUpIfDozing(long time, View where, String why, @PowerManager.WakeReason int wakeReason); + void wakeUpIfDozing(long time, String why, @PowerManager.WakeReason int wakeReason); NotificationShadeWindowView getNotificationShadeWindowView(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 0960efb7388f..6cb9582c344e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -1253,7 +1253,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // TODO: Deal with the ugliness that comes from having some of the status bar broken out // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot. - mNotificationIconAreaController.setupShelf(mNotificationShelfController); + if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { + mNotificationIconAreaController.setupShelf(mNotificationShelfController); + } mShadeExpansionStateManager.addExpansionListener(mWakeUpCoordinator); // Allow plugins to reference DarkIconDispatcher and StatusBarStateController @@ -1563,7 +1565,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mNotifListContainer, mHeadsUpManager, mJankMonitor); - mNotificationShelfController.setOnActivatedListener(mPresenter); mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController); mStackScrollerController.setNotificationActivityStarter(mNotificationActivityStarter); mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter); @@ -1590,12 +1591,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { * Ask the display to wake up if currently dozing, else do nothing * * @param time when to wake up - * @param where the view requesting the wakeup * @param why the reason for the wake up */ @Override - public void wakeUpIfDozing(long time, View where, String why, - @PowerManager.WakeReason int wakeReason) { + public void wakeUpIfDozing(long time, String why, @PowerManager.WakeReason int wakeReason) { if (mDozing && mScreenOffAnimationController.allowWakeUpIfDozing()) { mPowerManager.wakeUp( time, wakeReason, "com.android.systemui:" + why); @@ -3481,7 +3480,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mStatusBarHideIconsForBouncerManager.setBouncerShowingAndTriggerUpdate(bouncerShowing); mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */); if (mBouncerShowing) { - wakeUpIfDozing(SystemClock.uptimeMillis(), null, "BOUNCER_VISIBLE", + wakeUpIfDozing(SystemClock.uptimeMillis(), "BOUNCER_VISIBLE", PowerManager.WAKE_REASON_GESTURE); } updateScrimController(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index 057fa42bd347..55dc18859c25 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -23,6 +23,7 @@ import com.android.systemui.animation.Interpolators; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -92,6 +93,8 @@ public class NotificationIconAreaController implements private final DemoModeController mDemoModeController; + private final FeatureFlags mFeatureFlags; + private int mAodIconAppearTranslation; private boolean mAnimationsEnabled; @@ -122,11 +125,12 @@ public class NotificationIconAreaController implements Optional<Bubbles> bubblesOptional, DemoModeController demoModeController, DarkIconDispatcher darkIconDispatcher, - StatusBarWindowController statusBarWindowController, + FeatureFlags featureFlags, StatusBarWindowController statusBarWindowController, ScreenOffAnimationController screenOffAnimationController) { mContrastColorUtil = ContrastColorUtil.getInstance(context); mContext = context; mStatusBarStateController = statusBarStateController; + mFeatureFlags = featureFlags; mStatusBarStateController.addCallback(this); mMediaManager = notificationMediaManager; mDozeParameters = dozeParameters; @@ -192,9 +196,16 @@ public class NotificationIconAreaController implements } public void setupShelf(NotificationShelfController notificationShelfController) { + NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags); mShelfIcons = notificationShelfController.getShelfIcons(); } + public void setShelfIcons(NotificationIconContainer icons) { + if (NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) { + mShelfIcons = icons; + } + } + public void onDensityOrFontScaleChanged(Context context) { updateIconLayoutParams(context); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 39362cf29e14..0398a0957501 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -31,7 +31,6 @@ import android.util.Slog; import android.view.View; import android.view.accessibility.AccessibilityManager; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.InitController; import com.android.systemui.R; @@ -58,14 +57,12 @@ import com.android.systemui.statusbar.notification.collection.inflation.Notifica import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; -import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; -import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -84,7 +81,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, private final NotifShadeEventSource mNotifShadeEventSource; private final NotificationMediaManager mMediaManager; private final NotificationGutsManager mGutsManager; - private final LockscreenGestureLogger mLockscreenGestureLogger; private final NotificationPanelViewController mNotificationPanel; private final HeadsUpManagerPhone mHeadsUpManager; @@ -151,7 +147,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, mNotifShadeEventSource = notifShadeEventSource; mMediaManager = notificationMediaManager; mGutsManager = notificationGutsManager; - mLockscreenGestureLogger = lockscreenGestureLogger; mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView()); mNotificationShadeWindowController = notificationShadeWindowController; mNotifPipelineFlags = notifPipelineFlags; @@ -239,34 +234,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, } @Override - public void onActivated(ActivatableNotificationView view) { - onActivated(); - if (view != null) { - mNotificationPanel.getShadeNotificationPresenter().setActivatedChild(view); - } - } - - public void onActivated() { - mLockscreenGestureLogger.write( - MetricsEvent.ACTION_LS_NOTE, - 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */); - mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_NOTIFICATION_FALSE_TOUCH); - ActivatableNotificationView previousView = - mNotificationPanel.getShadeNotificationPresenter().getActivatedChild(); - if (previousView != null) { - previousView.makeInactive(true /* animate */); - } - } - - @Override - public void onActivationReset(ActivatableNotificationView view) { - if (view == mNotificationPanel.getShadeNotificationPresenter().getActivatedChild()) { - mNotificationPanel.getShadeNotificationPresenter().setActivatedChild(null); - mKeyguardIndicationController.hideTransientIndication(); - } - } - - @Override public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) { mMediaManager.updateMediaMetaData(metaDataChanged, allowEnterAnimation); } @@ -276,7 +243,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, boolean nowExpanded) { mHeadsUpManager.setExpanded(clickedEntry, nowExpanded); mCentralSurfaces.wakeUpIfDozing( - SystemClock.uptimeMillis(), clickedView, "NOTIFICATION_CLICK", + SystemClock.uptimeMillis(), "NOTIFICATION_CLICK", PowerManager.WAKE_REASON_GESTURE); if (nowExpanded) { if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { 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 5d4addab240a..b96001ffd74d 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 @@ -21,9 +21,7 @@ import android.content.ContentResolver; import android.os.Handler; import android.view.LayoutInflater; import android.view.ViewStub; - import androidx.constraintlayout.motion.widget.MotionLayout; - import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.LockIconView; import com.android.systemui.R; @@ -52,6 +50,7 @@ import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent; +import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule; import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; @@ -75,18 +74,16 @@ import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.CarrierConfigTracker; import com.android.systemui.util.settings.SecureSettings; - -import java.util.concurrent.Executor; - -import javax.inject.Named; -import javax.inject.Provider; - import dagger.Binds; import dagger.Module; import dagger.Provides; import dagger.multibindings.IntoSet; +import java.util.concurrent.Executor; +import javax.inject.Named; +import javax.inject.Provider; -@Module(subcomponents = StatusBarFragmentComponent.class) +@Module(subcomponents = StatusBarFragmentComponent.class, + includes = { ActivatableNotificationViewModelModule.class }) public abstract class StatusBarViewModule { public static final String SHADE_HEADER = "large_screen_shade_header"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt new file mode 100644 index 000000000000..aff52f511171 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.accessibility.data.repository + +import android.testing.AndroidTestingRunner +import android.view.accessibility.AccessibilityManager +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.mockito.withArgCaptor +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class AccessibilityRepositoryTest : SysuiTestCase() { + + @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule() + + // mocks + @Mock private lateinit var a11yManager: AccessibilityManager + + // real impls + private val underTest by lazy { AccessibilityRepository(a11yManager) } + + @Test + fun isTouchExplorationEnabled_reflectsA11yManager_initFalse() = runTest { + whenever(a11yManager.isTouchExplorationEnabled).thenReturn(false) + val isTouchExplorationEnabled by collectLastValue(underTest.isTouchExplorationEnabled) + assertThat(isTouchExplorationEnabled).isFalse() + } + + @Test + fun isTouchExplorationEnabled_reflectsA11yManager_initTrue() = runTest { + whenever(a11yManager.isTouchExplorationEnabled).thenReturn(true) + val isTouchExplorationEnabled by collectLastValue(underTest.isTouchExplorationEnabled) + assertThat(isTouchExplorationEnabled).isTrue() + } + + @Test + fun isTouchExplorationEnabled_reflectsA11yManager_changeTrue() = runTest { + whenever(a11yManager.isTouchExplorationEnabled).thenReturn(false) + val isTouchExplorationEnabled by collectLastValue(underTest.isTouchExplorationEnabled) + runCurrent() + withArgCaptor { verify(a11yManager).addTouchExplorationStateChangeListener(capture()) } + .onTouchExplorationStateChanged(/* enabled = */ true) + assertThat(isTouchExplorationEnabled).isTrue() + } + + @Test + fun isTouchExplorationEnabled_reflectsA11yManager_changeFalse() = runTest { + whenever(a11yManager.isTouchExplorationEnabled).thenReturn(true) + val isTouchExplorationEnabled by collectLastValue(underTest.isTouchExplorationEnabled) + runCurrent() + withArgCaptor { verify(a11yManager).addTouchExplorationStateChangeListener(capture()) } + .onTouchExplorationStateChanged(/* enabled = */ false) + assertThat(isTouchExplorationEnabled).isFalse() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt index 76aa08a1b92a..d7c06a76bf0f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt @@ -17,11 +17,11 @@ package com.android.systemui.shade import android.hardware.display.AmbientDisplayConfiguration +import android.os.PowerManager import android.provider.Settings.Secure.DOZE_DOUBLE_TAP_GESTURE import android.provider.Settings.Secure.DOZE_TAP_SCREEN_GESTURE import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper -import android.os.PowerManager import android.view.MotionEvent import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -38,15 +38,14 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor -import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyLong import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations +import org.mockito.Mockito.`when` as whenever @RunWith(AndroidTestingRunner::class) @RunWithLooper(setAsMainLooper = true) @@ -112,7 +111,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { // THEN wake up device if dozing verify(centralSurfaces).wakeUpIfDozing( - anyLong(), any(), anyString(), eq(PowerManager.WAKE_REASON_TAP)) + anyLong(), anyString(), eq(PowerManager.WAKE_REASON_TAP)) } @Test @@ -132,7 +131,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { // THEN wake up device if dozing verify(centralSurfaces).wakeUpIfDozing( - anyLong(), any(), anyString(), eq(PowerManager.WAKE_REASON_TAP)) + anyLong(), anyString(), eq(PowerManager.WAKE_REASON_TAP)) } @Test @@ -164,7 +163,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { // THEN the device doesn't wake up verify(centralSurfaces, never()).wakeUpIfDozing( - anyLong(), any(), anyString(), anyInt()) + anyLong(), anyString(), anyInt()) } @Test @@ -212,7 +211,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { // THEN the device doesn't wake up verify(centralSurfaces, never()).wakeUpIfDozing( - anyLong(), any(), anyString(), anyInt()) + anyLong(), anyString(), anyInt()) } @Test @@ -232,7 +231,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { // THEN the device doesn't wake up verify(centralSurfaces, never()).wakeUpIfDozing( - anyLong(), any(), anyString(), anyInt()) + anyLong(), anyString(), anyInt()) } @Test @@ -252,7 +251,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { // THEN the device doesn't wake up verify(centralSurfaces, never()).wakeUpIfDozing( - anyLong(), any(), anyString(), anyInt()) + anyLong(), anyString(), anyInt()) } fun updateSettings() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt new file mode 100644 index 000000000000..c9602307216d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.notification.row.ui.viewmodel + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.accessibility.data.repository.FakeAccessibilityRepository +import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor +import com.android.systemui.coroutines.collectLastValue +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class ActivatableNotificationViewModelTest : SysuiTestCase() { + + // fakes + private val a11yRepo = FakeAccessibilityRepository() + + // real impls + private val a11yInteractor = AccessibilityInteractor(a11yRepo) + private val underTest = ActivatableNotificationViewModel(a11yInteractor) + + @Test + fun isTouchable_whenA11yTouchExplorationDisabled() = runTest { + a11yRepo.isTouchExplorationEnabled.value = false + val isTouchable: Boolean? by collectLastValue(underTest.isTouchable) + assertThat(isTouchable).isTrue() + } + + @Test + fun isNotTouchable_whenA11yTouchExplorationEnabled() = runTest { + a11yRepo.isTouchExplorationEnabled.value = true + val isTouchable: Boolean? by collectLastValue(underTest.isTouchable) + assertThat(isTouchable).isFalse() + } + + @Test + fun isTouchable_whenA11yTouchExplorationChangesToDisabled() = runTest { + a11yRepo.isTouchExplorationEnabled.value = true + val isTouchable: Boolean? by collectLastValue(underTest.isTouchable) + runCurrent() + a11yRepo.isTouchExplorationEnabled.value = false + assertThat(isTouchable).isTrue() + } + + @Test + fun isNotTouchable_whenA11yTouchExplorationChangesToEnabled() = runTest { + a11yRepo.isTouchExplorationEnabled.value = false + val isTouchable: Boolean? by collectLastValue(underTest.isTouchable) + runCurrent() + a11yRepo.isTouchExplorationEnabled.value = true + assertThat(isTouchable).isFalse() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt index 2cc375b3d0ed..944eb2d8aadf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt @@ -18,17 +18,27 @@ package com.android.systemui.statusbar.notification.shelf.domain.interactor +import android.os.PowerManager import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.statusbar.LockscreenShadeTransitionController +import com.android.systemui.statusbar.phone.CentralSurfaces +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyLong +import org.mockito.Mockito.isNull +import org.mockito.Mockito.verify @RunWith(AndroidTestingRunner::class) @SmallTest @@ -36,8 +46,17 @@ class NotificationShelfInteractorTest : SysuiTestCase() { private val keyguardRepository = FakeKeyguardRepository() private val deviceEntryFaceAuthRepository = FakeDeviceEntryFaceAuthRepository() + private val centralSurfaces: CentralSurfaces = mock() + private val systemClock = FakeSystemClock() + private val keyguardTransitionController: LockscreenShadeTransitionController = mock() private val underTest = - NotificationShelfInteractor(keyguardRepository, deviceEntryFaceAuthRepository) + NotificationShelfInteractor( + keyguardRepository, + deviceEntryFaceAuthRepository, + centralSurfaces, + systemClock, + keyguardTransitionController, + ) @Test fun shelfIsNotStatic_whenKeyguardNotShowing() = runTest { @@ -85,4 +104,19 @@ class NotificationShelfInteractorTest : SysuiTestCase() { assertThat(onKeyguard).isFalse() } + + @Test + fun goToLockedShadeFromShelf_wakesUpFromDoze() { + underTest.goToLockedShadeFromShelf() + + verify(centralSurfaces) + .wakeUpIfDozing(anyLong(), any(), eq(PowerManager.WAKE_REASON_GESTURE)) + } + + @Test + fun goToLockedShadeFromShelf_invokesKeyguardTransitionController() { + underTest.goToLockedShadeFromShelf() + + verify(keyguardTransitionController).goToLockedShade(isNull(), eq(true)) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt index 439edaf3faaf..e9a8f3f0d60c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt @@ -18,28 +18,64 @@ package com.android.systemui.statusbar.notification.shelf.ui.viewmodel +import android.os.PowerManager import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.accessibility.data.repository.FakeAccessibilityRepository +import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.statusbar.LockscreenShadeTransitionController +import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModel import com.android.systemui.statusbar.notification.shelf.domain.interactor.NotificationShelfInteractor +import com.android.systemui.statusbar.phone.CentralSurfaces +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule @RunWith(AndroidTestingRunner::class) @SmallTest class NotificationShelfViewModelTest : SysuiTestCase() { + @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule() + + // mocks + @Mock private lateinit var centralSurfaces: CentralSurfaces + @Mock private lateinit var keyguardTransitionController: LockscreenShadeTransitionController + + // fakes private val keyguardRepository = FakeKeyguardRepository() private val deviceEntryFaceAuthRepository = FakeDeviceEntryFaceAuthRepository() - private val interactor = - NotificationShelfInteractor(keyguardRepository, deviceEntryFaceAuthRepository) - private val underTest = NotificationShelfViewModel(interactor) + private val systemClock = FakeSystemClock() + private val a11yRepo = FakeAccessibilityRepository() + + // real impls + private val a11yInteractor = AccessibilityInteractor(a11yRepo) + private val activatableViewModel = ActivatableNotificationViewModel(a11yInteractor) + private val interactor by lazy { + NotificationShelfInteractor( + keyguardRepository, + deviceEntryFaceAuthRepository, + centralSurfaces, + systemClock, + keyguardTransitionController, + ) + } + private val underTest by lazy { NotificationShelfViewModel(interactor, activatableViewModel) } @Test fun canModifyColorOfNotifications_whenKeyguardNotShowing() = runTest { @@ -87,4 +123,13 @@ class NotificationShelfViewModelTest : SysuiTestCase() { assertThat(isClickable).isFalse() } + + @Test + fun onClicked_goesToLockedShade() { + underTest.onShelfClicked() + + verify(centralSurfaces) + .wakeUpIfDozing(ArgumentMatchers.anyLong(), any(), eq(PowerManager.WAKE_REASON_GESTURE)) + verify(keyguardTransitionController).goToLockedShade(Mockito.isNull(), eq(true)) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 63320693831c..fdd6140267cd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -387,7 +387,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { when(mStackScroller.generateLayoutParams(any())).thenReturn(new LayoutParams(0, 0)); when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0)); when(mPowerManagerService.isInteractive()).thenReturn(true); - when(mStackScroller.getActivatedChild()).thenReturn(null); doAnswer(invocation -> { OnDismissAction onDismissAction = (OnDismissAction) invocation.getArguments()[0]; @@ -1327,7 +1326,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { // WHEN wakeup is requested final int wakeReason = PowerManager.WAKE_REASON_TAP; - mCentralSurfaces.wakeUpIfDozing(0, null, "", wakeReason); + mCentralSurfaces.wakeUpIfDozing(0, "", wakeReason); // THEN power manager receives wakeup verify(mPowerManagerService).wakeUp(eq(0L), eq(wakeReason), anyString(), anyString()); @@ -1341,7 +1340,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { // WHEN wakeup is requested final int wakeReason = PowerManager.WAKE_REASON_TAP; - mCentralSurfaces.wakeUpIfDozing(0, null, "", wakeReason); + mCentralSurfaces.wakeUpIfDozing(0, "", wakeReason); // THEN power manager receives wakeup verify(mPowerManagerService, never()).wakeUp(anyLong(), anyInt(), anyString(), anyString()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java index 7d9c0913e15a..8e1dcf0b1bb7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java @@ -27,6 +27,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.demomode.DemoModeController; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationListener; @@ -75,6 +76,8 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase { @Mock private DemoModeController mDemoModeController; @Mock private NotificationIconContainer mAodIcons; + @Mock + private FeatureFlags mFeatureFlags; @Before public void setup() { @@ -91,6 +94,7 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase { Optional.of(mBubbles), mDemoModeController, mDarkIconDispatcher, + mFeatureFlags, mStatusBarWindowController, mScreenOffAnimationController); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index e83e50d65ae9..fea5363c0592 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -25,15 +25,12 @@ import static org.mockito.Mockito.when; import android.app.Notification; import android.app.PendingIntent; import android.app.StatusBarManager; -import android.metrics.LogMaker; -import android.support.test.metricshelper.MetricsAsserts; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.testing.FakeMetricsLogger; import com.android.systemui.ForegroundServiceNotificationListener; import com.android.systemui.InitController; @@ -61,7 +58,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; -import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; @@ -283,15 +279,4 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { assertTrue("CentralSurfaces alerts disabled shouldn't allow interruptions", mInterruptSuppressor.suppressInterruptions(entry)); } - - @Test - public void onActivatedMetrics() { - ActivatableNotificationView view = mock(ActivatableNotificationView.class); - mStatusBarNotificationPresenter.onActivated(view); - - MetricsAsserts.assertHasLog("missing lockscreen note tap log", - mMetricsLogger.getLogs(), - new LogMaker(MetricsEvent.ACTION_LS_NOTE) - .setType(MetricsEvent.TYPE_ACTION)); - } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt new file mode 100644 index 000000000000..8444c7b52d1e --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.accessibility.data.repository + +import kotlinx.coroutines.flow.MutableStateFlow + +class FakeAccessibilityRepository( + override val isTouchExplorationEnabled: MutableStateFlow<Boolean> = MutableStateFlow(false) +) : AccessibilityRepository |