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

Commit 00990b25 authored by Lucas Silva's avatar Lucas Silva Committed by Android (Google) Code Review
Browse files

Merge "Update tiles component repository to remove broadcast" into main

parents fdaf4101 f8bc5c4c
Loading
Loading
Loading
Loading
+12 −86
Original line number Diff line number Diff line
@@ -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
@@ -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

@@ -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 {
@@ -169,7 +108,7 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() {
        }

    @Test
    fun componentAdded_foundAfterBroadcast() =
    fun componentAdded_foundAfterPackageChange() =
        testScope.runTest {
            val userId = 0
            val resolveInfo =
@@ -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)
        }
@@ -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 =
+1 −0
Original line number Diff line number Diff line
@@ -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]. */
+8 −0
Original line number Diff line number Diff line
@@ -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.
     *
+6 −30
Original line number Diff line number Diff line
@@ -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
@@ -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>> {
@@ -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)
@@ -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(