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

Commit bb7de483 authored by Govinda Wasserman's avatar Govinda Wasserman
Browse files

Adds recent task repository

Test: atest
SystemUiRoboTests: com.android.systemui.screencapture.common.data.repository.ScreenCaptureRecentTaskRepositoryImplTest
BUG: 398757866
Flag: com.android.systemui.new_screen_record_toolbar
Change-Id: Ib6085f89166acd96d482e6000509629af86228c1
parent 5def47df
Loading
Loading
Loading
Loading
+133 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.screencapture.common.data.repository

import android.annotation.SuppressLint
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.backgroundScope
import com.android.systemui.kosmos.currentValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
import com.android.systemui.shared.system.taskStackChangeListeners
import com.android.systemui.testKosmosNew
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.launch
import org.junit.Test
import org.junit.runner.RunWith

@SuppressLint("VisibleForTests")
@SmallTest
@RunWith(AndroidJUnit4::class)
class ScreenCaptureRecentTaskRepositoryImplTest : SysuiTestCase() {

    private val kosmos = testKosmosNew()

    private val fakeRecentTask1 =
        RecentTask(
            taskId = 1,
            displayId = 1,
            userId = 1,
            topActivityComponent = null,
            baseIntentComponent = null,
            colorBackground = null,
            isForegroundTask = true,
            userType = RecentTask.UserType.STANDARD,
            splitBounds = null,
        )

    private val fakeRecentTask2 =
        RecentTask(
            taskId = 2,
            displayId = 2,
            userId = 2,
            topActivityComponent = null,
            baseIntentComponent = null,
            colorBackground = null,
            isForegroundTask = false,
            userType = RecentTask.UserType.STANDARD,
            splitBounds = null,
        )

    private val fakeRecentTaskListProvider = FakeRecentTaskListProvider()

    @Test
    fun recentTasks_emitsCurrentValue() =
        kosmos.runTest {
            // Arrange
            val repository =
                ScreenCaptureRecentTaskRepositoryImpl(
                    scope = backgroundScope,
                    bgContext = testDispatcher,
                    recentTaskListProvider =
                        fakeRecentTaskListProvider.apply {
                            fakeRecentTasks = listOf(fakeRecentTask1)
                        },
                    taskStackChangeListeners = taskStackChangeListeners,
                )

            // Act
            val result = currentValue(repository.recentTasks)

            // Assert
            assertThat(result).containsExactly(fakeRecentTask1)
        }

    @Test
    fun recentTasks_whenTaskStackChanges_Updates() =
        kosmos.runTest {
            // Arrange
            val repository =
                ScreenCaptureRecentTaskRepositoryImpl(
                    scope = backgroundScope,
                    bgContext = testDispatcher,
                    recentTaskListProvider =
                        fakeRecentTaskListProvider.apply {
                            fakeRecentTasks = listOf(fakeRecentTask1)
                        },
                    taskStackChangeListeners = taskStackChangeListeners,
                )
            var result: List<RecentTask>? = null
            val job = testScope.launch { repository.recentTasks.collect { result = it } }
            assertThat(result).containsExactly(fakeRecentTask1)

            // Act
            fakeRecentTaskListProvider.fakeRecentTasks = listOf(fakeRecentTask1, fakeRecentTask2)
            taskStackChangeListeners.listenerImpl.onTaskStackChanged()

            // Assert
            assertThat(result).containsExactly(fakeRecentTask1, fakeRecentTask2).inOrder()

            // Cleanup
            job.cancel()
        }

    private class FakeRecentTaskListProvider : RecentTaskListProvider {

        var fakeRecentTasks = emptyList<RecentTask>()
        var loadRecentTasksCallCount = 0

        override suspend fun loadRecentTasks(): List<RecentTask> {
            loadRecentTasksCallCount++
            return fakeRecentTasks
        }
    }
}
+11 −0
Original line number Diff line number Diff line
@@ -18,10 +18,14 @@ package com.android.systemui.screencapture.common

import android.content.Context
import com.android.launcher3.icons.IconFactory
import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
import com.android.systemui.mediaprojection.appselector.data.ShellRecentTaskListProvider
import com.android.systemui.screencapture.common.data.repository.ScreenCaptureIconRepository
import com.android.systemui.screencapture.common.data.repository.ScreenCaptureIconRepositoryImpl
import com.android.systemui.screencapture.common.data.repository.ScreenCaptureLabelRepository
import com.android.systemui.screencapture.common.data.repository.ScreenCaptureLabelRepositoryImpl
import com.android.systemui.screencapture.common.data.repository.ScreenCaptureRecentTaskRepository
import com.android.systemui.screencapture.common.data.repository.ScreenCaptureRecentTaskRepositoryImpl
import com.android.systemui.screencapture.common.data.repository.ScreenCaptureThumbnailRepository
import com.android.systemui.screencapture.common.data.repository.ScreenCaptureThumbnailRepositoryImpl
import dagger.Binds
@@ -50,6 +54,13 @@ interface CommonModule {
        impl: ScreenCaptureThumbnailRepositoryImpl
    ): ScreenCaptureThumbnailRepository

    @Binds
    fun bindRecentTaskRepository(
        impl: ScreenCaptureRecentTaskRepositoryImpl
    ): ScreenCaptureRecentTaskRepository

    @Binds fun bindRecentTaskListProvider(impl: ShellRecentTaskListProvider): RecentTaskListProvider

    companion object {
        @Provides
        @ScreenCapture
+72 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.screencapture.common.data.repository

import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.kairos.awaitClose
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
import com.android.systemui.screencapture.common.ScreenCapture
import com.android.systemui.screencapture.common.ScreenCaptureScope
import com.android.systemui.shared.system.TaskStackChangeListener
import com.android.systemui.shared.system.TaskStackChangeListeners
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import com.android.systemui.utils.coroutines.flow.mapLatestConflated
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn

/** Repository of the current recent tasks. */
interface ScreenCaptureRecentTaskRepository {
    /** The current recent tasks. */
    val recentTasks: StateFlow<List<RecentTask>?>
}

/** The default implementation of [ScreenCaptureRecentTaskRepository]. */
@ScreenCaptureScope
class ScreenCaptureRecentTaskRepositoryImpl
@Inject
constructor(
    @ScreenCapture scope: CoroutineScope,
    @Background bgContext: CoroutineContext,
    recentTaskListProvider: RecentTaskListProvider,
    taskStackChangeListeners: TaskStackChangeListeners,
) : ScreenCaptureRecentTaskRepository {

    override val recentTasks: StateFlow<List<RecentTask>?> =
        conflatedCallbackFlow {
                val listener =
                    object : TaskStackChangeListener {
                        override fun onTaskStackChanged() {
                            trySend(Unit)
                        }
                    }

                taskStackChangeListeners.registerTaskStackListener(listener)

                awaitClose { taskStackChangeListeners.unregisterTaskStackListener(listener) }
            }
            .onStart { emit(Unit) }
            .mapLatestConflated { recentTaskListProvider.loadRecentTasks() }
            .flowOn(bgContext)
            .stateIn(scope, SharingStarted.Eagerly, null)
}
+35 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.screencapture.common.data.repository

import com.android.systemui.mediaprojection.appselector.data.RecentTask
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class FakeScreenCaptureRecentTaskRepository : ScreenCaptureRecentTaskRepository {
    private val _recentTasks = MutableStateFlow<List<RecentTask>?>(null)
    override val recentTasks: StateFlow<List<RecentTask>?> = _recentTasks.asStateFlow()

    fun setRecentTasks(tasks: List<RecentTask>?) {
        _recentTasks.value = tasks
    }

    fun setRecentTasks(vararg tasks: RecentTask) {
        _recentTasks.value = tasks.toList()
    }
}
+21 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.screencapture.common.data.repository

import com.android.systemui.kosmos.Kosmos

val Kosmos.fakeRecentTaskRepository by Kosmos.Fixture { FakeScreenCaptureRecentTaskRepository() }