Merge "Creating GestureExclusionManager for a single place to listen for gesture exclusion region changes" into main
This commit is contained in:
@@ -22,7 +22,6 @@ import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
|
||||
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
|
||||
import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
|
||||
import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
|
||||
import static com.android.launcher3.util.SettingsCache.ONE_HANDED_ENABLED;
|
||||
@@ -54,15 +53,11 @@ import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemProperties;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.ISystemGestureExclusionListener;
|
||||
import android.view.IWindowManager;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.WindowManagerGlobal;
|
||||
|
||||
import androidx.annotation.BinderThread;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
@@ -73,6 +68,8 @@ import com.android.launcher3.util.NavigationMode;
|
||||
import com.android.launcher3.util.SettingsCache;
|
||||
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
|
||||
import com.android.quickstep.util.ActiveGestureLog;
|
||||
import com.android.quickstep.util.GestureExclusionManager;
|
||||
import com.android.quickstep.util.GestureExclusionManager.ExclusionListener;
|
||||
import com.android.quickstep.util.NavBarPosition;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
@@ -86,7 +83,7 @@ import java.util.ArrayList;
|
||||
/**
|
||||
* Manages the state of the system during a swipe up gesture.
|
||||
*/
|
||||
public class RecentsAnimationDeviceState implements DisplayInfoChangeListener {
|
||||
public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, ExclusionListener {
|
||||
|
||||
private static final String TAG = "RecentsAnimationDeviceState";
|
||||
|
||||
@@ -99,25 +96,9 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener {
|
||||
private final Context mContext;
|
||||
private final DisplayController mDisplayController;
|
||||
|
||||
private final IWindowManager mIWindowManager;
|
||||
private final GestureExclusionManager mExclusionManager;
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
final ISystemGestureExclusionListener mGestureExclusionListener =
|
||||
new ISystemGestureExclusionListener.Stub() {
|
||||
@BinderThread
|
||||
@Override
|
||||
public void onSystemGestureExclusionChanged(int displayId,
|
||||
Region systemGestureExclusionRegion, Region unrestrictedOrNull) {
|
||||
if (displayId != DEFAULT_DISPLAY) {
|
||||
return;
|
||||
}
|
||||
// Assignments are atomic, it should be safe on binder thread. Also we don't
|
||||
// think systemGestureExclusionRegion can be null but just in case, don't
|
||||
// let mExclusionRegion be null.
|
||||
mExclusionRegion = systemGestureExclusionRegion != null
|
||||
? systemGestureExclusionRegion : new Region();
|
||||
}
|
||||
};
|
||||
private final RotationTouchHelper mRotationTouchHelper;
|
||||
private final TaskStackChangeListener mPipListener;
|
||||
// Cache for better performance since it doesn't change at runtime.
|
||||
@@ -140,20 +121,20 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener {
|
||||
private boolean mPipIsActive;
|
||||
|
||||
private int mGestureBlockingTaskId = -1;
|
||||
private @NonNull Region mExclusionRegion = new Region();
|
||||
private @NonNull Region mExclusionRegion = GestureExclusionManager.EMPTY_REGION;
|
||||
private boolean mExclusionListenerRegistered;
|
||||
|
||||
public RecentsAnimationDeviceState(Context context) {
|
||||
this(context, false, WindowManagerGlobal.getWindowManagerService());
|
||||
this(context, false, GestureExclusionManager.INSTANCE);
|
||||
}
|
||||
|
||||
public RecentsAnimationDeviceState(Context context, boolean isInstanceForTouches) {
|
||||
this(context, isInstanceForTouches, WindowManagerGlobal.getWindowManagerService());
|
||||
this(context, isInstanceForTouches, GestureExclusionManager.INSTANCE);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
RecentsAnimationDeviceState(Context context, IWindowManager windowManager) {
|
||||
this(context, false, windowManager);
|
||||
RecentsAnimationDeviceState(Context context, GestureExclusionManager exclusionManager) {
|
||||
this(context, false, exclusionManager);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,10 +143,10 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener {
|
||||
*/
|
||||
RecentsAnimationDeviceState(
|
||||
Context context, boolean isInstanceForTouches,
|
||||
IWindowManager windowManager) {
|
||||
GestureExclusionManager exclusionManager) {
|
||||
mContext = context;
|
||||
mDisplayController = DisplayController.INSTANCE.get(context);
|
||||
mIWindowManager = windowManager;
|
||||
mExclusionManager = exclusionManager;
|
||||
mIsOneHandedModeSupported = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
|
||||
mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(context);
|
||||
if (isInstanceForTouches) {
|
||||
@@ -276,43 +257,33 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers {@link mGestureExclusionListener} for getting exclusion rect changes. Note that we
|
||||
* make binder call on {@link UI_HELPER_EXECUTOR} to avoid jank.
|
||||
*/
|
||||
public void registerExclusionListener() {
|
||||
UI_HELPER_EXECUTOR.execute(() -> {
|
||||
if (mExclusionListenerRegistered) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mIWindowManager.registerSystemGestureExclusionListener(
|
||||
mGestureExclusionListener, DEFAULT_DISPLAY);
|
||||
mExclusionListenerRegistered = true;
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to register window manager callbacks", e);
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onGestureExclusionChanged(@Nullable Region exclusionRegion,
|
||||
@Nullable Region unrestrictedOrNull) {
|
||||
mExclusionRegion = exclusionRegion != null
|
||||
? exclusionRegion : GestureExclusionManager.EMPTY_REGION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters {@link mGestureExclusionListener} if previously registered. We make binder call
|
||||
* on same {@link UI_HELPER_EXECUTOR} as in {@link #registerExclusionListener()} so that
|
||||
* read/write {@link mExclusionListenerRegistered} field is thread safe.
|
||||
* Registers itself for getting exclusion rect changes.
|
||||
*/
|
||||
public void registerExclusionListener() {
|
||||
if (mExclusionListenerRegistered) {
|
||||
return;
|
||||
}
|
||||
mExclusionManager.addListener(this);
|
||||
mExclusionListenerRegistered = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters itself as gesture exclusion listener if previously registered.
|
||||
*/
|
||||
public void unregisterExclusionListener() {
|
||||
UI_HELPER_EXECUTOR.execute(() -> {
|
||||
if (!mExclusionListenerRegistered) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mIWindowManager.unregisterSystemGestureExclusionListener(
|
||||
mGestureExclusionListener, DEFAULT_DISPLAY);
|
||||
mExclusionListenerRegistered = false;
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to unregister window manager callbacks", e);
|
||||
}
|
||||
});
|
||||
if (!mExclusionListenerRegistered) {
|
||||
return;
|
||||
}
|
||||
mExclusionManager.removeListener(this);
|
||||
mExclusionListenerRegistered = false;
|
||||
}
|
||||
|
||||
public void onOneHandedModeChanged(int newGesturalHeight) {
|
||||
@@ -515,10 +486,8 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener {
|
||||
* This is only used for quickswitch, and not swipe up.
|
||||
*/
|
||||
public boolean isInExclusionRegion(MotionEvent event) {
|
||||
// mExclusionRegion can change on binder thread, use a local instance here.
|
||||
Region exclusionRegion = mExclusionRegion;
|
||||
return mMode == NO_BUTTON
|
||||
&& exclusionRegion.contains((int) event.getX(), (int) event.getY());
|
||||
&& mExclusionRegion.contains((int) event.getX(), (int) event.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.quickstep.util
|
||||
|
||||
import android.graphics.Region
|
||||
import android.os.RemoteException
|
||||
import android.util.Log
|
||||
import android.view.Display.DEFAULT_DISPLAY
|
||||
import android.view.ISystemGestureExclusionListener
|
||||
import android.view.IWindowManager
|
||||
import android.view.WindowManagerGlobal
|
||||
import androidx.annotation.BinderThread
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.launcher3.util.Executors
|
||||
|
||||
/** Wrapper over system gesture exclusion listener to optimize for multiple RPCs */
|
||||
class GestureExclusionManager(private val windowManager: IWindowManager) {
|
||||
|
||||
private val listeners = mutableListOf<ExclusionListener>()
|
||||
|
||||
private var lastExclusionRegion: Region? = null
|
||||
private var lastUnrestrictedOrNull: Region? = null
|
||||
|
||||
@VisibleForTesting
|
||||
val exclusionListener =
|
||||
object : ISystemGestureExclusionListener.Stub() {
|
||||
@BinderThread
|
||||
override fun onSystemGestureExclusionChanged(
|
||||
displayId: Int,
|
||||
exclusionRegion: Region?,
|
||||
unrestrictedOrNull: Region?
|
||||
) {
|
||||
if (displayId != DEFAULT_DISPLAY) {
|
||||
return
|
||||
}
|
||||
Executors.MAIN_EXECUTOR.execute {
|
||||
lastExclusionRegion = exclusionRegion
|
||||
lastUnrestrictedOrNull = unrestrictedOrNull
|
||||
listeners.forEach {
|
||||
it.onGestureExclusionChanged(exclusionRegion, unrestrictedOrNull)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds a listener for receiving gesture exclusion regions */
|
||||
fun addListener(listener: ExclusionListener) {
|
||||
val wasEmpty = listeners.isEmpty()
|
||||
listeners.add(listener)
|
||||
if (wasEmpty) {
|
||||
Executors.UI_HELPER_EXECUTOR.execute {
|
||||
try {
|
||||
windowManager.registerSystemGestureExclusionListener(
|
||||
exclusionListener,
|
||||
DEFAULT_DISPLAY
|
||||
)
|
||||
} catch (e: RemoteException) {
|
||||
Log.e(TAG, "Failed to register gesture exclusion listener", e)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If we had already registered before, dispatch the last known value,
|
||||
// otherwise registering the listener will initiate a dispatch
|
||||
listener.onGestureExclusionChanged(lastExclusionRegion, lastUnrestrictedOrNull)
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes a previously added exclusion listener */
|
||||
fun removeListener(listener: ExclusionListener) {
|
||||
if (listeners.remove(listener) && listeners.isEmpty()) {
|
||||
Executors.UI_HELPER_EXECUTOR.execute {
|
||||
try {
|
||||
windowManager.unregisterSystemGestureExclusionListener(
|
||||
exclusionListener,
|
||||
DEFAULT_DISPLAY
|
||||
)
|
||||
} catch (e: RemoteException) {
|
||||
Log.e(TAG, "Failed to unregister gesture exclusion listener", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ExclusionListener {
|
||||
fun onGestureExclusionChanged(exclusionRegion: Region?, unrestrictedOrNull: Region?)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val TAG = "GestureExclusionManager"
|
||||
|
||||
@JvmField
|
||||
val INSTANCE = GestureExclusionManager(WindowManagerGlobal.getWindowManagerService()!!)
|
||||
|
||||
@JvmField val EMPTY_REGION = Region()
|
||||
}
|
||||
}
|
||||
@@ -2,17 +2,15 @@ package com.android.quickstep
|
||||
|
||||
import android.content.Context
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.view.Display
|
||||
import android.view.IWindowManager
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.launcher3.util.DisplayController.CHANGE_DENSITY
|
||||
import com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE
|
||||
import com.android.launcher3.util.DisplayController.CHANGE_ROTATION
|
||||
import com.android.launcher3.util.DisplayController.Info
|
||||
import com.android.launcher3.util.Executors
|
||||
import com.android.launcher3.util.NavigationMode
|
||||
import com.android.launcher3.util.window.WindowManagerProxy
|
||||
import com.android.quickstep.util.GestureExclusionManager
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
@@ -28,7 +26,7 @@ import org.mockito.kotlin.whenever
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
class RecentsAnimationDeviceStateTest {
|
||||
|
||||
@Mock private lateinit var windowManager: IWindowManager
|
||||
@Mock private lateinit var exclusionManager: GestureExclusionManager
|
||||
@Mock private lateinit var windowManagerProxy: WindowManagerProxy
|
||||
@Mock private lateinit var info: Info
|
||||
|
||||
@@ -38,60 +36,45 @@ class RecentsAnimationDeviceStateTest {
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
underTest = RecentsAnimationDeviceState(context, windowManager)
|
||||
underTest = RecentsAnimationDeviceState(context, exclusionManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun registerExclusionListener_success() {
|
||||
underTest.registerExclusionListener()
|
||||
|
||||
awaitTasksCompleted()
|
||||
verify(windowManager)
|
||||
.registerSystemGestureExclusionListener(
|
||||
underTest.mGestureExclusionListener,
|
||||
Display.DEFAULT_DISPLAY
|
||||
)
|
||||
verify(exclusionManager).addListener(underTest)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun registerExclusionListener_again_fail() {
|
||||
underTest.registerExclusionListener()
|
||||
awaitTasksCompleted()
|
||||
reset(windowManager)
|
||||
reset(exclusionManager)
|
||||
|
||||
underTest.registerExclusionListener()
|
||||
|
||||
awaitTasksCompleted()
|
||||
verifyZeroInteractions(windowManager)
|
||||
verifyZeroInteractions(exclusionManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unregisterExclusionListener_success() {
|
||||
underTest.registerExclusionListener()
|
||||
awaitTasksCompleted()
|
||||
reset(windowManager)
|
||||
reset(exclusionManager)
|
||||
|
||||
underTest.unregisterExclusionListener()
|
||||
|
||||
awaitTasksCompleted()
|
||||
verify(windowManager)
|
||||
.unregisterSystemGestureExclusionListener(
|
||||
underTest.mGestureExclusionListener,
|
||||
Display.DEFAULT_DISPLAY
|
||||
)
|
||||
verify(exclusionManager).removeListener(underTest)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unregisterExclusionListener_again_fail() {
|
||||
underTest.registerExclusionListener()
|
||||
underTest.unregisterExclusionListener()
|
||||
awaitTasksCompleted()
|
||||
reset(windowManager)
|
||||
reset(exclusionManager)
|
||||
|
||||
underTest.unregisterExclusionListener()
|
||||
|
||||
awaitTasksCompleted()
|
||||
verifyZeroInteractions(windowManager)
|
||||
verifyZeroInteractions(exclusionManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -100,45 +83,28 @@ class RecentsAnimationDeviceStateTest {
|
||||
|
||||
underTest.onDisplayInfoChanged(context, info, CHANGE_ROTATION or CHANGE_NAVIGATION_MODE)
|
||||
|
||||
awaitTasksCompleted()
|
||||
verify(windowManager)
|
||||
.registerSystemGestureExclusionListener(
|
||||
underTest.mGestureExclusionListener,
|
||||
Display.DEFAULT_DISPLAY
|
||||
)
|
||||
verify(exclusionManager).addListener(underTest)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onDisplayInfoChanged_twoButton_unregisterExclusionListener() {
|
||||
underTest.registerExclusionListener()
|
||||
awaitTasksCompleted()
|
||||
whenever(info.getNavigationMode()).thenReturn(NavigationMode.TWO_BUTTONS)
|
||||
reset(windowManager)
|
||||
reset(exclusionManager)
|
||||
|
||||
underTest.onDisplayInfoChanged(context, info, CHANGE_ROTATION or CHANGE_NAVIGATION_MODE)
|
||||
|
||||
awaitTasksCompleted()
|
||||
verify(windowManager)
|
||||
.unregisterSystemGestureExclusionListener(
|
||||
underTest.mGestureExclusionListener,
|
||||
Display.DEFAULT_DISPLAY
|
||||
)
|
||||
verify(exclusionManager).removeListener(underTest)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onDisplayInfoChanged_changeDensity_noOp() {
|
||||
underTest.registerExclusionListener()
|
||||
awaitTasksCompleted()
|
||||
whenever(info.getNavigationMode()).thenReturn(NavigationMode.NO_BUTTON)
|
||||
reset(windowManager)
|
||||
reset(exclusionManager)
|
||||
|
||||
underTest.onDisplayInfoChanged(context, info, CHANGE_DENSITY)
|
||||
|
||||
awaitTasksCompleted()
|
||||
verifyZeroInteractions(windowManager)
|
||||
}
|
||||
|
||||
private fun awaitTasksCompleted() {
|
||||
Executors.UI_HELPER_EXECUTOR.submit<Any> { null }.get()
|
||||
verifyZeroInteractions(exclusionManager)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.quickstep.util
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.graphics.Region
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.view.Display.DEFAULT_DISPLAY
|
||||
import android.view.IWindowManager
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.launcher3.util.Executors
|
||||
import com.android.quickstep.util.GestureExclusionManager.ExclusionListener
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.mockito.kotlin.reset
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.verifyZeroInteractions
|
||||
|
||||
/** Unit test for [GestureExclusionManager]. */
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
class GestureExclusionManagerTest {
|
||||
|
||||
@Mock private lateinit var windowManager: IWindowManager
|
||||
|
||||
@Mock private lateinit var listener1: ExclusionListener
|
||||
@Mock private lateinit var listener2: ExclusionListener
|
||||
|
||||
private val r1 = Region().apply { union(Rect(0, 0, 100, 200)) }
|
||||
private val r2 = Region().apply { union(Rect(200, 200, 500, 800)) }
|
||||
|
||||
private lateinit var underTest: GestureExclusionManager
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
underTest = GestureExclusionManager(windowManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addListener_registers() {
|
||||
underTest.addListener(listener1)
|
||||
|
||||
awaitTasksCompleted()
|
||||
verify(windowManager)
|
||||
.registerSystemGestureExclusionListener(underTest.exclusionListener, DEFAULT_DISPLAY)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addListener_again_skips_register() {
|
||||
underTest.addListener(listener1)
|
||||
awaitTasksCompleted()
|
||||
reset(windowManager)
|
||||
|
||||
underTest.addListener(listener2)
|
||||
|
||||
awaitTasksCompleted()
|
||||
verifyZeroInteractions(windowManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeListener_unregisters() {
|
||||
underTest.addListener(listener1)
|
||||
awaitTasksCompleted()
|
||||
reset(windowManager)
|
||||
|
||||
underTest.removeListener(listener1)
|
||||
|
||||
awaitTasksCompleted()
|
||||
verify(windowManager)
|
||||
.unregisterSystemGestureExclusionListener(underTest.exclusionListener, DEFAULT_DISPLAY)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeListener_again_skips_unregister() {
|
||||
underTest.addListener(listener1)
|
||||
underTest.addListener(listener2)
|
||||
awaitTasksCompleted()
|
||||
reset(windowManager)
|
||||
|
||||
underTest.removeListener(listener1)
|
||||
|
||||
awaitTasksCompleted()
|
||||
verifyZeroInteractions(windowManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onSystemGestureExclusionChanged_dispatches_to_listeners() {
|
||||
underTest.addListener(listener1)
|
||||
underTest.addListener(listener2)
|
||||
awaitTasksCompleted()
|
||||
|
||||
underTest.exclusionListener.onSystemGestureExclusionChanged(DEFAULT_DISPLAY, r1, r2)
|
||||
awaitTasksCompleted()
|
||||
verify(listener1).onGestureExclusionChanged(r1, r2)
|
||||
verify(listener2).onGestureExclusionChanged(r1, r2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addLister_dispatches_second_time() {
|
||||
underTest.exclusionListener.onSystemGestureExclusionChanged(DEFAULT_DISPLAY, r1, r2)
|
||||
awaitTasksCompleted()
|
||||
underTest.addListener(listener1)
|
||||
awaitTasksCompleted()
|
||||
verifyZeroInteractions(listener1)
|
||||
|
||||
underTest.addListener(listener2)
|
||||
awaitTasksCompleted()
|
||||
|
||||
verifyZeroInteractions(listener1)
|
||||
verify(listener2).onGestureExclusionChanged(r1, r2)
|
||||
}
|
||||
|
||||
private fun awaitTasksCompleted() {
|
||||
Executors.UI_HELPER_EXECUTOR.submit<Any> { null }.get()
|
||||
Executors.MAIN_EXECUTOR.submit<Any> { null }.get()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user