Do not animate statusbar unfold when in portrait orientation

Disables unfold animation for statusbar when it is
perpendicular to the screen fold.
Moves ScopedUnfoldTransitionProgressProvider from launcher
to shared SysUI/Launcher code.
Introduces NaturalRotationUnfoldProgressProvider which emits
transition events only when in natural rotation.
Added proper disposal of resources when status bar is detached
as PhoneStatusBarView is re-created when folding/unfolding.

Bug: 201518277
Test: manual
Test: atest com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProviderTest
Change-Id: If4da3afca2b41c3675ccf6838e9ad6dac1b6f82e
This commit is contained in:
Nick Chameyev
2021-10-13 17:04:18 +01:00
parent 618c83b000
commit fa92428107
10 changed files with 523 additions and 60 deletions

View File

@@ -0,0 +1,73 @@
package com.android.systemui.unfold.util
import android.content.Context
import android.os.RemoteException
import android.util.Log
import android.view.IRotationWatcher
import android.view.IWindowManager
import android.view.Surface
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
/**
* [UnfoldTransitionProgressProvider] that emits transition progress only when the display has
* default rotation or 180 degrees opposite rotation (ROTATION_0 or ROTATION_180).
* It could be helpful to run the animation only when the display's rotation is perpendicular
* to the fold.
*/
class NaturalRotationUnfoldProgressProvider(
private val context: Context,
private val windowManagerInterface: IWindowManager,
unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider
) : UnfoldTransitionProgressProvider {
private val scopedUnfoldTransitionProgressProvider =
ScopedUnfoldTransitionProgressProvider(unfoldTransitionProgressProvider)
private val rotationWatcher = RotationWatcher()
private var isNaturalRotation: Boolean = false
fun init() {
try {
windowManagerInterface.watchRotation(rotationWatcher, context.display.displayId)
} catch (e: RemoteException) {
throw e.rethrowFromSystemServer()
}
onRotationChanged(context.display.rotation)
}
private fun onRotationChanged(rotation: Int) {
val isNewRotationNatural = rotation == Surface.ROTATION_0 ||
rotation == Surface.ROTATION_180
if (isNaturalRotation != isNewRotationNatural) {
isNaturalRotation = isNewRotationNatural
scopedUnfoldTransitionProgressProvider.setReadyToHandleTransition(isNewRotationNatural)
}
}
override fun destroy() {
try {
windowManagerInterface.removeRotationWatcher(rotationWatcher)
} catch (e: RemoteException) {
e.rethrowFromSystemServer()
}
scopedUnfoldTransitionProgressProvider.destroy()
}
override fun addCallback(listener: TransitionProgressListener) {
scopedUnfoldTransitionProgressProvider.addCallback(listener)
}
override fun removeCallback(listener: TransitionProgressListener) {
scopedUnfoldTransitionProgressProvider.removeCallback(listener)
}
private inner class RotationWatcher : IRotationWatcher.Stub() {
override fun onRotationChanged(rotation: Int) {
this@NaturalRotationUnfoldProgressProvider.onRotationChanged(rotation)
}
}
}

View File

@@ -0,0 +1,142 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.unfold.util;
import android.annotation.NonNull;
import android.annotation.Nullable;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
import java.util.ArrayList;
import java.util.List;
/**
* Manages progress listeners that can have smaller lifespan than the unfold animation.
* Allows to limit getting transition updates to only when
* {@link ScopedUnfoldTransitionProgressProvider#setReadyToHandleTransition} is called
* with readyToHandleTransition = true
*
* If the transition has already started by the moment when the clients are ready to play
* the transition then it will report transition started callback and current animation progress.
*/
public final class ScopedUnfoldTransitionProgressProvider implements
UnfoldTransitionProgressProvider, TransitionProgressListener {
private static final float PROGRESS_UNSET = -1f;
@Nullable
private UnfoldTransitionProgressProvider mSource;
private final List<TransitionProgressListener> mListeners = new ArrayList<>();
private boolean mIsReadyToHandleTransition;
private boolean mIsTransitionRunning;
private float mLastTransitionProgress = PROGRESS_UNSET;
public ScopedUnfoldTransitionProgressProvider() {
this(null);
}
public ScopedUnfoldTransitionProgressProvider(
@Nullable UnfoldTransitionProgressProvider source) {
setSourceProvider(source);
}
/**
* Sets the source for the unfold transition progress updates,
* it replaces current provider if it is already set
* @param provider transition provider that emits transition progress updates
*/
public void setSourceProvider(@Nullable UnfoldTransitionProgressProvider provider) {
if (mSource != null) {
mSource.removeCallback(this);
}
if (provider != null) {
mSource = provider;
mSource.addCallback(this);
} else {
mSource = null;
}
}
/**
* Allows to notify this provide whether the listeners can play the transition or not.
* Call this method with readyToHandleTransition = true when all listeners
* are ready to consume the transition progress events.
* Call it with readyToHandleTransition = false when listeners can't process the events.
*/
public void setReadyToHandleTransition(boolean isReadyToHandleTransition) {
if (mIsTransitionRunning) {
if (isReadyToHandleTransition) {
mListeners.forEach(TransitionProgressListener::onTransitionStarted);
if (mLastTransitionProgress != PROGRESS_UNSET) {
mListeners.forEach(listener ->
listener.onTransitionProgress(mLastTransitionProgress));
}
} else {
mIsTransitionRunning = false;
mListeners.forEach(TransitionProgressListener::onTransitionFinished);
}
}
mIsReadyToHandleTransition = isReadyToHandleTransition;
}
@Override
public void addCallback(@NonNull TransitionProgressListener listener) {
mListeners.add(listener);
}
@Override
public void removeCallback(@NonNull TransitionProgressListener listener) {
mListeners.remove(listener);
}
@Override
public void destroy() {
mSource.removeCallback(this);
}
@Override
public void onTransitionStarted() {
this.mIsTransitionRunning = true;
if (mIsReadyToHandleTransition) {
mListeners.forEach(TransitionProgressListener::onTransitionStarted);
}
}
@Override
public void onTransitionProgress(float progress) {
if (mIsReadyToHandleTransition) {
mListeners.forEach(listener -> listener.onTransitionProgress(progress));
}
mLastTransitionProgress = progress;
}
@Override
public void onTransitionFinished() {
if (mIsReadyToHandleTransition) {
mListeners.forEach(TransitionProgressListener::onTransitionFinished);
}
mIsTransitionRunning = false;
mLastTransitionProgress = PROGRESS_UNSET;
}
}

View File

@@ -18,25 +18,28 @@ package com.android.systemui.statusbar.phone
import android.graphics.Point
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import com.android.systemui.R
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.unfold.UNFOLD_STATUS_BAR
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.util.ViewController
import javax.inject.Inject
import javax.inject.Named
import dagger.Lazy
/** Controller for [PhoneStatusBarView]. */
class PhoneStatusBarViewController(
class PhoneStatusBarViewController private constructor(
view: PhoneStatusBarView,
statusBarMoveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
@Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider?,
private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
touchEventHandler: PhoneStatusBarView.TouchEventHandler,
) : ViewController<PhoneStatusBarView>(view) {
override fun onViewAttached() {}
override fun onViewDetached() {}
init {
mView.setTouchEventHandler(touchEventHandler)
statusBarMoveFromCenterAnimationController?.let { animationController ->
override fun onViewAttached() {
moveFromCenterAnimationController?.let { animationController ->
val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_left_side)
val systemIconArea: ViewGroup = mView.findViewById(R.id.system_icon_area)
@@ -46,15 +49,33 @@ class PhoneStatusBarViewController(
systemIconArea
)
animationController.init(viewsToAnimate, viewCenterProvider)
mView.viewTreeObserver.addOnPreDrawListener(object :
ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
animationController.onViewsReady(viewsToAnimate, viewCenterProvider)
mView.viewTreeObserver.removeOnPreDrawListener(this)
return true
}
})
mView.addOnLayoutChangeListener { _, left, _, right, _, oldLeft, _, oldRight, _ ->
val widthChanged = right - left != oldRight - oldLeft
if (widthChanged) {
statusBarMoveFromCenterAnimationController.onStatusBarWidthChanged()
moveFromCenterAnimationController.onStatusBarWidthChanged()
}
}
}
progressProvider?.setReadyToHandleTransition(true)
}
override fun onViewDetached() {
progressProvider?.setReadyToHandleTransition(false)
moveFromCenterAnimationController?.onViewDetached()
}
init {
mView.setTouchEventHandler(touchEventHandler)
}
fun setImportantForAccessibility(mode: Int) {
@@ -92,4 +113,23 @@ class PhoneStatusBarViewController(
outPoint.y = viewY + view.height / 2
}
}
class Factory @Inject constructor(
@Named(UNFOLD_STATUS_BAR)
private val progressProvider: Lazy<ScopedUnfoldTransitionProgressProvider>,
private val moveFromCenterController: Lazy<StatusBarMoveFromCenterAnimationController>,
private val unfoldConfig: UnfoldTransitionConfig,
) {
fun create(
view: PhoneStatusBarView,
touchEventHandler: PhoneStatusBarView.TouchEventHandler
): PhoneStatusBarViewController {
return PhoneStatusBarViewController(
view,
if (unfoldConfig.isEnabled) progressProvider.get() else null,
if (unfoldConfig.isEnabled) moveFromCenterController.get() else null,
touchEventHandler
)
}
}
}

View File

@@ -233,6 +233,7 @@ import com.android.systemui.tuner.TunerService;
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
import com.android.systemui.unfold.UnfoldTransitionWallpaperController;
import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.MessageRouter;
@@ -525,6 +526,7 @@ public class StatusBar extends SystemUI implements
private QSPanelController mQSPanelController;
private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
private final PhoneStatusBarViewController.Factory mPhoneStatusBarViewControllerFactory;
KeyguardIndicationController mKeyguardIndicationController;
private View mReportRejectedTouch;
@@ -543,8 +545,8 @@ public class StatusBar extends SystemUI implements
private final FeatureFlags mFeatureFlags;
private final UnfoldTransitionConfig mUnfoldTransitionConfig;
private final Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimation;
private final Lazy<NaturalRotationUnfoldProgressProvider> mNaturalUnfoldProgressProvider;
private final Lazy<UnfoldTransitionWallpaperController> mUnfoldWallpaperController;
private final Lazy<StatusBarMoveFromCenterAnimationController> mMoveFromCenterAnimation;
private final WallpaperController mWallpaperController;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private final MessageRouter mMessageRouter;
@@ -772,6 +774,7 @@ public class StatusBar extends SystemUI implements
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
OperatorNameViewController.Factory operatorNameViewControllerFactory,
PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
PhoneStatusBarPolicy phoneStatusBarPolicy,
KeyguardIndicationController keyguardIndicationController,
DemoModeController demoModeController,
@@ -782,7 +785,7 @@ public class StatusBar extends SystemUI implements
UnfoldTransitionConfig unfoldTransitionConfig,
Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
Lazy<UnfoldTransitionWallpaperController> unfoldTransitionWallpaperController,
Lazy<StatusBarMoveFromCenterAnimationController> statusBarUnfoldAnimationController,
Lazy<NaturalRotationUnfoldProgressProvider> naturalRotationUnfoldProgressProvider,
WallpaperController wallpaperController,
OngoingCallController ongoingCallController,
SystemStatusAnimationScheduler animationScheduler,
@@ -812,6 +815,7 @@ public class StatusBar extends SystemUI implements
mKeyguardStateController = keyguardStateController;
mHeadsUpManager = headsUpManagerPhone;
mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
mPhoneStatusBarViewControllerFactory = phoneStatusBarViewControllerFactory;
mKeyguardIndicationController = keyguardIndicationController;
mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
mDynamicPrivacyController = dynamicPrivacyController;
@@ -879,9 +883,9 @@ public class StatusBar extends SystemUI implements
mBrightnessSliderFactory = brightnessSliderFactory;
mUnfoldTransitionConfig = unfoldTransitionConfig;
mUnfoldLightRevealOverlayAnimation = unfoldLightRevealOverlayAnimation;
mNaturalUnfoldProgressProvider = naturalRotationUnfoldProgressProvider;
mUnfoldWallpaperController = unfoldTransitionWallpaperController;
mWallpaperController = wallpaperController;
mMoveFromCenterAnimation = statusBarUnfoldAnimationController;
mOngoingCallController = ongoingCallController;
mAnimationScheduler = animationScheduler;
mStatusBarLocationPublisher = locationPublisher;
@@ -1077,6 +1081,7 @@ public class StatusBar extends SystemUI implements
if (mUnfoldTransitionConfig.isEnabled()) {
mUnfoldLightRevealOverlayAnimation.get().init();
mUnfoldWallpaperController.get().init();
mNaturalUnfoldProgressProvider.get().init();
}
mPluginManager.addPluginListener(
@@ -1178,16 +1183,9 @@ public class StatusBar extends SystemUI implements
mNotificationPanelViewController.setBar(mStatusBarView);
StatusBarMoveFromCenterAnimationController moveFromCenterAnimation = null;
if (mUnfoldTransitionConfig.isEnabled()) {
moveFromCenterAnimation = mMoveFromCenterAnimation.get();
}
mPhoneStatusBarViewController =
new PhoneStatusBarViewController(
mStatusBarView,
moveFromCenterAnimation,
mNotificationPanelViewController.getStatusBarTouchEventHandler()
);
mPhoneStatusBarViewController = mPhoneStatusBarViewControllerFactory
.create(mStatusBarView, mNotificationPanelViewController
.getStatusBarTouchEventHandler());
mPhoneStatusBarViewController.init();
mBatteryMeterViewController = new BatteryMeterViewController(

View File

@@ -20,43 +20,48 @@ import android.view.WindowManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator.ViewCenterProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.UNFOLD_STATUS_BAR
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import javax.inject.Inject
import javax.inject.Named
@SysUISingleton
class StatusBarMoveFromCenterAnimationController @Inject constructor(
private val unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
private val windowManager: WindowManager
@Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider,
private val windowManager: WindowManager,
) {
private lateinit var moveFromCenterAnimator: UnfoldMoveFromCenterAnimator
private val transitionListener = TransitionListener()
private var moveFromCenterAnimator: UnfoldMoveFromCenterAnimator? = null
fun init(viewsToAnimate: Array<View>, viewCenterProvider: ViewCenterProvider) {
fun onViewsReady(viewsToAnimate: Array<View>, viewCenterProvider: ViewCenterProvider) {
moveFromCenterAnimator = UnfoldMoveFromCenterAnimator(windowManager,
viewCenterProvider = viewCenterProvider)
unfoldTransitionProgressProvider.addCallback(object : TransitionProgressListener {
override fun onTransitionStarted() {
moveFromCenterAnimator.updateDisplayProperties()
moveFromCenterAnimator?.updateDisplayProperties()
viewsToAnimate.forEach {
moveFromCenterAnimator.registerViewForAnimation(it)
}
}
viewsToAnimate.forEach {
moveFromCenterAnimator?.registerViewForAnimation(it)
}
override fun onTransitionFinished() {
moveFromCenterAnimator.onTransitionFinished()
moveFromCenterAnimator.clearRegisteredViews()
}
progressProvider.addCallback(transitionListener)
}
override fun onTransitionProgress(progress: Float) {
moveFromCenterAnimator.onTransitionProgress(progress)
}
})
fun onViewDetached() {
progressProvider.removeCallback(transitionListener)
moveFromCenterAnimator?.clearRegisteredViews()
moveFromCenterAnimator = null
}
fun onStatusBarWidthChanged() {
moveFromCenterAnimator.updateViewPositions()
moveFromCenterAnimator?.updateDisplayProperties()
moveFromCenterAnimator?.updateViewPositions()
}
private inner class TransitionListener : TransitionProgressListener {
override fun onTransitionProgress(progress: Float) {
moveFromCenterAnimator?.onTransitionProgress(progress)
}
}
}

View File

@@ -88,6 +88,7 @@ import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -111,6 +112,7 @@ import com.android.systemui.tuner.TunerService;
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
import com.android.systemui.unfold.UnfoldTransitionWallpaperController;
import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.MessageRouter;
@@ -211,6 +213,7 @@ public interface StatusBarPhoneModule {
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
OperatorNameViewController.Factory operatorNameViewControllerFactory,
PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
PhoneStatusBarPolicy phoneStatusBarPolicy,
KeyguardIndicationController keyguardIndicationController,
DemoModeController demoModeController,
@@ -220,6 +223,7 @@ public interface StatusBarPhoneModule {
BrightnessSlider.Factory brightnessSliderFactory,
UnfoldTransitionConfig unfoldTransitionConfig,
Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
Lazy<NaturalRotationUnfoldProgressProvider> naturalRotationUnfoldProgressProvider,
Lazy<UnfoldTransitionWallpaperController> unfoldTransitionWallpaperController,
Lazy<StatusBarMoveFromCenterAnimationController> statusBarMoveFromCenterAnimation,
WallpaperController wallpaperController,
@@ -311,6 +315,7 @@ public interface StatusBarPhoneModule {
extensionController,
userInfoControllerImpl,
operatorNameViewControllerFactory,
phoneStatusBarViewControllerFactory,
phoneStatusBarPolicy,
keyguardIndicationController,
demoModeController,
@@ -321,7 +326,7 @@ public interface StatusBarPhoneModule {
unfoldTransitionConfig,
unfoldLightRevealOverlayAnimation,
unfoldTransitionWallpaperController,
statusBarMoveFromCenterAnimation,
naturalRotationUnfoldProgressProvider,
wallpaperController,
ongoingCallController,
animationScheduler,

View File

@@ -20,15 +20,19 @@ import android.content.Context
import android.hardware.SensorManager
import android.hardware.devicestate.DeviceStateManager
import android.os.Handler
import android.view.IWindowManager
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.LifecycleScreenStatusProvider
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider
import dagger.Lazy
import dagger.Module
import dagger.Provides
import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Named
import javax.inject.Singleton
@Module
@@ -60,6 +64,27 @@ class UnfoldTransitionModule {
fun provideUnfoldTransitionConfig(context: Context): UnfoldTransitionConfig =
createConfig(context)
@Provides
@Singleton
fun provideNaturalRotationProgressProvider(
context: Context,
windowManager: IWindowManager,
unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider
): NaturalRotationUnfoldProgressProvider =
NaturalRotationUnfoldProgressProvider(
context,
windowManager,
unfoldTransitionProgressProvider
)
@Provides
@Named(UNFOLD_STATUS_BAR)
@Singleton
fun provideStatusBarScopedTransitionProvider(
source: NaturalRotationUnfoldProgressProvider
): ScopedUnfoldTransitionProgressProvider =
ScopedUnfoldTransitionProgressProvider(source)
@Provides
@Singleton
fun provideShellProgressProvider(
@@ -72,3 +97,5 @@ class UnfoldTransitionModule {
Optional.empty()
}
}
const val UNFOLD_STATUS_BAR = "unfold_status_bar"

View File

@@ -19,19 +19,25 @@ package com.android.systemui.statusbar.phone
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.view.ViewTreeObserver.OnPreDrawListener
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
@SmallTest
@@ -48,10 +54,14 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
@Mock
private lateinit var moveFromCenterAnimation: StatusBarMoveFromCenterAnimationController
@Mock
private lateinit var progressProvider: ScopedUnfoldTransitionProgressProvider
private lateinit var view: PhoneStatusBarView
private lateinit var controller: PhoneStatusBarViewController
private val unfoldConfig = UnfoldConfig()
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -66,11 +76,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
view.setBar(mock(StatusBar::class.java))
}
controller = PhoneStatusBarViewController(
view,
null,
touchEventHandler,
)
controller = createController(view)
}
@Test
@@ -83,12 +89,38 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
}
@Test
fun constructor_moveFromCenterAnimationIsNotNull_moveFromCenterAnimationInitialized() {
controller = PhoneStatusBarViewController(
view, moveFromCenterAnimation, touchEventHandler
)
fun onViewAttachedAndDrawn_moveFromCenterAnimationEnabled_moveFromCenterAnimationInitialized() {
val view = createViewMock()
val argumentCaptor = ArgumentCaptor.forClass(OnPreDrawListener::class.java)
unfoldConfig.isEnabled = true
controller = createController(view)
controller.init()
verify(moveFromCenterAnimation).init(any(), any())
verify(view.viewTreeObserver).addOnPreDrawListener(argumentCaptor.capture())
argumentCaptor.value.onPreDraw()
verify(moveFromCenterAnimation).onViewsReady(any(), any())
}
private fun createViewMock(): PhoneStatusBarView {
val view = spy(view)
val viewTreeObserver = mock(ViewTreeObserver::class.java)
`when`(view.viewTreeObserver).thenReturn(viewTreeObserver)
`when`(view.isAttachedToWindow).thenReturn(true)
return view
}
private fun createController(view: PhoneStatusBarView): PhoneStatusBarViewController {
return PhoneStatusBarViewController.Factory(
{ progressProvider },
{ moveFromCenterAnimation },
unfoldConfig
).create(view, touchEventHandler)
}
private class UnfoldConfig : UnfoldTransitionConfig {
override var isEnabled: Boolean = false
override var isHingeAngleEnabled: Boolean = false
}
private class TestTouchEventHandler : PhoneStatusBarView.TouchEventHandler {
@@ -97,6 +129,5 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
lastEvent = event
return false
}
}
}

View File

@@ -144,6 +144,7 @@ import com.android.systemui.tuner.TunerService;
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
import com.android.systemui.unfold.UnfoldTransitionWallpaperController;
import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.MessageRouterImpl;
@@ -259,7 +260,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private BrightnessSlider.Factory mBrightnessSliderFactory;
@Mock private UnfoldTransitionConfig mUnfoldTransitionConfig;
@Mock private Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimationLazy;
@Mock private Lazy<StatusBarMoveFromCenterAnimationController> mMoveFromCenterAnimationLazy;
@Mock private Lazy<NaturalRotationUnfoldProgressProvider> mNaturalRotationProgressProvider;
@Mock private Lazy<UnfoldTransitionWallpaperController> mUnfoldWallpaperController;
@Mock private WallpaperController mWallpaperController;
@Mock private OngoingCallController mOngoingCallController;
@@ -276,6 +277,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private StartingSurface mStartingSurface;
@Mock private OperatorNameViewController mOperatorNameViewController;
@Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
@Mock private PhoneStatusBarViewController.Factory mPhoneStatusBarViewControllerFactory;
@Mock private ActivityLaunchAnimator mActivityLaunchAnimator;
@Mock private DialogLaunchAnimator mDialogLaunchAnimator;
private ShadeController mShadeController;
@@ -430,6 +432,7 @@ public class StatusBarTest extends SysuiTestCase {
mExtensionController,
mUserInfoControllerImpl,
mOperatorNameViewControllerFactory,
mPhoneStatusBarViewControllerFactory,
mPhoneStatusBarPolicy,
mKeyguardIndicationController,
mDemoModeController,
@@ -440,7 +443,7 @@ public class StatusBarTest extends SysuiTestCase {
mUnfoldTransitionConfig,
mUnfoldLightRevealOverlayAnimationLazy,
mUnfoldWallpaperController,
mMoveFromCenterAnimationLazy,
mNaturalRotationProgressProvider,
mWallpaperController,
mOngoingCallController,
mAnimationScheduler,

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.unfold.util
import android.testing.AndroidTestingRunner
import android.view.IRotationWatcher
import android.view.IWindowManager
import android.view.Surface
import androidx.test.filters.SmallTest
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.util.mockito.any
import com.android.systemui.SysuiTestCase
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@SmallTest
class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
@Mock
lateinit var windowManager: IWindowManager
@Mock
lateinit var sourceProvider: UnfoldTransitionProgressProvider
@Mock
lateinit var transitionListener: TransitionProgressListener
lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
private val sourceProviderListenerCaptor =
ArgumentCaptor.forClass(TransitionProgressListener::class.java)
private val rotationWatcherCaptor =
ArgumentCaptor.forClass(IRotationWatcher.Stub::class.java)
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
progressProvider = NaturalRotationUnfoldProgressProvider(
context,
windowManager,
sourceProvider
)
progressProvider.init()
verify(sourceProvider).addCallback(sourceProviderListenerCaptor.capture())
verify(windowManager).watchRotation(rotationWatcherCaptor.capture(), any())
progressProvider.addCallback(transitionListener)
}
@Test
fun testNaturalRotation0_sendTransitionStartedEvent_eventReceived() {
onRotationChanged(Surface.ROTATION_0)
source.onTransitionStarted()
verify(transitionListener).onTransitionStarted()
}
@Test
fun testNaturalRotation0_sendTransitionProgressEvent_eventReceived() {
onRotationChanged(Surface.ROTATION_0)
source.onTransitionProgress(0.5f)
verify(transitionListener).onTransitionProgress(0.5f)
}
@Test
fun testNotNaturalRotation90_sendTransitionStartedEvent_eventNotReceived() {
onRotationChanged(Surface.ROTATION_90)
source.onTransitionStarted()
verify(transitionListener, never()).onTransitionStarted()
}
@Test
fun testNaturalRotation90_sendTransitionProgressEvent_eventNotReceived() {
onRotationChanged(Surface.ROTATION_90)
source.onTransitionProgress(0.5f)
verify(transitionListener, never()).onTransitionProgress(0.5f)
}
@Test
fun testRotationBecameUnnaturalDuringTransition_sendsTransitionFinishedEvent() {
onRotationChanged(Surface.ROTATION_0)
source.onTransitionStarted()
clearInvocations(transitionListener)
onRotationChanged(Surface.ROTATION_90)
verify(transitionListener).onTransitionFinished()
}
@Test
fun testRotationBecameNaturalDuringTransition_sendsTransitionStartedEvent() {
onRotationChanged(Surface.ROTATION_90)
source.onTransitionStarted()
clearInvocations(transitionListener)
onRotationChanged(Surface.ROTATION_0)
verify(transitionListener).onTransitionStarted()
}
private fun onRotationChanged(rotation: Int) {
rotationWatcherCaptor.value.onRotationChanged(rotation)
}
private val source: TransitionProgressListener
get() = sourceProviderListenerCaptor.value
}