Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ed991adc authored by Coco Duan's avatar Coco Duan Committed by cocod
Browse files

Implement UMO transition from hub to shade

When UMO moves from hub to shade, avoid resizing or changing the alpha
while the location remains on hub. Use fade instead of guided as the
transition type, since UMO on hub varies in size and has dynamic bounds
once user scrolls horizontally. Using its bounds as the start bounds in
the calculation results in a poor transition.

Also fix location calculation to return whether UMO is on hub for dreaming
and non-dreaming cases.
According to the motion spec, the squishiness fraction also needs to be
set on the media host as the shade fades in. This is handled in QsImpl.

Bug: b/311234666
Test: atest MediaHierarchyManagerTest
Test: atest QSImplTest
Flag: com.android.systemui.communal_hub
Change-Id: Ia38ff7141bfc6713872b2fa2f869a3c520f224d1
parent 8c255121
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -94,6 +94,9 @@ public interface QS extends FragmentBase {
    default void setHasNotifications(boolean hasNotifications) {
    }

    /** Sets whether the squishiness fraction should be updated on the media host. */
    default void setShouldUpdateSquishinessOnMedia(boolean shouldUpdate) {}

    /**
     * Should touches from the notification panel be disallowed?
     * The notification panel might grab any touches rom QS at any time to collapse the shade.
+62 −9
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.controls.util.MediaFlags
@@ -61,6 +62,11 @@ import com.android.systemui.util.animation.UniqueObjectHostView
import com.android.systemui.util.settings.SecureSettings
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.launch

private val TAG: String = MediaHierarchyManager::class.java.simpleName
@@ -89,6 +95,7 @@ val View.isShownNotFaded: Boolean
 * This manager is responsible for placement of the unique media view between the different hosts
 * and animate the positions of the views to achieve seamless transitions.
 */
@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class MediaHierarchyManager
@Inject
@@ -101,6 +108,7 @@ constructor(
    private val mediaManager: MediaDataManager,
    private val keyguardViewController: KeyguardViewController,
    private val dreamOverlayStateController: DreamOverlayStateController,
    private val keyguardInteractor: KeyguardInteractor,
    communalTransitionViewModel: CommunalTransitionViewModel,
    configurationController: ConfigurationController,
    wakefulnessLifecycle: WakefulnessLifecycle,
@@ -236,6 +244,15 @@ constructor(

    private var inSplitShade = false

    /**
     * Whether we are transitioning to the hub or from the hub to the shade. If so, use fade as the
     * transformation type and skip calculating state with the bounds and the transition progress.
     */
    private val isHubTransition
        get() =
            desiredLocation == LOCATION_COMMUNAL_HUB ||
                (previousLocation == LOCATION_COMMUNAL_HUB && desiredLocation == LOCATION_QS)

    /** Is there any active media or recommendation in the carousel? */
    private var hasActiveMediaOrRecommendation: Boolean = false
        get() = mediaManager.hasActiveMediaOrRecommendation()
@@ -413,6 +430,12 @@ constructor(
    /** Is the communal UI showing */
    private var isCommunalShowing: Boolean = false

    /** Is the communal UI showing and not dreaming */
    private var onCommunalNotDreaming: Boolean = false

    /** Is the communal UI showing, dreaming and shade expanding */
    private var onCommunalDreamingAndShadeExpanding: Boolean = false

    /**
     * The current cross fade progress. 0.5f means it's just switching between the start and the end
     * location and the content is fully faded, while 0.75f means that we're halfway faded in again
@@ -585,9 +608,24 @@ constructor(

        // Listen to the communal UI state. Make sure that communal UI is showing and hub itself is
        // available, ie. not disabled and able to be shown.
        // When dreaming, qs expansion is immediately set to 1f, so we listen to shade expansion to
        // calculate the new location.
        coroutineScope.launch {
            communalTransitionViewModel.isUmoOnCommunal.collect { value ->
                isCommunalShowing = value
            combine(
                    communalTransitionViewModel.isUmoOnCommunal,
                    keyguardInteractor.isDreaming,
                    // keep on communal before the shade is expanded enough to show the elements in
                    // QS
                    shadeInteractor.shadeExpansion
                        .mapLatest { it < EXPANSION_THRESHOLD }
                        .distinctUntilChanged(),
                    ::Triple
                )
                .collectLatest { (communalShowing, isDreaming, isShadeExpanding) ->
                    isCommunalShowing = communalShowing
                    onCommunalDreamingAndShadeExpanding =
                        communalShowing && isDreaming && isShadeExpanding
                    onCommunalNotDreaming = communalShowing && !isDreaming
                    updateDesiredLocation(forceNoAnimation = true)
                }
        }
@@ -805,6 +843,9 @@ constructor(
        if (skipQqsOnExpansion) {
            return false
        }
        if (isHubTransition) {
            return false
        }
        // This is an invalid transition, and can happen when using the camera gesture from the
        // lock screen. Disallow.
        if (
@@ -947,6 +988,9 @@ constructor(
    @VisibleForTesting
    @TransformationType
    fun calculateTransformationType(): Int {
        if (isHubTransition) {
            return TRANSFORMATION_TYPE_FADE
        }
        if (isTransitioningToFullShade) {
            if (inSplitShade && areGuidedTransitionHostsVisible()) {
                return TRANSFORMATION_TYPE_TRANSITION
@@ -977,7 +1021,7 @@ constructor(
     *   otherwise
     */
    private fun getTransformationProgress(): Float {
        if (skipQqsOnExpansion) {
        if (skipQqsOnExpansion || isHubTransition) {
            return -1.0f
        }
        val progress = getQSTransformationProgress()
@@ -1147,15 +1191,18 @@ constructor(
        }
        val onLockscreen =
            (!bypassController.bypassEnabled && (statusbarState == StatusBarState.KEYGUARD))

        // UMO should show on hub unless the qs is expanding when not dreaming, or shade is
        // expanding when dreaming
        val onCommunal =
            (onCommunalNotDreaming && qsExpansion == 0.0f) || onCommunalDreamingAndShadeExpanding
        val location =
            when {
                mediaFlags.isSceneContainerEnabled() -> desiredLocation
                dreamOverlayActive && dreamMediaComplicationActive -> LOCATION_DREAM_OVERLAY

                // UMO should show in communal unless the shade is expanding or visible.
                isCommunalShowing && qsExpansion == 0.0f -> LOCATION_COMMUNAL_HUB
                onCommunal -> LOCATION_COMMUNAL_HUB
                (qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
                qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
                qsExpansion > EXPANSION_THRESHOLD && onLockscreen -> LOCATION_QS
                onLockscreen && isSplitShadeExpanding() -> LOCATION_QS
                onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS

@@ -1190,6 +1237,9 @@ constructor(
            // reattach it without an animation
            return LOCATION_LOCKSCREEN
        }
        // When communal showing while dreaming, skipQqsOnExpansion is also true but we want to
        // return the calculated location, so it won't disappear as soon as shade is pulled down.
        if (isCommunalShowing) return location
        if (skipQqsOnExpansion) {
            // When doing an immediate expand or collapse, we want to keep it in QS.
            return LOCATION_QS
@@ -1288,6 +1338,9 @@ constructor(
         * transitioning
         */
        const val TRANSFORMATION_TYPE_FADE = 1

        /** Expansion amount value at which elements start to become visible in the QS panel. */
        const val EXPANSION_THRESHOLD = 0.4f
    }
}

+7 −0
Original line number Diff line number Diff line
@@ -218,6 +218,13 @@ public class QSFragmentLegacy extends LifecycleFragment implements QS {
        }
    }

    @Override
    public void setShouldUpdateSquishinessOnMedia(boolean shouldUpdate) {
        if (mQsImpl != null) {
            mQsImpl.setShouldUpdateSquishinessOnMedia(shouldUpdate);
        }
    }

    @Override
    public void setListening(boolean listening) {
        if (mQsImpl != null) {
+14 −2
Original line number Diff line number Diff line
@@ -168,6 +168,9 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl

    private boolean mIsSmallScreen;

    /** Should the squishiness fraction be updated on the media host. */
    private boolean mShouldUpdateMediaSquishiness;

    private CommandQueue mCommandQueue;

    private View mRootView;
@@ -661,6 +664,12 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl
        mIsSmallScreen = isFullWidth;
    }

    @Override
    public void setShouldUpdateSquishinessOnMedia(boolean shouldUpdate) {
        if (DEBUG) Log.d(TAG, "setShouldUpdateSquishinessOnMedia " + shouldUpdate);
        mShouldUpdateMediaSquishiness = shouldUpdate;
    }

    @Override
    public void setQsExpansion(float expansion, float panelExpansionFraction,
            float proposedTranslation, float squishinessFraction) {
@@ -740,9 +749,11 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl
        if (mQSAnimator != null) {
            mQSAnimator.setPosition(expansion);
        }
        if (!mInSplitShade
        if (!mShouldUpdateMediaSquishiness
                && (!mInSplitShade
                || mStatusBarStateController.getState() == KEYGUARD
                || mStatusBarStateController.getState() == SHADE_LOCKED) {
                || mStatusBarStateController.getState() == SHADE_LOCKED)
        ) {
            // At beginning, state is 0 and will apply wrong squishiness to MediaHost in lockscreen
            // and media player expect no change by squishiness in lock screen shade. Don't bother
            // squishing mQsMediaHost when not in split shade to prevent problems with stale state.
@@ -1038,6 +1049,7 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl
        indentingPw.println("mTransitioningToFullShade: " + mTransitioningToFullShade);
        indentingPw.println("mLockscreenToShadeProgress: " + mLockscreenToShadeProgress);
        indentingPw.println("mOverScrolling: " + mOverScrolling);
        indentingPw.println("mShouldUpdateMediaSquishiness: " + mShouldUpdateMediaSquishiness);
        indentingPw.println("isCustomizing: " + mQSCustomizerController.isCustomizing());
        View view = getView();
        if (view != null) {
+13 −0
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@ import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.dump.DumpManager;
@@ -157,6 +158,7 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
    private final ShadeRepository mShadeRepository;
    private final ShadeInteractor mShadeInteractor;
    private final ActiveNotificationsInteractor mActiveNotificationsInteractor;
    private final Lazy<CommunalTransitionViewModel> mCommunalTransitionViewModelLazy;
    private final JavaAdapter mJavaAdapter;
    private final FalsingManager mFalsingManager;
    private final AccessibilityManager mAccessibilityManager;
@@ -334,6 +336,7 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
            JavaAdapter javaAdapter,
            CastController castController,
            SplitShadeStateController splitShadeStateController,
            Lazy<CommunalTransitionViewModel> communalTransitionViewModelLazy,
            Lazy<LargeScreenHeaderHelper> largeScreenHeaderHelperLazy
    ) {
        SceneContainerFlag.assertInLegacyMode();
@@ -379,6 +382,7 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
        mShadeRepository = shadeRepository;
        mShadeInteractor = shadeInteractor;
        mActiveNotificationsInteractor = activeNotificationsInteractor;
        mCommunalTransitionViewModelLazy = communalTransitionViewModelLazy;
        mJavaAdapter = javaAdapter;

        mLockscreenShadeTransitionController.addCallback(new LockscreenShadeTransitionCallback());
@@ -458,6 +462,9 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
        initNotificationStackScrollLayoutController();
        mJavaAdapter.alwaysCollectFlow(
                mShadeInteractor.isExpandToQsEnabled(), this::setExpansionEnabledPolicy);
        mJavaAdapter.alwaysCollectFlow(
                mCommunalTransitionViewModelLazy.get().isUmoOnCommunal(),
                this::setShouldUpdateSquishinessOnMedia);
    }

    private void initNotificationStackScrollLayoutController() {
@@ -892,6 +899,12 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
        }
    }

    private void setShouldUpdateSquishinessOnMedia(boolean shouldUpdate) {
        if (mQs != null) {
            mQs.setShouldUpdateSquishinessOnMedia(shouldUpdate);
        }
    }

    void setOverScrollAmount(int overExpansion) {
        if (mQs != null) {
            mQs.setOverScrollAmount(overExpansion);
Loading