summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java47
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt26
5 files changed, 101 insertions, 44 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
index 0448ad517c6d..0a82f72f8f21 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
@@ -30,6 +30,9 @@ import com.android.systemui.shared.Flags
import com.google.common.truth.Truth.assertThat
import java.util.UUID
import kotlin.test.Test
+import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.runner.RunWith
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.kotlin.any
@@ -43,10 +46,19 @@ import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
class DefaultScreenshotActionsProviderTest : SysuiTestCase() {
+ private val scheduler = TestCoroutineScheduler()
+ private val mainDispatcher = UnconfinedTestDispatcher(scheduler)
+ private val testScope = TestScope(mainDispatcher)
private val actionExecutor = mock<ActionExecutor>()
private val uiEventLogger = mock<UiEventLogger>()
private val actionsCallback = mock<ScreenshotActionsController.ActionsCallback>()
- private val actionIntentCreator = ActionIntentCreator(context, context.packageManager)
+ private val actionIntentCreator =
+ ActionIntentCreator(
+ context,
+ context.packageManager,
+ testScope.backgroundScope,
+ mainDispatcher,
+ )
private val request = ScreenshotData.forTesting(userHandle = UserHandle.OWNER)
private val validResult = ScreenshotSavedResult(Uri.EMPTY, Process.myUserHandle(), 0)
@@ -198,6 +210,7 @@ class DefaultScreenshotActionsProviderTest : SysuiTestCase() {
context,
uiEventLogger,
actionIntentCreator,
+ testScope,
UUID.randomUUID(),
request,
actionExecutor,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
index 9208fc3dd016..271f4dce0aab 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
@@ -23,18 +23,31 @@ import android.content.ContentProvider
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.os.UserHandle
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 com.android.systemui.screenshot.scroll.LongScreenshotActivity
import com.android.systemui.shared.Flags.usePreferredImageEditor
+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(private val context: Context, private val packageManager: PackageManager) {
+constructor(
+ private val context: Context,
+ private val packageManager: PackageManager,
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
/** @return a chooser intent to share the given URI. */
fun createShare(uri: Uri): Intent = createShare(uri, subject = null, text = null)
@@ -76,11 +89,16 @@ constructor(private val context: Context, private val packageManager: PackageMan
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
+ // Non-suspend version for java compat
+ fun createEdit(rawUri: Uri, consumer: Consumer<Intent>) {
+ applicationScope.launch { consumer.accept(createEdit(rawUri)) }
+ }
+
/**
* @return an ACTION_EDIT intent for the given URI, directed to config_preferredScreenshotEditor
* if enabled, falling back to config_screenshotEditor if that's non-empty.
*/
- fun createEdit(rawUri: Uri): Intent {
+ suspend fun createEdit(rawUri: Uri): Intent {
val uri = uriWithoutUserId(rawUri)
val editIntent = Intent(Intent.ACTION_EDIT)
@@ -112,22 +130,30 @@ constructor(private val context: Context, private val packageManager: PackageMan
.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
}
- private fun preferredEditor(): ComponentName? =
+ 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,
)
-
- return info.activities
- ?.firstOrNull { it.componentName.className.equals(component.className) }
- ?.componentName
+ info.activities?.firstOrNull {
+ it.componentName.className == component.className
+ } != null
+ } catch (e: NameNotFoundException) {
+ false
}
- .getOrNull()
+ }
private fun defaultEditor(): ComponentName? =
runCatching {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
index 4373389f4a51..d91f267ff3ca 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
@@ -22,6 +22,7 @@ import android.net.Uri
import android.util.Log
import androidx.appcompat.content.res.AppCompatResources
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.DebugLogger.debugLog
import com.android.systemui.res.R
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_EDIT_TAPPED
@@ -34,6 +35,8 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import java.util.UUID
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
/**
* Provides actions for screenshots. This class can be overridden by a vendor-specific SysUI
@@ -68,6 +71,7 @@ constructor(
private val context: Context,
private val uiEventLogger: UiEventLogger,
private val actionIntentCreator: ActionIntentCreator,
+ @Application private val applicationScope: CoroutineScope,
@Assisted val requestId: UUID,
@Assisted val request: ScreenshotData,
@Assisted val actionExecutor: ActionExecutor,
@@ -75,7 +79,7 @@ constructor(
) : ScreenshotActionsProvider {
private var addedScrollChip = false
private var onScrollClick: Runnable? = null
- private var pendingAction: ((ScreenshotSavedResult) -> Unit)? = null
+ private var pendingAction: (suspend (ScreenshotSavedResult) -> Unit)? = null
private var result: ScreenshotSavedResult? = null
private var webUri: Uri? = null
@@ -166,15 +170,16 @@ constructor(
return
}
this.result = result
- pendingAction?.invoke(result)
+ pendingAction?.also { applicationScope.launch { it.invoke(result) } }
}
override fun onAssistContent(assistContent: AssistContent?) {
webUri = assistContent?.webUri
}
- private fun onDeferrableActionTapped(onResult: (ScreenshotSavedResult) -> Unit) {
- result?.let { onResult.invoke(it) } ?: run { pendingAction = onResult }
+ private fun onDeferrableActionTapped(onResult: suspend (ScreenshotSavedResult) -> Unit) {
+ result?.let { applicationScope.launch { onResult.invoke(it) } }
+ ?: run { pendingAction = onResult }
}
@AssistedFactory
@@ -188,6 +193,6 @@ constructor(
}
companion object {
- private const val TAG = "ScreenshotActionsProvider"
+ private const val TAG = "ScreenshotActionsPrvdr"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java
index ecea30f1b1c3..88ffd4fa2750 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java
@@ -352,30 +352,35 @@ public class LongScreenshotActivity extends Activity {
private void doEdit(Uri uri) {
if (mScreenshotUserHandle != Process.myUserHandle()) {
// TODO: Fix transition for work profile. Omitting it in the meantime.
- mActionExecutor.launchIntentAsync(
- mActionIntentCreator.createEdit(uri),
- mScreenshotUserHandle, false,
- /* activityOptions */ null, /* transitionCoordinator */ null);
+ mActionIntentCreator.createEdit(uri, intent -> {
+ mActionExecutor.launchIntentAsync(
+ intent,
+ mScreenshotUserHandle, false,
+ /* activityOptions */ null, /* transitionCoordinator */ null);
+ });
+
} else {
if (usePreferredImageEditor()) {
- Intent intent = mActionIntentCreator.createEdit(uri);
- Bundle options = null;
-
- if (intent.getComponent() != null) {
- // Modify intent for shared transition if we're opening a specific editor.
- intent.removeFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.removeFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
- mTransitionView.setImageBitmap(mOutputBitmap);
- mTransitionView.setVisibility(View.VISIBLE);
- mTransitionView.setTransitionName(
- ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME);
- options = ActivityOptions.makeSceneTransitionAnimation(this, mTransitionView,
- ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle();
- // TODO: listen for transition completing instead of finishing onStop
- mTransitionStarted = true;
- }
+ mActionIntentCreator.createEdit(uri, intent -> {
+ Bundle options = null;
+
+ if (intent.getComponent() != null) {
+ // Modify intent for shared transition if we're opening a specific editor.
+ intent.removeFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.removeFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ mTransitionView.setImageBitmap(mOutputBitmap);
+ mTransitionView.setVisibility(View.VISIBLE);
+ mTransitionView.setTransitionName(
+ ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME);
+ options = ActivityOptions.makeSceneTransitionAnimation(this,
+ mTransitionView,
+ ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle();
+ // TODO: listen for transition completing instead of finishing onStop
+ mTransitionStarted = true;
+ }
- startActivity(intent, options);
+ startActivity(intent, options);
+ });
} else {
String editorPackage = getString(R.string.config_screenshotEditor);
Intent intent = new Intent(Intent.ACTION_EDIT);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt
index 600572545d55..1f189a540aa2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentCreatorTest.kt
@@ -35,6 +35,10 @@ import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
+import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
@@ -43,9 +47,13 @@ import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class ActionIntentCreatorTest : SysuiTestCase() {
+ private val scheduler = TestCoroutineScheduler()
+ private val mainDispatcher = UnconfinedTestDispatcher(scheduler)
+ private val testScope = TestScope(mainDispatcher)
val context = mock<Context>()
val packageManager = mock<PackageManager>()
- private val actionIntentCreator = ActionIntentCreator(context, packageManager)
+ private val actionIntentCreator =
+ ActionIntentCreator(context, packageManager, testScope.backgroundScope, mainDispatcher)
@Test
fun testCreateShare() {
@@ -132,7 +140,7 @@ class ActionIntentCreatorTest : SysuiTestCase() {
@Test
@DisableFlags(Flags.FLAG_USE_PREFERRED_IMAGE_EDITOR)
- fun testCreateEditLegacy() {
+ fun testCreateEditLegacy() = runTest {
val uri = Uri.parse("content://fake")
whenever(context.getString(eq(R.string.config_screenshotEditor))).thenReturn("")
@@ -155,7 +163,7 @@ class ActionIntentCreatorTest : SysuiTestCase() {
@Test
@DisableFlags(Flags.FLAG_USE_PREFERRED_IMAGE_EDITOR)
- fun testCreateEditLegacy_embeddedUserIdRemoved() {
+ fun testCreateEditLegacy_embeddedUserIdRemoved() = runTest {
val uri = Uri.parse("content://555@fake")
whenever(context.getString(eq(R.string.config_screenshotEditor))).thenReturn("")
@@ -166,7 +174,7 @@ class ActionIntentCreatorTest : SysuiTestCase() {
@Test
@DisableFlags(Flags.FLAG_USE_PREFERRED_IMAGE_EDITOR)
- fun testCreateEditLegacy_withEditor() {
+ fun testCreateEditLegacy_withEditor() = runTest {
val uri = Uri.parse("content://fake")
val component = ComponentName("com.android.foo", "com.android.foo.Something")
@@ -180,7 +188,7 @@ class ActionIntentCreatorTest : SysuiTestCase() {
@Test
@EnableFlags(Flags.FLAG_USE_PREFERRED_IMAGE_EDITOR)
- fun testCreateEdit() {
+ fun testCreateEdit() = runTest {
val uri = Uri.parse("content://fake")
whenever(context.getString(eq(R.string.config_screenshotEditor))).thenReturn("")
@@ -203,7 +211,7 @@ class ActionIntentCreatorTest : SysuiTestCase() {
@Test
@EnableFlags(Flags.FLAG_USE_PREFERRED_IMAGE_EDITOR)
- fun testCreateEdit_embeddedUserIdRemoved() {
+ fun testCreateEdit_embeddedUserIdRemoved() = runTest {
val uri = Uri.parse("content://555@fake")
whenever(context.getString(eq(R.string.config_screenshotEditor))).thenReturn("")
@@ -214,7 +222,7 @@ class ActionIntentCreatorTest : SysuiTestCase() {
@Test
@EnableFlags(Flags.FLAG_USE_PREFERRED_IMAGE_EDITOR)
- fun testCreateEdit_withPreferredEditorEnabled() {
+ fun testCreateEdit_withPreferredEditorEnabled() = runTest {
val uri = Uri.parse("content://fake")
val fallbackComponent = ComponentName("com.android.foo", "com.android.foo.Something")
val preferredComponent = ComponentName("com.android.bar", "com.android.bar.Something")
@@ -243,7 +251,7 @@ class ActionIntentCreatorTest : SysuiTestCase() {
@Test
@EnableFlags(Flags.FLAG_USE_PREFERRED_IMAGE_EDITOR)
- fun testCreateEdit_withPreferredEditorDisabled() {
+ fun testCreateEdit_withPreferredEditorDisabled() = runTest {
val uri = Uri.parse("content://fake")
val fallbackComponent = ComponentName("com.android.foo", "com.android.foo.Something")
val preferredComponent = ComponentName("com.android.bar", "com.android.bar.Something")
@@ -266,7 +274,7 @@ class ActionIntentCreatorTest : SysuiTestCase() {
@Test
@EnableFlags(Flags.FLAG_USE_PREFERRED_IMAGE_EDITOR)
- fun testCreateEdit_withFallbackEditor() {
+ fun testCreateEdit_withFallbackEditor() = runTest {
val uri = Uri.parse("content://fake")
val component = ComponentName("com.android.foo", "com.android.foo.Something")