summaryrefslogtreecommitdiff
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/ChooserParamsUpdateRepository.kt34
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateTargetIntentInteractor.kt12
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/model/ShareouselUpdate.kt34
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallback.kt31
-rw-r--r--java/src/com/android/intentresolver/v2/ChooserActivity.java34
-rw-r--r--java/src/com/android/intentresolver/v2/domain/interactor/ChooserRequestUpdateInteractor.kt58
6 files changed, 176 insertions, 27 deletions
diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/ChooserParamsUpdateRepository.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/ChooserParamsUpdateRepository.kt
new file mode 100644
index 00000000..1a4f2b83
--- /dev/null
+++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/ChooserParamsUpdateRepository.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package com.android.intentresolver.contentpreview.payloadtoggle.data.repository
+
+import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ShareouselUpdate
+import dagger.hilt.android.scopes.ViewModelScoped
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/** Chooser parameters Updates received from the sharing application payload change callback */
+// TODO: a scaffolding repository to deliver chooser parameter updates before we developed some
+// other, more thought-through solution.
+@ViewModelScoped
+class ChooserParamsUpdateRepository @Inject constructor() {
+ val updates = MutableStateFlow<ShareouselUpdate?>(null)
+
+ fun setUpdates(update: ShareouselUpdate) {
+ updates.tryEmit(update)
+ }
+}
diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateTargetIntentInteractor.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateTargetIntentInteractor.kt
index 4619e478..4cb1f5b6 100644
--- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateTargetIntentInteractor.kt
+++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateTargetIntentInteractor.kt
@@ -18,6 +18,7 @@
package com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor
+import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.ChooserParamsUpdateRepository
import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PreviewSelectionsRepository
import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.TargetIntentRepository
import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.CustomAction
@@ -30,6 +31,7 @@ import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.launch
@@ -38,6 +40,7 @@ class UpdateTargetIntentInteractor
@Inject
constructor(
private val intentRepository: TargetIntentRepository,
+ private val chooserParamsUpdateRepository: ChooserParamsUpdateRepository,
@CustomAction private val pendingIntentSender: PendingIntentSender,
private val selectionCallback: SelectionChangeCallback,
private val selectionRepo: PreviewSelectionsRepository,
@@ -47,12 +50,13 @@ constructor(
suspend fun launch(): Unit = coroutineScope {
launch {
intentRepository.targetIntent
- .mapLatest { targetIntent ->
- selectionCallback.onSelectionChanged(targetIntent)?.customActions ?: emptyList()
- }
- .collect { actions ->
+ .mapLatest { targetIntent -> selectionCallback.onSelectionChanged(targetIntent) }
+ .filterNotNull()
+ .collect { updates ->
+ val actions = updates.customActions ?: emptyList()
intentRepository.customActions.value =
actions.map { it.toCustomActionModel(pendingIntentSender) }
+ chooserParamsUpdateRepository.setUpdates(updates)
}
}
launch {
diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/model/ShareouselUpdate.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/model/ShareouselUpdate.kt
new file mode 100644
index 00000000..41a34d1a
--- /dev/null
+++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/model/ShareouselUpdate.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package com.android.intentresolver.contentpreview.payloadtoggle.domain.model
+
+import android.content.Intent
+import android.content.IntentSender
+import android.service.chooser.ChooserAction
+import android.service.chooser.ChooserTarget
+
+/** Sharing session updates provided by the sharing app from the payload change callback */
+data class ShareouselUpdate(
+ // for all properties, null value means no change
+ val customActions: List<ChooserAction>? = null,
+ val modifyShareAction: ChooserAction? = null,
+ val alternateIntents: List<Intent>? = null,
+ val callerTargets: List<ChooserTarget>? = null,
+ val refinementIntentSender: IntentSender? = null,
+ val resultIntentSender: IntentSender? = null,
+ val metadataText: CharSequence? = null,
+)
diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallback.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallback.kt
index 03295a31..e7644dc5 100644
--- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallback.kt
+++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallback.kt
@@ -20,17 +20,20 @@ import android.content.ContentInterface
import android.content.Intent
import android.content.Intent.EXTRA_CHOOSER_MODIFY_SHARE_ACTION
import android.content.Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER
+import android.content.Intent.EXTRA_CHOOSER_RESULT_INTENT_SENDER
import android.content.Intent.EXTRA_CHOOSER_TARGETS
import android.content.Intent.EXTRA_INTENT
+import android.content.Intent.EXTRA_METADATA_TEXT
import android.content.IntentSender
import android.net.Uri
import android.os.Bundle
import android.service.chooser.AdditionalContentContract.MethodNames.ON_SELECTION_CHANGED
import android.service.chooser.ChooserAction
import android.service.chooser.ChooserTarget
-import com.android.intentresolver.contentpreview.payloadtoggle.domain.update.SelectionChangeCallback.ShareouselUpdate
+import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ShareouselUpdate
import com.android.intentresolver.inject.AdditionalContent
import com.android.intentresolver.inject.ChooserIntent
+import com.android.intentresolver.inject.ChooserServiceFlags
import com.android.intentresolver.v2.ui.viewmodel.readAlternateIntents
import com.android.intentresolver.v2.ui.viewmodel.readChooserActions
import com.android.intentresolver.v2.validation.Invalid
@@ -56,15 +59,6 @@ private const val TAG = "SelectionChangeCallback"
*/
fun interface SelectionChangeCallback {
suspend fun onSelectionChanged(targetIntent: Intent): ShareouselUpdate?
-
- data class ShareouselUpdate(
- // for all properties, null value means no change
- val customActions: List<ChooserAction>? = null,
- val modifyShareAction: ChooserAction? = null,
- val alternateIntents: List<Intent>? = null,
- val callerTargets: List<ChooserTarget>? = null,
- val refinementIntentSender: IntentSender? = null,
- )
}
class SelectionChangeCallbackImpl
@@ -73,6 +67,7 @@ constructor(
@AdditionalContent private val uri: Uri,
@ChooserIntent private val chooserIntent: Intent,
private val contentResolver: ContentInterface,
+ private val flags: ChooserServiceFlags,
) : SelectionChangeCallback {
private val mutex = Mutex()
@@ -92,7 +87,7 @@ constructor(
)
}
?.let { bundle ->
- return when (val result = readCallbackResponse(bundle)) {
+ return when (val result = readCallbackResponse(bundle, flags)) {
is Valid -> result.value
is Invalid -> {
result.errors.forEach { it.log(TAG) }
@@ -102,7 +97,10 @@ constructor(
}
}
-private fun readCallbackResponse(bundle: Bundle): ValidationResult<ShareouselUpdate> {
+private fun readCallbackResponse(
+ bundle: Bundle,
+ flags: ChooserServiceFlags
+): ValidationResult<ShareouselUpdate> {
return validateFrom(bundle::get) {
val customActions = readChooserActions()
val modifyShareAction = optional(value<ChooserAction>(EXTRA_CHOOSER_MODIFY_SHARE_ACTION))
@@ -110,6 +108,13 @@ private fun readCallbackResponse(bundle: Bundle): ValidationResult<ShareouselUpd
val callerTargets = optional(array<ChooserTarget>(EXTRA_CHOOSER_TARGETS))
val refinementIntentSender =
optional(value<IntentSender>(EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER))
+ val resultIntentSender = optional(value<IntentSender>(EXTRA_CHOOSER_RESULT_INTENT_SENDER))
+ val metadataText =
+ if (flags.enableSharesheetMetadataExtra()) {
+ optional(value<CharSequence>(EXTRA_METADATA_TEXT))
+ } else {
+ null
+ }
ShareouselUpdate(
customActions,
@@ -117,6 +122,8 @@ private fun readCallbackResponse(bundle: Bundle): ValidationResult<ShareouselUpd
alternateIntents,
callerTargets,
refinementIntentSender,
+ resultIntentSender,
+ metadataText,
)
}
}
diff --git a/java/src/com/android/intentresolver/v2/ChooserActivity.java b/java/src/com/android/intentresolver/v2/ChooserActivity.java
index bda393b8..110983d0 100644
--- a/java/src/com/android/intentresolver/v2/ChooserActivity.java
+++ b/java/src/com/android/intentresolver/v2/ChooserActivity.java
@@ -490,11 +490,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
mLatencyTracker.onActionStart(ACTION_LOAD_SHARE_SHEET);
mPinnedSharedPrefs = getPinnedSharedPrefs(this);
- IntentSender chosenComponentSender = mRequest.getChosenComponentSender();
- if (chosenComponentSender != null) {
- mShareResultSender = mShareResultSenderFactory.create(
- mViewModel.getActivityModel().getLaunchedFromUid(), chosenComponentSender);
- }
+ updateShareResultSender();
mMaxTargetsPerRow =
getResources().getInteger(R.integer.config_chooser_max_targets_per_row);
@@ -661,11 +657,37 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
}
private void onChooserRequestChanged(ChooserRequest chooserRequest) {
+ // intentional reference comarison
if (mRequest == chooserRequest) {
return;
}
+ boolean recreateAdapters = shouldUpdateAdapters(mRequest, chooserRequest);
mRequest = chooserRequest;
- recreatePagerAdapter();
+ updateShareResultSender();
+ if (recreateAdapters) {
+ recreatePagerAdapter();
+ }
+ }
+
+ private void updateShareResultSender() {
+ IntentSender chosenComponentSender = mRequest.getChosenComponentSender();
+ if (chosenComponentSender != null) {
+ mShareResultSender = mShareResultSenderFactory.create(
+ mViewModel.getActivityModel().getLaunchedFromUid(), chosenComponentSender);
+ } else {
+ mShareResultSender = null;
+ }
+ }
+
+ private boolean shouldUpdateAdapters(
+ ChooserRequest oldChooserRequest, ChooserRequest newChooserRequest) {
+ Intent oldTargetIntent = oldChooserRequest.getTargetIntent();
+ Intent newTargetIntent = newChooserRequest.getTargetIntent();
+
+ // TODO: a workaround for the unnecessary target reloading caused by multiple flow updates -
+ // an artifact of the current implementation; revisit.
+ // reference comparison is intentional
+ return oldTargetIntent != newTargetIntent;
}
private void recreatePagerAdapter() {
diff --git a/java/src/com/android/intentresolver/v2/domain/interactor/ChooserRequestUpdateInteractor.kt b/java/src/com/android/intentresolver/v2/domain/interactor/ChooserRequestUpdateInteractor.kt
index 99da5c81..534b2be3 100644
--- a/java/src/com/android/intentresolver/v2/domain/interactor/ChooserRequestUpdateInteractor.kt
+++ b/java/src/com/android/intentresolver/v2/domain/interactor/ChooserRequestUpdateInteractor.kt
@@ -18,7 +18,9 @@ package com.android.intentresolver.v2.domain.interactor
import android.content.Intent
import android.util.Log
+import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.ChooserParamsUpdateRepository
import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.TargetIntentRepository
+import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ShareouselUpdate
import com.android.intentresolver.inject.ChooserServiceFlags
import com.android.intentresolver.inject.TargetIntent
import com.android.intentresolver.v2.ui.model.ActivityModel
@@ -30,8 +32,12 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.android.scopes.ViewModelScoped
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
private const val TAG = "ChooserRequestUpdate"
@@ -43,17 +49,27 @@ constructor(
private val activityModel: ActivityModel,
@TargetIntent private val initialIntent: Intent,
private val targetIntentRepository: TargetIntentRepository,
+ private val paramsUpdateRepository: ChooserParamsUpdateRepository,
// TODO: replace with a proper repository, when available
@Assisted private val chooserRequestRepository: MutableStateFlow<ChooserRequest>,
private val flags: ChooserServiceFlags,
) {
suspend fun launch() {
- targetIntentRepository.targetIntent
- // TODO: maybe find a better way to exclude the initial intent (as here it's compared by
- // reference)
- .filter { it !== initialIntent }
- .collect(::updateTargetIntent)
+ coroutineScope {
+ launch {
+ targetIntentRepository.targetIntent
+ // TODO: maybe find a better way to exclude the initial intent (as here it's
+ // compared by
+ // reference)
+ .filter { it !== initialIntent }
+ .collect(::updateTargetIntent)
+ }
+
+ launch {
+ paramsUpdateRepository.updates.filterNotNull().collect(::updateChooserParameters)
+ }
+ }
}
private fun updateTargetIntent(targetIntent: Intent) {
@@ -64,6 +80,38 @@ constructor(
}
}
+ private fun updateChooserParameters(update: ShareouselUpdate) {
+ chooserRequestRepository.update { current ->
+ ChooserRequest(
+ current.targetIntent,
+ current.targetAction,
+ current.isSendActionTarget,
+ current.targetType,
+ current.launchedFromPackage,
+ current.title,
+ current.defaultTitleResource,
+ current.referrer,
+ current.filteredComponentNames,
+ update.callerTargets ?: current.callerChooserTargets,
+ // chooser actions are handled separately
+ current.chooserActions,
+ update.modifyShareAction ?: current.modifyShareAction,
+ current.shouldRetainInOnStop,
+ update.alternateIntents ?: current.additionalTargets,
+ current.replacementExtras,
+ current.initialIntents,
+ update.resultIntentSender ?: current.chosenComponentSender,
+ update.refinementIntentSender ?: current.refinementIntentSender,
+ current.sharedText,
+ current.shareTargetFilter,
+ current.additionalContentUri,
+ current.focusedItemPosition,
+ current.contentTypeHint,
+ update.metadataText ?: current.metadataText,
+ )
+ }
+ }
+
private fun ActivityModel.updateWithTargetIntent(targetIntent: Intent) =
ActivityModel(
Intent(intent).apply { putExtra(Intent.EXTRA_INTENT, targetIntent) },