Loading src/com/android/settings/spa/app/specialaccess/PictureInPicture.kt +39 −20 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.content.pm.PackageManager.GET_ACTIVITIES import android.content.pm.PackageManager.PackageInfoFlags import android.util.Log import androidx.compose.runtime.Composable import androidx.compose.runtime.livedata.observeAsState import com.android.settings.R Loading Loading @@ -56,9 +57,8 @@ class PictureInPictureListModel(private val context: Context) : private val packageManager = context.packageManager override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) = userIdFlow.map(::getPictureInPicturePackages).combine(appListFlow) { pictureInPicturePackages, appList -> userIdFlow.map(::getPictureInPicturePackages) .combine(appListFlow) { pictureInPicturePackages, appList -> appList.map { app -> createPictureInPictureRecord( app = app, Loading @@ -67,15 +67,11 @@ class PictureInPictureListModel(private val context: Context) : } } override fun transformItem(app: ApplicationInfo): PictureInPictureRecord { return createPictureInPictureRecord( override fun transformItem(app: ApplicationInfo) = createPictureInPictureRecord( app = app, isSupport = app.installed && packageManager .getPackageInfoAsUser(app.packageName, GET_ACTIVITIES_FLAGS, app.userId) .supportsPictureInPicture(), getPackageAndActivityInfo(app)?.supportsPictureInPicture() == true, ) } private fun createPictureInPictureRecord(app: ApplicationInfo, isSupport: Boolean) = PictureInPictureRecord( Loading Loading @@ -103,13 +99,36 @@ class PictureInPictureListModel(private val context: Context) : } private fun getPictureInPicturePackages(userId: Int): Set<String> = packageManager .getInstalledPackagesAsUser(GET_ACTIVITIES_FLAGS, userId) getPackageAndActivityInfoList(userId) .filter { it.supportsPictureInPicture() } .map { it.packageName } .toSet() private fun getPackageAndActivityInfo(app: ApplicationInfo): PackageInfo? = try { packageManager.getPackageInfoAsUser(app.packageName, GET_ACTIVITIES_FLAGS, app.userId) } catch (e: Exception) { // Query PackageManager.getPackageInfoAsUser() with GET_ACTIVITIES_FLAGS could cause // exception sometimes. Since we reply on this flag to retrieve the Picture In Picture // packages, we need to catch the exception to alleviate the impact before PackageManager // fixing this issue or provide a better api. Log.e(TAG, "Exception while getPackageInfoAsUser", e) null } private fun getPackageAndActivityInfoList(userId: Int): List<PackageInfo> = try { packageManager.getInstalledPackagesAsUser(GET_ACTIVITIES_FLAGS, userId) } catch (e: Exception) { // Query PackageManager.getPackageInfoAsUser() with GET_ACTIVITIES_FLAGS could cause // exception sometimes. Since we reply on this flag to retrieve the Picture In Picture // packages, we need to catch the exception to alleviate the impact before PackageManager // fixing this issue or provide a better api. Log.e(TAG, "Exception while getInstalledPackagesAsUser", e) emptyList() } companion object { private const val TAG = "PictureInPictureListModel" private fun PackageInfo.supportsPictureInPicture() = activities?.any(ActivityInfo::supportsPictureInPicture) ?: false Loading tests/spa_unit/src/com/android/settings/spa/app/specialaccess/PictureInPictureTest.kt +32 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.PackageManager.PackageInfoFlags import android.os.DeadSystemRuntimeException import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settings.R Loading Loading @@ -100,6 +101,23 @@ class PictureInPictureTest { assertThat(record.isSupport).isTrue() } @Test fun transform_getInstalledPackagesAsUserThrowsException_treatAsNotSupported() = runTest { whenever(packageManager.getInstalledPackagesAsUser(any<PackageInfoFlags>(), anyInt())) .thenThrow(DeadSystemRuntimeException()) val recordListFlow = listModel.transform( userIdFlow = flowOf(USER_ID), appListFlow = flowOf(listOf(PICTURE_IN_PICTURE_APP)), ) val recordList = recordListFlow.first() assertThat(recordList).hasSize(1) val record = recordList[0] assertThat(record.app).isSameInstanceAs(PICTURE_IN_PICTURE_APP) assertThat(record.isSupport).isFalse() } @Test fun transformItem() { whenever( Loading @@ -114,6 +132,20 @@ class PictureInPictureTest { assertThat(record.isSupport).isTrue() } @Test fun transformItem_getPackageInfoAsUserThrowsException_treatAsNotSupported() { whenever( packageManager.getPackageInfoAsUser( eq(PICTURE_IN_PICTURE_PACKAGE_NAME), any<PackageInfoFlags>(), eq(USER_ID) ) ).thenThrow(DeadSystemRuntimeException()) val record = listModel.transformItem(PICTURE_IN_PICTURE_APP) assertThat(record.app).isSameInstanceAs(PICTURE_IN_PICTURE_APP) assertThat(record.isSupport).isFalse() } @Test fun filter_isSupport() = runTest { val record = createRecord(isSupport = true) Loading Loading
src/com/android/settings/spa/app/specialaccess/PictureInPicture.kt +39 −20 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.content.pm.PackageManager.GET_ACTIVITIES import android.content.pm.PackageManager.PackageInfoFlags import android.util.Log import androidx.compose.runtime.Composable import androidx.compose.runtime.livedata.observeAsState import com.android.settings.R Loading Loading @@ -56,9 +57,8 @@ class PictureInPictureListModel(private val context: Context) : private val packageManager = context.packageManager override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) = userIdFlow.map(::getPictureInPicturePackages).combine(appListFlow) { pictureInPicturePackages, appList -> userIdFlow.map(::getPictureInPicturePackages) .combine(appListFlow) { pictureInPicturePackages, appList -> appList.map { app -> createPictureInPictureRecord( app = app, Loading @@ -67,15 +67,11 @@ class PictureInPictureListModel(private val context: Context) : } } override fun transformItem(app: ApplicationInfo): PictureInPictureRecord { return createPictureInPictureRecord( override fun transformItem(app: ApplicationInfo) = createPictureInPictureRecord( app = app, isSupport = app.installed && packageManager .getPackageInfoAsUser(app.packageName, GET_ACTIVITIES_FLAGS, app.userId) .supportsPictureInPicture(), getPackageAndActivityInfo(app)?.supportsPictureInPicture() == true, ) } private fun createPictureInPictureRecord(app: ApplicationInfo, isSupport: Boolean) = PictureInPictureRecord( Loading Loading @@ -103,13 +99,36 @@ class PictureInPictureListModel(private val context: Context) : } private fun getPictureInPicturePackages(userId: Int): Set<String> = packageManager .getInstalledPackagesAsUser(GET_ACTIVITIES_FLAGS, userId) getPackageAndActivityInfoList(userId) .filter { it.supportsPictureInPicture() } .map { it.packageName } .toSet() private fun getPackageAndActivityInfo(app: ApplicationInfo): PackageInfo? = try { packageManager.getPackageInfoAsUser(app.packageName, GET_ACTIVITIES_FLAGS, app.userId) } catch (e: Exception) { // Query PackageManager.getPackageInfoAsUser() with GET_ACTIVITIES_FLAGS could cause // exception sometimes. Since we reply on this flag to retrieve the Picture In Picture // packages, we need to catch the exception to alleviate the impact before PackageManager // fixing this issue or provide a better api. Log.e(TAG, "Exception while getPackageInfoAsUser", e) null } private fun getPackageAndActivityInfoList(userId: Int): List<PackageInfo> = try { packageManager.getInstalledPackagesAsUser(GET_ACTIVITIES_FLAGS, userId) } catch (e: Exception) { // Query PackageManager.getPackageInfoAsUser() with GET_ACTIVITIES_FLAGS could cause // exception sometimes. Since we reply on this flag to retrieve the Picture In Picture // packages, we need to catch the exception to alleviate the impact before PackageManager // fixing this issue or provide a better api. Log.e(TAG, "Exception while getInstalledPackagesAsUser", e) emptyList() } companion object { private const val TAG = "PictureInPictureListModel" private fun PackageInfo.supportsPictureInPicture() = activities?.any(ActivityInfo::supportsPictureInPicture) ?: false Loading
tests/spa_unit/src/com/android/settings/spa/app/specialaccess/PictureInPictureTest.kt +32 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.PackageManager.PackageInfoFlags import android.os.DeadSystemRuntimeException import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settings.R Loading Loading @@ -100,6 +101,23 @@ class PictureInPictureTest { assertThat(record.isSupport).isTrue() } @Test fun transform_getInstalledPackagesAsUserThrowsException_treatAsNotSupported() = runTest { whenever(packageManager.getInstalledPackagesAsUser(any<PackageInfoFlags>(), anyInt())) .thenThrow(DeadSystemRuntimeException()) val recordListFlow = listModel.transform( userIdFlow = flowOf(USER_ID), appListFlow = flowOf(listOf(PICTURE_IN_PICTURE_APP)), ) val recordList = recordListFlow.first() assertThat(recordList).hasSize(1) val record = recordList[0] assertThat(record.app).isSameInstanceAs(PICTURE_IN_PICTURE_APP) assertThat(record.isSupport).isFalse() } @Test fun transformItem() { whenever( Loading @@ -114,6 +132,20 @@ class PictureInPictureTest { assertThat(record.isSupport).isTrue() } @Test fun transformItem_getPackageInfoAsUserThrowsException_treatAsNotSupported() { whenever( packageManager.getPackageInfoAsUser( eq(PICTURE_IN_PICTURE_PACKAGE_NAME), any<PackageInfoFlags>(), eq(USER_ID) ) ).thenThrow(DeadSystemRuntimeException()) val record = listModel.transformItem(PICTURE_IN_PICTURE_APP) assertThat(record.app).isSameInstanceAs(PICTURE_IN_PICTURE_APP) assertThat(record.isSupport).isFalse() } @Test fun filter_isSupport() = runTest { val record = createRecord(isSupport = true) Loading