summaryrefslogtreecommitdiff
path: root/java/src/com
diff options
context:
space:
mode:
author Mark Renouf <mrenouf@google.com> 2024-01-29 14:10:07 -0500
committer Mark Renouf <mrenouf@google.com> 2024-02-03 10:43:48 -0500
commit29bd514e948c60b1dae2ae0fabc0d15adb2b0950 (patch)
tree6df50dbe87f34139d6ba818987ca777ac012b356 /java/src/com
parent0ccdb68644e8e531c67cfc73a56dd9c95352829f (diff)
Adds ResolverRequest, moves handing to tested code
This formalizes the inputs to ResolverActivity, replacing the equivalent inline code. Fields that were temporarily routed through 'ActivityLogic' are now removed. Bug: 300157408 Test: atest IntentResolver-tests-activity Test: atest IntentResolver-tests-unit:ResolveRequestTest Change-Id: I79d9fa21b91d0ce9b008af12ba3bffbd60e91a38
Diffstat (limited to 'java/src/com')
-rw-r--r--java/src/com/android/intentresolver/v2/ActivityLogic.kt36
-rw-r--r--java/src/com/android/intentresolver/v2/ChooserActivity.java58
-rw-r--r--java/src/com/android/intentresolver/v2/ChooserActivityLogic.kt21
-rw-r--r--java/src/com/android/intentresolver/v2/ResolverActivity.java114
-rw-r--r--java/src/com/android/intentresolver/v2/ResolverActivityLogic.kt30
-rw-r--r--java/src/com/android/intentresolver/v2/ui/model/ActivityLaunch.kt7
-rw-r--r--java/src/com/android/intentresolver/v2/ui/model/ActivityLaunchModule.kt5
-rw-r--r--java/src/com/android/intentresolver/v2/ui/model/ChooserRequest.kt38
-rw-r--r--java/src/com/android/intentresolver/v2/ui/model/ResolverRequest.kt68
-rw-r--r--java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestReader.kt7
-rw-r--r--java/src/com/android/intentresolver/v2/ui/viewmodel/ResolverRequestReader.kt59
11 files changed, 242 insertions, 201 deletions
diff --git a/java/src/com/android/intentresolver/v2/ActivityLogic.kt b/java/src/com/android/intentresolver/v2/ActivityLogic.kt
index b9686418..62ace0da 100644
--- a/java/src/com/android/intentresolver/v2/ActivityLogic.kt
+++ b/java/src/com/android/intentresolver/v2/ActivityLogic.kt
@@ -15,7 +15,6 @@
*/
package com.android.intentresolver.v2
-import android.content.Intent
import android.os.UserHandle
import android.os.UserManager
import android.util.Log
@@ -30,18 +29,7 @@ import com.android.intentresolver.WorkProfileAvailabilityManager
* activity, including test activities, but all implementations should delegate to a
* CommonActivityLogic implementation.
*/
-interface ActivityLogic : CommonActivityLogic {
- /** The intent for the target. This will always come before additional targets, if any. */
- val targetIntent: Intent
- /** Custom title to display. */
- val title: CharSequence?
- /** Resource ID for the title to display when there is no custom title. */
- val defaultTitleResId: Int
- /** Intents received to be processed. */
- val initialIntents: List<Intent>?
- /** The intents for potential actual targets. [targetIntent] must be first. */
- val payloadIntents: List<Intent>
-}
+interface ActivityLogic : CommonActivityLogic
/**
* Logic that is common to all IntentResolver activities. Anything that is the same across
@@ -50,14 +38,13 @@ interface ActivityLogic : CommonActivityLogic {
interface CommonActivityLogic {
/** The tag to use when logging. */
val tag: String
+
/** A reference to the activity owning, and used by, this logic. */
val activity: ComponentActivity
- /** The name of the referring package. */
- val referrerPackageName: String?
- /** User manager system service. */
- val userManager: UserManager
+
/** Current [UserHandle]s retrievable by type. */
val annotatedUserHandles: AnnotatedUserHandles?
+
/** Monitors for changes to work profile availability. */
val workProfileAvailabilityManager: WorkProfileAvailabilityManager
}
@@ -73,16 +60,7 @@ class CommonActivityLogicImpl(
onWorkProfileStatusUpdated: () -> Unit,
) : CommonActivityLogic {
- override val referrerPackageName: String? =
- activity.referrer.let {
- if (ANDROID_APP_URI_SCHEME == it?.scheme) {
- it.host
- } else {
- null
- }
- }
-
- override val userManager: UserManager = activity.getSystemService()!!
+ private val userManager: UserManager = activity.getSystemService()!!
override val annotatedUserHandles: AnnotatedUserHandles? =
try {
@@ -98,8 +76,4 @@ class CommonActivityLogicImpl(
annotatedUserHandles?.workProfileUserHandle,
onWorkProfileStatusUpdated,
)
-
- companion object {
- private const val ANDROID_APP_URI_SCHEME = "android-app"
- }
}
diff --git a/java/src/com/android/intentresolver/v2/ChooserActivity.java b/java/src/com/android/intentresolver/v2/ChooserActivity.java
index c1184a80..29a792f6 100644
--- a/java/src/com/android/intentresolver/v2/ChooserActivity.java
+++ b/java/src/com/android/intentresolver/v2/ChooserActivity.java
@@ -322,12 +322,11 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
private ChooserViewModel mViewModel;
@VisibleForTesting
- protected ChooserActivityLogic createActivityLogic(ChooserRequest chooserRequest) {
+ protected ChooserActivityLogic createActivityLogic() {
return new ChooserActivityLogic(
TAG,
/* activity = */ this,
- this::onWorkProfileStatusUpdated,
- chooserRequest);
+ this::onWorkProfileStatusUpdated);
}
@NonNull
@@ -355,7 +354,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
finish();
return;
}
- mLogic = createActivityLogic(mViewModel.getChooserRequest());
+ mLogic = createActivityLogic();
init();
}
@@ -381,14 +380,8 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
chooserRequest.getShareTargetFilter()
);
- Intent intent = mLogic.getTargetIntent();
- List<Intent> initialIntents = mLogic.getInitialIntents();
-
- // Calling UID did not have valid permissions
- if (mLogic.getAnnotatedUserHandles() == null) {
- finish();
- return;
- }
+ Intent intent = mViewModel.getChooserRequest().getTargetIntent();
+ List<Intent> initialIntents = mViewModel.getChooserRequest().getInitialIntents();
mChooserMultiProfilePagerAdapter = createMultiProfilePagerAdapter(
requireNonNullElse(initialIntents, emptyList()).toArray(new Intent[0]),
@@ -509,7 +502,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
Log.d(TAG, "System Time Cost is " + systemCost);
}
getEventLog().logShareStarted(
- mLogic.getReferrerPackageName(),
+ chooserRequest.getReferrerPackage(),
chooserRequest.getTargetType(),
chooserRequest.getCallerChooserTargets().size(),
chooserRequest.getInitialIntents().size(),
@@ -714,9 +707,10 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
}
}
- CharSequence title = mLogic.getTitle() != null
- ? mLogic.getTitle()
- : getTitleForAction(mLogic.getTargetIntent(), mLogic.getDefaultTitleResId());
+ CharSequence title = mViewModel.getChooserRequest().getTitle() != null
+ ? mViewModel.getChooserRequest().getTitle()
+ : getTitleForAction(mViewModel.getChooserRequest().getTargetIntent(),
+ mViewModel.getChooserRequest().getDefaultTitleResource());
if (!TextUtils.isEmpty(title)) {
final TextView titleView = findViewById(com.android.internal.R.id.title);
@@ -815,7 +809,8 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
}
// If needed, show that intent is forwarded
// from managed profile to owner or other way around.
- String profileSwitchMessage = mIntentForwarding.forwardMessageFor(mLogic.getTargetIntent());
+ String profileSwitchMessage = mIntentForwarding.forwardMessageFor(
+ mViewModel.getChooserRequest().getTargetIntent());
if (profileSwitchMessage != null) {
Toast.makeText(this, profileSwitchMessage, Toast.LENGTH_LONG).show();
}
@@ -1283,7 +1278,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
boolean filterLastUsed) {
ChooserGridAdapter adapter = createChooserGridAdapter(
/* context */ this,
- mLogic.getPayloadIntents(),
+ mViewModel.getChooserRequest().getPayloadIntents(),
initialIntents,
rList,
filterLastUsed,
@@ -1314,7 +1309,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
int selectedProfile = findSelectedProfile();
ChooserGridAdapter personalAdapter = createChooserGridAdapter(
/* context */ this,
- mLogic.getPayloadIntents(),
+ mViewModel.getChooserRequest().getPayloadIntents(),
selectedProfile == PROFILE_PERSONAL ? initialIntents : null,
rList,
filterLastUsed,
@@ -1322,7 +1317,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
);
ChooserGridAdapter workAdapter = createChooserGridAdapter(
/* context */ this,
- mLogic.getPayloadIntents(),
+ mViewModel.getChooserRequest().getPayloadIntents(),
selectedProfile == PROFILE_WORK ? initialIntents : null,
rList,
filterLastUsed,
@@ -1823,7 +1818,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
if (info != null) {
sendClickToAppPredictor(info);
final ResolveInfo ri = info.getResolveInfo();
- Intent targetIntent = mLogic.getTargetIntent();
+ Intent targetIntent = mViewModel.getChooserRequest().getTargetIntent();
if (ri != null && ri.activityInfo != null && targetIntent != null) {
ChooserListAdapter currentListAdapter =
mChooserMultiProfilePagerAdapter.getActiveListAdapter();
@@ -1959,7 +1954,6 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
}
}
- @VisibleForTesting
public ChooserGridAdapter createChooserGridAdapter(
Context context,
List<Intent> payloadIntents,
@@ -1967,7 +1961,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
List<ResolveInfo> rList,
boolean filterLastUsed,
UserHandle userHandle) {
- ChooserRequest parameters = mViewModel.getChooserRequest();
+ ChooserRequest request = mViewModel.getChooserRequest();
ChooserListAdapter chooserListAdapter = createChooserListAdapter(
context,
payloadIntents,
@@ -1976,8 +1970,8 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
filterLastUsed,
createListController(userHandle),
userHandle,
- mLogic.getTargetIntent(),
- parameters.getReferrerFillInIntent(),
+ request.getTargetIntent(),
+ request.getReferrerFillInIntent(),
mMaxTargetsPerRow
);
@@ -2081,8 +2075,8 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
if (appPredictor != null) {
resolverComparator = new AppPredictionServiceResolverComparator(
this,
- mLogic.getTargetIntent(),
- mLogic.getReferrerPackageName(),
+ mViewModel.getChooserRequest().getTargetIntent(),
+ mViewModel.getChooserRequest().getLaunchedFromPackage(),
appPredictor,
userHandle,
getEventLog(),
@@ -2092,8 +2086,8 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
resolverComparator =
new ResolverRankerServiceResolverComparator(
this,
- mLogic.getTargetIntent(),
- mLogic.getReferrerPackageName(),
+ mViewModel.getChooserRequest().getTargetIntent(),
+ mViewModel.getChooserRequest().getReferrerPackage(),
null,
getEventLog(),
getResolverRankerServiceUserHandleList(userHandle),
@@ -2103,9 +2097,9 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
return new ChooserListController(
this,
mPackageManager,
- mLogic.getTargetIntent(),
- mLogic.getReferrerPackageName(),
- mActivityLaunch.getFromUid(),
+ mViewModel.getChooserRequest().getTargetIntent(),
+ mViewModel.getChooserRequest().getReferrerPackage(),
+ requireAnnotatedUserHandles().userIdOfCallingApp,
resolverComparator,
getQueryIntentsUser(userHandle));
}
diff --git a/java/src/com/android/intentresolver/v2/ChooserActivityLogic.kt b/java/src/com/android/intentresolver/v2/ChooserActivityLogic.kt
index f6054885..84b7d9a9 100644
--- a/java/src/com/android/intentresolver/v2/ChooserActivityLogic.kt
+++ b/java/src/com/android/intentresolver/v2/ChooserActivityLogic.kt
@@ -1,11 +1,7 @@
package com.android.intentresolver.v2
-import android.content.Intent
import androidx.activity.ComponentActivity
import androidx.annotation.OpenForTesting
-import com.android.intentresolver.v2.ui.model.ChooserRequest
-
-private const val TAG = "ChooserActivityLogic"
/**
* Activity logic for [ChooserActivity].
@@ -18,25 +14,10 @@ open class ChooserActivityLogic(
tag: String,
activity: ComponentActivity,
onWorkProfileStatusUpdated: () -> Unit,
- private val chooserRequest: ChooserRequest? = null,
) :
ActivityLogic,
CommonActivityLogic by CommonActivityLogicImpl(
tag,
activity,
onWorkProfileStatusUpdated,
- ) {
-
- override val targetIntent: Intent = chooserRequest?.targetIntent ?: Intent()
-
- override val title: CharSequence? = chooserRequest?.title
-
- override val defaultTitleResId: Int = chooserRequest?.defaultTitleResource ?: 0
-
- override val initialIntents: List<Intent>? = chooserRequest?.initialIntents?.toList()
-
- override val payloadIntents: List<Intent> = buildList {
- add(targetIntent)
- chooserRequest?.additionalTargets?.let { addAll(it) }
- }
-}
+ )
diff --git a/java/src/com/android/intentresolver/v2/ResolverActivity.java b/java/src/com/android/intentresolver/v2/ResolverActivity.java
index b8638ba4..77d1dbf5 100644
--- a/java/src/com/android/intentresolver/v2/ResolverActivity.java
+++ b/java/src/com/android/intentresolver/v2/ResolverActivity.java
@@ -25,11 +25,10 @@ import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_S
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static com.android.intentresolver.v2.ext.CreationExtrasExtKt.addDefaultArgs;
+import static com.android.intentresolver.v2.ui.viewmodel.ResolverRequestReaderKt.readResolverRequest;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
-import static java.util.Collections.emptyList;
import static java.util.Objects.requireNonNull;
-import static java.util.Objects.requireNonNullElse;
import android.app.ActivityThread;
import android.app.VoiceInteractor.PickOptionRequest;
@@ -105,13 +104,15 @@ import com.android.intentresolver.v2.MultiProfilePagerAdapter.OnSwitchOnWorkSele
import com.android.intentresolver.v2.MultiProfilePagerAdapter.ProfileType;
import com.android.intentresolver.v2.MultiProfilePagerAdapter.TabConfig;
import com.android.intentresolver.v2.data.repository.DevicePolicyResources;
+import com.android.intentresolver.v2.domain.model.Profile;
import com.android.intentresolver.v2.emptystate.NoAppsAvailableEmptyStateProvider;
import com.android.intentresolver.v2.emptystate.NoCrossProfileEmptyStateProvider;
import com.android.intentresolver.v2.emptystate.NoCrossProfileEmptyStateProvider.DevicePolicyBlockerEmptyState;
import com.android.intentresolver.v2.emptystate.WorkProfilePausedEmptyStateProvider;
-import com.android.intentresolver.v2.ext.IntentExtKt;
import com.android.intentresolver.v2.ui.ActionTitle;
import com.android.intentresolver.v2.ui.model.ActivityLaunch;
+import com.android.intentresolver.v2.ui.model.ResolverRequest;
+import com.android.intentresolver.v2.validation.ValidationResult;
import com.android.intentresolver.widget.ResolverDrawerLayout;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
@@ -144,10 +145,11 @@ import javax.inject.Inject;
public class ResolverActivity extends Hilt_ResolverActivity implements
ResolverListAdapter.ResolverListCommunicator {
+ @Inject public PackageManager mPackageManager;
@Inject public ActivityLaunch mActivityLaunch;
@Inject public DevicePolicyResources mDevicePolicyResources;
@Inject public IntentForwarding mIntentForwarding;
- @Inject public PackageManager mPackageManager;
+ private ResolverRequest mResolverRequest;
protected ActivityLogic mLogic;
protected TargetDataLoader mTargetDataLoader;
@@ -185,32 +187,8 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
protected ResolverMultiProfilePagerAdapter mMultiProfilePagerAdapter;
-
- // Intent extra for connected audio devices
- public static final String EXTRA_IS_AUDIO_CAPTURE_DEVICE = "is_audio_capture_device";
-
- /**
- * Integer extra to indicate which profile should be automatically selected.
- * <p>Can only be used if there is a work profile.
- * <p>Possible values can be either {@link #PROFILE_PERSONAL} or {@link #PROFILE_WORK}.
- */
- protected static final String EXTRA_SELECTED_PROFILE =
- "com.android.internal.app.ResolverActivity.EXTRA_SELECTED_PROFILE";
-
- /**
- * {@link UserHandle} extra to indicate the user of the user that the starting intent
- * originated from.
- * <p>This is not necessarily the same as {@link #getUserId()} or {@link UserHandle#myUserId()},
- * as there are edge cases when the intent resolver is launched in the other profile.
- * For example, when we have 0 resolved apps in current profile and multiple resolved
- * apps in the other profile, opening a link from the current profile launches the intent
- * resolver in the other one. b/148536209 for more info.
- */
- static final String EXTRA_CALLING_USER =
- "com.android.internal.app.ResolverActivity.EXTRA_CALLING_USER";
-
- protected static final int PROFILE_PERSONAL = MultiProfilePagerAdapter.PROFILE_PERSONAL;
- protected static final int PROFILE_WORK = MultiProfilePagerAdapter.PROFILE_WORK;
+ public static final int PROFILE_PERSONAL = MultiProfilePagerAdapter.PROFILE_PERSONAL;
+ public static final int PROFILE_WORK = MultiProfilePagerAdapter.PROFILE_WORK;
private UserHandle mHeaderCreatorUser;
@@ -234,7 +212,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
}
@VisibleForTesting
- protected ResolverActivityLogic createActivityLogic() {
+ protected ActivityLogic createActivityLogic() {
return new ResolverActivityLogic(
TAG,
/* activity = */ this,
@@ -261,22 +239,24 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
finish();
}
+ ValidationResult<ResolverRequest> result = readResolverRequest(mActivityLaunch);
+ if (!result.isSuccess()) {
+ result.reportToLogcat(TAG);
+ finish();
+ }
+ mResolverRequest = result.getOrThrow();
mLogic = createActivityLogic();
- mResolvingHome = IntentExtKt.isHomeIntent(getIntent());
+ mResolvingHome = mResolverRequest.isResolvingHome();
mTargetDataLoader = new DefaultTargetDataLoader(
this,
getLifecycle(),
- getIntent().getBooleanExtra(
- ResolverActivity.EXTRA_IS_AUDIO_CAPTURE_DEVICE,
- /* defaultValue = */ false)
- );
+ mResolverRequest.isAudioCaptureDevice());
init();
restore(savedInstanceState);
}
private void init() {
- Intent intent = mLogic.getTargetIntent();
- List<Intent> initialIntents = mLogic.getInitialIntents();
+ Intent intent = mResolverRequest.getIntent();
// The last argument of createResolverListAdapter is whether to do special handling
// of the last used choice to highlight it in the list. We need to always
@@ -289,8 +269,8 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
boolean filterLastUsed = !isVoiceInteraction()
&& !hasWorkProfile() && !hasCloneProfile();
mMultiProfilePagerAdapter = createMultiProfilePagerAdapter(
- requireNonNullElse(initialIntents, emptyList()).toArray(new Intent[0]),
- /* resolutionList = */ null,
+ new Intent[0],
+ /* resolutionList = */ mResolverRequest.getResolutionList(),
filterLastUsed
);
if (configureContentView(mTargetDataLoader)) {
@@ -764,8 +744,8 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
ResolverRankerServiceResolverComparator resolverComparator =
new ResolverRankerServiceResolverComparator(
this,
- mLogic.getTargetIntent(),
- mLogic.getReferrerPackageName(),
+ mResolverRequest.getIntent(),
+ mActivityLaunch.getReferrerPackage(),
null,
null,
getResolverRankerServiceUserHandleList(userHandle),
@@ -773,8 +753,8 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
return new ResolverListController(
this,
mPackageManager,
- mLogic.getTargetIntent(),
- mLogic.getReferrerPackageName(),
+ mActivityLaunch.getIntent(),
+ mActivityLaunch.getReferrerPackage(),
mActivityLaunch.getFromUid(),
resolverComparator,
getQueryIntentsUser(userHandle));
@@ -920,7 +900,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
filterLastUsed,
createListController(userHandle),
userHandle,
- mLogic.getTargetIntent(),
+ mResolverRequest.getIntent(),
this,
initialIntentsUserSpace,
mTargetDataLoader);
@@ -964,7 +944,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
boolean filterLastUsed) {
ResolverListAdapter personalAdapter = createResolverListAdapter(
/* context */ this,
- mLogic.getPayloadIntents(),
+ mResolverRequest.getPayloadIntents(),
initialIntents,
resolutionList,
filterLastUsed,
@@ -987,9 +967,8 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
}
private UserHandle getIntentUser() {
- return getIntent().hasExtra(EXTRA_CALLING_USER)
- ? getIntent().getParcelableExtra(EXTRA_CALLING_USER)
- : requireAnnotatedUserHandles().tabOwnerUserHandleForLaunch;
+ return Objects.requireNonNullElse(mResolverRequest.getCallingUser(),
+ requireAnnotatedUserHandles().tabOwnerUserHandleForLaunch);
}
private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForTwoProfiles(
@@ -1018,7 +997,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
// resolver list. So filterLastUsed should be false for the other profile.
ResolverListAdapter personalAdapter = createResolverListAdapter(
/* context */ this,
- mLogic.getPayloadIntents(),
+ mResolverRequest.getPayloadIntents(),
selectedProfile == PROFILE_PERSONAL ? initialIntents : null,
resolutionList,
(filterLastUsed && UserHandle.myUserId()
@@ -1028,7 +1007,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
UserHandle workProfileUserHandle = requireAnnotatedUserHandles().workProfileUserHandle;
ResolverListAdapter workAdapter = createResolverListAdapter(
/* context */ this,
- mLogic.getPayloadIntents(),
+ mResolverRequest.getPayloadIntents(),
selectedProfile == PROFILE_WORK ? initialIntents : null,
resolutionList,
(filterLastUsed && UserHandle.myUserId()
@@ -1060,20 +1039,17 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
/**
* Returns {@link #PROFILE_PERSONAL} or {@link #PROFILE_WORK} if the {@link
* #EXTRA_SELECTED_PROFILE} extra was supplied, or {@code -1} if no extra was supplied.
- * @throws IllegalArgumentException if the value passed to the {@link #EXTRA_SELECTED_PROFILE}
- * extra is not {@link #PROFILE_PERSONAL} or {@link #PROFILE_WORK}
*/
final int getSelectedProfileExtra() {
- int selectedProfile = -1;
- if (getIntent().hasExtra(EXTRA_SELECTED_PROFILE)) {
- selectedProfile = getIntent().getIntExtra(EXTRA_SELECTED_PROFILE, /* defValue = */ -1);
- if (selectedProfile != PROFILE_PERSONAL && selectedProfile != PROFILE_WORK) {
- throw new IllegalArgumentException(EXTRA_SELECTED_PROFILE + " has invalid value "
- + selectedProfile + ". Must be either ResolverActivity.PROFILE_PERSONAL or "
- + "ResolverActivity.PROFILE_WORK.");
- }
+ Profile.Type selected = mResolverRequest.getSelectedProfile();
+ if (selected == null) {
+ return -1;
+ }
+ switch (selected) {
+ case PERSONAL: return PROFILE_PERSONAL;
+ case WORK: return PROFILE_WORK;
+ default: return -1;
}
- return selectedProfile;
}
protected final @ProfileType int getCurrentProfile() {
@@ -1302,9 +1278,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
if (!hasRecordPermission) {
// OK, we know the record permission, is this a capture device
- boolean hasAudioCapture =
- getIntent().getBooleanExtra(
- ResolverActivity.EXTRA_IS_AUDIO_CAPTURE_DEVICE, false);
+ boolean hasAudioCapture = mResolverRequest.isAudioCaptureDevice();
enabled = !hasAudioCapture;
}
}
@@ -1491,7 +1465,8 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
}
// If needed, show that intent is forwarded
// from managed profile to owner or other way around.
- String profileSwitchMessage = mIntentForwarding.forwardMessageFor(mLogic.getTargetIntent());
+ String profileSwitchMessage =
+ mIntentForwarding.forwardMessageFor(mResolverRequest.getIntent());
if (profileSwitchMessage != null) {
Toast.makeText(this, profileSwitchMessage, Toast.LENGTH_LONG).show();
}
@@ -1771,10 +1746,9 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
}
}
-
- CharSequence title = mLogic.getTitle() != null
- ? mLogic.getTitle()
- : getTitleForAction(mLogic.getTargetIntent(), mLogic.getDefaultTitleResId());
+ CharSequence title = mResolverRequest.getTitle() != null
+ ? mResolverRequest.getTitle()
+ : getTitleForAction(mResolverRequest.getIntent(), 0);
if (!TextUtils.isEmpty(title)) {
final TextView titleView = findViewById(com.android.internal.R.id.title);
diff --git a/java/src/com/android/intentresolver/v2/ResolverActivityLogic.kt b/java/src/com/android/intentresolver/v2/ResolverActivityLogic.kt
index 13353041..7eb63ab3 100644
--- a/java/src/com/android/intentresolver/v2/ResolverActivityLogic.kt
+++ b/java/src/com/android/intentresolver/v2/ResolverActivityLogic.kt
@@ -1,6 +1,5 @@
package com.android.intentresolver.v2
-import android.content.Intent
import androidx.activity.ComponentActivity
import androidx.annotation.OpenForTesting
@@ -16,31 +15,4 @@ open class ResolverActivityLogic(
tag,
activity,
onWorkProfileStatusUpdated,
- ) {
-
- final override val targetIntent: Intent = let {
- val intent = Intent(activity.intent)
- intent.setComponent(null)
- // The resolver activity is set to be hidden from recent tasks.
- // we don't want this attribute to be propagated to the next activity
- // being launched. Note that if the original Intent also had this
- // flag set, we are now losing it. That should be a very rare case
- // and we can live with this.
- intent.setFlags(intent.flags and Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS.inv())
-
- // If FLAG_ACTIVITY_LAUNCH_ADJACENT was set, ResolverActivity was opened in the alternate
- // side, which means we want to open the target app on the same side as ResolverActivity.
- if (intent.flags and Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT != 0) {
- intent.setFlags(intent.flags and Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT.inv())
- }
- intent
- }
-
- override val title: CharSequence? = null
-
- override val defaultTitleResId: Int = 0
-
- override val initialIntents: List<Intent>? = null
-
- override val payloadIntents: List<Intent> = listOf(targetIntent)
-}
+ )
diff --git a/java/src/com/android/intentresolver/v2/ui/model/ActivityLaunch.kt b/java/src/com/android/intentresolver/v2/ui/model/ActivityLaunch.kt
index fd25ea42..e5f342d9 100644
--- a/java/src/com/android/intentresolver/v2/ui/model/ActivityLaunch.kt
+++ b/java/src/com/android/intentresolver/v2/ui/model/ActivityLaunch.kt
@@ -29,7 +29,7 @@ data class ActivityLaunch(
/** The identifier for the sending app and user */
val fromUid: Int,
/** The package of the sending app */
- val fromPackage: String?,
+ val fromPackage: String,
/** The referrer as supplied to the activity. */
val referrer: Uri?
) : Parcelable {
@@ -38,10 +38,13 @@ data class ActivityLaunch(
) : this(
intent = source.requireParcelable(),
fromUid = source.readInt(),
- fromPackage = source.readString(),
+ fromPackage = requireNotNull(source.readString()),
referrer = source.readParcelable()
)
+ /** A package name from referrer, if it is an android-app URI */
+ val referrerPackage = referrer?.takeIf { it.scheme == ANDROID_APP_SCHEME }?.authority
+
override fun describeContents() = 0 /* flags */
override fun writeToParcel(dest: Parcel, flags: Int) {
diff --git a/java/src/com/android/intentresolver/v2/ui/model/ActivityLaunchModule.kt b/java/src/com/android/intentresolver/v2/ui/model/ActivityLaunchModule.kt
index 3311467e..bb8f3a54 100644
--- a/java/src/com/android/intentresolver/v2/ui/model/ActivityLaunchModule.kt
+++ b/java/src/com/android/intentresolver/v2/ui/model/ActivityLaunchModule.kt
@@ -33,7 +33,10 @@ object ActivityLaunchModule {
return ActivityLaunch(
activity.intent,
activity.launchedFromUid,
- activity.launchedFromPackage,
+ requireNotNull(activity.launchedFromPackage) {
+ "activity.launchedFromPackage was null. This is expected to be non-null for " +
+ "any system-signed application!"
+ },
activity.referrer
)
}
diff --git a/java/src/com/android/intentresolver/v2/ui/model/ChooserRequest.kt b/java/src/com/android/intentresolver/v2/ui/model/ChooserRequest.kt
index 2fbf94a2..d41d0874 100644
--- a/java/src/com/android/intentresolver/v2/ui/model/ChooserRequest.kt
+++ b/java/src/com/android/intentresolver/v2/ui/model/ChooserRequest.kt
@@ -19,16 +19,17 @@ import android.content.ComponentName
import android.content.Intent
import android.content.Intent.ACTION_SEND
import android.content.Intent.ACTION_SEND_MULTIPLE
+import android.content.Intent.EXTRA_REFERRER
import android.content.IntentFilter
import android.content.IntentSender
+import android.net.Uri
import android.os.Bundle
import android.service.chooser.ChooserAction
import android.service.chooser.ChooserTarget
import androidx.annotation.StringRes
import com.android.intentresolver.v2.ext.hasAction
-const val MAX_CHOOSER_ACTIONS = 5
-const val MAX_INITIAL_INTENTS = 2
+const val ANDROID_APP_SCHEME = "android-app"
/** All of the things that are consumed from an incoming share Intent (+Extras). */
data class ChooserRequest(
@@ -58,10 +59,10 @@ data class ChooserRequest(
@get:StringRes val defaultTitleResource: Int = 0,
/**
- * An empty intent which carries an extra of [Intent.EXTRA_REFERRER]. To be merged with outgoing
- * intents. This provides the original referrer value to the target.
+ * The referrer value as received by the caller. It may have been supplied via [EXTRA_REFERRER]
+ * or synthesized from callerPackageName. This value is merged into outgoing intents.
*/
- val referrerFillInIntent: Intent,
+ val referrer: Uri?,
/**
* Choices to exclude from results.
@@ -163,18 +164,29 @@ data class ChooserRequest(
*/
val shareTargetFilter: IntentFilter? = null
) {
+ val referrerPackage = referrer?.takeIf { it.scheme == ANDROID_APP_SCHEME }?.authority
+
+ fun getReferrerFillInIntent(): Intent {
+ return Intent().apply {
+ referrerPackage?.also { pkg ->
+ putExtra(EXTRA_REFERRER, Uri.parse("$ANDROID_APP_SCHEME://$pkg"))
+ }
+ }
+ }
+
+ val payloadIntents = listOf(targetIntent) + additionalTargets
/** Constructs an instance from only the required values. */
constructor(
targetIntent: Intent,
- referrerPackageName: String
+ launchedFromPackage: String,
+ referrer: Uri?
) : this(
- targetIntent,
- targetIntent.action,
- targetIntent.hasAction(ACTION_SEND, ACTION_SEND_MULTIPLE),
- targetIntent.type,
- referrerPackageName,
- referrerFillInIntent =
- Intent().apply { putExtra(Intent.EXTRA_REFERRER, referrerPackageName) }
+ targetIntent = targetIntent,
+ targetAction = targetIntent.action,
+ isSendActionTarget = targetIntent.hasAction(ACTION_SEND, ACTION_SEND_MULTIPLE),
+ targetType = targetIntent.type,
+ launchedFromPackage = launchedFromPackage,
+ referrer = referrer
)
}
diff --git a/java/src/com/android/intentresolver/v2/ui/model/ResolverRequest.kt b/java/src/com/android/intentresolver/v2/ui/model/ResolverRequest.kt
new file mode 100644
index 00000000..5abfb602
--- /dev/null
+++ b/java/src/com/android/intentresolver/v2/ui/model/ResolverRequest.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 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.v2.ui.model
+
+import android.content.Intent
+import android.content.pm.ResolveInfo
+import android.os.UserHandle
+import com.android.intentresolver.v2.domain.model.Profile
+import com.android.intentresolver.v2.ext.isHomeIntent
+
+/** All of the things that are consumed from an incoming Intent Resolution request (+Extras). */
+data class ResolverRequest(
+ /** The intent to be resolved to a target. */
+ val intent: Intent,
+
+ /**
+ * Supplied by the system to indicate which profile should be selected by default. This is
+ * required since ResolverActivity may be launched as either the originating OR target user when
+ * resolving a cross profile intent.
+ *
+ * Valid values are: [PERSONAL][Profile.Type.PERSONAL] and [WORK][Profile.Type.WORK] and null
+ * when the intent is not a forwarded cross-profile intent.
+ */
+ val selectedProfile: Profile.Type?,
+
+ /**
+ * When handing a cross profile forwarded intent, this is the user which started the original
+ * intent. This is required to allow ResolverActivity to be launched as the target user under
+ * some conditions.
+ */
+ val callingUser: UserHandle?,
+
+ /**
+ * Indicates if resolving actions for a connected device which has audio capture capability
+ * (e.g. is a USB Microphone).
+ *
+ * When used to handle a connected device, ResolverActivity uses this signal to present a
+ * warning when a resolved application does not hold the RECORD_AUDIO permission. (If selected
+ * the app would be able to capture audio directly via the device, bypassing audio API
+ * permissions.)
+ */
+ val isAudioCaptureDevice: Boolean = false,
+
+ /** A list of a resolved activity targets. This list overrides normal intent resolution. */
+ val resolutionList: List<ResolveInfo>? = null,
+
+ /** A customized title for the resolver interface. */
+ val title: String? = null,
+) {
+ val isResolvingHome = intent.isHomeIntent()
+
+ /** For compatibility with existing code shared between chooser/resolver. */
+ val payloadIntents: List<Intent> = listOf(intent)
+}
diff --git a/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestReader.kt b/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestReader.kt
index 33868aaf..45e2ea64 100644
--- a/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestReader.kt
+++ b/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestReader.kt
@@ -46,14 +46,15 @@ import com.android.intentresolver.v2.ext.hasAction
import com.android.intentresolver.v2.ext.ifMatch
import com.android.intentresolver.v2.ui.model.ActivityLaunch
import com.android.intentresolver.v2.ui.model.ChooserRequest
-import com.android.intentresolver.v2.ui.model.MAX_CHOOSER_ACTIONS
-import com.android.intentresolver.v2.ui.model.MAX_INITIAL_INTENTS
import com.android.intentresolver.v2.validation.ValidationResult
import com.android.intentresolver.v2.validation.types.IntentOrUri
import com.android.intentresolver.v2.validation.types.array
import com.android.intentresolver.v2.validation.types.value
import com.android.intentresolver.v2.validation.validateFrom
+private const val MAX_CHOOSER_ACTIONS = 5
+private const val MAX_INITIAL_INTENTS = 2
+
private fun Intent.hasSendAction() = hasAction(ACTION_SEND, ACTION_SEND_MULTIPLE)
internal fun Intent.maybeAddSendActionFlags() =
@@ -134,7 +135,7 @@ fun readChooserRequest(launch: ActivityLaunch): ValidationResult<ChooserRequest>
},
title = customTitle,
defaultTitleResource = defaultTitleResource,
- referrerFillInIntent = referrerFillIn,
+ referrer = launch.referrer,
filteredComponentNames = filteredComponents,
callerChooserTargets = callerChooserTargets,
chooserActions = chooserActions,
diff --git a/java/src/com/android/intentresolver/v2/ui/viewmodel/ResolverRequestReader.kt b/java/src/com/android/intentresolver/v2/ui/viewmodel/ResolverRequestReader.kt
new file mode 100644
index 00000000..fc9f1e01
--- /dev/null
+++ b/java/src/com/android/intentresolver/v2/ui/viewmodel/ResolverRequestReader.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 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.v2.ui.viewmodel
+
+import android.os.Bundle
+import android.os.UserHandle
+import com.android.intentresolver.v2.ResolverActivity.PROFILE_PERSONAL
+import com.android.intentresolver.v2.ResolverActivity.PROFILE_WORK
+import com.android.intentresolver.v2.domain.model.Profile
+import com.android.intentresolver.v2.ui.model.ActivityLaunch
+import com.android.intentresolver.v2.ui.model.ResolverRequest
+import com.android.intentresolver.v2.validation.Validation
+import com.android.intentresolver.v2.validation.ValidationResult
+import com.android.intentresolver.v2.validation.types.value
+import com.android.intentresolver.v2.validation.validateFrom
+
+const val EXTRA_CALLING_USER = "com.android.internal.app.ResolverActivity.EXTRA_CALLING_USER"
+const val EXTRA_SELECTED_PROFILE =
+ "com.android.internal.app.ResolverActivity.EXTRA_SELECTED_PROFILE"
+const val EXTRA_IS_AUDIO_CAPTURE_DEVICE = "is_audio_capture_device"
+
+fun readResolverRequest(launch: ActivityLaunch): ValidationResult<ResolverRequest> {
+ @Suppress("DEPRECATION")
+ return validateFrom((launch.intent.extras ?: Bundle())::get) {
+ val callingUser = optional(value<UserHandle>(EXTRA_CALLING_USER))
+ val selectedProfile = checkSelectedProfile()
+ val audioDevice = optional(value<Boolean>(EXTRA_IS_AUDIO_CAPTURE_DEVICE)) ?: false
+ ResolverRequest(launch.intent, selectedProfile, callingUser, audioDevice)
+ }
+}
+
+private fun Validation.checkSelectedProfile(): Profile.Type? {
+ return when (val selected = optional(value<Int>(EXTRA_SELECTED_PROFILE))) {
+ null -> null
+ PROFILE_PERSONAL -> Profile.Type.PERSONAL
+ PROFILE_WORK -> Profile.Type.WORK
+ else ->
+ error(
+ EXTRA_SELECTED_PROFILE +
+ " has invalid value ($selected)." +
+ " Must be either ResolverActivity.PROFILE_PERSONAL ($PROFILE_PERSONAL)" +
+ " or ResolverActivity.PROFILE_WORK ($PROFILE_WORK)."
+ )
+ }
+}