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

Commit ef50f662 authored by Matt Casey's avatar Matt Casey Committed by Android (Google) Code Review
Browse files

Merge "Move package manager lookup to background thread." into main

parents f6383eb9 f7209d15
Loading
Loading
Loading
Loading
+14 −1
Original line number Diff line number Diff line
@@ -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,
+34 −8
Original line number Diff line number Diff line
@@ -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 {
+10 −5
Original line number Diff line number Diff line
@@ -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"
    }
}
+26 −21
Original line number Diff line number Diff line
@@ -352,13 +352,16 @@ 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.
            mActionIntentCreator.createEdit(uri, intent -> {
                mActionExecutor.launchIntentAsync(
                    mActionIntentCreator.createEdit(uri),
                        intent,
                        mScreenshotUserHandle, false,
                        /* activityOptions */ null, /* transitionCoordinator */ null);
            });

        } else {
            if (usePreferredImageEditor()) {
                Intent intent = mActionIntentCreator.createEdit(uri);
                mActionIntentCreator.createEdit(uri, intent -> {
                    Bundle options = null;

                    if (intent.getComponent() != null) {
@@ -369,13 +372,15 @@ public class LongScreenshotActivity extends Activity {
                        mTransitionView.setVisibility(View.VISIBLE);
                        mTransitionView.setTransitionName(
                                ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME);
                    options = ActivityOptions.makeSceneTransitionAnimation(this, mTransitionView,
                        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);
                });
            } else {
                String editorPackage = getString(R.string.config_screenshotEditor);
                Intent intent = new Intent(Intent.ACTION_EDIT);
+17 −9
Original line number Diff line number Diff line
@@ -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")