Loading packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt +12 −86 Original line number Diff line number Diff line Loading @@ -17,11 +17,9 @@ package com.android.systemui.qs.pipeline.data.repository import android.Manifest.permission.BIND_QUICK_SETTINGS_TILE import android.content.BroadcastReceiver import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.pm.PackageManager.ResolveInfoFlags Loading @@ -33,44 +31,36 @@ import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.data.repository.fakePackageChangeRepository import com.android.systemui.common.data.repository.packageChangeRepository import com.android.systemui.common.data.shared.model.PackageChangeModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argThat import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.nullable import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyInt import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @OptIn(ExperimentalCoroutinesApi::class) class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() { private val testDispatcher = StandardTestDispatcher() private val testScope = TestScope(testDispatcher) private val kosmos = testKosmos() private val testScope = kosmos.testScope @Mock private lateinit var context: Context @Mock private lateinit var packageManager: PackageManager @Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver> private lateinit var underTest: InstalledTilesComponentRepositoryImpl Loading @@ -92,62 +82,11 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() { underTest = InstalledTilesComponentRepositoryImpl( context, testDispatcher, kosmos.testDispatcher, kosmos.packageChangeRepository ) } @Test fun registersAndUnregistersBroadcastReceiver() = testScope.runTest { val user = 10 val job = launch { underTest.getInstalledTilesComponents(user).collect {} } runCurrent() verify(context) .registerReceiverAsUser( capture(receiverCaptor), eq(UserHandle.of(user)), any(), nullable(), nullable(), ) verify(context, never()).unregisterReceiver(receiverCaptor.value) job.cancel() runCurrent() verify(context).unregisterReceiver(receiverCaptor.value) } @Test fun intentFilterForCorrectActionsAndScheme() = testScope.runTest { val filterCaptor = argumentCaptor<IntentFilter>() backgroundScope.launch { underTest.getInstalledTilesComponents(0).collect {} } runCurrent() verify(context) .registerReceiverAsUser( any(), any(), capture(filterCaptor), nullable(), nullable(), ) with(filterCaptor.value) { assertThat(matchAction(Intent.ACTION_PACKAGE_CHANGED)).isTrue() assertThat(matchAction(Intent.ACTION_PACKAGE_ADDED)).isTrue() assertThat(matchAction(Intent.ACTION_PACKAGE_REMOVED)).isTrue() assertThat(matchAction(Intent.ACTION_PACKAGE_REPLACED)).isTrue() assertThat(countActions()).isEqualTo(4) assertThat(hasDataScheme("package")).isTrue() assertThat(countDataSchemes()).isEqualTo(1) } } @Test fun componentsLoadedOnStart() = testScope.runTest { Loading @@ -169,7 +108,7 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() { } @Test fun componentAdded_foundAfterBroadcast() = fun componentAdded_foundAfterPackageChange() = testScope.runTest { val userId = 0 val resolveInfo = Loading @@ -186,7 +125,7 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() { ) ) .thenReturn(listOf(resolveInfo)) getRegisteredReceiver().onReceive(context, Intent(Intent.ACTION_PACKAGE_ADDED)) kosmos.fakePackageChangeRepository.notifyChange(PackageChangeModel.Empty) assertThat(componentNames).containsExactly(TEST_COMPONENT) } Loading Loading @@ -275,19 +214,6 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() { assertThat(componentNames).containsExactly(TEST_COMPONENT) } private fun getRegisteredReceiver(): BroadcastReceiver { verify(context) .registerReceiverAsUser( capture(receiverCaptor), any(), any(), nullable(), nullable(), ) return receiverCaptor.value } companion object { private val INTENT = Intent(TileService.ACTION_QS_TILE) private val FLAGS = Loading packages/SystemUI/src/com/android/systemui/common/data/repository/PackageUpdateLogger.kt +1 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ private fun getChangeString(model: PackageChangeModel) = is PackageChangeModel.UpdateStarted -> "started updating" is PackageChangeModel.UpdateFinished -> "finished updating" is PackageChangeModel.Changed -> "changed" is PackageChangeModel.Empty -> throw IllegalStateException("Unexpected empty value: $model") } /** A debug logger for [PackageChangeRepository]. */ Loading packages/SystemUI/src/com/android/systemui/common/data/shared/model/PackageChangeModel.kt +8 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,14 @@ sealed interface PackageChangeModel { val packageName: String val packageUid: Int /** Empty change, provided for convenience when a sensible default value is needed. */ data object Empty : PackageChangeModel { override val packageName: String get() = "" override val packageUid: Int get() = 0 } /** * An existing application package was uninstalled. * Loading packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt +6 −30 Original line number Diff line number Diff line Loading @@ -18,23 +18,21 @@ package com.android.systemui.qs.pipeline.data.repository import android.Manifest.permission.BIND_QUICK_SETTINGS_TILE import android.annotation.WorkerThread import android.content.BroadcastReceiver import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.pm.PackageManager import android.content.pm.PackageManager.ResolveInfoFlags import android.os.UserHandle import android.service.quicksettings.TileService import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.common.data.repository.PackageChangeRepository import com.android.systemui.common.data.shared.model.PackageChangeModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.util.kotlin.isComponentActuallyEnabled import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn Loading @@ -52,6 +50,7 @@ class InstalledTilesComponentRepositoryImpl constructor( @Application private val applicationContext: Context, @Background private val backgroundDispatcher: CoroutineDispatcher, private val packageChangeRepository: PackageChangeRepository ) : InstalledTilesComponentRepository { override fun getInstalledTilesComponents(userId: Int): Flow<Set<ComponentName>> { Loading @@ -70,24 +69,9 @@ constructor( ) .packageManager } return conflatedCallbackFlow { val receiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { trySend(Unit) } } applicationContext.registerReceiverAsUser( receiver, UserHandle.of(userId), INTENT_FILTER, /* broadcastPermission = */ null, /* scheduler = */ null ) awaitClose { applicationContext.unregisterReceiver(receiver) } } .onStart { emit(Unit) } return packageChangeRepository .packageChanged(UserHandle.of(userId)) .onStart { emit(PackageChangeModel.Empty) } .map { reloadComponents(userId, packageManager) } .distinctUntilChanged() .flowOn(backgroundDispatcher) Loading @@ -104,14 +88,6 @@ constructor( } companion object { private val INTENT_FILTER = IntentFilter().apply { addAction(Intent.ACTION_PACKAGE_ADDED) addAction(Intent.ACTION_PACKAGE_CHANGED) addAction(Intent.ACTION_PACKAGE_REMOVED) addAction(Intent.ACTION_PACKAGE_REPLACED) addDataScheme("package") } private val INTENT = Intent(TileService.ACTION_QS_TILE) private val FLAGS = ResolveInfoFlags.of( Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt +12 −86 Original line number Diff line number Diff line Loading @@ -17,11 +17,9 @@ package com.android.systemui.qs.pipeline.data.repository import android.Manifest.permission.BIND_QUICK_SETTINGS_TILE import android.content.BroadcastReceiver import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.pm.PackageManager.ResolveInfoFlags Loading @@ -33,44 +31,36 @@ import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.data.repository.fakePackageChangeRepository import com.android.systemui.common.data.repository.packageChangeRepository import com.android.systemui.common.data.shared.model.PackageChangeModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argThat import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.nullable import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyInt import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper @OptIn(ExperimentalCoroutinesApi::class) class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() { private val testDispatcher = StandardTestDispatcher() private val testScope = TestScope(testDispatcher) private val kosmos = testKosmos() private val testScope = kosmos.testScope @Mock private lateinit var context: Context @Mock private lateinit var packageManager: PackageManager @Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver> private lateinit var underTest: InstalledTilesComponentRepositoryImpl Loading @@ -92,62 +82,11 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() { underTest = InstalledTilesComponentRepositoryImpl( context, testDispatcher, kosmos.testDispatcher, kosmos.packageChangeRepository ) } @Test fun registersAndUnregistersBroadcastReceiver() = testScope.runTest { val user = 10 val job = launch { underTest.getInstalledTilesComponents(user).collect {} } runCurrent() verify(context) .registerReceiverAsUser( capture(receiverCaptor), eq(UserHandle.of(user)), any(), nullable(), nullable(), ) verify(context, never()).unregisterReceiver(receiverCaptor.value) job.cancel() runCurrent() verify(context).unregisterReceiver(receiverCaptor.value) } @Test fun intentFilterForCorrectActionsAndScheme() = testScope.runTest { val filterCaptor = argumentCaptor<IntentFilter>() backgroundScope.launch { underTest.getInstalledTilesComponents(0).collect {} } runCurrent() verify(context) .registerReceiverAsUser( any(), any(), capture(filterCaptor), nullable(), nullable(), ) with(filterCaptor.value) { assertThat(matchAction(Intent.ACTION_PACKAGE_CHANGED)).isTrue() assertThat(matchAction(Intent.ACTION_PACKAGE_ADDED)).isTrue() assertThat(matchAction(Intent.ACTION_PACKAGE_REMOVED)).isTrue() assertThat(matchAction(Intent.ACTION_PACKAGE_REPLACED)).isTrue() assertThat(countActions()).isEqualTo(4) assertThat(hasDataScheme("package")).isTrue() assertThat(countDataSchemes()).isEqualTo(1) } } @Test fun componentsLoadedOnStart() = testScope.runTest { Loading @@ -169,7 +108,7 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() { } @Test fun componentAdded_foundAfterBroadcast() = fun componentAdded_foundAfterPackageChange() = testScope.runTest { val userId = 0 val resolveInfo = Loading @@ -186,7 +125,7 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() { ) ) .thenReturn(listOf(resolveInfo)) getRegisteredReceiver().onReceive(context, Intent(Intent.ACTION_PACKAGE_ADDED)) kosmos.fakePackageChangeRepository.notifyChange(PackageChangeModel.Empty) assertThat(componentNames).containsExactly(TEST_COMPONENT) } Loading Loading @@ -275,19 +214,6 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() { assertThat(componentNames).containsExactly(TEST_COMPONENT) } private fun getRegisteredReceiver(): BroadcastReceiver { verify(context) .registerReceiverAsUser( capture(receiverCaptor), any(), any(), nullable(), nullable(), ) return receiverCaptor.value } companion object { private val INTENT = Intent(TileService.ACTION_QS_TILE) private val FLAGS = Loading
packages/SystemUI/src/com/android/systemui/common/data/repository/PackageUpdateLogger.kt +1 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ private fun getChangeString(model: PackageChangeModel) = is PackageChangeModel.UpdateStarted -> "started updating" is PackageChangeModel.UpdateFinished -> "finished updating" is PackageChangeModel.Changed -> "changed" is PackageChangeModel.Empty -> throw IllegalStateException("Unexpected empty value: $model") } /** A debug logger for [PackageChangeRepository]. */ Loading
packages/SystemUI/src/com/android/systemui/common/data/shared/model/PackageChangeModel.kt +8 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,14 @@ sealed interface PackageChangeModel { val packageName: String val packageUid: Int /** Empty change, provided for convenience when a sensible default value is needed. */ data object Empty : PackageChangeModel { override val packageName: String get() = "" override val packageUid: Int get() = 0 } /** * An existing application package was uninstalled. * Loading
packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt +6 −30 Original line number Diff line number Diff line Loading @@ -18,23 +18,21 @@ package com.android.systemui.qs.pipeline.data.repository import android.Manifest.permission.BIND_QUICK_SETTINGS_TILE import android.annotation.WorkerThread import android.content.BroadcastReceiver import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.pm.PackageManager import android.content.pm.PackageManager.ResolveInfoFlags import android.os.UserHandle import android.service.quicksettings.TileService import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.common.data.repository.PackageChangeRepository import com.android.systemui.common.data.shared.model.PackageChangeModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.util.kotlin.isComponentActuallyEnabled import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn Loading @@ -52,6 +50,7 @@ class InstalledTilesComponentRepositoryImpl constructor( @Application private val applicationContext: Context, @Background private val backgroundDispatcher: CoroutineDispatcher, private val packageChangeRepository: PackageChangeRepository ) : InstalledTilesComponentRepository { override fun getInstalledTilesComponents(userId: Int): Flow<Set<ComponentName>> { Loading @@ -70,24 +69,9 @@ constructor( ) .packageManager } return conflatedCallbackFlow { val receiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { trySend(Unit) } } applicationContext.registerReceiverAsUser( receiver, UserHandle.of(userId), INTENT_FILTER, /* broadcastPermission = */ null, /* scheduler = */ null ) awaitClose { applicationContext.unregisterReceiver(receiver) } } .onStart { emit(Unit) } return packageChangeRepository .packageChanged(UserHandle.of(userId)) .onStart { emit(PackageChangeModel.Empty) } .map { reloadComponents(userId, packageManager) } .distinctUntilChanged() .flowOn(backgroundDispatcher) Loading @@ -104,14 +88,6 @@ constructor( } companion object { private val INTENT_FILTER = IntentFilter().apply { addAction(Intent.ACTION_PACKAGE_ADDED) addAction(Intent.ACTION_PACKAGE_CHANGED) addAction(Intent.ACTION_PACKAGE_REMOVED) addAction(Intent.ACTION_PACKAGE_REPLACED) addDataScheme("package") } private val INTENT = Intent(TileService.ACTION_QS_TILE) private val FLAGS = ResolveInfoFlags.of( Loading