summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--java/src/com/android/intentresolver/contentpreview/ContentPreviewType.java4
-rw-r--r--java/src/com/android/intentresolver/contentpreview/PreviewDataProvider.kt25
-rw-r--r--java/src/com/android/intentresolver/contentpreview/PreviewViewModel.kt8
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt35
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt137
5 files changed, 177 insertions, 32 deletions
diff --git a/java/src/com/android/intentresolver/contentpreview/ContentPreviewType.java b/java/src/com/android/intentresolver/contentpreview/ContentPreviewType.java
index ad1c6c01..79bb9d3c 100644
--- a/java/src/com/android/intentresolver/contentpreview/ContentPreviewType.java
+++ b/java/src/com/android/intentresolver/contentpreview/ContentPreviewType.java
@@ -25,11 +25,13 @@ import java.lang.annotation.Retention;
@Retention(SOURCE)
@IntDef({ContentPreviewType.CONTENT_PREVIEW_FILE,
ContentPreviewType.CONTENT_PREVIEW_IMAGE,
- ContentPreviewType.CONTENT_PREVIEW_TEXT})
+ ContentPreviewType.CONTENT_PREVIEW_TEXT,
+ ContentPreviewType.CONTENT_PREVIEW_PAYLOAD_SELECTION})
public @interface ContentPreviewType {
// Starting at 1 since 0 is considered "undefined" for some of the database transformations
// of tron logs.
int CONTENT_PREVIEW_IMAGE = 1;
int CONTENT_PREVIEW_FILE = 2;
int CONTENT_PREVIEW_TEXT = 3;
+ int CONTENT_PREVIEW_PAYLOAD_SELECTION = 4;
}
diff --git a/java/src/com/android/intentresolver/contentpreview/PreviewDataProvider.kt b/java/src/com/android/intentresolver/contentpreview/PreviewDataProvider.kt
index 659f7dc9..8073cfec 100644
--- a/java/src/com/android/intentresolver/contentpreview/PreviewDataProvider.kt
+++ b/java/src/com/android/intentresolver/contentpreview/PreviewDataProvider.kt
@@ -31,6 +31,7 @@ import androidx.annotation.OpenForTesting
import androidx.annotation.VisibleForTesting
import com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_FILE
import com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_IMAGE
+import com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_PAYLOAD_SELECTION
import com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_TEXT
import com.android.intentresolver.measurements.runTracing
import com.android.intentresolver.util.ownedByCurrentUser
@@ -74,7 +75,11 @@ open class PreviewDataProvider
constructor(
private val scope: CoroutineScope,
private val targetIntent: Intent,
+ private val additionalContentUri: Uri?,
private val contentResolver: ContentInterface,
+ // TODO: replace with the ChooserServiceFlags ref when PreviewViewModel dependencies are sorted
+ // out
+ private val isPayloadTogglingEnabled: Boolean,
private val typeClassifier: MimeTypeClassifier = DefaultMimeTypeClassifier,
) {
@@ -125,6 +130,9 @@ constructor(
* IMAGE, FILE, TEXT. */
if (!targetIntent.isSend || records.isEmpty()) {
CONTENT_PREVIEW_TEXT
+ } else if (isPayloadTogglingEnabled && shouldShowPayloadSelection()) {
+ // TODO: replace with the proper flags injection
+ CONTENT_PREVIEW_PAYLOAD_SELECTION
} else {
try {
runBlocking(scope.coroutineContext) {
@@ -143,6 +151,23 @@ constructor(
}
}
+ private fun shouldShowPayloadSelection(): Boolean {
+ val extraContentUri = additionalContentUri ?: return false
+ return runCatching {
+ val authority = extraContentUri.authority
+ // TODO: verify that authority is case-sensitive
+ records.firstOrNull { authority == it.uri.authority } == null
+ }
+ .onFailure {
+ Log.w(
+ ContentPreviewUi.TAG,
+ "Failed to check URI authorities; no payload toggling",
+ it
+ )
+ }
+ .getOrDefault(false)
+ }
+
/**
* The first shared URI's metadata. This call wait's for the data to be loaded and falls back to
* a crude value if the data is not loaded within a time limit.
diff --git a/java/src/com/android/intentresolver/contentpreview/PreviewViewModel.kt b/java/src/com/android/intentresolver/contentpreview/PreviewViewModel.kt
index 7369fa0f..d694c6ff 100644
--- a/java/src/com/android/intentresolver/contentpreview/PreviewViewModel.kt
+++ b/java/src/com/android/intentresolver/contentpreview/PreviewViewModel.kt
@@ -49,7 +49,13 @@ class PreviewViewModel(
override val previewDataProvider by lazy {
val targetIntent = requireNotNull(this.targetIntent) { "Not initialized" }
- PreviewDataProvider(viewModelScope + dispatcher, targetIntent, contentResolver)
+ PreviewDataProvider(
+ viewModelScope + dispatcher,
+ targetIntent,
+ additionalContentUri,
+ contentResolver,
+ isPayloadTogglingEnabled,
+ )
}
override val imageLoader by lazy {
diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt
index 560f4be4..ad53eef4 100644
--- a/tests/unit/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt
+++ b/tests/unit/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUiTest.kt
@@ -18,6 +18,9 @@ package com.android.intentresolver.contentpreview
import android.content.Intent
import android.net.Uri
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import com.android.intentresolver.ContentTypeHint
import com.android.intentresolver.TestPreviewImageLoader
import com.android.intentresolver.contentpreview.ChooserContentPreviewUi.ActionFactory
@@ -31,6 +34,7 @@ import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import org.junit.Rule
import org.junit.Test
import org.mockito.Mockito.never
import org.mockito.Mockito.times
@@ -51,6 +55,7 @@ class ChooserContentPreviewUiTest {
override fun getExcludeSharedTextAction(): Consumer<Boolean> = Consumer<Boolean> {}
}
private val transitionCallback = mock<ImagePreviewView.TransitionElementStatusCallback>()
+ @get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
@Test
fun test_textPreviewType_useTextPreviewUi() {
@@ -146,4 +151,34 @@ class ChooserContentPreviewUiTest {
verify(previewData, times(1)).imagePreviewFileInfoFlow
verify(transitionCallback, never()).onAllTransitionElementsReady()
}
+
+ @Test
+ @RequiresFlagsDisabled(android.service.chooser.Flags.FLAG_CHOOSER_PAYLOAD_TOGGLING)
+ fun test_imagePayloadSelectionType_useImagePreviewUi() {
+ // Event if we returned wrong type due to a bug, we should not use payload selection UI
+ val uri = Uri.parse("content://org.pkg.app/img.png")
+ whenever(previewData.previewType)
+ .thenReturn(ContentPreviewType.CONTENT_PREVIEW_PAYLOAD_SELECTION)
+ whenever(previewData.uriCount).thenReturn(2)
+ whenever(previewData.firstFileInfo)
+ .thenReturn(FileInfo.Builder(uri).withPreviewUri(uri).withMimeType("image/png").build())
+ whenever(previewData.imagePreviewFileInfoFlow).thenReturn(MutableSharedFlow())
+ val testSubject =
+ ChooserContentPreviewUi(
+ testScope,
+ previewData,
+ Intent(Intent.ACTION_SEND),
+ imageLoader,
+ actionFactory,
+ transitionCallback,
+ headlineGenerator,
+ ContentTypeHint.NONE,
+ testMetadataText,
+ )
+ assertThat(testSubject.preferredContentPreview)
+ .isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
+ assertThat(testSubject.mContentPreviewUi).isInstanceOf(UnifiedContentPreviewUi::class.java)
+ verify(previewData, times(1)).imagePreviewFileInfoFlow
+ verify(transitionCallback, never()).onAllTransitionElementsReady()
+ }
}
diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt
index 4a8c1392..babfaaf5 100644
--- a/tests/unit/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt
+++ b/tests/unit/src/com/android/intentresolver/contentpreview/PreviewDataProviderTest.kt
@@ -21,16 +21,20 @@ import android.content.Intent
import android.database.MatrixCursor
import android.media.MediaMetadata
import android.net.Uri
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.provider.DocumentsContract
import com.android.intentresolver.mock
import com.android.intentresolver.whenever
import com.google.common.truth.Truth.assertThat
import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
+import org.junit.Rule
import org.junit.Test
import org.mockito.Mockito.any
import org.mockito.Mockito.never
@@ -42,12 +46,29 @@ class PreviewDataProviderTest {
private val contentResolver = mock<ContentInterface>()
private val mimeTypeClassifier = DefaultMimeTypeClassifier
private val testScope = TestScope(EmptyCoroutineContext + UnconfinedTestDispatcher())
+ @get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+ private fun createDataProvider(
+ targetIntent: Intent,
+ scope: CoroutineScope = testScope,
+ additionalContentUri: Uri? = null,
+ resolver: ContentInterface = contentResolver,
+ typeClassifier: MimeTypeClassifier = mimeTypeClassifier,
+ isPayloadTogglingEnabled: Boolean = false
+ ) =
+ PreviewDataProvider(
+ scope,
+ targetIntent,
+ additionalContentUri,
+ resolver,
+ isPayloadTogglingEnabled,
+ typeClassifier,
+ )
@Test
fun test_nonSendIntentAction_resolvesToTextPreviewUiSynchronously() {
val targetIntent = Intent(Intent.ACTION_VIEW)
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
+ val testSubject = createDataProvider(targetIntent)
assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_TEXT)
verify(contentResolver, never()).getType(any())
@@ -62,8 +83,7 @@ class PreviewDataProviderTest {
type = "text/plain"
}
whenever(contentResolver.getType(uri)).thenReturn("text/plain")
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
+ val testSubject = createDataProvider(targetIntent)
assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
assertThat(testSubject.uriCount).isEqualTo(1)
@@ -74,8 +94,7 @@ class PreviewDataProviderTest {
@Test
fun test_sendIntentWithoutUris_resolvesToTextPreviewUiSynchronously() {
val targetIntent = Intent(Intent.ACTION_SEND).apply { type = "image/png" }
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
+ val testSubject = createDataProvider(targetIntent)
assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_TEXT)
verify(contentResolver, never()).getType(any())
@@ -86,8 +105,7 @@ class PreviewDataProviderTest {
val uri = Uri.parse("content://org.pkg.app/image.png")
val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) }
whenever(contentResolver.getType(uri)).thenReturn("image/png")
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
+ val testSubject = createDataProvider(targetIntent)
assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
assertThat(testSubject.uriCount).isEqualTo(1)
@@ -101,8 +119,7 @@ class PreviewDataProviderTest {
val uri = Uri.parse("content://org.pkg.app/paper.pdf")
val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) }
whenever(contentResolver.getType(uri)).thenReturn("application/pdf")
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
+ val testSubject = createDataProvider(targetIntent)
assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
assertThat(testSubject.uriCount).isEqualTo(1)
@@ -120,8 +137,7 @@ class PreviewDataProviderTest {
putExtra(Intent.EXTRA_STREAM, uri)
}
whenever(contentResolver.getType(uri)).thenThrow(SecurityException("test failure"))
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
+ val testSubject = createDataProvider(targetIntent)
assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
assertThat(testSubject.uriCount).isEqualTo(1)
@@ -142,8 +158,7 @@ class PreviewDataProviderTest {
.thenThrow(SecurityException("test failure"))
whenever(contentResolver.query(uri, METADATA_COLUMNS, null, null))
.thenThrow(SecurityException("test failure"))
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
+ val testSubject = createDataProvider(targetIntent)
assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
assertThat(testSubject.uriCount).isEqualTo(1)
@@ -158,8 +173,7 @@ class PreviewDataProviderTest {
val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) }
whenever(contentResolver.getStreamTypes(uri, "*/*"))
.thenReturn(arrayOf("application/pdf", "image/png"))
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
+ val testSubject = createDataProvider(targetIntent)
assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
assertThat(testSubject.uriCount).isEqualTo(1)
@@ -195,8 +209,7 @@ class PreviewDataProviderTest {
val cursor = MatrixCursor(columns).apply { addRow(values) }
whenever(contentResolver.query(uri, METADATA_COLUMNS, null, null)).thenReturn(cursor)
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
+ val testSubject = createDataProvider(targetIntent)
assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
assertThat(testSubject.uriCount).isEqualTo(1)
@@ -214,8 +227,7 @@ class PreviewDataProviderTest {
val cursor = MatrixCursor(emptyArray())
whenever(contentResolver.query(uri, METADATA_COLUMNS, null, null)).thenReturn(cursor)
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
+ val testSubject = createDataProvider(targetIntent)
assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
verify(contentResolver, times(1)).query(uri, METADATA_COLUMNS, null, null)
@@ -238,8 +250,7 @@ class PreviewDataProviderTest {
}
whenever(contentResolver.getType(uri1)).thenReturn("image/png")
whenever(contentResolver.getType(uri2)).thenReturn("image/jpeg")
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
+ val testSubject = createDataProvider(targetIntent)
assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
assertThat(testSubject.uriCount).isEqualTo(2)
@@ -265,8 +276,7 @@ class PreviewDataProviderTest {
}
)
}
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
+ val testSubject = createDataProvider(targetIntent)
assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
assertThat(testSubject.uriCount).isEqualTo(2)
@@ -293,8 +303,7 @@ class PreviewDataProviderTest {
whenever(contentResolver.getType(uri1)).thenReturn("video/mpeg4")
whenever(contentResolver.getStreamTypes(uri1, "*/*")).thenReturn(arrayOf("image/png"))
whenever(contentResolver.getType(uri2)).thenReturn("application/pdf")
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
+ val testSubject = createDataProvider(targetIntent)
assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
assertThat(testSubject.uriCount).isEqualTo(2)
@@ -319,8 +328,7 @@ class PreviewDataProviderTest {
}
whenever(contentResolver.getType(uri1)).thenReturn("text/html")
whenever(contentResolver.getType(uri2)).thenReturn("application/pdf")
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
+ val testSubject = createDataProvider(targetIntent)
assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_FILE)
assertThat(testSubject.uriCount).isEqualTo(2)
@@ -350,8 +358,7 @@ class PreviewDataProviderTest {
.thenReturn(arrayOf("text/html", "image/jpeg"))
whenever(contentResolver.getStreamTypes(uri2, "*/*"))
.thenReturn(arrayOf("application/pdf", "image/png"))
- val testSubject =
- PreviewDataProvider(testScope, targetIntent, contentResolver, mimeTypeClassifier)
+ val testSubject = createDataProvider(targetIntent)
val fileInfoListOne = testSubject.imagePreviewFileInfoFlow.toList()
val fileInfoListTwo = testSubject.imagePreviewFileInfoFlow.toList()
@@ -364,4 +371,74 @@ class PreviewDataProviderTest {
verify(contentResolver, times(1)).getType(uri2)
verify(contentResolver, times(1)).getStreamTypes(uri2, "*/*")
}
+
+ @Test
+ fun sendItemsWithAdditionalContentUri_showPayloadTogglingUi() {
+ val uri = Uri.parse("content://org.pkg.app/image.png")
+ val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) }
+ whenever(contentResolver.getType(uri)).thenReturn("image/png")
+ val testSubject =
+ createDataProvider(
+ targetIntent,
+ additionalContentUri = Uri.parse("content://org.pkg.app.extracontent"),
+ isPayloadTogglingEnabled = true,
+ )
+
+ assertThat(testSubject.previewType)
+ .isEqualTo(ContentPreviewType.CONTENT_PREVIEW_PAYLOAD_SELECTION)
+ assertThat(testSubject.uriCount).isEqualTo(1)
+ assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
+ assertThat(testSubject.firstFileInfo?.previewUri).isEqualTo(uri)
+ verify(contentResolver, times(1)).getType(any())
+ }
+
+ @Test
+ fun sendItemsWithAdditionalContentUri_showImagePreviewUi() {
+ val uri = Uri.parse("content://org.pkg.app/image.png")
+ val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) }
+ whenever(contentResolver.getType(uri)).thenReturn("image/png")
+ val testSubject =
+ createDataProvider(
+ targetIntent,
+ additionalContentUri = Uri.parse("content://org.pkg.app.extracontent"),
+ )
+
+ assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
+ assertThat(testSubject.uriCount).isEqualTo(1)
+ assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
+ assertThat(testSubject.firstFileInfo?.previewUri).isEqualTo(uri)
+ verify(contentResolver, times(1)).getType(any())
+ }
+
+ @Test
+ fun sendItemsWithAdditionalContentUriWithSameAuthority_showImagePreviewUi() {
+ val uri = Uri.parse("content://org.pkg.app/image.png")
+ val targetIntent = Intent(Intent.ACTION_SEND).apply { putExtra(Intent.EXTRA_STREAM, uri) }
+ whenever(contentResolver.getType(uri)).thenReturn("image/png")
+ val testSubject =
+ createDataProvider(
+ targetIntent,
+ additionalContentUri = Uri.parse("content://org.pkg.app/extracontent"),
+ isPayloadTogglingEnabled = true,
+ )
+
+ assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_IMAGE)
+ assertThat(testSubject.uriCount).isEqualTo(1)
+ assertThat(testSubject.firstFileInfo?.uri).isEqualTo(uri)
+ assertThat(testSubject.firstFileInfo?.previewUri).isEqualTo(uri)
+ verify(contentResolver, times(1)).getType(any())
+ }
+
+ @Test
+ fun test_nonSendIntentActionWithAdditionalContentUri_resolvesToTextPreviewUiSynchronously() {
+ val targetIntent = Intent(Intent.ACTION_VIEW)
+ val testSubject =
+ createDataProvider(
+ targetIntent,
+ additionalContentUri = Uri.parse("content://org.pkg.app/extracontent")
+ )
+
+ assertThat(testSubject.previewType).isEqualTo(ContentPreviewType.CONTENT_PREVIEW_TEXT)
+ verify(contentResolver, never()).getType(any())
+ }
}