Merge "[Partial Screensharing] Implement loading of recent tasks, thumbnails and icons" into tm-qpr-dev

This commit is contained in:
Nick Chameyev
2022-09-28 09:46:56 +00:00
committed by Android (Google) Code Review
11 changed files with 270 additions and 39 deletions

View File

@@ -484,12 +484,14 @@ public abstract class WMShellBaseModule {
ShellInit shellInit, ShellInit shellInit,
ShellCommandHandler shellCommandHandler, ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener, TaskStackListenerImpl taskStackListener,
ActivityTaskManager activityTaskManager,
Optional<DesktopModeTaskRepository> desktopModeTaskRepository, Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
@ShellMainThread ShellExecutor mainExecutor @ShellMainThread ShellExecutor mainExecutor
) { ) {
return Optional.ofNullable( return Optional.ofNullable(
RecentTasksController.create(context, shellInit, shellCommandHandler, RecentTasksController.create(context, shellInit, shellCommandHandler,
taskStackListener, desktopModeTaskRepository, mainExecutor)); taskStackListener, activityTaskManager, desktopModeTaskRepository,
mainExecutor));
} }
// //

View File

@@ -17,6 +17,11 @@
package com.android.wm.shell.recents; package com.android.wm.shell.recents;
import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/** /**
* Interface for interacting with the recent tasks. * Interface for interacting with the recent tasks.
@@ -29,4 +34,11 @@ public interface RecentTasks {
default IRecentTasks createExternalInterface() { default IRecentTasks createExternalInterface() {
return null; return null;
} }
/**
* Gets the set of recent tasks.
*/
default void getRecentTasks(int maxNum, int flags, int userId, Executor callbackExecutor,
Consumer<List<GroupedRecentTaskInfo>> callback) {
}
} }

View File

@@ -58,6 +58,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/** /**
* Manages the recent task list from the system, caching it as necessary. * Manages the recent task list from the system, caching it as necessary.
@@ -72,6 +74,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
private final ShellExecutor mMainExecutor; private final ShellExecutor mMainExecutor;
private final TaskStackListenerImpl mTaskStackListener; private final TaskStackListenerImpl mTaskStackListener;
private final RecentTasks mImpl = new RecentTasksImpl(); private final RecentTasks mImpl = new RecentTasksImpl();
private final ActivityTaskManager mActivityTaskManager;
private IRecentTasksListener mListener; private IRecentTasksListener mListener;
private final boolean mIsDesktopMode; private final boolean mIsDesktopMode;
@@ -96,6 +99,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
ShellInit shellInit, ShellInit shellInit,
ShellCommandHandler shellCommandHandler, ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener, TaskStackListenerImpl taskStackListener,
ActivityTaskManager activityTaskManager,
Optional<DesktopModeTaskRepository> desktopModeTaskRepository, Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
@ShellMainThread ShellExecutor mainExecutor @ShellMainThread ShellExecutor mainExecutor
) { ) {
@@ -103,17 +107,19 @@ public class RecentTasksController implements TaskStackListenerCallback,
return null; return null;
} }
return new RecentTasksController(context, shellInit, shellCommandHandler, taskStackListener, return new RecentTasksController(context, shellInit, shellCommandHandler, taskStackListener,
desktopModeTaskRepository, mainExecutor); activityTaskManager, desktopModeTaskRepository, mainExecutor);
} }
RecentTasksController(Context context, RecentTasksController(Context context,
ShellInit shellInit, ShellInit shellInit,
ShellCommandHandler shellCommandHandler, ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener, TaskStackListenerImpl taskStackListener,
ActivityTaskManager activityTaskManager,
Optional<DesktopModeTaskRepository> desktopModeTaskRepository, Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
ShellExecutor mainExecutor) { ShellExecutor mainExecutor) {
mContext = context; mContext = context;
mShellCommandHandler = shellCommandHandler; mShellCommandHandler = shellCommandHandler;
mActivityTaskManager = activityTaskManager;
mIsDesktopMode = mContext.getPackageManager().hasSystemFeature(FEATURE_PC); mIsDesktopMode = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
mTaskStackListener = taskStackListener; mTaskStackListener = taskStackListener;
mDesktopModeTaskRepository = desktopModeTaskRepository; mDesktopModeTaskRepository = desktopModeTaskRepository;
@@ -269,16 +275,11 @@ public class RecentTasksController implements TaskStackListenerCallback,
mListener = null; mListener = null;
} }
@VisibleForTesting
List<ActivityManager.RecentTaskInfo> getRawRecentTasks(int maxNum, int flags, int userId) {
return ActivityTaskManager.getInstance().getRecentTasks(maxNum, flags, userId);
}
@VisibleForTesting @VisibleForTesting
ArrayList<GroupedRecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) { ArrayList<GroupedRecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) {
// Note: the returned task list is from the most-recent to least-recent order // Note: the returned task list is from the most-recent to least-recent order
final List<ActivityManager.RecentTaskInfo> rawList = getRawRecentTasks(maxNum, flags, final List<ActivityManager.RecentTaskInfo> rawList = mActivityTaskManager.getRecentTasks(
userId); maxNum, flags, userId);
// Make a mapping of task id -> task info // Make a mapping of task id -> task info
final SparseArray<ActivityManager.RecentTaskInfo> rawMapping = new SparseArray<>(); final SparseArray<ActivityManager.RecentTaskInfo> rawMapping = new SparseArray<>();
@@ -335,8 +336,9 @@ public class RecentTasksController implements TaskStackListenerCallback,
if (componentName == null) { if (componentName == null) {
return null; return null;
} }
List<ActivityManager.RecentTaskInfo> tasks = getRawRecentTasks(Integer.MAX_VALUE, List<ActivityManager.RecentTaskInfo> tasks = mActivityTaskManager.getRecentTasks(
ActivityManager.RECENT_IGNORE_UNAVAILABLE, ActivityManager.getCurrentUser()); Integer.MAX_VALUE, ActivityManager.RECENT_IGNORE_UNAVAILABLE,
ActivityManager.getCurrentUser());
for (int i = 0; i < tasks.size(); i++) { for (int i = 0; i < tasks.size(); i++) {
final ActivityManager.RecentTaskInfo task = tasks.get(i); final ActivityManager.RecentTaskInfo task = tasks.get(i);
if (task.isVisible) { if (task.isVisible) {
@@ -374,6 +376,16 @@ public class RecentTasksController implements TaskStackListenerCallback,
mIRecentTasks = new IRecentTasksImpl(RecentTasksController.this); mIRecentTasks = new IRecentTasksImpl(RecentTasksController.this);
return mIRecentTasks; return mIRecentTasks;
} }
@Override
public void getRecentTasks(int maxNum, int flags, int userId, Executor executor,
Consumer<List<GroupedRecentTaskInfo>> callback) {
mMainExecutor.execute(() -> {
List<GroupedRecentTaskInfo> tasks =
RecentTasksController.this.getRecentTasks(maxNum, flags, userId);
executor.execute(() -> callback.accept(tasks));
});
}
} }

View File

@@ -40,6 +40,7 @@ import static org.mockito.Mockito.when;
import static java.lang.Integer.MAX_VALUE; import static java.lang.Integer.MAX_VALUE;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Rect; import android.graphics.Rect;
@@ -52,7 +53,6 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
@@ -68,7 +68,9 @@ import org.mockito.Mock;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Consumer;
/** /**
* Tests for {@link RecentTasksController}. * Tests for {@link RecentTasksController}.
@@ -85,11 +87,13 @@ public class RecentTasksControllerTest extends ShellTestCase {
private ShellCommandHandler mShellCommandHandler; private ShellCommandHandler mShellCommandHandler;
@Mock @Mock
private DesktopModeTaskRepository mDesktopModeTaskRepository; private DesktopModeTaskRepository mDesktopModeTaskRepository;
@Mock
private ActivityTaskManager mActivityTaskManager;
private ShellTaskOrganizer mShellTaskOrganizer; private ShellTaskOrganizer mShellTaskOrganizer;
private RecentTasksController mRecentTasksController; private RecentTasksController mRecentTasksController;
private ShellInit mShellInit; private ShellInit mShellInit;
private ShellExecutor mMainExecutor; private TestShellExecutor mMainExecutor;
@Before @Before
public void setUp() { public void setUp() {
@@ -97,8 +101,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class)); when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
mShellInit = spy(new ShellInit(mMainExecutor)); mShellInit = spy(new ShellInit(mMainExecutor));
mRecentTasksController = spy(new RecentTasksController(mContext, mShellInit, mRecentTasksController = spy(new RecentTasksController(mContext, mShellInit,
mShellCommandHandler, mTaskStackListener, Optional.of(mDesktopModeTaskRepository), mShellCommandHandler, mTaskStackListener, mActivityTaskManager,
mMainExecutor)); Optional.of(mDesktopModeTaskRepository), mMainExecutor));
mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler, mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController), null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController),
mMainExecutor); mMainExecutor);
@@ -187,6 +191,37 @@ public class RecentTasksControllerTest extends ShellTestCase {
t6.taskId, -1); t6.taskId, -1);
} }
@Test
public void testGetRecentTasks_ReturnsRecentTasksAsynchronously() {
@SuppressWarnings("unchecked")
final List<GroupedRecentTaskInfo>[] recentTasks = new List[1];
Consumer<List<GroupedRecentTaskInfo>> consumer = argument -> recentTasks[0] = argument;
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
ActivityManager.RecentTaskInfo t5 = makeTaskInfo(5);
ActivityManager.RecentTaskInfo t6 = makeTaskInfo(6);
setRawList(t1, t2, t3, t4, t5, t6);
// Mark a couple pairs [t2, t4], [t3, t5]
SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 4);
SplitBounds pair2Bounds = new SplitBounds(new Rect(), new Rect(), 3, 5);
mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds);
mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);
mRecentTasksController.asRecentTasks()
.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0, Runnable::run, consumer);
mMainExecutor.flushAll();
assertGroupedTasksListEquals(recentTasks[0],
t1.taskId, -1,
t2.taskId, t4.taskId,
t3.taskId, t5.taskId,
t6.taskId, -1);
}
@Test @Test
public void testGetRecentTasks_groupActiveFreeformTasks() { public void testGetRecentTasks_groupActiveFreeformTasks() {
StaticMockitoSession mockitoSession = mockitoSession().mockStatic( StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
@@ -296,7 +331,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
for (ActivityManager.RecentTaskInfo task : tasks) { for (ActivityManager.RecentTaskInfo task : tasks) {
rawList.add(task); rawList.add(task);
} }
doReturn(rawList).when(mRecentTasksController).getRawRecentTasks(anyInt(), anyInt(), doReturn(rawList).when(mActivityTaskManager).getRecentTasks(anyInt(), anyInt(),
anyInt()); anyInt());
return rawList; return rawList;
} }
@@ -307,7 +342,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
* @param expectedTaskIds list of task ids that map to the flattened task ids of the tasks in * @param expectedTaskIds list of task ids that map to the flattened task ids of the tasks in
* the grouped task list * the grouped task list
*/ */
private void assertGroupedTasksListEquals(ArrayList<GroupedRecentTaskInfo> recentTasks, private void assertGroupedTasksListEquals(List<GroupedRecentTaskInfo> recentTasks,
int... expectedTaskIds) { int... expectedTaskIds) {
int[] flattenedTaskIds = new int[recentTasks.size() * 2]; int[] flattenedTaskIds = new int[recentTasks.size() * 2];
for (int i = 0; i < recentTasks.size(); i++) { for (int i = 0; i < recentTasks.size(); i++) {

View File

@@ -42,7 +42,7 @@ class MediaProjectionAppSelectorController(
} }
private fun List<RecentTask>.sortTasks(): List<RecentTask> = private fun List<RecentTask>.sortTasks(): List<RecentTask> =
asReversed().sortedBy { sortedBy {
// Show normal tasks first and only then tasks with opened app selector // Show normal tasks first and only then tasks with opened app selector
it.topActivityComponent == appSelectorComponentName it.topActivityComponent == appSelectorComponentName
} }

View File

@@ -17,11 +17,17 @@
package com.android.systemui.mediaprojection.appselector.data package com.android.systemui.mediaprojection.appselector.data
import android.content.ComponentName import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.PackageManager.ComponentInfoFlags
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.UserHandle
import com.android.launcher3.icons.BaseIconFactory.IconOptions
import com.android.launcher3.icons.IconFactory
import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Background
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import javax.inject.Inject
interface AppIconLoader { interface AppIconLoader {
suspend fun loadIcon(userId: Int, component: ComponentName): Drawable? suspend fun loadIcon(userId: Int, component: ComponentName): Drawable?
@@ -31,11 +37,20 @@ class IconLoaderLibAppIconLoader
@Inject @Inject
constructor( constructor(
@Background private val backgroundDispatcher: CoroutineDispatcher, @Background private val backgroundDispatcher: CoroutineDispatcher,
private val context: Context,
private val packageManager: PackageManager
) : AppIconLoader { ) : AppIconLoader {
override suspend fun loadIcon(userId: Int, component: ComponentName): Drawable? = override suspend fun loadIcon(userId: Int, component: ComponentName): Drawable? =
withContext(backgroundDispatcher) { withContext(backgroundDispatcher) {
// TODO(b/240924731): add a blocking call to load an icon using iconloaderlib IconFactory.obtain(context).use<IconFactory, Drawable?> { iconFactory ->
null val activityInfo = packageManager
.getActivityInfo(component, ComponentInfoFlags.of(0))
val icon = activityInfo.loadIcon(packageManager) ?: return@withContext null
val userHandler = UserHandle.of(userId)
val options = IconOptions().apply { setUser(userHandler) }
val badgedIcon = iconFactory.createBadgedIconBitmap(icon, options)
badgedIcon.newIcon(context)
}
} }
} }

View File

@@ -16,11 +16,13 @@
package com.android.systemui.mediaprojection.appselector.data package com.android.systemui.mediaprojection.appselector.data
import android.annotation.ColorInt
import android.content.ComponentName import android.content.ComponentName
data class RecentTask( data class RecentTask(
val taskId: Int, val taskId: Int,
val userId: Int, val userId: Int,
val topActivityComponent: ComponentName?, val topActivityComponent: ComponentName?,
val baseIntentComponent: ComponentName? val baseIntentComponent: ComponentName?,
@ColorInt val colorBackground: Int?
) )

View File

@@ -16,23 +16,61 @@
package com.android.systemui.mediaprojection.appselector.data package com.android.systemui.mediaprojection.appselector.data
import android.app.ActivityManager
import android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE
import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.util.kotlin.getOrNull
import com.android.wm.shell.recents.RecentTasks
import com.android.wm.shell.util.GroupedRecentTaskInfo
import java.util.Optional
import javax.inject.Inject
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import javax.inject.Inject import java.util.concurrent.Executor
interface RecentTaskListProvider { interface RecentTaskListProvider {
/** Loads recent tasks, the returned task list is from the most-recent to least-recent order */
suspend fun loadRecentTasks(): List<RecentTask> suspend fun loadRecentTasks(): List<RecentTask>
} }
class ShellRecentTaskListProvider class ShellRecentTaskListProvider
@Inject @Inject
constructor(@Background private val coroutineDispatcher: CoroutineDispatcher) : constructor(
RecentTaskListProvider { @Background private val coroutineDispatcher: CoroutineDispatcher,
@Background private val backgroundExecutor: Executor,
private val recentTasks: Optional<RecentTasks>
) : RecentTaskListProvider {
private val recents by lazy { recentTasks.getOrNull() }
override suspend fun loadRecentTasks(): List<RecentTask> = override suspend fun loadRecentTasks(): List<RecentTask> =
withContext(coroutineDispatcher) { withContext(coroutineDispatcher) {
// TODO(b/240924731): add blocking call to load the recents val rawRecentTasks: List<GroupedRecentTaskInfo> = recents?.getTasks() ?: emptyList()
emptyList()
rawRecentTasks
.flatMap { listOfNotNull(it.taskInfo1, it.taskInfo2) }
.map {
RecentTask(
it.taskId,
it.userId,
it.topActivity,
it.baseIntent?.component,
it.taskDescription?.backgroundColor
)
}
}
private suspend fun RecentTasks.getTasks(): List<GroupedRecentTaskInfo> =
suspendCoroutine { continuation ->
getRecentTasks(
Integer.MAX_VALUE,
RECENT_IGNORE_UNAVAILABLE,
ActivityManager.getCurrentUser(),
backgroundExecutor
) { tasks ->
continuation.resume(tasks)
}
} }
} }

View File

@@ -18,6 +18,7 @@ package com.android.systemui.mediaprojection.appselector.data
import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.shared.recents.model.ThumbnailData import com.android.systemui.shared.recents.model.ThumbnailData
import com.android.systemui.shared.system.ActivityManagerWrapper
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@@ -30,12 +31,13 @@ class ActivityTaskManagerThumbnailLoader
@Inject @Inject
constructor( constructor(
@Background private val coroutineDispatcher: CoroutineDispatcher, @Background private val coroutineDispatcher: CoroutineDispatcher,
) : private val activityManager: ActivityManagerWrapper
RecentTaskThumbnailLoader { ) : RecentTaskThumbnailLoader {
override suspend fun loadThumbnail(taskId: Int): ThumbnailData? = override suspend fun loadThumbnail(taskId: Int): ThumbnailData? =
withContext(coroutineDispatcher) { withContext(coroutineDispatcher) {
// TODO(b/240924731): add blocking call to load a thumbnail val thumbnailData =
null activityManager.getTaskThumbnail(taskId, /* isLowResolution= */ false)
if (thumbnailData.thumbnail == null) null else thumbnailData
} }
} }

View File

@@ -54,7 +54,7 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
} }
@Test @Test
fun initMultipleRecentTasksWithoutAppSelectorTask_bindsListInReverse() { fun initMultipleRecentTasksWithoutAppSelectorTask_bindsListInTheSameOrder() {
val tasks = listOf( val tasks = listOf(
createRecentTask(taskId = 1), createRecentTask(taskId = 1),
createRecentTask(taskId = 2), createRecentTask(taskId = 2),
@@ -66,15 +66,15 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
verify(view).bind( verify(view).bind(
listOf( listOf(
createRecentTask(taskId = 3),
createRecentTask(taskId = 2),
createRecentTask(taskId = 1), createRecentTask(taskId = 1),
createRecentTask(taskId = 2),
createRecentTask(taskId = 3),
) )
) )
} }
@Test @Test
fun initRecentTasksWithAppSelectorTasks_bindsListInReverseAndAppSelectorTasksAtTheEnd() { fun initRecentTasksWithAppSelectorTasks_bindsAppSelectorTasksAtTheEnd() {
val tasks = listOf( val tasks = listOf(
createRecentTask(taskId = 1), createRecentTask(taskId = 1),
createRecentTask(taskId = 2, topActivityComponent = appSelectorComponentName), createRecentTask(taskId = 2, topActivityComponent = appSelectorComponentName),
@@ -88,11 +88,11 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
verify(view).bind( verify(view).bind(
listOf( listOf(
createRecentTask(taskId = 5),
createRecentTask(taskId = 3),
createRecentTask(taskId = 1), createRecentTask(taskId = 1),
createRecentTask(taskId = 4, topActivityComponent = appSelectorComponentName), createRecentTask(taskId = 3),
createRecentTask(taskId = 5),
createRecentTask(taskId = 2, topActivityComponent = appSelectorComponentName), createRecentTask(taskId = 2, topActivityComponent = appSelectorComponentName),
createRecentTask(taskId = 4, topActivityComponent = appSelectorComponentName),
) )
) )
} }
@@ -105,7 +105,8 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
taskId = taskId, taskId = taskId,
topActivityComponent = topActivityComponent, topActivityComponent = topActivityComponent,
baseIntentComponent = ComponentName("com", "Test"), baseIntentComponent = ComponentName("com", "Test"),
userId = 0 userId = 0,
colorBackground = 0
) )
} }

View File

@@ -0,0 +1,112 @@
package com.android.systemui.mediaprojection.appselector.data
import android.app.ActivityManager.RecentTaskInfo
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.wm.shell.recents.RecentTasks
import com.android.wm.shell.util.GroupedRecentTaskInfo
import com.google.common.truth.Truth.assertThat
import java.util.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.junit.Test
import org.junit.runner.RunWith
import java.util.function.Consumer
@RunWith(AndroidTestingRunner::class)
@SmallTest
class ShellRecentTaskListProviderTest : SysuiTestCase() {
private val dispatcher = Dispatchers.Unconfined
private val recentTasks: RecentTasks = mock()
private val recentTaskListProvider =
ShellRecentTaskListProvider(dispatcher, Runnable::run, Optional.of(recentTasks))
@Test
fun loadRecentTasks_oneTask_returnsTheSameTask() {
givenRecentTasks(createSingleTask(taskId = 1))
val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
assertThat(result).containsExactly(createRecentTask(taskId = 1))
}
@Test
fun loadRecentTasks_multipleTasks_returnsTheSameTasks() {
givenRecentTasks(
createSingleTask(taskId = 1),
createSingleTask(taskId = 2),
createSingleTask(taskId = 3),
)
val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
assertThat(result)
.containsExactly(
createRecentTask(taskId = 1),
createRecentTask(taskId = 2),
createRecentTask(taskId = 3),
)
}
@Test
fun loadRecentTasks_groupedTask_returnsUngroupedTasks() {
givenRecentTasks(createTaskPair(taskId1 = 1, taskId2 = 2))
val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
assertThat(result)
.containsExactly(createRecentTask(taskId = 1), createRecentTask(taskId = 2))
}
@Test
fun loadRecentTasks_mixedSingleAndGroupedTask_returnsUngroupedTasks() {
givenRecentTasks(
createSingleTask(taskId = 1),
createTaskPair(taskId1 = 2, taskId2 = 3),
createSingleTask(taskId = 4),
createTaskPair(taskId1 = 5, taskId2 = 6),
)
val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
assertThat(result)
.containsExactly(
createRecentTask(taskId = 1),
createRecentTask(taskId = 2),
createRecentTask(taskId = 3),
createRecentTask(taskId = 4),
createRecentTask(taskId = 5),
createRecentTask(taskId = 6),
)
}
@Suppress("UNCHECKED_CAST")
private fun givenRecentTasks(vararg tasks: GroupedRecentTaskInfo) {
whenever(recentTasks.getRecentTasks(any(), any(), any(), any(), any())).thenAnswer {
val consumer = it.arguments.last() as Consumer<List<GroupedRecentTaskInfo>>
consumer.accept(tasks.toList())
}
}
private fun createRecentTask(taskId: Int): RecentTask =
RecentTask(
taskId = taskId,
userId = 0,
topActivityComponent = null,
baseIntentComponent = null,
colorBackground = null
)
private fun createSingleTask(taskId: Int): GroupedRecentTaskInfo =
GroupedRecentTaskInfo.forSingleTask(createTaskInfo(taskId))
private fun createTaskPair(taskId1: Int, taskId2: Int): GroupedRecentTaskInfo =
GroupedRecentTaskInfo.forSplitTasks(createTaskInfo(taskId1), createTaskInfo(taskId2), null)
private fun createTaskInfo(taskId: Int) = RecentTaskInfo().apply { this.taskId = taskId }
}