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

Commit 13c60038 authored by Bryce Lee's avatar Bryce Lee
Browse files

Allow gesture blocking by activity type.

This changelist adds another way activities can have gestures blocked by
activity type. This blocking is done globally for all top activities of
a given type. This changelist also utilizes this functionality for
dreams via the DreamOverlayService.

Test: atest DreamOverlayServiceTest#testDreamActivityGesturesBlockedWhenDreaming
Test: atest GestureInteractorTest
Test: atest GestureRepositoryTest
Test: atest TaskmatcherTest
Flag: EXEMPT bugfix
Fixes: 333734282
Change-Id: I882726aa01ea36fba25316a8e368251ae793bd86
parent 18fb3cee
Loading
Loading
Loading
Loading
+22 −14
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package com.android.systemui.dreams

import android.app.WindowConfiguration
import android.content.ComponentName
import android.content.Intent
import android.os.RemoteException
@@ -65,6 +66,8 @@ import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.gesture.domain.gestureInteractor
import com.android.systemui.kosmos.testScope
import com.android.systemui.navigationbar.gestural.domain.GestureInteractor
import com.android.systemui.navigationbar.gestural.domain.TaskInfo
import com.android.systemui.navigationbar.gestural.domain.TaskMatcher
import com.android.systemui.testKosmos
import com.android.systemui.touch.TouchInsetManager
import com.android.systemui.util.concurrency.FakeExecutor
@@ -83,6 +86,7 @@ import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.isNull
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
@@ -1044,7 +1048,7 @@ class DreamOverlayServiceTest : SysuiTestCase() {
    }

    @Test
    fun testDreamActivityGesturesBlockedOnStart() {
    fun testDreamActivityGesturesBlockedWhenDreaming() {
        val client = client

        // Inform the overlay service of dream starting.
@@ -1056,15 +1060,24 @@ class DreamOverlayServiceTest : SysuiTestCase() {
            false /*shouldShowComplication*/
        )
        mMainExecutor.runAllReady()
        val captor = argumentCaptor<ComponentName>()

        val matcherCaptor = argumentCaptor<TaskMatcher>()
        verify(gestureInteractor)
            .addGestureBlockedActivity(captor.capture(), eq(GestureInteractor.Scope.Global))
        assertThat(captor.firstValue.packageName)
            .isEqualTo(ComponentName.unflattenFromString(DREAM_COMPONENT)?.packageName)
            .addGestureBlockedMatcher(matcherCaptor.capture(), eq(GestureInteractor.Scope.Global))
        val matcher = matcherCaptor.firstValue

        val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM)
        assertThat(matcher.matches(dreamTaskInfo)).isTrue()

        client.endDream()
        mMainExecutor.runAllReady()

        verify(gestureInteractor)
            .removeGestureBlockedMatcher(eq(matcher), eq(GestureInteractor.Scope.Global))
    }

    @Test
    fun testDreamActivityGesturesUnblockedOnEnd() {
    fun testDreamActivityGesturesNotBlockedWhenPreview() {
        val client = client

        // Inform the overlay service of dream starting.
@@ -1072,18 +1085,13 @@ class DreamOverlayServiceTest : SysuiTestCase() {
            mWindowParams,
            mDreamOverlayCallback,
            DREAM_COMPONENT,
            false /*isPreview*/,
            true /*isPreview*/,
            false /*shouldShowComplication*/
        )
        mMainExecutor.runAllReady()

        client.endDream()
        mMainExecutor.runAllReady()
        val captor = argumentCaptor<ComponentName>()
        verify(gestureInteractor)
            .removeGestureBlockedActivity(captor.capture(), eq(GestureInteractor.Scope.Global))
        assertThat(captor.firstValue.packageName)
            .isEqualTo(ComponentName.unflattenFromString(DREAM_COMPONENT)?.packageName)
        verify(gestureInteractor, never())
            .addGestureBlockedMatcher(any(), eq(GestureInteractor.Scope.Global))
    }

    @Test
+31 −7
Original line number Diff line number Diff line
@@ -16,12 +16,14 @@

package com.android.systemui.gesture.data

import android.app.WindowConfiguration
import android.content.ComponentName
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.navigationbar.gestural.data.respository.GestureRepositoryImpl
import com.android.systemui.navigationbar.gestural.domain.TaskMatcher
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
@@ -40,14 +42,36 @@ class GestureRepositoryTest : SysuiTestCase() {
    @Test
    fun addRemoveComponentToBlock_updatesBlockedComponentSet() =
        testScope.runTest {
            val component = mock<ComponentName>()
            val matcher = TaskMatcher.TopActivityComponent(mock<ComponentName>())

            underTest.addGestureBlockedActivity(component)
            val addedBlockedComponents by collectLastValue(underTest.gestureBlockedActivities)
            assertThat(addedBlockedComponents).contains(component)
            kotlin.run {
                underTest.addGestureBlockedMatcher(matcher)
                val blockedMatchers by collectLastValue(underTest.gestureBlockedMatchers)
                assertThat(blockedMatchers).contains(matcher)
            }

            kotlin.run {
                underTest.removeGestureBlockedMatcher(matcher)
                val blockedMatchers by collectLastValue(underTest.gestureBlockedMatchers)
                assertThat(blockedMatchers).doesNotContain(matcher)
            }
        }

    @Test
    fun addRemoveActivityTypeToBlock_updatesBlockedActivityTypesSet() =
        testScope.runTest {
            val matcher = TaskMatcher.TopActivityType(WindowConfiguration.ACTIVITY_TYPE_STANDARD)

            underTest.removeGestureBlockedActivity(component)
            val removedBlockedComponents by collectLastValue(underTest.gestureBlockedActivities)
            assertThat(removedBlockedComponents).isEmpty()
            kotlin.run {
                underTest.addGestureBlockedMatcher(matcher)
                val blockedMatchers by collectLastValue(underTest.gestureBlockedMatchers)
                assertThat(blockedMatchers).contains(matcher)
            }

            kotlin.run {
                underTest.removeGestureBlockedMatcher(matcher)
                val blockedMatchers by collectLastValue(underTest.gestureBlockedMatchers)
                assertThat(blockedMatchers).doesNotContain(matcher)
            }
        }
}
+34 −4
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.navigationbar.gestural.data.gestureRepository
import com.android.systemui.navigationbar.gestural.domain.GestureInteractor
import com.android.systemui.navigationbar.gestural.domain.TaskMatcher
import com.android.systemui.shared.system.activityManagerWrapper
import com.android.systemui.shared.system.taskStackChangeListeners
import com.android.systemui.testKosmos
@@ -76,13 +77,16 @@ class GestureInteractorTest : SysuiTestCase() {
    fun addBlockedActivity_testCombination() =
        testScope.runTest {
            val globalComponent = mock<ComponentName>()
            repository.addGestureBlockedActivity(globalComponent)
            repository.addGestureBlockedMatcher(TaskMatcher.TopActivityComponent(globalComponent))

            val localComponent = mock<ComponentName>()

            val blocked by collectLastValue(underTest.topActivityBlocked)

            underTest.addGestureBlockedActivity(localComponent, GestureInteractor.Scope.Local)
            underTest.addGestureBlockedMatcher(
                TaskMatcher.TopActivityComponent(localComponent),
                GestureInteractor.Scope.Local
            )

            assertThat(blocked).isFalse()

@@ -95,7 +99,7 @@ class GestureInteractorTest : SysuiTestCase() {
    fun initialization_testEmit() =
        testScope.runTest {
            val globalComponent = mock<ComponentName>()
            repository.addGestureBlockedActivity(globalComponent)
            repository.addGestureBlockedMatcher(TaskMatcher.TopActivityComponent(globalComponent))
            setTopActivity(globalComponent)

            val interactor = createInteractor()
@@ -114,10 +118,36 @@ class GestureInteractorTest : SysuiTestCase() {

            val localComponent = mock<ComponentName>()

            interactor1.addGestureBlockedActivity(localComponent, GestureInteractor.Scope.Local)
            interactor1.addGestureBlockedMatcher(
                TaskMatcher.TopActivityComponent(localComponent),
                GestureInteractor.Scope.Local
            )
            setTopActivity(localComponent)

            assertThat(interactor1Blocked).isTrue()
            assertThat(interactor2Blocked).isFalse()
        }

    @Test
    fun matchingBlockers_separatelyManaged() =
        testScope.runTest {
            val interactor = createInteractor()
            val interactorBlocked by collectLastValue(interactor.topActivityBlocked)

            val localComponent = mock<ComponentName>()

            val matcher1 = TaskMatcher.TopActivityComponent(localComponent)
            val matcher2 = TaskMatcher.TopActivityComponent(localComponent)

            interactor.addGestureBlockedMatcher(matcher1, GestureInteractor.Scope.Local)
            interactor.addGestureBlockedMatcher(matcher2, GestureInteractor.Scope.Local)
            setTopActivity(localComponent)
            assertThat(interactorBlocked).isTrue()

            interactor.removeGestureBlockedMatcher(matcher1, GestureInteractor.Scope.Local)
            assertThat(interactorBlocked).isTrue()

            interactor.removeGestureBlockedMatcher(matcher2, GestureInteractor.Scope.Local)
            assertThat(interactorBlocked).isFalse()
        }
}
+91 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.gesture.domain

import android.app.WindowConfiguration
import android.content.ComponentName
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.navigationbar.gestural.domain.TaskInfo
import com.android.systemui.navigationbar.gestural.domain.TaskMatcher
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock

@RunWith(AndroidJUnit4::class)
@SmallTest
class TaskMatcherTest : SysuiTestCase() {
    @Test
    fun activityMatcher_matchesComponentName() {
        val componentName = ComponentName.unflattenFromString("com.foo/.bar")!!
        val matcher = TaskMatcher.TopActivityComponent(componentName)

        val taskInfo = TaskInfo(componentName, WindowConfiguration.ACTIVITY_TYPE_STANDARD)
        assertThat(matcher.matches(taskInfo)).isTrue()
    }

    @Test
    fun activityMatcher_doesNotMatchComponentName() {
        val componentName = ComponentName.unflattenFromString("com.foo/.bar")!!
        val matcher = TaskMatcher.TopActivityComponent(componentName)

        val taskInfo =
            TaskInfo(
                ComponentName.unflattenFromString("com.bar/.baz"),
                WindowConfiguration.ACTIVITY_TYPE_STANDARD
            )
        assertThat(matcher.matches(taskInfo)).isFalse()
    }

    @Test
    fun activityMatcher_matchesActivityType() {
        val activityType = WindowConfiguration.ACTIVITY_TYPE_HOME
        val matcher = TaskMatcher.TopActivityType(activityType)

        val taskInfo = TaskInfo(mock<ComponentName>(), activityType)
        assertThat(matcher.matches(taskInfo)).isTrue()
    }

    @Test
    fun activityMatcher_doesNotMatchEmptyActivityType() {
        val activityType = WindowConfiguration.ACTIVITY_TYPE_HOME
        val matcher = TaskMatcher.TopActivityType(activityType)

        val taskInfo = TaskInfo(null, activityType)
        assertThat(matcher.matches(taskInfo)).isFalse()
    }

    @Test
    fun activityMatcher_doesNotMatchActivityType() {
        val activityType = WindowConfiguration.ACTIVITY_TYPE_HOME
        val matcher = TaskMatcher.TopActivityType(activityType)

        val taskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_STANDARD)
        assertThat(matcher.matches(taskInfo)).isFalse()
    }

    @Test
    fun activityMatcher_equivalentMatchersAreNotEqual() {
        val activityType = WindowConfiguration.ACTIVITY_TYPE_HOME
        val matcher1 = TaskMatcher.TopActivityType(activityType)
        val matcher2 = TaskMatcher.TopActivityType(activityType)

        assertThat(matcher1).isNotEqualTo(matcher2)
    }
}
+11 −24
Original line number Diff line number Diff line
@@ -24,12 +24,11 @@ import static com.android.systemui.dreams.dagger.DreamModule.DREAM_TOUCH_INSET_M
import static com.android.systemui.dreams.dagger.DreamModule.HOME_CONTROL_PANEL_DREAM_COMPONENT;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;

import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.drawable.ColorDrawable;
import android.service.dreams.DreamActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -65,6 +64,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.navigationbar.gestural.domain.GestureInteractor;
import com.android.systemui.navigationbar.gestural.domain.TaskMatcher;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -89,6 +89,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
        LifecycleOwner {
    private static final String TAG = "DreamOverlayService";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    private static final TaskMatcher DREAM_TYPE_MATCHER =
            new TaskMatcher.TopActivityType(WindowConfiguration.ACTIVITY_TYPE_DREAM);

    // The Context is used to construct the hosting constraint layout and child overlay views.
    private final Context mContext;
@@ -141,10 +143,6 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
    private final TouchInsetManager mTouchInsetManager;
    private final LifecycleOwner mLifecycleOwner;



    private ComponentName mCurrentBlockedGestureDreamActivityComponent;

    private final ArrayList<Job> mFlows = new ArrayList<>();

    /**
@@ -420,7 +418,11 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
        mStarted = true;

        updateRedirectWakeup();
        updateBlockedGestureDreamActivityComponent();

        if (!isDreamInPreviewMode()) {
            mGestureInteractor.addGestureBlockedMatcher(DREAM_TYPE_MATCHER,
                    GestureInteractor.Scope.Global);
        }
    }

    private void updateRedirectWakeup() {
@@ -431,18 +433,6 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
        redirectWake(mCommunalAvailable && !glanceableHubAllowKeyguardWhenDreaming());
    }

    private void updateBlockedGestureDreamActivityComponent() {
        // TODO(b/343815446): We should not be crafting this ActivityInfo ourselves. It should be
        // in a common place, Such as DreamActivity itself.
        final ActivityInfo info = new ActivityInfo();
        info.name = DreamActivity.class.getName();
        info.packageName = getDreamComponent().getPackageName();
        mCurrentBlockedGestureDreamActivityComponent = info.getComponentName();

        mGestureInteractor.addGestureBlockedActivity(mCurrentBlockedGestureDreamActivityComponent,
                GestureInteractor.Scope.Global);
    }

    @Override
    public void onEndDream() {
        resetCurrentDreamOverlayLocked();
@@ -613,11 +603,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
        mWindow = null;

        // Always unregister the any set DreamActivity from being blocked from gestures.
        if (mCurrentBlockedGestureDreamActivityComponent != null) {
            mGestureInteractor.removeGestureBlockedActivity(
                    mCurrentBlockedGestureDreamActivityComponent, GestureInteractor.Scope.Global);
            mCurrentBlockedGestureDreamActivityComponent = null;
        }
        mGestureInteractor.removeGestureBlockedMatcher(DREAM_TYPE_MATCHER,
                GestureInteractor.Scope.Global);

        mStarted = false;
    }
Loading