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

Commit 03a96eb8 authored by Andy Wickham's avatar Andy Wickham
Browse files

End live tile when invoking Circle to Search over Overview.

This fixes many animation/state issues related to switching tasks
from the live tile to AGA.

Demo before: https://drive.google.com/file/d/1aBsn_-4tRHRsfGSqsx44_1B7dgdPOq_v/view?usp=drive_link&resourcekey=0-EHyEiKRVEC2ooUo-0rcjHg
Demo after: https://drive.google.com/file/d/1Sf5cUh8hC-slUZc_efOChzxxw710pG65/view?usp=drive_link&resourcekey=0-Wr33tL3ytedMibcVNd6auw

Bug: 372592549
Bug: 297831970
Test: Manual, updated ContextualSearchInvokerTest.
Flag: EXEMPT bugfix
Change-Id: I538cb1de61e48de4cf7c6fca8710a655933d07a3
parent 423c85a2
Loading
Loading
Loading
Loading
+9 −3
Original line number Diff line number Diff line
@@ -87,10 +87,16 @@ public final class OverviewComponentObserver {
        mCurrentHomeIntent = createHomeIntent();
        mMyHomeIntent = new Intent(mCurrentHomeIntent).setPackage(mContext.getPackageName());
        ResolveInfo info = context.getPackageManager().resolveActivity(mMyHomeIntent, 0);
        ActivityInfo myHomeActivityInfo = info == null ? null : info.activityInfo;
        int myHomeConfigChanges = myHomeActivityInfo == null ? 0 : myHomeActivityInfo.configChanges;
        ComponentName myHomeComponent =
                new ComponentName(context.getPackageName(), info.activityInfo.name);
                myHomeActivityInfo == null
                        ? mMyHomeIntent.resolveActivity(context.getPackageManager())
                        : new ComponentName(context.getPackageName(), myHomeActivityInfo.name);
        if (myHomeComponent != null) {
            mMyHomeIntent.setComponent(myHomeComponent);
        mConfigChangesMap.append(myHomeComponent.hashCode(), info.activityInfo.configChanges);
            mConfigChangesMap.append(myHomeComponent.hashCode(), myHomeConfigChanges);
        }
        mSetupWizardPkg = context.getString(R.string.setup_wizard_pkg);

        ComponentName fallbackComponent = new ComponentName(mContext, RecentsActivity.class);
+50 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.app.contextualsearch.ContextualSearchManager.ENTRYPOINT_LONG_PRES
import android.app.contextualsearch.ContextualSearchManager.FEATURE_CONTEXTUAL_SEARCH
import android.content.Context
import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.internal.app.AssistUtils
import com.android.launcher3.R
import com.android.launcher3.logging.StatsLogManager
@@ -32,9 +33,13 @@ import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUN
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_OMNI_FAILED_SETTING_DISABLED
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_OMNI_SUCCESSFUL_HOME
import com.android.launcher3.util.ResourceBasedOverride
import com.android.quickstep.BaseContainerInterface
import com.android.quickstep.DeviceConfigWrapper
import com.android.quickstep.OverviewComponentObserver
import com.android.quickstep.RecentsAnimationDeviceState
import com.android.quickstep.SystemUiProxy
import com.android.quickstep.TopTaskTracker
import com.android.quickstep.views.RecentsView
import com.android.systemui.shared.system.QuickStepContract

/** Handles invocations and checks for Contextual Search. */
@@ -183,7 +188,15 @@ internal constructor(
        if (contextualSearchManager == null) {
            return false
        }
        val recentsContainerInterface = getRecentsContainerInterface()
        if (recentsContainerInterface?.isInLiveTileMode() == true) {
            Log.i(TAG, "Contextual Search invocation attempted: live tile")
            endLiveTileMode(recentsContainerInterface) {
                contextualSearchManager.startContextualSearch(entryPoint)
            }
        } else {
            contextualSearchManager.startContextualSearch(entryPoint)
        }
        return true
    }

@@ -199,6 +212,42 @@ internal constructor(
        return systemUiProxy.lastSystemUiStateFlags and KEYGUARD_SHOWING_SYSUI_FLAGS != 0L
    }

    @VisibleForTesting
    fun getRecentsContainerInterface(): BaseContainerInterface<*, *>? {
        val rads = RecentsAnimationDeviceState(context)
        val observer = OverviewComponentObserver(context, rads)
        try {
            return observer.containerInterface
        } finally {
            observer.onDestroy()
            rads.destroy()
        }
    }

    /**
     * End the live tile mode.
     *
     * @param onCompleteRunnable Runnable to run when the live tile is paused. May run immediately.
     */
    private fun endLiveTileMode(
        recentsContainerInterface: BaseContainerInterface<*, *>?,
        onCompleteRunnable: Runnable,
    ) {
        val recentsViewContainer = recentsContainerInterface?.createdContainer
        if (recentsViewContainer == null) {
            onCompleteRunnable.run()
            return
        }
        val recentsView: RecentsView<*, *> = recentsViewContainer.getOverviewPanel()
        recentsView.switchToScreenshot {
            recentsView.finishRecentsAnimation(
                true, /* toRecents */
                false, /* shouldPip */
                onCompleteRunnable,
            )
        }
    }

    companion object {
        private const val TAG = "ContextualSearchInvoker"
        const val SHADE_EXPANDED_SYSUI_FLAGS =
+74 −2
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@ import static com.android.quickstep.util.ContextualSearchInvoker.SHADE_EXPANDED_

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -46,13 +48,17 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.BaseContainerInterface;
import com.android.quickstep.DeviceConfigWrapper;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TopTaskTracker;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.RecentsViewContainer;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

@@ -73,6 +79,9 @@ public class ContextualSearchInvokerTest {
    private @Mock StatsLogManager.StatsLogger mMockStatsLogger;
    private @Mock ContextualSearchHapticManager mMockContextualSearchHapticManager;
    private @Mock ContextualSearchManager mMockContextualSearchManager;
    private @Mock BaseContainerInterface mMockContainerInterface;
    private @Mock RecentsViewContainer mMockRecentsViewContainer;
    private @Mock RecentsView mMockRecentsView;
    private ContextualSearchInvoker mContextualSearchInvoker;

    @Before
@@ -86,10 +95,15 @@ public class ContextualSearchInvokerTest {
        when(mMockStateManager.isContextualSearchIntentAvailable()).thenReturn(true);
        when(mMockStateManager.isContextualSearchSettingEnabled()).thenReturn(true);
        when(mMockStatsLogManager.logger()).thenReturn(mMockStatsLogger);
        when(mMockContainerInterface.getCreatedContainer()).thenReturn(mMockRecentsViewContainer);
        when(mMockRecentsViewContainer.getOverviewPanel()).thenReturn(mMockRecentsView);

        mContextualSearchInvoker = new ContextualSearchInvoker(context, mMockStateManager,
        mContextualSearchInvoker = spy(new ContextualSearchInvoker(context, mMockStateManager,
                mMockTopTaskTracker, mMockSystemUiProxy, mMockStatsLogManager,
                mMockContextualSearchHapticManager, mMockContextualSearchManager);
                mMockContextualSearchHapticManager, mMockContextualSearchManager
        ));
        doReturn(mMockContainerInterface).when(mContextualSearchInvoker)
                .getRecentsContainerInterface();
    }

    @Test
@@ -244,6 +258,64 @@ public class ContextualSearchInvokerTest {
        }
    }

    @Test
    public void invokeContextualSearchUncheckedWithHaptic_liveTile() {
        when(mMockContainerInterface.isInLiveTileMode()).thenReturn(true);
        ArgumentCaptor<Runnable> switchToScreenshotCaptor = ArgumentCaptor.forClass(Runnable.class);
        ArgumentCaptor<Runnable> finishRecentsAnimationCaptor =
                ArgumentCaptor.forClass(Runnable.class);

        assertTrue("Expected invocation unchecked to succeed",
                mContextualSearchInvoker.invokeContextualSearchUncheckedWithHaptic(
                        CONTEXTUAL_SEARCH_ENTRY_POINT));
        verify(mMockRecentsView).switchToScreenshot(switchToScreenshotCaptor.capture());
        switchToScreenshotCaptor.getValue().run();
        verify(mMockRecentsView).finishRecentsAnimation(anyBoolean(), anyBoolean(),
                finishRecentsAnimationCaptor.capture());
        finishRecentsAnimationCaptor.getValue().run();
        verify(mMockContextualSearchManager).startContextualSearch(CONTEXTUAL_SEARCH_ENTRY_POINT);
        verifyNoMoreInteractions(mMockStatsLogManager);
    }

    @Test
    public void invokeContextualSearchUncheckedWithHaptic_liveTile_failsToSwitchToScreenshot() {
        when(mMockContainerInterface.isInLiveTileMode()).thenReturn(true);
        ArgumentCaptor<Runnable> switchToScreenshotCaptor = ArgumentCaptor.forClass(Runnable.class);
        ArgumentCaptor<Runnable> finishRecentsAnimationCaptor =
                ArgumentCaptor.forClass(Runnable.class);

        assertTrue("Expected invocation unchecked to succeed",
                mContextualSearchInvoker.invokeContextualSearchUncheckedWithHaptic(
                        CONTEXTUAL_SEARCH_ENTRY_POINT));
        verify(mMockRecentsView).switchToScreenshot(switchToScreenshotCaptor.capture());

        // Don't run switchToScreenshot's callback. Therefore, recents animation should not finish.
        verify(mMockRecentsView, never()).finishRecentsAnimation(anyBoolean(), anyBoolean(),
                finishRecentsAnimationCaptor.capture());
        // And ContextualSearch should not start.
        verify(mMockContextualSearchManager, never()).startContextualSearch(anyInt());
        verifyNoMoreInteractions(mMockStatsLogManager);
    }

    @Test
    public void invokeContextualSearchUncheckedWithHaptic_liveTile_failsToFinishRecentsAnimation() {
        when(mMockContainerInterface.isInLiveTileMode()).thenReturn(true);
        ArgumentCaptor<Runnable> switchToScreenshotCaptor = ArgumentCaptor.forClass(Runnable.class);
        ArgumentCaptor<Runnable> finishRecentsAnimationCaptor =
                ArgumentCaptor.forClass(Runnable.class);

        assertTrue("Expected invocation unchecked to succeed",
                mContextualSearchInvoker.invokeContextualSearchUncheckedWithHaptic(
                        CONTEXTUAL_SEARCH_ENTRY_POINT));
        verify(mMockRecentsView).switchToScreenshot(switchToScreenshotCaptor.capture());
        switchToScreenshotCaptor.getValue().run();
        verify(mMockRecentsView).finishRecentsAnimation(anyBoolean(), anyBoolean(),
                finishRecentsAnimationCaptor.capture());
        // Don't run finishRecentsAnimation's callback. Therefore ContextualSearch should not start.
        verify(mMockContextualSearchManager, never()).startContextualSearch(anyInt());
        verifyNoMoreInteractions(mMockStatsLogManager);
    }

    private AutoCloseable overrideSearchHapticCommitFlag(boolean value) {
        return TestExtensions.overrideNavConfigFlag(
                "ENABLE_SEARCH_HAPTIC_COMMIT",