Loading packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt +77 −4 Original line number Diff line number Diff line Loading @@ -19,12 +19,17 @@ package com.android.systemui.clipboardoverlay import android.content.ClipData import android.content.ComponentName import android.content.Intent import android.content.pm.ActivityInfo import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.net.Uri import android.text.SpannableString import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.systemui.SysuiTestCase import com.android.systemui.res.R import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock import kotlinx.coroutines.test.TestCoroutineScheduler import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher Loading @@ -33,6 +38,8 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) Loading @@ -40,8 +47,10 @@ class ActionIntentCreatorTest : SysuiTestCase() { private val scheduler = TestCoroutineScheduler() private val mainDispatcher = UnconfinedTestDispatcher(scheduler) private val testScope = TestScope(mainDispatcher) val packageManager = mock<PackageManager>() val creator = ActionIntentCreator(testScope.backgroundScope) val creator = ActionIntentCreator(context, packageManager, testScope.backgroundScope, mainDispatcher) @Test fun test_getTextEditorIntent() { Loading Loading @@ -73,7 +82,7 @@ class ActionIntentCreatorTest : SysuiTestCase() { } @Test fun test_getImageEditIntent() = runTest { fun test_getImageEditIntent_noDefault() = runTest { context.getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, "") val fakeUri = Uri.parse("content://foo") var intent = creator.getImageEditIntent(fakeUri, context) Loading @@ -83,17 +92,81 @@ class ActionIntentCreatorTest : SysuiTestCase() { assertEquals(null, intent.component) assertEquals("clipboard", intent.getStringExtra("edit_source")) assertFlags(intent, EXTERNAL_INTENT_FLAGS) } @Test fun test_getImageEditIntent_defaultProvided() = runTest { val fakeUri = Uri.parse("content://foo") // try again with an editor component val fakeComponent = ComponentName("com.android.remotecopy", "com.android.remotecopy.RemoteCopyActivity") context .getOrCreateTestableResources() .addOverride(R.string.config_screenshotEditor, fakeComponent.flattenToString()) intent = creator.getImageEditIntent(fakeUri, context) val intent = creator.getImageEditIntent(fakeUri, context) assertEquals(fakeComponent, intent.component) } @Test fun test_getImageEditIntent_preferredProvidedButDisabled() = runTest { val fakeUri = Uri.parse("content://foo") val defaultComponent = ComponentName("com.android.foo", "com.android.foo.Something") val preferredComponent = ComponentName("com.android.bar", "com.android.bar.Something") val packageInfo = PackageInfo().apply { activities = arrayOf() // no activities } whenever(packageManager.getPackageInfo(eq(preferredComponent.packageName), anyInt())) .thenReturn(packageInfo) context .getOrCreateTestableResources() .addOverride(R.string.config_screenshotEditor, defaultComponent.flattenToString()) context .getOrCreateTestableResources() .addOverride( R.string.config_preferredScreenshotEditor, preferredComponent.flattenToString(), ) val intent = creator.getImageEditIntent(fakeUri, context) assertEquals(defaultComponent, intent.component) } @Test fun test_getImageEditIntent_preferredProvided() = runTest { val fakeUri = Uri.parse("content://foo") val defaultComponent = ComponentName("com.android.foo", "com.android.foo.Something") val preferredComponent = ComponentName("com.android.bar", "com.android.bar.Something") val packageInfo = PackageInfo().apply { activities = arrayOf( ActivityInfo().apply { packageName = preferredComponent.packageName name = preferredComponent.className } ) } whenever(packageManager.getPackageInfo(eq(preferredComponent.packageName), anyInt())) .thenReturn(packageInfo) context .getOrCreateTestableResources() .addOverride(R.string.config_screenshotEditor, defaultComponent.flattenToString()) context .getOrCreateTestableResources() .addOverride( R.string.config_preferredScreenshotEditor, preferredComponent.flattenToString(), ) val intent = creator.getImageEditIntent(fakeUri, context) assertEquals(preferredComponent, intent.component) } @Test fun test_getShareIntent_plaintext() { val clipData = ClipData.newPlainText("Test", "Test Item") Loading packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt +46 −5 Original line number Diff line number Diff line Loading @@ -21,20 +21,30 @@ import android.content.ClipDescription import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.PackageManager.NameNotFoundException import android.net.Uri import android.text.TextUtils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.res.R import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @SysUISingleton class ActionIntentCreator @Inject constructor(@Application private val applicationScope: CoroutineScope) : IntentCreator { constructor( private val context: Context, private val packageManager: PackageManager, @Application private val applicationScope: CoroutineScope, @Background private val backgroundDispatcher: CoroutineDispatcher, ) : IntentCreator { override fun getTextEditorIntent(context: Context?) = Intent(context, EditTextActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) Loading Loading @@ -72,11 +82,9 @@ constructor(@Application private val applicationScope: CoroutineScope) : IntentC } suspend fun getImageEditIntent(uri: Uri?, context: Context): Intent { val editorPackage = context.getString(R.string.config_screenshotEditor) return Intent(Intent.ACTION_EDIT).apply { if (!TextUtils.isEmpty(editorPackage)) { setComponent(ComponentName.unflattenFromString(editorPackage)) } // Use the preferred editor if it's available, otherwise fall back to the default editor component = preferredEditor() ?: defaultEditor() setDataAndType(uri, "image/*") addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) Loading Loading @@ -105,6 +113,39 @@ constructor(@Application private val applicationScope: CoroutineScope) : IntentC } } private suspend fun preferredEditor(): ComponentName? = runCatching { val preferredEditor = context.getString(R.string.config_preferredScreenshotEditor) val component = ComponentName.unflattenFromString(preferredEditor) ?: return null return if (isComponentAvailable(component)) component else null } .getOrNull() private suspend fun isComponentAvailable(component: ComponentName): Boolean = withContext(backgroundDispatcher) { try { val info = packageManager.getPackageInfo( component.packageName, PackageManager.GET_ACTIVITIES, ) info.activities?.firstOrNull { it.componentName.className == component.className } != null } catch (e: NameNotFoundException) { false } } private fun defaultEditor(): ComponentName? = runCatching { context.getString(R.string.config_screenshotEditor).let { ComponentName.unflattenFromString(it) } } .getOrNull() companion object { private const val EXTRA_EDIT_SOURCE: String = "edit_source" private const val EDIT_SOURCE_CLIPBOARD: String = "clipboard" Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ActionIntentCreatorTest.kt +77 −4 Original line number Diff line number Diff line Loading @@ -19,12 +19,17 @@ package com.android.systemui.clipboardoverlay import android.content.ClipData import android.content.ComponentName import android.content.Intent import android.content.pm.ActivityInfo import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.net.Uri import android.text.SpannableString import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.systemui.SysuiTestCase import com.android.systemui.res.R import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock import kotlinx.coroutines.test.TestCoroutineScheduler import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher Loading @@ -33,6 +38,8 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) Loading @@ -40,8 +47,10 @@ class ActionIntentCreatorTest : SysuiTestCase() { private val scheduler = TestCoroutineScheduler() private val mainDispatcher = UnconfinedTestDispatcher(scheduler) private val testScope = TestScope(mainDispatcher) val packageManager = mock<PackageManager>() val creator = ActionIntentCreator(testScope.backgroundScope) val creator = ActionIntentCreator(context, packageManager, testScope.backgroundScope, mainDispatcher) @Test fun test_getTextEditorIntent() { Loading Loading @@ -73,7 +82,7 @@ class ActionIntentCreatorTest : SysuiTestCase() { } @Test fun test_getImageEditIntent() = runTest { fun test_getImageEditIntent_noDefault() = runTest { context.getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor, "") val fakeUri = Uri.parse("content://foo") var intent = creator.getImageEditIntent(fakeUri, context) Loading @@ -83,17 +92,81 @@ class ActionIntentCreatorTest : SysuiTestCase() { assertEquals(null, intent.component) assertEquals("clipboard", intent.getStringExtra("edit_source")) assertFlags(intent, EXTERNAL_INTENT_FLAGS) } @Test fun test_getImageEditIntent_defaultProvided() = runTest { val fakeUri = Uri.parse("content://foo") // try again with an editor component val fakeComponent = ComponentName("com.android.remotecopy", "com.android.remotecopy.RemoteCopyActivity") context .getOrCreateTestableResources() .addOverride(R.string.config_screenshotEditor, fakeComponent.flattenToString()) intent = creator.getImageEditIntent(fakeUri, context) val intent = creator.getImageEditIntent(fakeUri, context) assertEquals(fakeComponent, intent.component) } @Test fun test_getImageEditIntent_preferredProvidedButDisabled() = runTest { val fakeUri = Uri.parse("content://foo") val defaultComponent = ComponentName("com.android.foo", "com.android.foo.Something") val preferredComponent = ComponentName("com.android.bar", "com.android.bar.Something") val packageInfo = PackageInfo().apply { activities = arrayOf() // no activities } whenever(packageManager.getPackageInfo(eq(preferredComponent.packageName), anyInt())) .thenReturn(packageInfo) context .getOrCreateTestableResources() .addOverride(R.string.config_screenshotEditor, defaultComponent.flattenToString()) context .getOrCreateTestableResources() .addOverride( R.string.config_preferredScreenshotEditor, preferredComponent.flattenToString(), ) val intent = creator.getImageEditIntent(fakeUri, context) assertEquals(defaultComponent, intent.component) } @Test fun test_getImageEditIntent_preferredProvided() = runTest { val fakeUri = Uri.parse("content://foo") val defaultComponent = ComponentName("com.android.foo", "com.android.foo.Something") val preferredComponent = ComponentName("com.android.bar", "com.android.bar.Something") val packageInfo = PackageInfo().apply { activities = arrayOf( ActivityInfo().apply { packageName = preferredComponent.packageName name = preferredComponent.className } ) } whenever(packageManager.getPackageInfo(eq(preferredComponent.packageName), anyInt())) .thenReturn(packageInfo) context .getOrCreateTestableResources() .addOverride(R.string.config_screenshotEditor, defaultComponent.flattenToString()) context .getOrCreateTestableResources() .addOverride( R.string.config_preferredScreenshotEditor, preferredComponent.flattenToString(), ) val intent = creator.getImageEditIntent(fakeUri, context) assertEquals(preferredComponent, intent.component) } @Test fun test_getShareIntent_plaintext() { val clipData = ClipData.newPlainText("Test", "Test Item") Loading
packages/SystemUI/src/com/android/systemui/clipboardoverlay/ActionIntentCreator.kt +46 −5 Original line number Diff line number Diff line Loading @@ -21,20 +21,30 @@ import android.content.ClipDescription import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.PackageManager.NameNotFoundException import android.net.Uri import android.text.TextUtils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.res.R import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @SysUISingleton class ActionIntentCreator @Inject constructor(@Application private val applicationScope: CoroutineScope) : IntentCreator { constructor( private val context: Context, private val packageManager: PackageManager, @Application private val applicationScope: CoroutineScope, @Background private val backgroundDispatcher: CoroutineDispatcher, ) : IntentCreator { override fun getTextEditorIntent(context: Context?) = Intent(context, EditTextActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) Loading Loading @@ -72,11 +82,9 @@ constructor(@Application private val applicationScope: CoroutineScope) : IntentC } suspend fun getImageEditIntent(uri: Uri?, context: Context): Intent { val editorPackage = context.getString(R.string.config_screenshotEditor) return Intent(Intent.ACTION_EDIT).apply { if (!TextUtils.isEmpty(editorPackage)) { setComponent(ComponentName.unflattenFromString(editorPackage)) } // Use the preferred editor if it's available, otherwise fall back to the default editor component = preferredEditor() ?: defaultEditor() setDataAndType(uri, "image/*") addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) Loading Loading @@ -105,6 +113,39 @@ constructor(@Application private val applicationScope: CoroutineScope) : IntentC } } private suspend fun preferredEditor(): ComponentName? = runCatching { val preferredEditor = context.getString(R.string.config_preferredScreenshotEditor) val component = ComponentName.unflattenFromString(preferredEditor) ?: return null return if (isComponentAvailable(component)) component else null } .getOrNull() private suspend fun isComponentAvailable(component: ComponentName): Boolean = withContext(backgroundDispatcher) { try { val info = packageManager.getPackageInfo( component.packageName, PackageManager.GET_ACTIVITIES, ) info.activities?.firstOrNull { it.componentName.className == component.className } != null } catch (e: NameNotFoundException) { false } } private fun defaultEditor(): ComponentName? = runCatching { context.getString(R.string.config_screenshotEditor).let { ComponentName.unflattenFromString(it) } } .getOrNull() companion object { private const val EXTRA_EDIT_SOURCE: String = "edit_source" private const val EDIT_SOURCE_CLIPBOARD: String = "clipboard" Loading