summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--java/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUi.java9
-rw-r--r--java/src/com/android/intentresolver/contentpreview/PayloadToggleInteractor.kt2
-rw-r--r--java/src/com/android/intentresolver/contentpreview/ShareouselContentPreviewUi.kt2
-rw-r--r--java/src/com/android/intentresolver/contentpreview/shareousel/ui/viewmodel/ShareouselViewModel.kt29
-rw-r--r--java/src/com/android/intentresolver/v2/ChooserActivity.java21
-rw-r--r--java/src/com/android/intentresolver/v2/JavaFlowHelper.kt28
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/PayloadToggleInteractorTest.kt39
7 files changed, 115 insertions, 15 deletions
diff --git a/java/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUi.java b/java/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUi.java
index 471a33e6..acdf6ec6 100644
--- a/java/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUi.java
+++ b/java/src/com/android/intentresolver/contentpreview/ChooserContentPreviewUi.java
@@ -18,12 +18,14 @@ package com.android.intentresolver.contentpreview;
import static com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_FILE;
import static com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_IMAGE;
+import static com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_PAYLOAD_SELECTION;
import static com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_TEXT;
import android.content.ClipData;
import android.content.Intent;
import android.content.res.Resources;
import android.net.Uri;
+import android.service.chooser.Flags;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -154,6 +156,13 @@ public final class ChooserContentPreviewUi {
}
return fileContentPreviewUi;
}
+
+ //TODO: use flags injection
+ if (previewType == CONTENT_PREVIEW_PAYLOAD_SELECTION && Flags.chooserPayloadToggling()) {
+ transitionElementStatusCallback.onAllTransitionElementsReady(); // TODO
+ return new ShareouselContentPreviewUi(actionFactory);
+ }
+
boolean isSingleImageShare = previewData.getUriCount() == 1
&& typeClassifier.isImageType(previewData.getFirstFileInfo().getMimeType());
CharSequence text = targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT);
diff --git a/java/src/com/android/intentresolver/contentpreview/PayloadToggleInteractor.kt b/java/src/com/android/intentresolver/contentpreview/PayloadToggleInteractor.kt
index 3393dcfc..8a34e6a9 100644
--- a/java/src/com/android/intentresolver/contentpreview/PayloadToggleInteractor.kt
+++ b/java/src/com/android/intentresolver/contentpreview/PayloadToggleInteractor.kt
@@ -27,6 +27,7 @@ import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.channels.BufferOverflow.DROP_LATEST
@@ -40,6 +41,7 @@ import kotlinx.coroutines.launch
private const val TAG = "PayloadToggleInteractor"
+@OptIn(ExperimentalCoroutinesApi::class)
class PayloadToggleInteractor(
// must use single-thread dispatcher (or we should enforce it with a lock)
private val scope: CoroutineScope,
diff --git a/java/src/com/android/intentresolver/contentpreview/ShareouselContentPreviewUi.kt b/java/src/com/android/intentresolver/contentpreview/ShareouselContentPreviewUi.kt
index 51a3cb14..4dd0d3f5 100644
--- a/java/src/com/android/intentresolver/contentpreview/ShareouselContentPreviewUi.kt
+++ b/java/src/com/android/intentresolver/contentpreview/ShareouselContentPreviewUi.kt
@@ -63,7 +63,7 @@ internal class ShareouselContentPreviewUi(
val vm: BasePreviewViewModel = viewModel()
val interactor =
requireNotNull(vm.payloadToggleInteractor) { "Should not be null" }
- val viewModel = interactor.toShareouselViewModel(vm.imageLoader)
+ val viewModel = interactor.toShareouselViewModel(vm.imageLoader, actionFactory)
if (headlineViewParent != null) {
LaunchedEffect(Unit) {
diff --git a/java/src/com/android/intentresolver/contentpreview/shareousel/ui/viewmodel/ShareouselViewModel.kt b/java/src/com/android/intentresolver/contentpreview/shareousel/ui/viewmodel/ShareouselViewModel.kt
index 05523c7e..fae439e5 100644
--- a/java/src/com/android/intentresolver/contentpreview/shareousel/ui/viewmodel/ShareouselViewModel.kt
+++ b/java/src/com/android/intentresolver/contentpreview/shareousel/ui/viewmodel/ShareouselViewModel.kt
@@ -16,11 +16,17 @@
package com.android.intentresolver.contentpreview.shareousel.ui.viewmodel
import android.graphics.Bitmap
+import androidx.core.graphics.drawable.toBitmap
+import com.android.intentresolver.contentpreview.ChooserContentPreviewUi.ActionFactory
import com.android.intentresolver.contentpreview.ImageLoader
+import com.android.intentresolver.contentpreview.MutableActionFactory
import com.android.intentresolver.contentpreview.PayloadToggleInteractor
+import com.android.intentresolver.icon.BitmapIcon
import com.android.intentresolver.icon.ComposeIcon
+import com.android.intentresolver.widget.ActionRow.Action
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
data class ShareouselViewModel(
@@ -41,11 +47,23 @@ data class ShareouselImageViewModel(
val setSelected: (Boolean) -> Unit,
)
-fun PayloadToggleInteractor.toShareouselViewModel(imageLoader: ImageLoader): ShareouselViewModel {
+fun PayloadToggleInteractor.toShareouselViewModel(
+ imageLoader: ImageLoader,
+ actionFactory: ActionFactory
+): ShareouselViewModel {
return ShareouselViewModel(
headline = MutableStateFlow("Shareousel"),
previewKeys = previewKeys,
- actions = MutableStateFlow(emptyList()),
+ actions =
+ if (actionFactory is MutableActionFactory) {
+ actionFactory.customActionsFlow.map { actions ->
+ actions.map { it.toActionChipViewModel() }
+ }
+ } else {
+ flow {
+ emit(actionFactory.createCustomActions().map { it.toActionChipViewModel() })
+ }
+ },
centerIndex = targetPosition,
previewForKey = { key ->
val previewInteractor = previewInteractor(key)
@@ -59,3 +77,10 @@ fun PayloadToggleInteractor.toShareouselViewModel(imageLoader: ImageLoader): Sha
previewRowKey = { getKey(it) },
)
}
+
+private fun Action.toActionChipViewModel() =
+ ActionChipViewModel(
+ label?.toString() ?: "",
+ icon?.let { BitmapIcon(it.toBitmap()) },
+ onClick = { onClicked.run() }
+ )
diff --git a/java/src/com/android/intentresolver/v2/ChooserActivity.java b/java/src/com/android/intentresolver/v2/ChooserActivity.java
index 2ffd31d8..cdc05b95 100644
--- a/java/src/com/android/intentresolver/v2/ChooserActivity.java
+++ b/java/src/com/android/intentresolver/v2/ChooserActivity.java
@@ -29,6 +29,7 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE
import static androidx.lifecycle.LifecycleKt.getCoroutineScope;
+import static com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_PAYLOAD_SELECTION;
import static com.android.intentresolver.v2.ext.CreationExtrasExtKt.addDefaultArgs;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
import static com.android.internal.util.LatencyTracker.ACTION_LOAD_SHARE_SHEET;
@@ -122,6 +123,7 @@ import com.android.intentresolver.chooser.TargetInfo;
import com.android.intentresolver.contentpreview.BasePreviewViewModel;
import com.android.intentresolver.contentpreview.ChooserContentPreviewUi;
import com.android.intentresolver.contentpreview.HeadlineGeneratorImpl;
+import com.android.intentresolver.contentpreview.PayloadToggleInteractor;
import com.android.intentresolver.contentpreview.PreviewViewModel;
import com.android.intentresolver.emptystate.CompositeEmptyStateProvider;
import com.android.intentresolver.emptystate.CrossProfileIntentsChecker;
@@ -487,12 +489,29 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
chooserRequest.getAdditionalContentUri(),
chooserRequest.getFocusedItemPosition(),
mChooserServiceFeatureFlags.chooserPayloadToggling());
+ ChooserActionFactory chooserActionFactory = createChooserActionFactory();
+ ChooserContentPreviewUi.ActionFactory actionFactory = chooserActionFactory;
+ if (previewViewModel.getPreviewDataProvider().getPreviewType()
+ == CONTENT_PREVIEW_PAYLOAD_SELECTION
+ && android.service.chooser.Flags.chooserPayloadToggling()) {
+ PayloadToggleInteractor payloadToggleInteractor =
+ previewViewModel.getPayloadToggleInteractor();
+ if (payloadToggleInteractor != null) {
+ ChooserMutableActionFactory mutableActionFactory =
+ new ChooserMutableActionFactory(chooserActionFactory);
+ actionFactory = mutableActionFactory;
+ JavaFlowHelper.collect(
+ getCoroutineScope(getLifecycle()),
+ payloadToggleInteractor.getCustomActions(),
+ mutableActionFactory::updateCustomActions);
+ }
+ }
mChooserContentPreviewUi = new ChooserContentPreviewUi(
getCoroutineScope(getLifecycle()),
previewViewModel.getPreviewDataProvider(),
chooserRequest.getTargetIntent(),
previewViewModel.getImageLoader(),
- createChooserActionFactory(),
+ actionFactory,
mEnterTransitionAnimationDelegate,
new HeadlineGeneratorImpl(this),
chooserRequest.getContentTypeHint(),
diff --git a/java/src/com/android/intentresolver/v2/JavaFlowHelper.kt b/java/src/com/android/intentresolver/v2/JavaFlowHelper.kt
new file mode 100644
index 00000000..c6c977f6
--- /dev/null
+++ b/java/src/com/android/intentresolver/v2/JavaFlowHelper.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmName("JavaFlowHelper")
+
+package com.android.intentresolver.v2
+
+import java.util.function.Consumer
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+
+fun <T> collect(scope: CoroutineScope, flow: Flow<T>, collector: Consumer<T>): Job =
+ scope.launch { flow.collect { collector.accept(it) } }
diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/PayloadToggleInteractorTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/PayloadToggleInteractorTest.kt
index 472c2ba4..88e62a40 100644
--- a/tests/unit/src/com/android/intentresolver/contentpreview/PayloadToggleInteractorTest.kt
+++ b/tests/unit/src/com/android/intentresolver/contentpreview/PayloadToggleInteractorTest.kt
@@ -20,7 +20,7 @@ import android.content.Intent
import android.database.Cursor
import android.database.MatrixCursor
import android.net.Uri
-import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.TestScope
@@ -56,13 +56,22 @@ class PayloadToggleInteractorTest {
scheduler.runCurrent()
testSubject.stateFlow.first().let { initialState ->
- assertThat(initialState.items).hasSize(4)
- assertThat(initialState.items.map { it.uri })
+ assertWithMessage("Two pages (2 items each) are expected to be initially read")
+ .that(initialState.items)
+ .hasSize(4)
+ assertWithMessage("Unexpected cursor values")
+ .that(initialState.items.map { it.uri })
.containsExactly(*Array<Uri>(4, ::makeUri))
.inOrder()
- assertThat(initialState.hasMoreItemsBefore).isFalse()
- assertThat(initialState.hasMoreItemsAfter).isTrue()
- assertThat(initialState.allowSelectionChange).isTrue()
+ assertWithMessage("No more items are expected to the left")
+ .that(initialState.hasMoreItemsBefore)
+ .isFalse()
+ assertWithMessage("No more items are expected to the right")
+ .that(initialState.hasMoreItemsAfter)
+ .isTrue()
+ assertWithMessage("Selections should no be disabled")
+ .that(initialState.allowSelectionChange)
+ .isTrue()
}
testSubject.loadMoreNextItems()
@@ -71,13 +80,21 @@ class PayloadToggleInteractorTest {
scheduler.runCurrent()
testSubject.stateFlow.first().let { state ->
- assertThat(state.items.map { it.uri })
+ assertWithMessage("Unexpected cursor values")
+ .that(state.items.map { it.uri })
.containsExactly(*Array(6, ::makeUri))
.inOrder()
- assertThat(state.hasMoreItemsBefore).isFalse()
- assertThat(state.hasMoreItemsAfter).isTrue()
- assertThat(state.allowSelectionChange).isTrue()
- assertThat(state.items.map { testSubject.selected(it).first() })
+ assertWithMessage("No more items are expected to the left")
+ .that(state.hasMoreItemsBefore)
+ .isFalse()
+ assertWithMessage("No more items are expected to the right")
+ .that(state.hasMoreItemsAfter)
+ .isTrue()
+ assertWithMessage("Selections should no be disabled")
+ .that(state.allowSelectionChange)
+ .isTrue()
+ assertWithMessage("Wrong selected items")
+ .that(state.items.map { testSubject.selected(it).first() })
.containsExactly(true, false, true, false, false, true)
.inOrder()
}