summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt51
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/data/GestureRepositoryTest.kt53
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/GestureInteractorTest.kt135
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/dagger/GestureModule.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/data/respository/GestureRepository.kt63
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt103
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/data/GestureRepositoryKosmos.kt25
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/domain/GestureInteractorKosmos.kt27
11 files changed, 560 insertions, 19 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index 4c24ce2ad8c5..5c09777ddde5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -62,12 +62,13 @@ import com.android.systemui.complication.dagger.ComplicationComponent
import com.android.systemui.dreams.complication.HideComplicationTouchHandler
import com.android.systemui.dreams.dagger.DreamOverlayComponent
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.testKosmos
import com.android.systemui.touch.TouchInsetManager
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -84,9 +85,11 @@ import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.isNull
-import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.spy
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -166,6 +169,7 @@ class DreamOverlayServiceTest : SysuiTestCase() {
private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
private lateinit var communalRepository: FakeCommunalSceneRepository
private var viewCaptureSpy = spy(ViewCaptureFactory.getInstance(context))
+ private lateinit var gestureInteractor: GestureInteractor
@Captor var mViewCaptor: ArgumentCaptor<View>? = null
private lateinit var mService: DreamOverlayService
@@ -177,6 +181,7 @@ class DreamOverlayServiceTest : SysuiTestCase() {
lifecycleRegistry = FakeLifecycleRegistry(mLifecycleOwner)
bouncerRepository = kosmos.fakeKeyguardBouncerRepository
communalRepository = kosmos.fakeCommunalSceneRepository
+ gestureInteractor = spy(kosmos.gestureInteractor)
whenever(mDreamOverlayComponent.getDreamOverlayContainerViewController())
.thenReturn(mDreamOverlayContainerViewController)
@@ -231,6 +236,7 @@ class DreamOverlayServiceTest : SysuiTestCase() {
HOME_CONTROL_PANEL_DREAM_COMPONENT,
mDreamOverlayCallbackController,
kosmos.keyguardInteractor,
+ gestureInteractor,
WINDOW_NAME
)
}
@@ -955,6 +961,47 @@ class DreamOverlayServiceTest : SysuiTestCase() {
assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.RESUMED)
}
+ @Test
+ fun testDreamActivityGesturesBlockedOnStart() {
+ val client = client
+
+ // Inform the overlay service of dream starting.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ false /*shouldShowComplication*/
+ )
+ mMainExecutor.runAllReady()
+ val captor = argumentCaptor<ComponentName>()
+ verify(gestureInteractor)
+ .addGestureBlockedActivity(captor.capture(), eq(GestureInteractor.Scope.Global))
+ assertThat(captor.firstValue.packageName)
+ .isEqualTo(ComponentName.unflattenFromString(DREAM_COMPONENT)?.packageName)
+ }
+
+ @Test
+ fun testDreamActivityGesturesUnblockedOnEnd() {
+ val client = client
+
+ // Inform the overlay service of dream starting.
+ client.startDream(
+ mWindowParams,
+ mDreamOverlayCallback,
+ DREAM_COMPONENT,
+ 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)
+ }
+
internal class FakeLifecycleRegistry(provider: LifecycleOwner) : LifecycleRegistry(provider) {
val mLifecycles: MutableList<State> = ArrayList()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/data/GestureRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/data/GestureRepositoryTest.kt
new file mode 100644
index 000000000000..91d37cf1816a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/data/GestureRepositoryTest.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.data
+
+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.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class GestureRepositoryTest : SysuiTestCase() {
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+ private val underTest by lazy { GestureRepositoryImpl(testDispatcher) }
+
+ @Test
+ fun addRemoveComponentToBlock_updatesBlockedComponentSet() =
+ testScope.runTest {
+ val component = mock<ComponentName>()
+
+ underTest.addGestureBlockedActivity(component)
+ val addedBlockedComponents by collectLastValue(underTest.gestureBlockedActivities)
+ assertThat(addedBlockedComponents).contains(component)
+
+ underTest.removeGestureBlockedActivity(component)
+ val removedBlockedComponents by collectLastValue(underTest.gestureBlockedActivities)
+ assertThat(removedBlockedComponents).isEmpty()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/GestureInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/GestureInteractorTest.kt
new file mode 100644
index 000000000000..bc142e62a0aa
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/GestureInteractorTest.kt
@@ -0,0 +1,135 @@
+/*
+ * 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.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.GestureRepository
+import com.android.systemui.navigationbar.gestural.domain.GestureInteractor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class GestureInteractorTest : SysuiTestCase() {
+ @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+ val dispatcher = StandardTestDispatcher()
+ val testScope = TestScope(dispatcher)
+
+ @Mock private lateinit var gestureRepository: GestureRepository
+
+ private val underTest by lazy {
+ GestureInteractor(gestureRepository, testScope.backgroundScope)
+ }
+
+ @Before
+ fun setup() {
+ Dispatchers.setMain(dispatcher)
+ whenever(gestureRepository.gestureBlockedActivities).thenReturn(MutableStateFlow(setOf()))
+ }
+
+ @After
+ fun tearDown() {
+ Dispatchers.resetMain()
+ }
+
+ @Test
+ fun addBlockedActivity_testCombination() =
+ testScope.runTest {
+ val globalComponent = mock<ComponentName>()
+ whenever(gestureRepository.gestureBlockedActivities)
+ .thenReturn(MutableStateFlow(setOf(globalComponent)))
+ val localComponent = mock<ComponentName>()
+ underTest.addGestureBlockedActivity(localComponent, GestureInteractor.Scope.Local)
+ val lastSeen by collectLastValue(underTest.gestureBlockedActivities)
+ testScope.runCurrent()
+ verify(gestureRepository, never()).addGestureBlockedActivity(any())
+ assertThat(lastSeen).hasSize(2)
+ assertThat(lastSeen).containsExactly(globalComponent, localComponent)
+ }
+
+ @Test
+ fun addBlockedActivityLocally_onlyAffectsLocalInteractor() =
+ testScope.runTest {
+ val component = mock<ComponentName>()
+ underTest.addGestureBlockedActivity(component, GestureInteractor.Scope.Local)
+ val lastSeen by collectLastValue(underTest.gestureBlockedActivities)
+ testScope.runCurrent()
+ verify(gestureRepository, never()).addGestureBlockedActivity(any())
+ assertThat(lastSeen).contains(component)
+ }
+
+ @Test
+ fun removeBlockedActivityLocally_onlyAffectsLocalInteractor() =
+ testScope.runTest {
+ val component = mock<ComponentName>()
+ underTest.addGestureBlockedActivity(component, GestureInteractor.Scope.Local)
+ val lastSeen by collectLastValue(underTest.gestureBlockedActivities)
+ testScope.runCurrent()
+ underTest.removeGestureBlockedActivity(component, GestureInteractor.Scope.Local)
+ testScope.runCurrent()
+ verify(gestureRepository, never()).removeGestureBlockedActivity(any())
+ assertThat(lastSeen).isEmpty()
+ }
+
+ @Test
+ fun addBlockedActivity_invokesRepository() =
+ testScope.runTest {
+ val component = mock<ComponentName>()
+ underTest.addGestureBlockedActivity(component, GestureInteractor.Scope.Global)
+ runCurrent()
+ val captor = argumentCaptor<ComponentName>()
+ verify(gestureRepository).addGestureBlockedActivity(captor.capture())
+ assertThat(captor.firstValue).isEqualTo(component)
+ }
+
+ @Test
+ fun removeBlockedActivity_invokesRepository() =
+ testScope.runTest {
+ val component = mock<ComponentName>()
+ underTest.removeGestureBlockedActivity(component, GestureInteractor.Scope.Global)
+ runCurrent()
+ val captor = argumentCaptor<ComponentName>()
+ verify(gestureRepository).removeGestureBlockedActivity(captor.capture())
+ assertThat(captor.firstValue).isEqualTo(component)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 1771f4dc8572..15ddf5bfd281 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -80,6 +80,7 @@ import com.android.systemui.model.SceneContainerPlugin;
import com.android.systemui.model.SysUiState;
import com.android.systemui.motiontool.MotionToolModule;
import com.android.systemui.navigationbar.NavigationBarComponent;
+import com.android.systemui.navigationbar.gestural.dagger.GestureModule;
import com.android.systemui.notetask.NoteTaskModule;
import com.android.systemui.people.PeopleModule;
import com.android.systemui.plugins.BcSmartspaceConfigPlugin;
@@ -215,6 +216,7 @@ import javax.inject.Named;
FlagsModule.class,
FlagDependenciesModule.class,
FooterActionsModule.class,
+ GestureModule.class,
InputMethodModule.class,
KeyEventRepositoryModule.class,
KeyboardModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 982398579477..931066d5c582 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -27,7 +27,9 @@ import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
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;
@@ -63,6 +65,7 @@ import com.android.systemui.complication.dagger.ComplicationComponent;
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.shade.ShadeExpansionChangeEvent;
import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -135,6 +138,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
private final DreamOverlayComponent mDreamOverlayComponent;
+ private ComponentName mCurrentBlockedGestureDreamActivityComponent;
+
/**
* This {@link LifecycleRegistry} controls when dream overlay functionality, like touch
* handling, should be active. It will automatically be paused when the dream overlay is hidden
@@ -222,6 +227,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
private final DreamOverlayStateController mStateController;
+ private final GestureInteractor mGestureInteractor;
+
@VisibleForTesting
public enum DreamOverlayEvent implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "The dream overlay has entered start.")
@@ -265,6 +272,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
ComponentName homeControlPanelDreamComponent,
DreamOverlayCallbackController dreamOverlayCallbackController,
KeyguardInteractor keyguardInteractor,
+ GestureInteractor gestureInteractor,
@Named(DREAM_OVERLAY_WINDOW_TITLE) String windowTitle) {
super(executor);
mContext = context;
@@ -281,6 +289,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
mWindowTitle = windowTitle;
mCommunalInteractor = communalInteractor;
mSystemDialogsCloser = systemDialogsCloser;
+ mGestureInteractor = gestureInteractor;
final ViewModelStore viewModelStore = new ViewModelStore();
final Complication.Host host =
@@ -391,6 +400,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
mStarted = true;
updateRedirectWakeup();
+ updateBlockedGestureDreamActivityComponent();
}
private void updateRedirectWakeup() {
@@ -401,6 +411,18 @@ 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();
@@ -472,6 +494,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
* into the dream window.
*/
private boolean addOverlayWindowLocked(WindowManager.LayoutParams layoutParams) {
+
mWindow = new PhoneWindow(mContext);
// Default to SystemUI name for TalkBack.
mWindow.setTitle(mWindowTitle);
@@ -554,6 +577,14 @@ 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;
+ }
+
mStarted = false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 947336d41590..9eca34f88a78 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -76,6 +76,7 @@ import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.navigationbar.gestural.domain.GestureInteractor;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.NavigationEdgeBackPlugin;
import com.android.systemui.plugins.PluginListener;
@@ -95,15 +96,14 @@ import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.util.concurrency.BackPanelUiThread;
import com.android.systemui.util.concurrency.UiThreadContext;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.pip.Pip;
import java.io.PrintWriter;
import java.util.ArrayDeque;
-import java.util.ArrayList;
import java.util.Date;
-import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
@@ -157,12 +157,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@Override
public void onTaskStackChanged() {
- if (edgebackGestureHandlerGetRunningTasksBackground()) {
- mBackgroundExecutor.execute(() -> mGestureBlockingActivityRunning.set(
- isGestureBlockingActivityRunning()));
- } else {
- mGestureBlockingActivityRunning.set(isGestureBlockingActivityRunning());
- }
+ updateRunningActivityGesturesBlocked();
}
@Override
public void onTaskCreated(int taskId, ComponentName componentName) {
@@ -209,8 +204,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
private final Optional<DesktopMode> mDesktopModeOptional;
private final FalsingManager mFalsingManager;
private final Configuration mLastReportedConfig = new Configuration();
- // Activities which should not trigger Back gesture.
- private final List<ComponentName> mGestureBlockingActivities = new ArrayList<>();
private final Point mDisplaySize = new Point();
private final int mDisplayId;
@@ -227,6 +220,10 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
mBackGestureTfClassifierProviderProvider;
private final Provider<LightBarController> mLightBarControllerProvider;
+ private final GestureInteractor mGestureInteractor;
+
+ private final JavaAdapter mJavaAdapter;
+
// The left side edge width where touch down is allowed
private int mEdgeWidthLeft;
// The right side edge width where touch down is allowed
@@ -426,7 +423,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
FalsingManager falsingManager,
Provider<BackGestureTfClassifierProvider> backGestureTfClassifierProviderProvider,
Provider<LightBarController> lightBarControllerProvider,
- NotificationShadeWindowController notificationShadeWindowController) {
+ NotificationShadeWindowController notificationShadeWindowController,
+ GestureInteractor gestureInteractor,
+ JavaAdapter javaAdapter) {
mContext = context;
mDisplayId = context.getDisplayId();
mUiThreadContext = uiThreadContext;
@@ -446,7 +445,13 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
mFalsingManager = falsingManager;
mBackGestureTfClassifierProviderProvider = backGestureTfClassifierProviderProvider;
mLightBarControllerProvider = lightBarControllerProvider;
+ mGestureInteractor = gestureInteractor;
+ mJavaAdapter = javaAdapter;
mLastReportedConfig.setTo(mContext.getResources().getConfiguration());
+
+ mJavaAdapter.alwaysCollectFlow(mGestureInteractor.getGestureBlockedActivities(),
+ componentNames -> updateRunningActivityGesturesBlocked());
+
ComponentName recentsComponentName = ComponentName.unflattenFromString(
context.getString(com.android.internal.R.string.config_recentsComponentName));
if (recentsComponentName != null) {
@@ -466,8 +471,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
} else {
String[] gestureBlockingActivities = resources.getStringArray(resId);
for (String gestureBlockingActivity : gestureBlockingActivities) {
- mGestureBlockingActivities.add(
- ComponentName.unflattenFromString(gestureBlockingActivity));
+ mGestureInteractor.addGestureBlockedActivity(
+ ComponentName.unflattenFromString(gestureBlockingActivity),
+ GestureInteractor.Scope.Local);
}
}
} catch (NameNotFoundException e) {
@@ -561,6 +567,15 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
}
}
+ private void updateRunningActivityGesturesBlocked() {
+ if (edgebackGestureHandlerGetRunningTasksBackground()) {
+ mBackgroundExecutor.execute(() -> mGestureBlockingActivityRunning.set(
+ isGestureBlockingActivityRunning()));
+ } else {
+ mGestureBlockingActivityRunning.set(isGestureBlockingActivityRunning());
+ }
+ }
+
/**
* Called when the nav/task bar is attached.
*/
@@ -1293,7 +1308,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
} else {
mPackageName = "_UNKNOWN";
}
- return topActivity != null && mGestureBlockingActivities.contains(topActivity);
+
+ return topActivity != null && mGestureInteractor.areGesturesBlocked(topActivity);
}
public void setBackAnimation(BackAnimation backAnimation) {
@@ -1342,6 +1358,10 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
private final Provider<LightBarController> mLightBarControllerProvider;
private final NotificationShadeWindowController mNotificationShadeWindowController;
+ private final GestureInteractor mGestureInteractor;
+
+ private final JavaAdapter mJavaAdapter;
+
@Inject
public Factory(OverviewProxyService overviewProxyService,
SysUiState sysUiState,
@@ -1361,8 +1381,10 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
FalsingManager falsingManager,
Provider<BackGestureTfClassifierProvider>
backGestureTfClassifierProviderProvider,
- Provider<LightBarController> lightBarControllerProvider,
- NotificationShadeWindowController notificationShadeWindowController) {
+ Provider<LightBarController> lightBarControllerProvider,
+ NotificationShadeWindowController notificationShadeWindowController,
+ GestureInteractor gestureInteractor,
+ JavaAdapter javaAdapter) {
mOverviewProxyService = overviewProxyService;
mSysUiState = sysUiState;
mPluginManager = pluginManager;
@@ -1382,6 +1404,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
mBackGestureTfClassifierProviderProvider = backGestureTfClassifierProviderProvider;
mLightBarControllerProvider = lightBarControllerProvider;
mNotificationShadeWindowController = notificationShadeWindowController;
+ mGestureInteractor = gestureInteractor;
+ mJavaAdapter = javaAdapter;
}
/** Construct a {@link EdgeBackGestureHandler}. */
@@ -1407,7 +1431,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
mFalsingManager,
mBackGestureTfClassifierProviderProvider,
mLightBarControllerProvider,
- mNotificationShadeWindowController));
+ mNotificationShadeWindowController,
+ mGestureInteractor,
+ mJavaAdapter));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/dagger/GestureModule.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/dagger/GestureModule.kt
new file mode 100644
index 000000000000..72a84f54ab9f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/dagger/GestureModule.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.navigationbar.gestural.dagger
+
+import com.android.systemui.navigationbar.gestural.data.respository.GestureRepository
+import com.android.systemui.navigationbar.gestural.data.respository.GestureRepositoryImpl
+import dagger.Binds
+import dagger.Module
+
+/** {@link Module} for gesture related dependencies */
+@Module
+interface GestureModule {
+ /** */
+ @Binds fun gestureRespoitory(impl: GestureRepositoryImpl): GestureRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/data/respository/GestureRepository.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/data/respository/GestureRepository.kt
new file mode 100644
index 000000000000..8f35343626e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/data/respository/GestureRepository.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.navigationbar.gestural.data.respository
+
+import android.content.ComponentName
+import android.util.ArraySet
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.withContext
+
+/** A repository for storing gesture related information */
+interface GestureRepository {
+ /** A {@link StateFlow} tracking activities currently blocked from gestures. */
+ val gestureBlockedActivities: StateFlow<Set<ComponentName>>
+
+ /** Adds an activity to be blocked from gestures. */
+ suspend fun addGestureBlockedActivity(activity: ComponentName)
+
+ /** Removes an activity from being blocked from gestures. */
+ suspend fun removeGestureBlockedActivity(activity: ComponentName)
+}
+
+@SysUISingleton
+class GestureRepositoryImpl
+@Inject
+constructor(@Main private val mainDispatcher: CoroutineDispatcher) : GestureRepository {
+ private val _gestureBlockedActivities = MutableStateFlow<Set<ComponentName>>(ArraySet())
+
+ override val gestureBlockedActivities: StateFlow<Set<ComponentName>>
+ get() = _gestureBlockedActivities
+
+ override suspend fun addGestureBlockedActivity(activity: ComponentName) =
+ withContext(mainDispatcher) {
+ _gestureBlockedActivities.emit(
+ _gestureBlockedActivities.value.toMutableSet().apply { add(activity) }
+ )
+ }
+
+ override suspend fun removeGestureBlockedActivity(activity: ComponentName) =
+ withContext(mainDispatcher) {
+ _gestureBlockedActivities.emit(
+ _gestureBlockedActivities.value.toMutableSet().apply { remove(activity) }
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt
new file mode 100644
index 000000000000..6dc5939b7c1a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.navigationbar.gestural.domain
+
+import android.content.ComponentName
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.navigationbar.gestural.data.respository.GestureRepository
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+/**
+ * {@link GestureInteractor} helps interact with gesture-related logic, including accessing the
+ * underlying {@link GestureRepository}.
+ */
+class GestureInteractor
+@Inject
+constructor(
+ private val gestureRepository: GestureRepository,
+ @Application private val scope: CoroutineScope
+) {
+ enum class Scope {
+ Local,
+ Global
+ }
+
+ private val _localGestureBlockedActivities = MutableStateFlow<Set<ComponentName>>(setOf())
+ /** A {@link StateFlow} for listening to changes in Activities where gestures are blocked */
+ val gestureBlockedActivities: StateFlow<Set<ComponentName>>
+ get() =
+ combine(
+ gestureRepository.gestureBlockedActivities,
+ _localGestureBlockedActivities.asStateFlow()
+ ) { global, local ->
+ global + local
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), setOf())
+
+ /**
+ * Adds an {@link Activity} to be blocked based on component when the topmost, focused {@link
+ * Activity}.
+ */
+ fun addGestureBlockedActivity(activity: ComponentName, gestureScope: Scope) {
+ scope.launch {
+ when (gestureScope) {
+ Scope.Local -> {
+ _localGestureBlockedActivities.emit(
+ _localGestureBlockedActivities.value.toMutableSet().apply { add(activity) }
+ )
+ }
+ Scope.Global -> {
+ gestureRepository.addGestureBlockedActivity(activity)
+ }
+ }
+ }
+ }
+
+ /** Removes an {@link Activity} from being blocked from gestures. */
+ fun removeGestureBlockedActivity(activity: ComponentName, gestureScope: Scope) {
+ scope.launch {
+ when (gestureScope) {
+ Scope.Local -> {
+ _localGestureBlockedActivities.emit(
+ _localGestureBlockedActivities.value.toMutableSet().apply {
+ remove(activity)
+ }
+ )
+ }
+ Scope.Global -> {
+ gestureRepository.removeGestureBlockedActivity(activity)
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks whether the specified {@link Activity} {@link ComponentName} is being blocked from
+ * gestures.
+ */
+ fun areGesturesBlocked(activity: ComponentName): Boolean {
+ return gestureBlockedActivities.value.contains(activity)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/data/GestureRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/data/GestureRepositoryKosmos.kt
new file mode 100644
index 000000000000..9bd346effdc3
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/data/GestureRepositoryKosmos.kt
@@ -0,0 +1,25 @@
+/*
+ * 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.keyguard.gesture.data
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.navigationbar.gestural.data.respository.GestureRepository
+import com.android.systemui.navigationbar.gestural.data.respository.GestureRepositoryImpl
+
+val Kosmos.gestureRepository: GestureRepository by
+ Kosmos.Fixture { GestureRepositoryImpl(testDispatcher) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/domain/GestureInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/domain/GestureInteractorKosmos.kt
new file mode 100644
index 000000000000..658aaa6c1a62
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/domain/GestureInteractorKosmos.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.keyguard.gesture.domain
+
+import com.android.systemui.keyguard.gesture.data.gestureRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.navigationbar.gestural.domain.GestureInteractor
+
+val Kosmos.gestureInteractor: GestureInteractor by
+ Kosmos.Fixture {
+ GestureInteractor(gestureRepository = gestureRepository, scope = applicationCoroutineScope)
+ }