summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--java/src/com/android/intentresolver/v2/ActivityLogic.kt90
-rw-r--r--java/src/com/android/intentresolver/v2/ChooserActivity.java164
-rw-r--r--java/src/com/android/intentresolver/v2/ChooserActivityLogic.kt56
-rw-r--r--java/src/com/android/intentresolver/v2/ResolverActivity.java150
-rw-r--r--java/src/com/android/intentresolver/v2/ResolverActivityLogic.kt61
5 files changed, 332 insertions, 189 deletions
diff --git a/java/src/com/android/intentresolver/v2/ActivityLogic.kt b/java/src/com/android/intentresolver/v2/ActivityLogic.kt
new file mode 100644
index 00000000..0613882e
--- /dev/null
+++ b/java/src/com/android/intentresolver/v2/ActivityLogic.kt
@@ -0,0 +1,90 @@
+package com.android.intentresolver.v2
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import androidx.activity.ComponentActivity
+import com.android.intentresolver.icons.TargetDataLoader
+
+/**
+ * Logic for IntentResolver Activities. Anything that is not the same across activities (including
+ * test activities) should be in this interface. Expect there to be one implementation for each
+ * 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 [additionalTargets], if any. */
+ val targetIntent: Intent
+ /** Whether the intent is for home. */
+ val resolvingHome: Boolean
+ /** Intents for additional targets. These will always come after [targetIntent]. */
+ val additionalTargets: List<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>?
+ /** Whether or not this activity supports choosing a default handler for the intent. */
+ val supportsAlwaysUseOption: Boolean
+ /** Fetches display info for processed candidates. */
+ val targetDataLoader: TargetDataLoader
+
+ /**
+ * Called after Activity superclass creation, but before any other onCreate logic is performed.
+ */
+ fun preInitialization()
+}
+
+/**
+ * Logic that is common to all IntentResolver activities. Anything that is the same across
+ * activities (including test activities), should live here.
+ */
+interface CommonActivityLogic {
+ /** A reference to the activity owning, and used by, this logic. */
+ val activity: ComponentActivity
+ /** The name of the referring package. */
+ val referrerPackageName: String?
+
+ // TODO: For some reason the IDE complains about getting Activity fields from a
+ // ComponentActivity. These are a band-aid until the bug is fixed and should be removed when
+ // possible.
+ val ComponentActivity.context: Context
+ val ComponentActivity.intent: Intent
+ val ComponentActivity.referrer: Uri?
+}
+
+/**
+ * Concrete implementation of the [CommonActivityLogic] interface meant to be delegated to by
+ * [ActivityLogic] implementations. Test implementations of [ActivityLogic] may need to create their
+ * own [CommonActivityLogic] implementation.
+ */
+class CommonActivityLogicImpl(activityProvider: () -> ComponentActivity) : CommonActivityLogic {
+
+ override val activity: ComponentActivity by lazy { activityProvider() }
+
+ override val referrerPackageName: String? by lazy {
+ activity.referrer.let {
+ if (ANDROID_APP_URI_SCHEME == it?.scheme) {
+ it.host
+ } else {
+ null
+ }
+ }
+ }
+
+ companion object {
+ private const val ANDROID_APP_URI_SCHEME = "android-app"
+ }
+
+ // TODO: For some reason the IDE complains about getting Activity fields from a
+ // ComponentActivity. These are a band-aid until the bug is fixed and should be removed when
+ // possible.
+ override val ComponentActivity.context: Context
+ get() = (this as Activity)
+ override val ComponentActivity.intent: Intent
+ get() = (this as Activity).intent
+ override val ComponentActivity.referrer: Uri?
+ get() = (this as Activity).referrer
+}
diff --git a/java/src/com/android/intentresolver/v2/ChooserActivity.java b/java/src/com/android/intentresolver/v2/ChooserActivity.java
index c1d73e69..d2dabfb3 100644
--- a/java/src/com/android/intentresolver/v2/ChooserActivity.java
+++ b/java/src/com/android/intentresolver/v2/ChooserActivity.java
@@ -28,6 +28,8 @@ import static androidx.lifecycle.LifecycleKt.getCoroutineScope;
import static com.android.internal.util.LatencyTracker.ACTION_LOAD_SHARE_SHEET;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.Activity;
@@ -116,6 +118,8 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import dagger.hilt.android.AndroidEntryPoint;
+import kotlin.Unit;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.text.Collator;
@@ -129,6 +133,7 @@ import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -195,15 +200,6 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
@Inject @NearbyShare public Optional<ComponentName> mNearbyShare;
@Inject public TargetDataLoader mTargetDataLoader;
- /* TODO: this is `nullable` because we have to defer the assignment til onCreate(). We make the
- * only assignment there, and expect it to be ready by the time we ever use it --
- * someday if we move all the usage to a component with a narrower lifecycle (something that
- * matches our Activity's create/destroy lifecycle, not its Java object lifecycle) then we
- * should be able to make this assignment as "final."
- */
- @Nullable
- private ChooserRequestParameters mChooserRequest;
-
private ChooserRefinementManager mRefinementManager;
private ChooserContentPreviewUi mChooserContentPreviewUi;
@@ -251,44 +247,50 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
@Override
protected void onCreate(Bundle savedInstanceState) {
Tracer.INSTANCE.markLaunched();
+ AtomicLong intentReceivedTime = new AtomicLong(-1);
+ mLogic = new ChooserActivityLogic(
+ TAG,
+ () -> this,
+ () -> mTargetDataLoader,
+ () -> {
+ intentReceivedTime.set(System.currentTimeMillis());
+ mLatencyTracker.onActionStart(ACTION_LOAD_SHARE_SHEET);
+
+ mPinnedSharedPrefs = getPinnedSharedPrefs(this);
+ mMaxTargetsPerRow =
+ getResources().getInteger(R.integer.config_chooser_max_targets_per_row);
+ mShouldDisplayLandscape =
+ shouldDisplayLandscape(getResources().getConfiguration().orientation);
+
+
+ ChooserRequestParameters chooserRequest =
+ ((ChooserActivityLogic) mLogic).getChooserRequestParameters();
+ if (chooserRequest == null) {
+ return Unit.INSTANCE;
+ }
+ setRetainInOnStop(chooserRequest.shouldRetainInOnStop());
+
+ createProfileRecords(
+ new AppPredictorFactory(
+ this,
+ chooserRequest.getSharedText(),
+ chooserRequest.getTargetIntentFilter()
+ ),
+ chooserRequest.getTargetIntentFilter()
+ );
+ return Unit.INSTANCE;
+ }
+ );
super.onCreate(savedInstanceState);
-
- final long intentReceivedTime = System.currentTimeMillis();
- mLatencyTracker.onActionStart(ACTION_LOAD_SHARE_SHEET);
-
- try {
- mChooserRequest = new ChooserRequestParameters(
- getIntent(),
- getReferrerPackageName(),
- getReferrer());
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Caller provided invalid Chooser request parameters", e);
+ if (getChooserRequest() == null) {
finish();
return;
}
- mPinnedSharedPrefs = getPinnedSharedPrefs(this);
- mMaxTargetsPerRow = getResources().getInteger(R.integer.config_chooser_max_targets_per_row);
- mShouldDisplayLandscape =
- shouldDisplayLandscape(getResources().getConfiguration().orientation);
- setRetainInOnStop(mChooserRequest.shouldRetainInOnStop());
-
- createProfileRecords(
- new AppPredictorFactory(
- this,
- mChooserRequest.getSharedText(),
- mChooserRequest.getTargetIntentFilter()),
- mChooserRequest.getTargetIntentFilter());
-
- init(
- mChooserRequest.getTargetIntent(),
- mChooserRequest.getAdditionalTargets(),
- mChooserRequest.getTitle(),
- mChooserRequest.getDefaultTitleResource(),
- mChooserRequest.getInitialIntents(),
- /* resolutionList= */ null,
- /* supportsAlwaysUseOption= */ false,
- mTargetDataLoader,
- /* safeForwardingMode= */ true);
+ if (isFinishing()) {
+ // Performing a clean exit:
+ // Skip initializing any additional resources.
+ return;
+ }
getEventLog().logSharesheetTriggered();
@@ -315,10 +317,11 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
BasePreviewViewModel previewViewModel =
new ViewModelProvider(this, createPreviewViewModelFactory())
.get(BasePreviewViewModel.class);
+ ChooserRequestParameters chooserRequest = requireChooserRequest();
mChooserContentPreviewUi = new ChooserContentPreviewUi(
getCoroutineScope(getLifecycle()),
- previewViewModel.createOrReuseProvider(mChooserRequest),
- mChooserRequest.getTargetIntent(),
+ previewViewModel.createOrReuseProvider(chooserRequest),
+ chooserRequest.getTargetIntent(),
previewViewModel.createOrReuseImageLoader(),
createChooserActionFactory(),
mEnterTransitionAnimationDelegate,
@@ -333,9 +336,9 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
}
mChooserShownTime = System.currentTimeMillis();
- final long systemCost = mChooserShownTime - intentReceivedTime;
+ final long systemCost = mChooserShownTime - intentReceivedTime.get();
getEventLog().logChooserActivityShown(
- isWorkProfile(), mChooserRequest.getTargetType(), systemCost);
+ isWorkProfile(), chooserRequest.getTargetType(), systemCost);
if (mResolverDrawerLayout != null) {
mResolverDrawerLayout.addOnLayoutChangeListener(this::handleLayoutChange);
@@ -352,21 +355,30 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
}
getEventLog().logShareStarted(
- getReferrerPackageName(),
- mChooserRequest.getTargetType(),
- mChooserRequest.getCallerChooserTargets().size(),
- (mChooserRequest.getInitialIntents() == null)
- ? 0 : mChooserRequest.getInitialIntents().length,
+ mLogic.getReferrerPackageName(),
+ chooserRequest.getTargetType(),
+ chooserRequest.getCallerChooserTargets().size(),
+ (chooserRequest.getInitialIntents() == null)
+ ? 0 : chooserRequest.getInitialIntents().length,
isWorkProfile(),
mChooserContentPreviewUi.getPreferredContentPreview(),
- mChooserRequest.getTargetAction(),
- mChooserRequest.getChooserActions().size(),
- mChooserRequest.getModifyShareAction() != null
+ chooserRequest.getTargetAction(),
+ chooserRequest.getChooserActions().size(),
+ chooserRequest.getModifyShareAction() != null
);
mEnterTransitionAnimationDelegate.postponeTransition();
}
+ @Nullable
+ private ChooserRequestParameters getChooserRequest() {
+ return ((ChooserActivityLogic) mLogic).getChooserRequestParameters();
+ }
+
+ private ChooserRequestParameters requireChooserRequest() {
+ return requireNonNull(getChooserRequest());
+ }
+
@Override
protected int appliedThemeResId() {
return R.style.Theme_DeviceDefault_Chooser;
@@ -445,7 +457,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
@Override
protected EmptyStateProvider createBlockerEmptyStateProvider() {
- final boolean isSendAction = mChooserRequest.isSendActionTarget();
+ final boolean isSendAction = requireChooserRequest().isSendActionTarget();
final EmptyState noWorkToPersonalEmptyState =
new DevicePolicyBlockerEmptyState(
@@ -740,14 +752,15 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
@Override // ResolverListCommunicator
public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) {
- if (mChooserRequest == null) {
+ ChooserRequestParameters chooserRequest = getChooserRequest();
+ if (chooserRequest == null) {
return defIntent;
}
Intent result = defIntent;
- if (mChooserRequest.getReplacementExtras() != null) {
+ if (chooserRequest.getReplacementExtras() != null) {
final Bundle replExtras =
- mChooserRequest.getReplacementExtras().getBundle(aInfo.packageName);
+ chooserRequest.getReplacementExtras().getBundle(aInfo.packageName);
if (replExtras != null) {
result = new Intent(defIntent);
result.putExtras(replExtras);
@@ -768,12 +781,13 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
@Override
public void onActivityStarted(TargetInfo cti) {
- if (mChooserRequest.getChosenComponentSender() != null) {
+ ChooserRequestParameters chooserRequest = requireChooserRequest();
+ if (chooserRequest.getChosenComponentSender() != null) {
final ComponentName target = cti.getResolvedComponentName();
if (target != null) {
final Intent fillIn = new Intent().putExtra(Intent.EXTRA_CHOSEN_COMPONENT, target);
try {
- mChooserRequest.getChosenComponentSender().sendIntent(
+ chooserRequest.getChosenComponentSender().sendIntent(
this, Activity.RESULT_OK, fillIn, null, null);
} catch (IntentSender.SendIntentException e) {
Slog.e(TAG, "Unable to launch supplied IntentSender to report "
@@ -784,7 +798,8 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
}
private void addCallerChooserTargets() {
- if (!mChooserRequest.getCallerChooserTargets().isEmpty()) {
+ ChooserRequestParameters chooserRequest = requireChooserRequest();
+ if (!chooserRequest.getCallerChooserTargets().isEmpty()) {
// Send the caller's chooser targets only to the default profile.
UserHandle defaultUser = (findSelectedProfile() == PROFILE_WORK)
? getAnnotatedUserHandles().workProfileUserHandle
@@ -792,7 +807,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
if (mChooserMultiProfilePagerAdapter.getCurrentUserHandle() == defaultUser) {
mChooserMultiProfilePagerAdapter.getActiveListAdapter().addServiceResults(
/* origTarget */ null,
- new ArrayList<>(mChooserRequest.getCallerChooserTargets()),
+ new ArrayList<>(chooserRequest.getCallerChooserTargets()),
TARGET_TYPE_DEFAULT,
/* directShareShortcutInfoCache */ Collections.emptyMap(),
/* directShareAppTargetCache */ Collections.emptyMap());
@@ -837,7 +852,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
// the logic into `ChooserTargetActionsDialogFragment.show()`.
boolean isShortcutPinned = targetInfo.isSelectableTargetInfo() && targetInfo.isPinned();
IntentFilter intentFilter = targetInfo.isSelectableTargetInfo()
- ? mChooserRequest.getTargetIntentFilter() : null;
+ ? requireChooserRequest().getTargetIntentFilter() : null;
String shortcutTitle = targetInfo.isSelectableTargetInfo()
? targetInfo.getDisplayLabel().toString() : null;
String shortcutIdKey = targetInfo.getDirectShareShortcutId();
@@ -858,7 +873,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
if (mRefinementManager.maybeHandleSelection(
target,
- mChooserRequest.getRefinementIntentSender(),
+ requireChooserRequest().getRefinementIntentSender(),
getApplication(),
getMainThreadHandler())) {
return false;
@@ -913,7 +928,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
targetInfo.getResolveInfo().activityInfo.processName,
which,
/* directTargetAlsoRanked= */ getRankedPosition(targetInfo),
- mChooserRequest.getCallerChooserTargets().size(),
+ requireChooserRequest().getCallerChooserTargets().size(),
targetInfo.getHashedTargetIdForMetrics(this),
targetInfo.isPinned(),
mIsSuccessfullySelected,
@@ -1032,7 +1047,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
if (targetIntent == null) {
return;
}
- Intent originalTargetIntent = new Intent(mChooserRequest.getTargetIntent());
+ Intent originalTargetIntent = new Intent(requireChooserRequest().getTargetIntent());
// Our TargetInfo implementations add associated component to the intent, let's do the same
// for the sake of the comparison below.
if (targetIntent.getComponent() != null) {
@@ -1152,7 +1167,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
@Override
public boolean isComponentFiltered(ComponentName name) {
- return mChooserRequest.getFilteredComponentNames().contains(name);
+ return requireChooserRequest().getFilteredComponentNames().contains(name);
}
@Override
@@ -1179,7 +1194,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
createListController(userHandle),
userHandle,
getTargetIntent(),
- mChooserRequest,
+ requireChooserRequest(),
mMaxTargetsPerRow,
targetDataLoader);
@@ -1279,14 +1294,14 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
AbstractResolverComparator resolverComparator;
if (appPredictor != null) {
resolverComparator = new AppPredictionServiceResolverComparator(this, getTargetIntent(),
- getReferrerPackageName(), appPredictor, userHandle, getEventLog(),
+ mLogic.getReferrerPackageName(), appPredictor, userHandle, getEventLog(),
mNearbyShare.orElse(null));
} else {
resolverComparator =
new ResolverRankerServiceResolverComparator(
this,
getTargetIntent(),
- getReferrerPackageName(),
+ mLogic.getReferrerPackageName(),
null,
getEventLog(),
getResolverRankerServiceUserHandleList(userHandle),
@@ -1297,7 +1312,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
this,
mPm,
getTargetIntent(),
- getReferrerPackageName(),
+ mLogic.getReferrerPackageName(),
getAnnotatedUserHandles().userIdOfCallingApp,
resolverComparator,
getQueryIntentsUser(userHandle));
@@ -1311,7 +1326,7 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
private ChooserActionFactory createChooserActionFactory() {
return new ChooserActionFactory(
this,
- mChooserRequest,
+ requireChooserRequest(),
mImageEditor,
getEventLog(),
(isExcluded) -> mExcludeSharedText = isExcluded,
@@ -1681,7 +1696,8 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
* @return true if we want to show the content preview area
*/
protected boolean shouldShowContentPreview() {
- return (mChooserRequest != null) && mChooserRequest.isSendActionTarget();
+ ChooserRequestParameters chooserRequest = getChooserRequest();
+ return (chooserRequest != null) && chooserRequest.isSendActionTarget();
}
private void updateStickyContentPreview() {
diff --git a/java/src/com/android/intentresolver/v2/ChooserActivityLogic.kt b/java/src/com/android/intentresolver/v2/ChooserActivityLogic.kt
new file mode 100644
index 00000000..1db3f407
--- /dev/null
+++ b/java/src/com/android/intentresolver/v2/ChooserActivityLogic.kt
@@ -0,0 +1,56 @@
+package com.android.intentresolver.v2
+
+import android.app.Activity
+import android.content.Intent
+import android.util.Log
+import androidx.activity.ComponentActivity
+import com.android.intentresolver.ChooserRequestParameters
+import com.android.intentresolver.icons.TargetDataLoader
+
+/** Activity logic for [ChooserActivity]. */
+class ChooserActivityLogic(
+ private val tag: String,
+ activityProvider: () -> ComponentActivity,
+ targetDataLoaderProvider: () -> TargetDataLoader,
+ private val onPreInitialization: () -> Unit,
+) : ActivityLogic, CommonActivityLogic by CommonActivityLogicImpl(activityProvider) {
+
+ override val targetIntent: Intent by lazy { chooserRequestParameters?.targetIntent ?: Intent() }
+
+ override val resolvingHome: Boolean = false
+
+ override val additionalTargets: List<Intent>? by lazy {
+ chooserRequestParameters?.additionalTargets?.toList()
+ }
+
+ override val title: CharSequence? by lazy { chooserRequestParameters?.title }
+
+ override val defaultTitleResId: Int by lazy {
+ chooserRequestParameters?.defaultTitleResource ?: 0
+ }
+
+ override val initialIntents: List<Intent>? by lazy {
+ chooserRequestParameters?.initialIntents?.toList()
+ }
+
+ override val supportsAlwaysUseOption: Boolean = false
+
+ override val targetDataLoader: TargetDataLoader by lazy { targetDataLoaderProvider() }
+
+ val chooserRequestParameters: ChooserRequestParameters? by lazy {
+ try {
+ ChooserRequestParameters(
+ (activity as Activity).intent,
+ referrerPackageName,
+ (activity as Activity).referrer,
+ )
+ } catch (e: IllegalArgumentException) {
+ Log.e(tag, "Caller provided invalid Chooser request parameters", e)
+ null
+ }
+ }
+
+ override fun preInitialization() {
+ onPreInitialization()
+ }
+}
diff --git a/java/src/com/android/intentresolver/v2/ResolverActivity.java b/java/src/com/android/intentresolver/v2/ResolverActivity.java
index a0388456..1c9ee99d 100644
--- a/java/src/com/android/intentresolver/v2/ResolverActivity.java
+++ b/java/src/com/android/intentresolver/v2/ResolverActivity.java
@@ -27,7 +27,6 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERS
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PROFILE_NOT_SUPPORTED;
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB;
import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_TAB_ACCESSIBILITY;
-import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.PermissionChecker.PID_UNKNOWN;
import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL;
@@ -109,7 +108,6 @@ import com.android.intentresolver.emptystate.CompositeEmptyStateProvider;
import com.android.intentresolver.emptystate.CrossProfileIntentsChecker;
import com.android.intentresolver.emptystate.EmptyState;
import com.android.intentresolver.emptystate.EmptyStateProvider;
-import com.android.intentresolver.icons.DefaultTargetDataLoader;
import com.android.intentresolver.icons.TargetDataLoader;
import com.android.intentresolver.model.ResolverRankerServiceResolverComparator;
import com.android.intentresolver.v2.MultiProfilePagerAdapter.MyUserIdProvider;
@@ -145,6 +143,8 @@ import java.util.function.Supplier;
public class ResolverActivity extends FragmentActivity implements
ResolverListAdapter.ResolverListCommunicator {
+ protected ActivityLogic mLogic = new ResolverActivityLogic(() -> this);
+
public ResolverActivity() {
mIsIntentPicker = getClass().equals(ResolverActivity.class);
}
@@ -153,33 +153,17 @@ public class ResolverActivity extends FragmentActivity implements
mIsIntentPicker = isIntentPicker;
}
- /**
- * Whether to enable a launch mode that is safe to use when forwarding intents received from
- * applications and running in system processes. This mode uses Activity.startActivityAsCaller
- * instead of the normal Activity.startActivity for launching the activity selected
- * by the user.
- */
- private boolean mSafeForwardingMode;
-
private Button mAlwaysButton;
private Button mOnceButton;
protected View mProfileView;
private int mLastSelected = AbsListView.INVALID_POSITION;
- private boolean mResolvingHome = false;
private String mProfileSwitchMessage;
private int mLayoutId;
@VisibleForTesting
protected final ArrayList<Intent> mIntents = new ArrayList<>();
private PickTargetOptionRequest mPickOptionRequest;
- private String mReferrerPackage;
- private CharSequence mTitle;
- private int mDefaultTitleResId;
// Expected to be true if this object is ResolverActivity or is ResolverWrapperActivity.
private final boolean mIsIntentPicker;
-
- // Whether or not this activity supports choosing a default handler for the intent.
- @VisibleForTesting
- protected boolean mSupportsAlwaysUseOption;
protected ResolverDrawerLayout mResolverDrawerLayout;
protected PackageManager mPm;
@@ -207,8 +191,6 @@ public class ResolverActivity extends FragmentActivity implements
private PackageMonitor mPersonalPackageMonitor;
private PackageMonitor mWorkPackageMonitor;
- private TargetDataLoader mTargetDataLoader;
-
@VisibleForTesting
protected MultiProfilePagerAdapter mMultiProfilePagerAdapter;
@@ -355,41 +337,23 @@ public class ResolverActivity extends FragmentActivity implements
// Skip initializing any additional resources.
return;
}
- if (mIsIntentPicker) {
- // Use a specialized prompt when we're handling the 'Home' app startActivity()
- final Intent intent = makeMyIntent();
- final Set<String> categories = intent.getCategories();
- if (Intent.ACTION_MAIN.equals(intent.getAction())
- && categories != null
- && categories.size() == 1
- && categories.contains(Intent.CATEGORY_HOME)) {
- // Note: this field is not set to true in the compatibility version.
- mResolvingHome = true;
- }
-
- init(
- intent,
- /* additionalTargets= */ null,
- /* title= */ null,
- /* defaultTitleRes= */ 0,
- /* initialIntents= */ null,
- /* resolutionList= */ null,
- /* supportsAlwaysUseOption= */ true,
- createIconLoader(),
- /* safeForwardingMode= */ true);
- }
+ mLogic.preInitialization();
+ init(
+ mLogic.getTargetIntent(),
+ mLogic.getAdditionalTargets() == null
+ ? null : mLogic.getAdditionalTargets().toArray(new Intent[0]),
+ mLogic.getInitialIntents() == null
+ ? null : mLogic.getInitialIntents().toArray(new Intent[0]),
+ mLogic.getTargetDataLoader()
+ );
}
protected void init(
Intent intent,
Intent[] additionalTargets,
- CharSequence title,
- int defaultTitleRes,
Intent[] initialIntents,
- List<ResolveInfo> resolutionList,
- boolean supportsAlwaysUseOption,
- TargetDataLoader targetDataLoader,
- boolean safeForwardingMode) {
+ TargetDataLoader targetDataLoader
+ ) {
setTheme(appliedThemeResId());
// Determine whether we should show that intent is forwarded
@@ -404,21 +368,12 @@ public class ResolverActivity extends FragmentActivity implements
mPm = getPackageManager();
- mReferrerPackage = getReferrerPackageName();
-
// The initial intent must come before any other targets that are to be added.
mIntents.add(0, new Intent(intent));
if (additionalTargets != null) {
Collections.addAll(mIntents, additionalTargets);
}
- mTitle = title;
- mDefaultTitleResId = defaultTitleRes;
-
- mSupportsAlwaysUseOption = supportsAlwaysUseOption;
- mSafeForwardingMode = safeForwardingMode;
- mTargetDataLoader = targetDataLoader;
-
// 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
// turn this off when running under voice interaction, since it results in
@@ -427,10 +382,14 @@ public class ResolverActivity extends FragmentActivity implements
// We also turn it off when clonedProfile is present on the device, because we might have
// different "last chosen" activities in the different profiles, and PackageManager doesn't
// provide any more information to help us select between them.
- boolean filterLastUsed = mSupportsAlwaysUseOption && !isVoiceInteraction()
+ boolean filterLastUsed = mLogic.getSupportsAlwaysUseOption() && !isVoiceInteraction()
&& !shouldShowTabs() && !hasCloneProfile();
mMultiProfilePagerAdapter = createMultiProfilePagerAdapter(
- initialIntents, resolutionList, filterLastUsed, targetDataLoader);
+ initialIntents,
+ /* resolutionList = */ null,
+ filterLastUsed,
+ targetDataLoader
+ );
if (configureContentView(targetDataLoader)) {
return;
}
@@ -632,7 +591,7 @@ public class ResolverActivity extends FragmentActivity implements
}
final Intent intent = getIntent();
if ((intent.getFlags() & FLAG_ACTIVITY_NEW_TASK) != 0 && !isVoiceInteraction()
- && !mResolvingHome && !mRetainInOnStop) {
+ && !mLogic.getResolvingHome() && !mRetainInOnStop) {
// This resolver is in the unusual situation where it has been
// launched at the top of a new task. We don't let it be added
// to the recent tasks shown to the user, and we need to make sure
@@ -677,7 +636,7 @@ public class ResolverActivity extends FragmentActivity implements
}
ResolveInfo ri = mMultiProfilePagerAdapter.getActiveListAdapter()
.resolveInfoForPosition(which, hasIndexBeenFiltered);
- if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) {
+ if (mLogic.getResolvingHome() && hasManagedProfile() && !supportsManagedProfiles(ri)) {
Toast.makeText(this,
getWorkProfileNotSupportedMsg(
ri.activityInfo.loadLabel(getPackageManager()).toString()),
@@ -691,10 +650,10 @@ public class ResolverActivity extends FragmentActivity implements
return;
}
if (onTargetSelected(target, always)) {
- if (always && mSupportsAlwaysUseOption) {
+ if (always && mLogic.getSupportsAlwaysUseOption()) {
MetricsLogger.action(
this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_ALWAYS);
- } else if (mSupportsAlwaysUseOption) {
+ } else if (mLogic.getSupportsAlwaysUseOption()) {
MetricsLogger.action(
this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_JUST_ONCE);
} else {
@@ -735,7 +694,7 @@ public class ResolverActivity extends FragmentActivity implements
final ResolveInfo ri = target.getResolveInfo();
final Intent intent = target != null ? target.getResolvedIntent() : null;
- if (intent != null && (mSupportsAlwaysUseOption
+ if (intent != null && (mLogic.getSupportsAlwaysUseOption()
|| mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem())
&& mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredResolveList() != null) {
// Build a reasonable intent filter, based on what matched.
@@ -921,7 +880,7 @@ public class ResolverActivity extends FragmentActivity implements
new ResolverRankerServiceResolverComparator(
this,
getTargetIntent(),
- getReferrerPackageName(),
+ mLogic.getReferrerPackageName(),
null,
null,
getResolverRankerServiceUserHandleList(userHandle),
@@ -930,7 +889,7 @@ public class ResolverActivity extends FragmentActivity implements
this,
mPm,
getTargetIntent(),
- getReferrerPackageName(),
+ mLogic.getReferrerPackageName(),
getAnnotatedUserHandles().userIdOfCallingApp,
resolverComparator,
getQueryIntentsUser(userHandle));
@@ -973,7 +932,7 @@ public class ResolverActivity extends FragmentActivity implements
}
protected void resetButtonBar() {
- if (!mSupportsAlwaysUseOption) {
+ if (!mLogic.getSupportsAlwaysUseOption()) {
return;
}
final ViewGroup buttonLayout = findViewById(com.android.internal.R.id.button_bar);
@@ -1099,13 +1058,6 @@ public class ResolverActivity extends FragmentActivity implements
targetDataLoader);
}
- private TargetDataLoader createIconLoader() {
- Intent startIntent = getIntent();
- boolean isAudioCaptureDevice =
- startIntent.getBooleanExtra(EXTRA_IS_AUDIO_CAPTURE_DEVICE, false);
- return new DefaultTargetDataLoader(this, getLifecycle(), isAudioCaptureDevice);
- }
-
private LatencyTracker getLatencyTracker() {
return LatencyTracker.getInstance(this);
}
@@ -1153,24 +1105,6 @@ public class ResolverActivity extends FragmentActivity implements
);
}
- private Intent makeMyIntent() {
- Intent intent = new Intent(getIntent());
- 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.getFlags() & ~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-
- // 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.getFlags() & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
- intent.setFlags(intent.getFlags() & ~FLAG_ACTIVITY_LAUNCH_ADJACENT);
- }
- return intent;
- }
-
private ResolverMultiProfilePagerAdapter
createResolverMultiProfilePagerAdapterForOneProfile(
Intent[] initialIntents,
@@ -1377,14 +1311,6 @@ public class ResolverActivity extends FragmentActivity implements
return mIntents.isEmpty() ? null : mIntents.get(0);
}
- protected final String getReferrerPackageName() {
- final Uri referrer = getReferrer();
- if (referrer != null && "android-app".equals(referrer.getScheme())) {
- return referrer.getHost();
- }
- return null;
- }
-
@Override // ResolverListCommunicator
public final void updateProfileViewButton() {
if (mProfileView == null) {
@@ -1434,7 +1360,7 @@ public class ResolverActivity extends FragmentActivity implements
}
protected final CharSequence getTitleForAction(Intent intent, int defaultTitleRes) {
- final ActionTitle title = mResolvingHome
+ final ActionTitle title = mLogic.getResolvingHome()
? ActionTitle.HOME
: ActionTitle.forAction(intent.getAction());
@@ -1689,13 +1615,6 @@ public class ResolverActivity extends FragmentActivity implements
if (mProfileSwitchMessage != null) {
Toast.makeText(this, mProfileSwitchMessage, Toast.LENGTH_LONG).show();
}
- if (!mSafeForwardingMode) {
- if (cti.startAsUser(this, options, user)) {
- onActivityStarted(cti);
- maybeLogCrossProfileTargetLaunch(cti, user);
- }
- return;
- }
try {
if (cti.startAsCaller(this, options, user.getIdentifier())) {
onActivityStarted(cti);
@@ -2167,7 +2086,7 @@ public class ResolverActivity extends FragmentActivity implements
listView.setOnItemClickListener(listener);
listView.setOnItemLongClickListener(listener);
- if (mSupportsAlwaysUseOption) {
+ if (mLogic.getSupportsAlwaysUseOption()) {
listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
}
}
@@ -2188,9 +2107,10 @@ public class ResolverActivity extends FragmentActivity implements
}
}
- CharSequence title = mTitle != null
- ? mTitle
- : getTitleForAction(getTargetIntent(), mDefaultTitleResId);
+
+ CharSequence title = mLogic.getTitle() != null
+ ? mLogic.getTitle()
+ : getTitleForAction(getTargetIntent(), mLogic.getDefaultTitleResId());
if (!TextUtils.isEmpty(title)) {
final TextView titleView = findViewById(com.android.internal.R.id.title);
@@ -2238,7 +2158,7 @@ public class ResolverActivity extends FragmentActivity implements
boolean adapterForCurrentUserHasFilteredItem =
mMultiProfilePagerAdapter.getListAdapterForUserHandle(
getAnnotatedUserHandles().tabOwnerUserHandleForLaunch).hasFilteredItem();
- return mSupportsAlwaysUseOption && adapterForCurrentUserHasFilteredItem;
+ return mLogic.getSupportsAlwaysUseOption() && adapterForCurrentUserHasFilteredItem;
}
/**
@@ -2387,7 +2307,7 @@ public class ResolverActivity extends FragmentActivity implements
private CharSequence getOrLoadDisplayLabel(TargetInfo info) {
if (info.isDisplayResolveInfo()) {
- mTargetDataLoader.getOrLoadLabel((DisplayResolveInfo) info);
+ mLogic.getTargetDataLoader().getOrLoadLabel((DisplayResolveInfo) info);
}
CharSequence displayLabel = info.getDisplayLabel();
return displayLabel == null ? "" : displayLabel;
diff --git a/java/src/com/android/intentresolver/v2/ResolverActivityLogic.kt b/java/src/com/android/intentresolver/v2/ResolverActivityLogic.kt
new file mode 100644
index 00000000..1d02e6c2
--- /dev/null
+++ b/java/src/com/android/intentresolver/v2/ResolverActivityLogic.kt
@@ -0,0 +1,61 @@
+package com.android.intentresolver.v2
+
+import android.content.Intent
+import androidx.activity.ComponentActivity
+import com.android.intentresolver.icons.DefaultTargetDataLoader
+import com.android.intentresolver.icons.TargetDataLoader
+
+/** Activity logic for [ResolverActivity]. */
+class ResolverActivityLogic(
+ activityProvider: () -> ComponentActivity,
+) : ActivityLogic, CommonActivityLogic by CommonActivityLogicImpl(activityProvider) {
+
+ override val targetIntent: Intent by lazy {
+ 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 resolvingHome: Boolean by lazy {
+ Intent.ACTION_MAIN == targetIntent.action &&
+ targetIntent.categories?.size == 1 &&
+ targetIntent.categories.contains(Intent.CATEGORY_HOME)
+ }
+
+ override val additionalTargets: List<Intent>? = null
+
+ override val title: CharSequence? = null
+
+ override val defaultTitleResId: Int = 0
+
+ override val initialIntents: List<Intent>? = null
+
+ override val supportsAlwaysUseOption: Boolean = true
+
+ override val targetDataLoader: TargetDataLoader by lazy {
+ DefaultTargetDataLoader(
+ activity.context,
+ activity.lifecycle,
+ activity.intent.getBooleanExtra(
+ ResolverActivity.EXTRA_IS_AUDIO_CAPTURE_DEVICE,
+ /* defaultValue = */ false,
+ ),
+ )
+ }
+
+ override fun preInitialization() {
+ // Do nothing
+ }
+}