summaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
author Andrey Epin <ayepin@google.com> 2023-09-06 21:15:01 -0700
committer Andrey Epin <ayepin@google.com> 2023-09-08 16:16:11 +0000
commitb8f2dd471020d6334d7552ecc6cd428e0f796cdb (patch)
treec22de657863cbf106a5ef13f11c18e24d4b83282 /java
parente7b6dce2df1de1e57ccbe617fe34a51666958ff3 (diff)
ShortcutLoader to use a CoroutineScope instead of a Lifecycle
A preparation step to make transfer ShortcutLoader ownership from ChooserActivity to a view model. Test: atest IntentResolverUnitTests Change-Id: Id4e02b3418f49644c11e562a89ce3c00e61f8e5f
Diffstat (limited to 'java')
-rw-r--r--java/src/com/android/intentresolver/ChooserActivity.java3
-rw-r--r--java/src/com/android/intentresolver/shortcuts/ShortcutLoader.kt20
-rw-r--r--java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt606
3 files changed, 319 insertions, 310 deletions
diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java
index b27f054e..d244fa2d 100644
--- a/java/src/com/android/intentresolver/ChooserActivity.java
+++ b/java/src/com/android/intentresolver/ChooserActivity.java
@@ -24,6 +24,7 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROS
import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL;
import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK;
+import static androidx.lifecycle.LifecycleKt.getCoroutineScope;
import static com.android.internal.util.LatencyTracker.ACTION_LOAD_SHARE_SHEET;
import android.annotation.IntDef;
@@ -406,7 +407,7 @@ public class ChooserActivity extends ResolverActivity implements
Consumer<ShortcutLoader.Result> callback) {
return new ShortcutLoader(
context,
- getLifecycle(),
+ getCoroutineScope(getLifecycle()),
appPredictor,
userHandle,
targetIntentFilter,
diff --git a/java/src/com/android/intentresolver/shortcuts/ShortcutLoader.kt b/java/src/com/android/intentresolver/shortcuts/ShortcutLoader.kt
index f05542e2..e7f71661 100644
--- a/java/src/com/android/intentresolver/shortcuts/ShortcutLoader.kt
+++ b/java/src/com/android/intentresolver/shortcuts/ShortcutLoader.kt
@@ -35,14 +35,13 @@ import androidx.annotation.MainThread
import androidx.annotation.OpenForTesting
import androidx.annotation.VisibleForTesting
import androidx.annotation.WorkerThread
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.coroutineScope
import com.android.intentresolver.chooser.DisplayResolveInfo
import com.android.intentresolver.measurements.Tracer
import com.android.intentresolver.measurements.runTracing
import java.util.concurrent.Executor
import java.util.function.Consumer
import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.BufferOverflow
@@ -50,6 +49,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
/**
@@ -58,14 +58,14 @@ import kotlinx.coroutines.launch
* A ShortcutLoader instance can be viewed as a per-profile singleton hot stream of shortcut
* updates. The shortcut loading is triggered in the constructor or by the [reset] method, the
* processing happens on the [dispatcher] and the result is delivered through the [callback] on the
- * default [lifecycle]'s dispatcher, the main thread.
+ * default [scope]'s dispatcher, the main thread.
*/
@OpenForTesting
open class ShortcutLoader
@VisibleForTesting
constructor(
private val context: Context,
- private val lifecycle: Lifecycle,
+ private val scope: CoroutineScope,
private val appPredictor: AppPredictorProxy?,
private val userHandle: UserHandle,
private val isPersonalProfile: Boolean,
@@ -84,19 +84,19 @@ constructor(
private val shortcutSource =
MutableSharedFlow<ShortcutData?>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
private val isDestroyed
- get() = !lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)
+ get() = !scope.isActive
@MainThread
constructor(
context: Context,
- lifecycle: Lifecycle,
+ scope: CoroutineScope,
appPredictor: AppPredictor?,
userHandle: UserHandle,
targetIntentFilter: IntentFilter?,
callback: Consumer<Result>
) : this(
context,
- lifecycle,
+ scope,
appPredictor?.let { AppPredictorProxy(it) },
userHandle,
userHandle == UserHandle.of(ActivityManager.getCurrentUser()),
@@ -107,7 +107,7 @@ constructor(
init {
appPredictor?.registerPredictionUpdates(dispatcher.asExecutor(), appPredictorCallback)
- lifecycle.coroutineScope
+ scope
.launch {
appTargetSource
.combine(shortcutSource) { appTargets, shortcutData ->
@@ -135,13 +135,13 @@ constructor(
reset()
}
- /** Clear application targets (see [updateAppTargets] and initiate shrtcuts loading. */
+ /** Clear application targets (see [updateAppTargets] and initiate shortcuts loading. */
@OpenForTesting
open fun reset() {
Log.d(TAG, "reset shortcut loader for user $userHandle")
appTargetSource.tryEmit(null)
shortcutSource.tryEmit(null)
- lifecycle.coroutineScope.launch(dispatcher) { loadShortcuts() }
+ scope.launch(dispatcher) { loadShortcuts() }
}
/**
diff --git a/java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt b/java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt
index 9b4a8057..43d0df79 100644
--- a/java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt
+++ b/java/tests/src/com/android/intentresolver/shortcuts/ShortcutLoaderTest.kt
@@ -26,8 +26,6 @@ import android.content.pm.PackageManager.ApplicationInfoFlags
import android.content.pm.ShortcutManager
import android.os.UserHandle
import android.os.UserManager
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.test.filters.SmallTest
import com.android.intentresolver.any
import com.android.intentresolver.argumentCaptor
@@ -39,18 +37,15 @@ import com.android.intentresolver.createShortcutInfo
import com.android.intentresolver.mock
import com.android.intentresolver.whenever
import java.util.function.Consumer
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.resetMain
-import kotlinx.coroutines.test.setMain
-import org.junit.After
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
-import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.atLeastOnce
@@ -84,7 +79,7 @@ class ShortcutLoaderTest {
}
private val scheduler = TestCoroutineScheduler()
private val dispatcher = UnconfinedTestDispatcher(scheduler)
- private val lifecycleOwner = TestLifecycleOwner()
+ private val scope = TestScope(dispatcher)
private val intentFilter = mock<IntentFilter>()
private val appPredictor = mock<ShortcutLoader.AppPredictorProxy>()
private val callback = mock<Consumer<ShortcutLoader.Result>>()
@@ -94,135 +89,239 @@ class ShortcutLoaderTest {
private val appTargets = arrayOf(appTarget)
private val matchingShortcutInfo = createShortcutInfo("id-0", componentName, 1)
- @Before
- fun setup() {
- Dispatchers.setMain(dispatcher)
- lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
- }
-
- @After
- fun cleanup() {
- lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
- Dispatchers.resetMain()
- }
-
@Test
- fun test_loadShortcutsWithAppPredictor_resultIntegrity() {
- val testSubject =
- ShortcutLoader(
- context,
- lifecycleOwner.lifecycle,
- appPredictor,
- UserHandle.of(0),
- true,
- intentFilter,
- dispatcher,
- callback
- )
-
- testSubject.updateAppTargets(appTargets)
+ fun test_loadShortcutsWithAppPredictor_resultIntegrity() =
+ scope.runTest {
+ val testSubject =
+ ShortcutLoader(
+ context,
+ backgroundScope,
+ appPredictor,
+ UserHandle.of(0),
+ true,
+ intentFilter,
+ dispatcher,
+ callback
+ )
- val matchingAppTarget = createAppTarget(matchingShortcutInfo)
- val shortcuts =
- listOf(
- matchingAppTarget,
- // an AppTarget that does not belong to any resolved application; should be ignored
- createAppTarget(
- createShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1)
+ testSubject.updateAppTargets(appTargets)
+
+ val matchingAppTarget = createAppTarget(matchingShortcutInfo)
+ val shortcuts =
+ listOf(
+ matchingAppTarget,
+ // an AppTarget that does not belong to any resolved application; should be
+ // ignored
+ createAppTarget(
+ createShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1)
+ )
)
+ val appPredictorCallbackCaptor = argumentCaptor<AppPredictor.Callback>()
+ verify(appPredictor, atLeastOnce())
+ .registerPredictionUpdates(any(), capture(appPredictorCallbackCaptor))
+ appPredictorCallbackCaptor.value.onTargetsAvailable(shortcuts)
+
+ val resultCaptor = argumentCaptor<ShortcutLoader.Result>()
+ verify(callback, times(1)).accept(capture(resultCaptor))
+
+ val result = resultCaptor.value
+ assertTrue("An app predictor result is expected", result.isFromAppPredictor)
+ assertArrayEquals(
+ "Wrong input app targets in the result",
+ appTargets,
+ result.appTargets
)
- val appPredictorCallbackCaptor = argumentCaptor<AppPredictor.Callback>()
- verify(appPredictor, atLeastOnce())
- .registerPredictionUpdates(any(), capture(appPredictorCallbackCaptor))
- appPredictorCallbackCaptor.value.onTargetsAvailable(shortcuts)
-
- val resultCaptor = argumentCaptor<ShortcutLoader.Result>()
- verify(callback, times(1)).accept(capture(resultCaptor))
-
- val result = resultCaptor.value
- assertTrue("An app predictor result is expected", result.isFromAppPredictor)
- assertArrayEquals("Wrong input app targets in the result", appTargets, result.appTargets)
- assertEquals("Wrong shortcut count", 1, result.shortcutsByApp.size)
- assertEquals("Wrong app target", appTarget, result.shortcutsByApp[0].appTarget)
- for (shortcut in result.shortcutsByApp[0].shortcuts) {
- assertEquals(
- "Wrong AppTarget in the cache",
- matchingAppTarget,
- result.directShareAppTargetCache[shortcut]
- )
- assertEquals(
- "Wrong ShortcutInfo in the cache",
- matchingShortcutInfo,
- result.directShareShortcutInfoCache[shortcut]
- )
+ assertEquals("Wrong shortcut count", 1, result.shortcutsByApp.size)
+ assertEquals("Wrong app target", appTarget, result.shortcutsByApp[0].appTarget)
+ for (shortcut in result.shortcutsByApp[0].shortcuts) {
+ assertEquals(
+ "Wrong AppTarget in the cache",
+ matchingAppTarget,
+ result.directShareAppTargetCache[shortcut]
+ )
+ assertEquals(
+ "Wrong ShortcutInfo in the cache",
+ matchingShortcutInfo,
+ result.directShareShortcutInfoCache[shortcut]
+ )
+ }
}
- }
@Test
- fun test_loadShortcutsWithShortcutManager_resultIntegrity() {
- val shortcutManagerResult =
- listOf(
- ShortcutManager.ShareShortcutInfo(matchingShortcutInfo, componentName),
- // mismatching shortcut
- createShareShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1)
+ fun test_loadShortcutsWithShortcutManager_resultIntegrity() =
+ scope.runTest {
+ val shortcutManagerResult =
+ listOf(
+ ShortcutManager.ShareShortcutInfo(matchingShortcutInfo, componentName),
+ // mismatching shortcut
+ createShareShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1)
+ )
+ val shortcutManager =
+ mock<ShortcutManager> {
+ whenever(getShareTargets(intentFilter)).thenReturn(shortcutManagerResult)
+ }
+ whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager)
+ val testSubject =
+ ShortcutLoader(
+ context,
+ backgroundScope,
+ null,
+ UserHandle.of(0),
+ true,
+ intentFilter,
+ dispatcher,
+ callback
+ )
+
+ testSubject.updateAppTargets(appTargets)
+
+ val resultCaptor = argumentCaptor<ShortcutLoader.Result>()
+ verify(callback, times(1)).accept(capture(resultCaptor))
+
+ val result = resultCaptor.value
+ assertFalse("An ShortcutManager result is expected", result.isFromAppPredictor)
+ assertArrayEquals(
+ "Wrong input app targets in the result",
+ appTargets,
+ result.appTargets
)
- val shortcutManager =
- mock<ShortcutManager> {
- whenever(getShareTargets(intentFilter)).thenReturn(shortcutManagerResult)
+ assertEquals("Wrong shortcut count", 1, result.shortcutsByApp.size)
+ assertEquals("Wrong app target", appTarget, result.shortcutsByApp[0].appTarget)
+ for (shortcut in result.shortcutsByApp[0].shortcuts) {
+ assertTrue(
+ "AppTargets are not expected the cache of a ShortcutManager result",
+ result.directShareAppTargetCache.isEmpty()
+ )
+ assertEquals(
+ "Wrong ShortcutInfo in the cache",
+ matchingShortcutInfo,
+ result.directShareShortcutInfoCache[shortcut]
+ )
}
- whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager)
- val testSubject =
- ShortcutLoader(
- context,
- lifecycleOwner.lifecycle,
- null,
- UserHandle.of(0),
- true,
- intentFilter,
- dispatcher,
- callback
- )
+ }
- testSubject.updateAppTargets(appTargets)
+ @Test
+ fun test_appPredictorReturnsEmptyList_fallbackToShortcutManager() =
+ scope.runTest {
+ val shortcutManagerResult =
+ listOf(
+ ShortcutManager.ShareShortcutInfo(matchingShortcutInfo, componentName),
+ // mismatching shortcut
+ createShareShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1)
+ )
+ val shortcutManager =
+ mock<ShortcutManager> {
+ whenever(getShareTargets(intentFilter)).thenReturn(shortcutManagerResult)
+ }
+ whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager)
+ val testSubject =
+ ShortcutLoader(
+ context,
+ backgroundScope,
+ appPredictor,
+ UserHandle.of(0),
+ true,
+ intentFilter,
+ dispatcher,
+ callback
+ )
- val resultCaptor = argumentCaptor<ShortcutLoader.Result>()
- verify(callback, times(1)).accept(capture(resultCaptor))
+ testSubject.updateAppTargets(appTargets)
- val result = resultCaptor.value
- assertFalse("An ShortcutManager result is expected", result.isFromAppPredictor)
- assertArrayEquals("Wrong input app targets in the result", appTargets, result.appTargets)
- assertEquals("Wrong shortcut count", 1, result.shortcutsByApp.size)
- assertEquals("Wrong app target", appTarget, result.shortcutsByApp[0].appTarget)
- for (shortcut in result.shortcutsByApp[0].shortcuts) {
- assertTrue(
- "AppTargets are not expected the cache of a ShortcutManager result",
- result.directShareAppTargetCache.isEmpty()
- )
- assertEquals(
- "Wrong ShortcutInfo in the cache",
- matchingShortcutInfo,
- result.directShareShortcutInfoCache[shortcut]
+ verify(appPredictor, times(1)).requestPredictionUpdate()
+ val appPredictorCallbackCaptor = argumentCaptor<AppPredictor.Callback>()
+ verify(appPredictor, times(1))
+ .registerPredictionUpdates(any(), capture(appPredictorCallbackCaptor))
+ appPredictorCallbackCaptor.value.onTargetsAvailable(emptyList())
+
+ val resultCaptor = argumentCaptor<ShortcutLoader.Result>()
+ verify(callback, times(1)).accept(capture(resultCaptor))
+
+ val result = resultCaptor.value
+ assertFalse("An ShortcutManager result is expected", result.isFromAppPredictor)
+ assertArrayEquals(
+ "Wrong input app targets in the result",
+ appTargets,
+ result.appTargets
)
+ assertEquals("Wrong shortcut count", 1, result.shortcutsByApp.size)
+ assertEquals("Wrong app target", appTarget, result.shortcutsByApp[0].appTarget)
+ for (shortcut in result.shortcutsByApp[0].shortcuts) {
+ assertTrue(
+ "AppTargets are not expected the cache of a ShortcutManager result",
+ result.directShareAppTargetCache.isEmpty()
+ )
+ assertEquals(
+ "Wrong ShortcutInfo in the cache",
+ matchingShortcutInfo,
+ result.directShareShortcutInfoCache[shortcut]
+ )
+ }
}
- }
@Test
- fun test_appPredictorReturnsEmptyList_fallbackToShortcutManager() {
- val shortcutManagerResult =
- listOf(
- ShortcutManager.ShareShortcutInfo(matchingShortcutInfo, componentName),
- // mismatching shortcut
- createShareShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1)
+ fun test_appPredictor_requestPredictionUpdateFailure_fallbackToShortcutManager() =
+ scope.runTest {
+ val shortcutManagerResult =
+ listOf(
+ ShortcutManager.ShareShortcutInfo(matchingShortcutInfo, componentName),
+ // mismatching shortcut
+ createShareShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1)
+ )
+ val shortcutManager =
+ mock<ShortcutManager> {
+ whenever(getShareTargets(intentFilter)).thenReturn(shortcutManagerResult)
+ }
+ whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager)
+ whenever(appPredictor.requestPredictionUpdate())
+ .thenThrow(IllegalStateException("Test exception"))
+ val testSubject =
+ ShortcutLoader(
+ context,
+ backgroundScope,
+ appPredictor,
+ UserHandle.of(0),
+ true,
+ intentFilter,
+ dispatcher,
+ callback
+ )
+
+ testSubject.updateAppTargets(appTargets)
+
+ verify(appPredictor, times(1)).requestPredictionUpdate()
+
+ val resultCaptor = argumentCaptor<ShortcutLoader.Result>()
+ verify(callback, times(1)).accept(capture(resultCaptor))
+
+ val result = resultCaptor.value
+ assertFalse("An ShortcutManager result is expected", result.isFromAppPredictor)
+ assertArrayEquals(
+ "Wrong input app targets in the result",
+ appTargets,
+ result.appTargets
)
- val shortcutManager =
- mock<ShortcutManager> {
- whenever(getShareTargets(intentFilter)).thenReturn(shortcutManagerResult)
+ assertEquals("Wrong shortcut count", 1, result.shortcutsByApp.size)
+ assertEquals("Wrong app target", appTarget, result.shortcutsByApp[0].appTarget)
+ for (shortcut in result.shortcutsByApp[0].shortcuts) {
+ assertTrue(
+ "AppTargets are not expected the cache of a ShortcutManager result",
+ result.directShareAppTargetCache.isEmpty()
+ )
+ assertEquals(
+ "Wrong ShortcutInfo in the cache",
+ matchingShortcutInfo,
+ result.directShareShortcutInfoCache[shortcut]
+ )
}
- whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager)
- val testSubject =
+ }
+
+ @Test
+ fun test_ShortcutLoader_shortcutsRequestedIndependentlyFromAppTargets() =
+ scope.runTest {
ShortcutLoader(
context,
- lifecycleOwner.lifecycle,
+ backgroundScope,
appPredictor,
UserHandle.of(0),
true,
@@ -231,122 +330,57 @@ class ShortcutLoaderTest {
callback
)
- testSubject.updateAppTargets(appTargets)
-
- verify(appPredictor, times(1)).requestPredictionUpdate()
- val appPredictorCallbackCaptor = argumentCaptor<AppPredictor.Callback>()
- verify(appPredictor, times(1))
- .registerPredictionUpdates(any(), capture(appPredictorCallbackCaptor))
- appPredictorCallbackCaptor.value.onTargetsAvailable(emptyList())
-
- val resultCaptor = argumentCaptor<ShortcutLoader.Result>()
- verify(callback, times(1)).accept(capture(resultCaptor))
-
- val result = resultCaptor.value
- assertFalse("An ShortcutManager result is expected", result.isFromAppPredictor)
- assertArrayEquals("Wrong input app targets in the result", appTargets, result.appTargets)
- assertEquals("Wrong shortcut count", 1, result.shortcutsByApp.size)
- assertEquals("Wrong app target", appTarget, result.shortcutsByApp[0].appTarget)
- for (shortcut in result.shortcutsByApp[0].shortcuts) {
- assertTrue(
- "AppTargets are not expected the cache of a ShortcutManager result",
- result.directShareAppTargetCache.isEmpty()
- )
- assertEquals(
- "Wrong ShortcutInfo in the cache",
- matchingShortcutInfo,
- result.directShareShortcutInfoCache[shortcut]
- )
+ verify(appPredictor, times(1)).requestPredictionUpdate()
+ verify(callback, never()).accept(any())
}
- }
@Test
- fun test_appPredictor_requestPredictionUpdateFailure_fallbackToShortcutManager() {
- val shortcutManagerResult =
- listOf(
- ShortcutManager.ShareShortcutInfo(matchingShortcutInfo, componentName),
- // mismatching shortcut
- createShareShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1)
- )
- val shortcutManager =
- mock<ShortcutManager> {
- whenever(getShareTargets(intentFilter)).thenReturn(shortcutManagerResult)
- }
- whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager)
- whenever(appPredictor.requestPredictionUpdate())
- .thenThrow(IllegalStateException("Test exception"))
- val testSubject =
- ShortcutLoader(
- context,
- lifecycleOwner.lifecycle,
- appPredictor,
- UserHandle.of(0),
- true,
- intentFilter,
- dispatcher,
- callback
- )
+ fun test_ShortcutLoader_noResultsWithoutAppTargets() =
+ scope.runTest {
+ val shortcutManagerResult =
+ listOf(
+ ShortcutManager.ShareShortcutInfo(matchingShortcutInfo, componentName),
+ // mismatching shortcut
+ createShareShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1)
+ )
+ val shortcutManager =
+ mock<ShortcutManager> {
+ whenever(getShareTargets(intentFilter)).thenReturn(shortcutManagerResult)
+ }
+ whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager)
+ val testSubject =
+ ShortcutLoader(
+ context,
+ backgroundScope,
+ null,
+ UserHandle.of(0),
+ true,
+ intentFilter,
+ dispatcher,
+ callback
+ )
- testSubject.updateAppTargets(appTargets)
+ verify(shortcutManager, times(1)).getShareTargets(any())
+ verify(callback, never()).accept(any())
- verify(appPredictor, times(1)).requestPredictionUpdate()
+ testSubject.reset()
- val resultCaptor = argumentCaptor<ShortcutLoader.Result>()
- verify(callback, times(1)).accept(capture(resultCaptor))
+ verify(shortcutManager, times(2)).getShareTargets(any())
+ verify(callback, never()).accept(any())
- val result = resultCaptor.value
- assertFalse("An ShortcutManager result is expected", result.isFromAppPredictor)
- assertArrayEquals("Wrong input app targets in the result", appTargets, result.appTargets)
- assertEquals("Wrong shortcut count", 1, result.shortcutsByApp.size)
- assertEquals("Wrong app target", appTarget, result.shortcutsByApp[0].appTarget)
- for (shortcut in result.shortcutsByApp[0].shortcuts) {
- assertTrue(
- "AppTargets are not expected the cache of a ShortcutManager result",
- result.directShareAppTargetCache.isEmpty()
- )
- assertEquals(
- "Wrong ShortcutInfo in the cache",
- matchingShortcutInfo,
- result.directShareShortcutInfoCache[shortcut]
- )
- }
- }
+ testSubject.updateAppTargets(appTargets)
- @Test
- fun test_ShortcutLoader_shortcutsRequestedIndependentlyFromAppTargets() {
- ShortcutLoader(
- context,
- lifecycleOwner.lifecycle,
- appPredictor,
- UserHandle.of(0),
- true,
- intentFilter,
- dispatcher,
- callback
- )
-
- verify(appPredictor, times(1)).requestPredictionUpdate()
- verify(callback, never()).accept(any())
- }
+ verify(shortcutManager, times(2)).getShareTargets(any())
+ verify(callback, times(1)).accept(any())
+ }
@Test
- fun test_ShortcutLoader_noResultsWithoutAppTargets() {
- val shortcutManagerResult =
- listOf(
- ShortcutManager.ShareShortcutInfo(matchingShortcutInfo, componentName),
- // mismatching shortcut
- createShareShortcutInfo("id-1", ComponentName("mismatching.pkg", "Class"), 1)
- )
- val shortcutManager =
- mock<ShortcutManager> {
- whenever(getShareTargets(intentFilter)).thenReturn(shortcutManagerResult)
- }
- whenever(context.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(shortcutManager)
- val testSubject =
+ fun test_OnScopeCancellation_unsubscribeFromAppPredictor() {
+ scope.runTest {
ShortcutLoader(
context,
- lifecycleOwner.lifecycle,
- null,
+ backgroundScope,
+ appPredictor,
UserHandle.of(0),
true,
intentFilter,
@@ -354,36 +388,8 @@ class ShortcutLoaderTest {
callback
)
- verify(shortcutManager, times(1)).getShareTargets(any())
- verify(callback, never()).accept(any())
-
- testSubject.reset()
-
- verify(shortcutManager, times(2)).getShareTargets(any())
- verify(callback, never()).accept(any())
-
- testSubject.updateAppTargets(appTargets)
-
- verify(shortcutManager, times(2)).getShareTargets(any())
- verify(callback, times(1)).accept(any())
- }
-
- @Test
- fun test_OnLifecycleDestroyed_unsubscribeFromAppPredictor() {
- ShortcutLoader(
- context,
- lifecycleOwner.lifecycle,
- appPredictor,
- UserHandle.of(0),
- true,
- intentFilter,
- dispatcher,
- callback
- )
-
- verify(appPredictor, never()).unregisterPredictionUpdates(any())
-
- lifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+ verify(appPredictor, never()).unregisterPredictionUpdates(any())
+ }
verify(appPredictor, times(1)).unregisterPredictionUpdates(any())
}
@@ -422,61 +428,63 @@ class ShortcutLoaderTest {
isUserRunning: Boolean = true,
isUserUnlocked: Boolean = true,
isQuietModeEnabled: Boolean = false
- ) {
- val userHandle = UserHandle.of(10)
- with(userManager) {
- whenever(isUserRunning(userHandle)).thenReturn(isUserRunning)
- whenever(isUserUnlocked(userHandle)).thenReturn(isUserUnlocked)
- whenever(isQuietModeEnabled(userHandle)).thenReturn(isQuietModeEnabled)
- }
- whenever(context.getSystemService(Context.USER_SERVICE)).thenReturn(userManager)
- val appPredictor = mock<ShortcutLoader.AppPredictorProxy>()
- val callback = mock<Consumer<ShortcutLoader.Result>>()
- val testSubject =
- ShortcutLoader(
- context,
- lifecycleOwner.lifecycle,
- appPredictor,
- userHandle,
- false,
- intentFilter,
- dispatcher,
- callback
- )
+ ) =
+ scope.runTest {
+ val userHandle = UserHandle.of(10)
+ with(userManager) {
+ whenever(isUserRunning(userHandle)).thenReturn(isUserRunning)
+ whenever(isUserUnlocked(userHandle)).thenReturn(isUserUnlocked)
+ whenever(isQuietModeEnabled(userHandle)).thenReturn(isQuietModeEnabled)
+ }
+ whenever(context.getSystemService(Context.USER_SERVICE)).thenReturn(userManager)
+ val appPredictor = mock<ShortcutLoader.AppPredictorProxy>()
+ val callback = mock<Consumer<ShortcutLoader.Result>>()
+ val testSubject =
+ ShortcutLoader(
+ context,
+ backgroundScope,
+ appPredictor,
+ userHandle,
+ false,
+ intentFilter,
+ dispatcher,
+ callback
+ )
- testSubject.updateAppTargets(arrayOf<DisplayResolveInfo>(mock()))
+ testSubject.updateAppTargets(arrayOf<DisplayResolveInfo>(mock()))
- verify(appPredictor, never()).requestPredictionUpdate()
- }
+ verify(appPredictor, never()).requestPredictionUpdate()
+ }
private fun testAlwaysCallSystemForMainProfile(
isUserRunning: Boolean = true,
isUserUnlocked: Boolean = true,
isQuietModeEnabled: Boolean = false
- ) {
- val userHandle = UserHandle.of(10)
- with(userManager) {
- whenever(isUserRunning(userHandle)).thenReturn(isUserRunning)
- whenever(isUserUnlocked(userHandle)).thenReturn(isUserUnlocked)
- whenever(isQuietModeEnabled(userHandle)).thenReturn(isQuietModeEnabled)
- }
- whenever(context.getSystemService(Context.USER_SERVICE)).thenReturn(userManager)
- val appPredictor = mock<ShortcutLoader.AppPredictorProxy>()
- val callback = mock<Consumer<ShortcutLoader.Result>>()
- val testSubject =
- ShortcutLoader(
- context,
- lifecycleOwner.lifecycle,
- appPredictor,
- userHandle,
- true,
- intentFilter,
- dispatcher,
- callback
- )
+ ) =
+ scope.runTest {
+ val userHandle = UserHandle.of(10)
+ with(userManager) {
+ whenever(isUserRunning(userHandle)).thenReturn(isUserRunning)
+ whenever(isUserUnlocked(userHandle)).thenReturn(isUserUnlocked)
+ whenever(isQuietModeEnabled(userHandle)).thenReturn(isQuietModeEnabled)
+ }
+ whenever(context.getSystemService(Context.USER_SERVICE)).thenReturn(userManager)
+ val appPredictor = mock<ShortcutLoader.AppPredictorProxy>()
+ val callback = mock<Consumer<ShortcutLoader.Result>>()
+ val testSubject =
+ ShortcutLoader(
+ context,
+ backgroundScope,
+ appPredictor,
+ userHandle,
+ true,
+ intentFilter,
+ dispatcher,
+ callback
+ )
- testSubject.updateAppTargets(arrayOf<DisplayResolveInfo>(mock()))
+ testSubject.updateAppTargets(arrayOf<DisplayResolveInfo>(mock()))
- verify(appPredictor, times(1)).requestPredictionUpdate()
- }
+ verify(appPredictor, times(1)).requestPredictionUpdate()
+ }
}