summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Mark Renouf <mrenouf@google.com> 2024-03-01 12:37:23 -0500
committer Mark Renouf <mrenouf@google.com> 2024-04-04 00:09:05 -0400
commit9f4e2ece250f07e214231a89c9aa74ab19d35d30 (patch)
tree983088328ff213c10daa42710ec3fdc6cfc99dbe
parenta0691fb844a0edef901e00be9acac68f32dd2a88 (diff)
ResolverActivity Profile integration
* connects ResolverActivity with UserInteractor * replaces all existing references as the source of UserHandles * app continues to explicity use the same profile types as previous * updates Activity tests to use FakeUserRepository * removes ResolverWorkProfilePausedEmptyStateProvider * removes ResolverNoCrossProfileEmptyStateProvider * removes ActivityLogic * removes ResolverActivityLogic * removes TestResolverActivityLogic Bug: 300157408 Bug: 311348033 Test: atest IntentResolver-tests-activity:com.android.intentresolver.v2 Change-Id: Ia4a8bf458ebad12af06b1c6c1f0d8586be452d43
-rw-r--r--java/src/com/android/intentresolver/v2/ActivityLogic.kt79
-rw-r--r--java/src/com/android/intentresolver/v2/ResolverActivity.java459
-rw-r--r--java/src/com/android/intentresolver/v2/ResolverActivityLogic.kt18
-rw-r--r--java/src/com/android/intentresolver/v2/ResolverHelper.kt129
-rw-r--r--java/src/com/android/intentresolver/v2/emptystate/ResolverNoCrossProfileEmptyStateProvider.java138
-rw-r--r--java/src/com/android/intentresolver/v2/emptystate/ResolverWorkProfilePausedEmptyStateProvider.java116
-rw-r--r--java/src/com/android/intentresolver/v2/ui/viewmodel/ResolverViewModel.kt70
-rw-r--r--tests/activity/src/com/android/intentresolver/ResolverActivityTest.java2
-rw-r--r--tests/activity/src/com/android/intentresolver/v2/ResolverActivityTest.java81
-rw-r--r--tests/activity/src/com/android/intentresolver/v2/ResolverWrapperActivity.java63
-rw-r--r--tests/activity/src/com/android/intentresolver/v2/TestResolverActivityLogic.kt22
11 files changed, 458 insertions, 719 deletions
diff --git a/java/src/com/android/intentresolver/v2/ActivityLogic.kt b/java/src/com/android/intentresolver/v2/ActivityLogic.kt
deleted file mode 100644
index 62ace0da..00000000
--- a/java/src/com/android/intentresolver/v2/ActivityLogic.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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
-
-import android.os.UserHandle
-import android.os.UserManager
-import android.util.Log
-import androidx.activity.ComponentActivity
-import androidx.core.content.getSystemService
-import com.android.intentresolver.AnnotatedUserHandles
-import com.android.intentresolver.WorkProfileAvailabilityManager
-
-/**
- * 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
-
-/**
- * Logic that is common to all IntentResolver activities. Anything that is the same across
- * activities (including test activities), should live here.
- */
-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
-
- /** Current [UserHandle]s retrievable by type. */
- val annotatedUserHandles: AnnotatedUserHandles?
-
- /** Monitors for changes to work profile availability. */
- val workProfileAvailabilityManager: WorkProfileAvailabilityManager
-}
-
-/**
- * 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(
- override val tag: String,
- override val activity: ComponentActivity,
- onWorkProfileStatusUpdated: () -> Unit,
-) : CommonActivityLogic {
-
- private val userManager: UserManager = activity.getSystemService()!!
-
- override val annotatedUserHandles: AnnotatedUserHandles? =
- try {
- AnnotatedUserHandles.forShareActivity(activity)
- } catch (e: SecurityException) {
- Log.e(tag, "Request from UID without necessary permissions", e)
- null
- }
-
- override val workProfileAvailabilityManager =
- WorkProfileAvailabilityManager(
- userManager,
- annotatedUserHandles?.workProfileUserHandle,
- onWorkProfileStatusUpdated,
- )
-}
diff --git a/java/src/com/android/intentresolver/v2/ResolverActivity.java b/java/src/com/android/intentresolver/v2/ResolverActivity.java
index 4e694c3a..86f32864 100644
--- a/java/src/com/android/intentresolver/v2/ResolverActivity.java
+++ b/java/src/com/android/intentresolver/v2/ResolverActivity.java
@@ -24,8 +24,9 @@ import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_S
import static android.stats.devicepolicy.nano.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static androidx.lifecycle.LifecycleKt.getCoroutineScope;
+
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.Objects.requireNonNull;
@@ -83,14 +84,14 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.viewmodel.CreationExtras;
import androidx.viewpager.widget.ViewPager;
-import com.android.intentresolver.AnnotatedUserHandles;
+import com.android.intentresolver.FeatureFlags;
import com.android.intentresolver.R;
import com.android.intentresolver.ResolverListAdapter;
import com.android.intentresolver.ResolverListController;
-import com.android.intentresolver.WorkProfileAvailabilityManager;
import com.android.intentresolver.chooser.DisplayResolveInfo;
import com.android.intentresolver.chooser.TargetInfo;
import com.android.intentresolver.emptystate.CompositeEmptyStateProvider;
@@ -99,12 +100,14 @@ 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.inject.Background;
import com.android.intentresolver.model.ResolverRankerServiceResolverComparator;
import com.android.intentresolver.v2.data.repository.DevicePolicyResources;
+import com.android.intentresolver.v2.domain.interactor.UserInteractor;
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.ResolverNoCrossProfileEmptyStateProvider;
-import com.android.intentresolver.v2.emptystate.ResolverWorkProfilePausedEmptyStateProvider;
+import com.android.intentresolver.v2.emptystate.WorkProfilePausedEmptyStateProvider;
import com.android.intentresolver.v2.profiles.MultiProfilePagerAdapter;
import com.android.intentresolver.v2.profiles.MultiProfilePagerAdapter.ProfileType;
import com.android.intentresolver.v2.profiles.OnProfileSelectedListener;
@@ -115,11 +118,7 @@ import com.android.intentresolver.v2.shared.model.Profile;
import com.android.intentresolver.v2.ui.ActionTitle;
import com.android.intentresolver.v2.ui.model.ActivityModel;
import com.android.intentresolver.v2.ui.model.ResolverRequest;
-import com.android.intentresolver.v2.validation.Finding;
-import com.android.intentresolver.v2.validation.FindingsKt;
-import com.android.intentresolver.v2.validation.Invalid;
-import com.android.intentresolver.v2.validation.Valid;
-import com.android.intentresolver.v2.validation.ValidationResult;
+import com.android.intentresolver.v2.ui.viewmodel.ResolverViewModel;
import com.android.intentresolver.widget.ResolverDrawerLayout;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
@@ -131,7 +130,6 @@ import com.google.common.collect.ImmutableList;
import dagger.hilt.android.AndroidEntryPoint;
import kotlin.Pair;
-import kotlin.Unit;
import java.util.ArrayList;
import java.util.Arrays;
@@ -139,10 +137,11 @@ import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
-import java.util.function.Consumer;
import javax.inject.Inject;
+import kotlinx.coroutines.CoroutineDispatcher;
+
/**
* This is a copy of ResolverActivity to support IntentResolver's ChooserActivity. This code is
* *not* the resolver that is actually triggered by the system right now (you want
@@ -153,12 +152,18 @@ import javax.inject.Inject;
public class ResolverActivity extends Hilt_ResolverActivity implements
ResolverListAdapter.ResolverListCommunicator {
+ @Inject @Background public CoroutineDispatcher mBackgroundDispatcher;
+ @Inject public UserInteractor mUserInteractor;
+ @Inject public ResolverHelper mResolverHelper;
@Inject public PackageManager mPackageManager;
@Inject public DevicePolicyResources mDevicePolicyResources;
@Inject public IntentForwarding mIntentForwarding;
- private ResolverRequest mResolverRequest;
- private ActivityModel mActivityModel;
- protected ActivityLogic mLogic;
+ @Inject public FeatureFlags mFeatureFlags;
+
+ private ResolverViewModel mViewModel;
+ private ResolverRequest mRequest;
+ private ProfileHelper mProfiles;
+ private ProfileAvailability mProfileAvailability;
protected TargetDataLoader mTargetDataLoader;
private boolean mResolvingHome;
@@ -217,63 +222,133 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
}
};
}
+
protected ActivityModel createActivityModel() {
return ActivityModel.createFrom(this);
}
- @VisibleForTesting
- protected ActivityLogic createActivityLogic() {
- return new ResolverActivityLogic(
- TAG,
- /* activity = */ this,
- this::onWorkProfileStatusUpdated);
- }
-
@NonNull
@Override
public CreationExtras getDefaultViewModelCreationExtras() {
return addDefaultArgs(
super.getDefaultViewModelCreationExtras(),
- new Pair<>(ActivityModel.ACTIVITY_MODEL_KEY, ActivityModel.createFrom(this)));
+ new Pair<>(ActivityModel.ACTIVITY_MODEL_KEY, createActivityModel()));
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ Log.i(TAG, "onCreate");
setTheme(R.style.Theme_DeviceDefault_Resolver);
- mActivityModel = createActivityModel();
+ mResolverHelper.setInitializer(this::initialize);
+ }
- Log.i(TAG, "onCreate");
- Log.i(TAG, "activityModel=" + mActivityModel.toString());
- int callerUid = mActivityModel.getLaunchedFromUid();
- if (callerUid < 0 || UserHandle.isIsolated(callerUid)) {
- Log.e(TAG, "Can't start a resolver from uid " + callerUid);
- finish();
+ @Override
+ protected final void onStart() {
+ super.onStart();
+ this.getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+
+ final Window window = this.getWindow();
+ final WindowManager.LayoutParams attrs = window.getAttributes();
+ attrs.privateFlags &= ~SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+ window.setAttributes(attrs);
+
+ if (mRegistered) {
+ mPersonalPackageMonitor.unregister();
+ if (mWorkPackageMonitor != null) {
+ mWorkPackageMonitor.unregister();
+ }
+ mRegistered = false;
+ }
+ final Intent intent = getIntent();
+ if ((intent.getFlags() & FLAG_ACTIVITY_NEW_TASK) != 0 && !isVoiceInteraction()
+ && !mResolvingHome) {
+ // 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
+ // that each time we are launched we get the correct launching
+ // uid (not re-using the same resolver from an old launching uid),
+ // so we will now finish ourself since being no longer visible,
+ // the user probably can't get back to us.
+ if (!isChangingConfigurations()) {
+ finish();
+ }
}
+ }
- ValidationResult<ResolverRequest> result = readResolverRequest(mActivityModel);
- if (result instanceof Invalid) {
- ((Invalid) result).getErrors().forEach(new Consumer<Finding>() {
- @Override
- public void accept(Finding finding) {
- FindingsKt.log(finding, TAG);
+ @Override
+ protected final void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ ViewPager viewPager = findViewById(com.android.internal.R.id.profile_pager);
+ if (viewPager != null) {
+ outState.putInt(LAST_SHOWN_TAB_KEY, viewPager.getCurrentItem());
+ }
+ }
+
+ @Override
+ protected final void onRestart() {
+ super.onRestart();
+ if (!mRegistered) {
+ mPersonalPackageMonitor.register(
+ this,
+ getMainLooper(),
+ mProfiles.getPersonalHandle(),
+ false);
+ if (mProfiles.getWorkProfilePresent()) {
+ if (mWorkPackageMonitor == null) {
+ mWorkPackageMonitor = createPackageMonitor(
+ mMultiProfilePagerAdapter.getWorkListAdapter());
}
- });
- finish();
+ mWorkPackageMonitor.register(
+ this,
+ getMainLooper(),
+ mProfiles.getWorkHandle(),
+ false);
+ }
+ mRegistered = true;
+ }
+ mMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (!isChangingConfigurations() && mPickOptionRequest != null) {
+ mPickOptionRequest.cancel();
+ }
+ if (mMultiProfilePagerAdapter != null
+ && mMultiProfilePagerAdapter.getActiveListAdapter() != null) {
+ mMultiProfilePagerAdapter.getActiveListAdapter().onDestroy();
}
- mResolverRequest = ((Valid<ResolverRequest>) result).getValue();
- mLogic = createActivityLogic();
- mResolvingHome = mResolverRequest.isResolvingHome();
+ }
+
+ private void initialize() {
+ mViewModel = new ViewModelProvider(this).get(ResolverViewModel.class);
+ mRequest = mViewModel.getRequest().getValue();
+
+ mProfiles = new ProfileHelper(
+ mUserInteractor,
+ getCoroutineScope(getLifecycle()),
+ mBackgroundDispatcher,
+ mFeatureFlags);
+
+ mProfileAvailability = new ProfileAvailability(
+ mUserInteractor,
+ getCoroutineScope(getLifecycle()),
+ mBackgroundDispatcher);
+
+ mProfileAvailability.setOnProfileStatusChange(this::onWorkProfileStatusUpdated);
+
+ mResolvingHome = mRequest.isResolvingHome();
mTargetDataLoader = new DefaultTargetDataLoader(
this,
getLifecycle(),
- mResolverRequest.isAudioCaptureDevice());
- init();
- restore(savedInstanceState);
- }
-
- private void init() {
- Intent intent = mResolverRequest.getIntent();
+ mRequest.isAudioCaptureDevice());
// 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
@@ -284,10 +359,10 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
// different "last chosen" activities in the different profiles, and PackageManager doesn't
// provide any more information to help us select between them.
boolean filterLastUsed = !isVoiceInteraction()
- && !hasWorkProfile() && !hasCloneProfile();
+ && !mProfiles.getWorkProfilePresent() && !mProfiles.getCloneUserPresent();
mMultiProfilePagerAdapter = createMultiProfilePagerAdapter(
new Intent[0],
- /* resolutionList = */ mResolverRequest.getResolutionList(),
+ /* resolutionList = */ mRequest.getResolutionList(),
filterLastUsed
);
if (configureContentView(mTargetDataLoader)) {
@@ -299,16 +374,16 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
mPersonalPackageMonitor.register(
this,
getMainLooper(),
- requireAnnotatedUserHandles().personalProfileUserHandle,
+ mProfiles.getPersonalHandle(),
false
);
- if (hasWorkProfile()) {
+ if (mProfiles.getWorkProfilePresent()) {
mWorkPackageMonitor = createPackageMonitor(
mMultiProfilePagerAdapter.getWorkListAdapter());
mWorkPackageMonitor.register(
this,
getMainLooper(),
- requireAnnotatedUserHandles().workProfileUserHandle,
+ mProfiles.getWorkHandle(),
false
);
}
@@ -337,7 +412,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
mResolverDrawerLayout = rdl;
}
-
+ Intent intent = mViewModel.getRequest().getValue().getIntent();
final Set<String> categories = intent.getCategories();
MetricsLogger.action(this, mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem()
? MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_APP_FEATURED
@@ -364,7 +439,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
List<ResolveInfo> resolutionList,
boolean filterLastUsed) {
ResolverMultiProfilePagerAdapter resolverMultiProfilePagerAdapter = null;
- if (hasWorkProfile()) {
+ if (mProfiles.getWorkProfilePresent()) {
resolverMultiProfilePagerAdapter =
createResolverMultiProfilePagerAdapterForTwoProfiles(
initialIntents, resolutionList, filterLastUsed);
@@ -407,12 +482,11 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
/* devicePolicyEventCategory= */
ResolverActivity.METRICS_CATEGORY_RESOLVER);
- return new ResolverNoCrossProfileEmptyStateProvider(
- requireAnnotatedUserHandles().personalProfileUserHandle,
+ return new NoCrossProfileEmptyStateProvider(
+ mProfiles,
noWorkToPersonalEmptyState,
noPersonalToWorkEmptyState,
- createCrossProfileIntentsChecker(),
- requireAnnotatedUserHandles().tabOwnerUserHandleForLaunch);
+ createCrossProfileIntentsChecker());
}
/**
@@ -432,12 +506,12 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
mFooterSpacer = new Space(getApplicationContext());
} else {
((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
- .getActiveAdapterView().removeFooterView(mFooterSpacer);
+ .getActiveAdapterView().removeFooterView(mFooterSpacer);
}
mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
- mSystemWindowInsets.bottom));
+ mSystemWindowInsets.bottom));
((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
- .getActiveAdapterView().addFooterView(mFooterSpacer);
+ .getActiveAdapterView().addFooterView(mFooterSpacer);
}
protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
@@ -466,7 +540,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
- if (hasWorkProfile() && !useLayoutWithDefault()
+ if (mProfiles.getWorkProfilePresent() && !useLayoutWithDefault()
&& !shouldUseMiniResolver()) {
updateIntentPickerPaddings();
}
@@ -481,52 +555,6 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
return R.layout.resolver_list;
}
- @Override
- protected void onStop() {
- super.onStop();
-
- final Window window = this.getWindow();
- final WindowManager.LayoutParams attrs = window.getAttributes();
- attrs.privateFlags &= ~SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
- window.setAttributes(attrs);
-
- if (mRegistered) {
- mPersonalPackageMonitor.unregister();
- if (mWorkPackageMonitor != null) {
- mWorkPackageMonitor.unregister();
- }
- mRegistered = false;
- }
- final Intent intent = getIntent();
- if ((intent.getFlags() & FLAG_ACTIVITY_NEW_TASK) != 0 && !isVoiceInteraction()
- && !mResolvingHome) {
- // 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
- // that each time we are launched we get the correct launching
- // uid (not re-using the same resolver from an old launching uid),
- // so we will now finish ourself since being no longer visible,
- // the user probably can't get back to us.
- if (!isChangingConfigurations()) {
- finish();
- }
- }
- // TODO: should we clean up the work-profile manager before we potentially finish() above?
- mLogic.getWorkProfileAvailabilityManager().unregisterWorkProfileStateReceiver(this);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- if (!isChangingConfigurations() && mPickOptionRequest != null) {
- mPickOptionRequest.cancel();
- }
- if (mMultiProfilePagerAdapter != null
- && mMultiProfilePagerAdapter.getActiveListAdapter() != null) {
- mMultiProfilePagerAdapter.getActiveListAdapter().onDestroy();
- }
- }
-
// referenced by layout XML: android:onClick="onButtonClick"
public void onButtonClick(View v) {
final int id = v.getId();
@@ -582,7 +610,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
protected void onListRebuilt(ResolverListAdapter listAdapter, boolean rebuildCompleted) {
final ItemClickListener listener = new ItemClickListener();
setupAdapterListView((ListView) mMultiProfilePagerAdapter.getActiveAdapterView(), listener);
- if (hasWorkProfile()) {
+ if (mProfiles.getWorkProfilePresent()) {
final ResolverDrawerLayout rdl = findViewById(com.android.internal.R.id.contentPanel);
if (rdl != null) {
rdl.setMaxCollapsedHeight(getResources()
@@ -598,7 +626,8 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
final Intent intent = target != null ? target.getResolvedIntent() : null;
if (intent != null /*&& mMultiProfilePagerAdapter.getActiveListAdapter().hasFilteredItem()*/
- && mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredResolveList() != null) {
+ && mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredResolveList()
+ != null) {
// Build a reasonable intent filter, based on what matched.
IntentFilter filter = new IntentFilter();
Intent filterIntent;
@@ -761,8 +790,8 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
ResolverRankerServiceResolverComparator resolverComparator =
new ResolverRankerServiceResolverComparator(
this,
- mResolverRequest.getIntent(),
- mActivityModel.getReferrerPackage(),
+ mRequest.getIntent(),
+ mViewModel.getActivityModel().getReferrerPackage(),
null,
null,
getResolverRankerServiceUserHandleList(userHandle),
@@ -770,17 +799,17 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
return new ResolverListController(
this,
mPackageManager,
- mActivityModel.getIntent(),
- mActivityModel.getReferrerPackage(),
- mActivityModel.getLaunchedFromUid(),
+ mRequest.getIntent(),
+ mViewModel.getActivityModel().getReferrerPackage(),
+ mViewModel.getActivityModel().getLaunchedFromUid(),
resolverComparator,
- getQueryIntentsUser(userHandle));
+ mProfiles.getQueryIntentsHandle(userHandle));
}
/**
* Finishing procedures to be performed after the list has been rebuilt.
* </p>Subclasses must call postRebuildListInternal at the end of postRebuildList.
- * @param rebuildCompleted
+ *
* @return <code>true</code> if the activity is finishing and creation should halt.
*/
protected boolean postRebuildList(boolean rebuildCompleted) {
@@ -802,7 +831,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
protected void onProfileTabSelected(int currentPage) {
setupViewVisibilities();
maybeLogProfileChange();
- if (hasWorkProfile()) {
+ if (mProfiles.getWorkProfilePresent()) {
// The device policy logger is only concerned with sessions that include a work profile.
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.RESOLVER_SWITCH_TABS)
@@ -814,6 +843,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
/**
* Add a label to signify that the user can pick a different app.
+ *
* @param adapter The adapter used to provide data to item views.
*/
public void addUseDifferentAppLabelIfNecessary(ResolverListAdapter adapter) {
@@ -823,7 +853,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
stub.setVisibility(View.VISIBLE);
TextView textView = (TextView) LayoutInflater.from(this).inflate(
R.layout.resolver_different_item_header, null, false);
- if (hasWorkProfile()) {
+ if (mProfiles.getWorkProfilePresent()) {
textView.setGravity(Gravity.CENTER);
}
stub.addView(textView);
@@ -875,7 +905,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
public final void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
if (!mMultiProfilePagerAdapter.onHandlePackagesChanged(
listAdapter,
- mLogic.getWorkProfileAvailabilityManager().isWaitingToEnableWorkProfile())) {
+ mProfileAvailability.getWaitingToEnableProfile())) {
// We no longer have any items... just finish the activity.
finish();
}
@@ -888,13 +918,12 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
return new CrossProfileIntentsChecker(getContentResolver());
}
- protected Unit onWorkProfileStatusUpdated() {
+ private void onWorkProfileStatusUpdated() {
if (mMultiProfilePagerAdapter.getActiveProfile() == PROFILE_WORK) {
mMultiProfilePagerAdapter.rebuildActiveTab(true);
} else {
mMultiProfilePagerAdapter.clearInactiveProfileCache();
}
- return Unit.INSTANCE;
}
// @NonFinalForTesting
@@ -906,9 +935,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
List<ResolveInfo> resolutionList,
boolean filterLastUsed,
UserHandle userHandle) {
- UserHandle initialIntentsUserSpace = isLaunchedAsCloneProfile()
- && userHandle.equals(requireAnnotatedUserHandles().personalProfileUserHandle)
- ? requireAnnotatedUserHandles().cloneProfileUserHandle : userHandle;
+ UserHandle initialIntentsUserSpace = mProfiles.getQueryIntentsHandle(userHandle);
return new ResolverListAdapter(
context,
payloadIntents,
@@ -917,7 +944,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
filterLastUsed,
createListController(userHandle),
userHandle,
- mResolverRequest.getIntent(),
+ mRequest.getIntent(),
this,
initialIntentsUserSpace,
mTargetDataLoader);
@@ -928,8 +955,10 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
final EmptyStateProvider blockerEmptyStateProvider = createBlockerEmptyStateProvider();
final EmptyStateProvider workProfileOffEmptyStateProvider =
- new ResolverWorkProfilePausedEmptyStateProvider(this, workProfileUserHandle,
- mLogic.getWorkProfileAvailabilityManager(),
+ new WorkProfilePausedEmptyStateProvider(
+ this,
+ mProfiles,
+ mProfileAvailability,
/* onSwitchOnWorkSelectedListener= */
() -> {
if (mOnSwitchOnWorkSelectedListener != null) {
@@ -941,9 +970,9 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
final EmptyStateProvider noAppsEmptyStateProvider = new NoAppsAvailableEmptyStateProvider(
this,
workProfileUserHandle,
- requireAnnotatedUserHandles().personalProfileUserHandle,
+ mProfiles.getPersonalHandle(),
getMetricsCategory(),
- requireAnnotatedUserHandles().tabOwnerUserHandleForLaunch
+ mProfiles.getTabOwnerUserHandleForLaunch()
);
// Return composite provider, the order matters (the higher, the more priority)
@@ -954,18 +983,17 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
);
}
- private ResolverMultiProfilePagerAdapter
- createResolverMultiProfilePagerAdapterForOneProfile(
- Intent[] initialIntents,
- List<ResolveInfo> resolutionList,
- boolean filterLastUsed) {
+ private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForOneProfile(
+ Intent[] initialIntents,
+ List<ResolveInfo> resolutionList,
+ boolean filterLastUsed) {
ResolverListAdapter personalAdapter = createResolverListAdapter(
/* context */ this,
- mResolverRequest.getPayloadIntents(),
+ mRequest.getPayloadIntents(),
initialIntents,
resolutionList,
filterLastUsed,
- /* userHandle */ requireAnnotatedUserHandles().personalProfileUserHandle
+ /* userHandle */ mProfiles.getPersonalHandle()
);
return new ResolverMultiProfilePagerAdapter(
/* context */ this,
@@ -980,12 +1008,12 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
/* workProfileQuietModeChecker= */ () -> false,
/* defaultProfile= */ PROFILE_PERSONAL,
/* workProfileUserHandle= */ null,
- requireAnnotatedUserHandles().cloneProfileUserHandle);
+ mProfiles.getCloneHandle());
}
private UserHandle getIntentUser() {
- return Objects.requireNonNullElse(mResolverRequest.getCallingUser(),
- requireAnnotatedUserHandles().tabOwnerUserHandleForLaunch);
+ return Objects.requireNonNullElse(mRequest.getCallingUser(),
+ mProfiles.getTabOwnerUserHandleForLaunch());
}
private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForTwoProfiles(
@@ -997,10 +1025,10 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
// this happens, we check for it here and set the current profile's tab.
int selectedProfile = getCurrentProfile();
UserHandle intentUser = getIntentUser();
- if (!requireAnnotatedUserHandles().tabOwnerUserHandleForLaunch.equals(intentUser)) {
- if (requireAnnotatedUserHandles().personalProfileUserHandle.equals(intentUser)) {
+ if (!mProfiles.getTabOwnerUserHandleForLaunch().equals(intentUser)) {
+ if (mProfiles.getPersonalHandle().equals(intentUser)) {
selectedProfile = PROFILE_PERSONAL;
- } else if (requireAnnotatedUserHandles().workProfileUserHandle.equals(intentUser)) {
+ } else if (mProfiles.getWorkHandle().equals(intentUser)) {
selectedProfile = PROFILE_WORK;
}
} else {
@@ -1014,17 +1042,17 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
// resolver list. So filterLastUsed should be false for the other profile.
ResolverListAdapter personalAdapter = createResolverListAdapter(
/* context */ this,
- mResolverRequest.getPayloadIntents(),
+ mRequest.getPayloadIntents(),
selectedProfile == PROFILE_PERSONAL ? initialIntents : null,
resolutionList,
(filterLastUsed && UserHandle.myUserId()
- == requireAnnotatedUserHandles().personalProfileUserHandle.getIdentifier()),
- /* userHandle */ requireAnnotatedUserHandles().personalProfileUserHandle
+ == mProfiles.getPersonalHandle().getIdentifier()),
+ /* userHandle */ mProfiles.getPersonalHandle()
);
- UserHandle workProfileUserHandle = requireAnnotatedUserHandles().workProfileUserHandle;
+ UserHandle workProfileUserHandle = mProfiles.getWorkHandle();
ResolverListAdapter workAdapter = createResolverListAdapter(
/* context */ this,
- mResolverRequest.getPayloadIntents(),
+ mRequest.getPayloadIntents(),
selectedProfile == PROFILE_WORK ? initialIntents : null,
resolutionList,
(filterLastUsed && UserHandle.myUserId()
@@ -1047,10 +1075,13 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
TAB_TAG_WORK,
workAdapter)),
createEmptyStateProvider(workProfileUserHandle),
- () -> mLogic.getWorkProfileAvailabilityManager().isQuietModeEnabled(),
+ /* Supplier<Boolean> (QuietMode enabled) == !(available) */
+ () -> !(mProfiles.getWorkProfilePresent()
+ && mProfileAvailability.isAvailable(
+ requireNonNull(mProfiles.getWorkProfile()))),
selectedProfile,
workProfileUserHandle,
- requireAnnotatedUserHandles().cloneProfileUserHandle);
+ mProfiles.getCloneHandle());
}
/**
@@ -1058,7 +1089,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
* #EXTRA_SELECTED_PROFILE} extra was supplied, or {@code -1} if no extra was supplied.
*/
final int getSelectedProfileExtra() {
- Profile.Type selected = mResolverRequest.getSelectedProfile();
+ Profile.Type selected = mRequest.getSelectedProfile();
if (selected == null) {
return -1;
}
@@ -1070,29 +1101,11 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
}
protected final @ProfileType int getCurrentProfile() {
- UserHandle launchUser = requireAnnotatedUserHandles().tabOwnerUserHandleForLaunch;
- UserHandle personalUser = requireAnnotatedUserHandles().personalProfileUserHandle;
+ UserHandle launchUser = mProfiles.getTabOwnerUserHandleForLaunch();
+ UserHandle personalUser = mProfiles.getPersonalHandle();
return launchUser.equals(personalUser) ? PROFILE_PERSONAL : PROFILE_WORK;
}
- private AnnotatedUserHandles requireAnnotatedUserHandles() {
- return requireNonNull(mLogic.getAnnotatedUserHandles());
- }
-
- private boolean hasWorkProfile() {
- return requireAnnotatedUserHandles().workProfileUserHandle != null;
- }
-
- private boolean hasCloneProfile() {
- return requireAnnotatedUserHandles().cloneProfileUserHandle != null;
- }
-
- protected final boolean isLaunchedAsCloneProfile() {
- UserHandle launchUser = requireAnnotatedUserHandles().userHandleSharesheetLaunchedAs;
- UserHandle cloneUser = requireAnnotatedUserHandles().cloneProfileUserHandle;
- return hasCloneProfile() && launchUser.equals(cloneUser);
- }
-
private void updateIntentPickerPaddings() {
View titleCont = findViewById(com.android.internal.R.id.title_container);
titleCont.setPadding(
@@ -1109,14 +1122,15 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
}
private void maybeLogCrossProfileTargetLaunch(TargetInfo cti, UserHandle currentUserHandle) {
- if (!hasWorkProfile() || currentUserHandle.equals(getUser())) {
+ // TODO: Test isolation bug, referencing getUser() will break tests with faked profiles
+ if (!mProfiles.getWorkProfilePresent() || currentUserHandle.equals(getUser())) {
return;
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.RESOLVER_CROSS_PROFILE_TARGET_OPENED)
.setBoolean(
currentUserHandle.equals(
- requireAnnotatedUserHandles().personalProfileUserHandle))
+ mProfiles.getPersonalHandle()))
.setStrings(getMetricsCategory(),
cti.isInDirectShareMetricsCategory() ? "direct_share" : "other_target")
.write();
@@ -1172,56 +1186,6 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
}
}
- @Override
- protected final void onRestart() {
- super.onRestart();
- if (!mRegistered) {
- mPersonalPackageMonitor.register(
- this,
- getMainLooper(),
- requireAnnotatedUserHandles().personalProfileUserHandle,
- false);
- if (hasWorkProfile()) {
- if (mWorkPackageMonitor == null) {
- mWorkPackageMonitor = createPackageMonitor(
- mMultiProfilePagerAdapter.getWorkListAdapter());
- }
- mWorkPackageMonitor.register(
- this,
- getMainLooper(),
- requireAnnotatedUserHandles().workProfileUserHandle,
- false);
- }
- mRegistered = true;
- }
- WorkProfileAvailabilityManager workProfileAvailabilityManager =
- mLogic.getWorkProfileAvailabilityManager();
- if (hasWorkProfile() && workProfileAvailabilityManager.isWaitingToEnableWorkProfile()) {
- if (workProfileAvailabilityManager.isQuietModeEnabled()) {
- workProfileAvailabilityManager.markWorkProfileEnabledBroadcastReceived();
- }
- }
- mMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
- }
-
- @Override
- protected final void onSaveInstanceState(@NonNull Bundle outState) {
- super.onSaveInstanceState(outState);
- ViewPager viewPager = findViewById(com.android.internal.R.id.profile_pager);
- if (viewPager != null) {
- outState.putInt(LAST_SHOWN_TAB_KEY, viewPager.getCurrentItem());
- }
- }
-
- @Override
- protected final void onStart() {
- super.onStart();
- this.getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
- if (hasWorkProfile()) {
- mLogic.getWorkProfileAvailabilityManager().registerWorkProfileStateReceiver(this);
- }
- }
-
private boolean hasManagedProfile() {
UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
if (userManager == null) {
@@ -1261,7 +1225,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
// In case of clonedProfile being active, we do not allow the 'Always' option in the
// disambiguation dialog of Personal Profile as the package manager cannot distinguish
// between cross-profile preferred activities.
- if (hasCloneProfile()
+ if (mProfiles.getCloneUserPresent()
&& (mMultiProfilePagerAdapter.getActiveProfile() == PROFILE_PERSONAL)) {
mAlwaysButton.setEnabled(false);
return;
@@ -1295,7 +1259,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
if (!hasRecordPermission) {
// OK, we know the record permission, is this a capture device
- boolean hasAudioCapture = mResolverRequest.isAudioCaptureDevice();
+ boolean hasAudioCapture = mViewModel.getRequest().getValue().isAudioCaptureDevice();
enabled = !hasAudioCapture;
}
}
@@ -1378,7 +1342,8 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
// We partially rebuild the inactive adapter to determine if we should auto launch
// isTabLoaded will be true here if the empty state screen is shown instead of the list.
// To date, we really only care about "partially rebuilding" tabs for work and/or personal.
- boolean rebuildCompleted = mMultiProfilePagerAdapter.rebuildTabs(hasWorkProfile());
+ boolean rebuildCompleted =
+ mMultiProfilePagerAdapter.rebuildTabs(mProfiles.getWorkProfilePresent());
if (shouldUseMiniResolver()) {
configureMiniResolverContent(targetDataLoader);
@@ -1392,7 +1357,8 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
mLayoutId = getLayoutResource();
}
setContentView(mLayoutId);
- mMultiProfilePagerAdapter.setupViewPager(findViewById(com.android.internal.R.id.profile_pager));
+ mMultiProfilePagerAdapter.setupViewPager(
+ findViewById(com.android.internal.R.id.profile_pager));
boolean result = postRebuildList(rebuildCompleted);
Trace.endSection();
return result;
@@ -1483,7 +1449,7 @@ 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(mResolverRequest.getIntent());
+ mIntentForwarding.forwardMessageFor(mRequest.getIntent());
if (profileSwitchMessage != null) {
Toast.makeText(this, profileSwitchMessage, Toast.LENGTH_LONG).show();
}
@@ -1493,9 +1459,10 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
}
} catch (RuntimeException e) {
Slog.wtf(TAG,
- "Unable to launch as uid " + mActivityModel.getLaunchedFromUid()
- + " package " + getLaunchedFromPackage() + ", while running in "
- + ActivityThread.currentProcessName(), e);
+ "Unable to launch as uid "
+ + mViewModel.getActivityModel().getLaunchedFromUid()
+ + " package " + mViewModel.getActivityModel().getLaunchedFromPackage()
+ + ", while running in " + ActivityThread.currentProcessName(), e);
}
}
@@ -1515,7 +1482,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
setupViewVisibilities();
- if (hasWorkProfile()) {
+ if (mProfiles.getWorkProfilePresent()) {
setupProfileTabs();
}
@@ -1638,7 +1605,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET)
.setBoolean(activeListAdapter.getUserHandle()
- .equals(requireAnnotatedUserHandles().personalProfileUserHandle))
+ .equals(mProfiles.getPersonalHandle()))
.setStrings(getMetricsCategory())
.write();
safelyStartActivity(activeProfileTarget);
@@ -1697,7 +1664,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET)
.setBoolean(activeListAdapter.getUserHandle()
- .equals(requireAnnotatedUserHandles().personalProfileUserHandle))
+ .equals(mProfiles.getPersonalHandle()))
.setStrings(getMetricsCategory())
.write();
safelyStartActivity(activeProfileTarget);
@@ -1755,17 +1722,17 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
&& !listAdapter.getUserHandle().equals(mHeaderCreatorUser)) {
return;
}
- if (!hasWorkProfile()
+ if (!mProfiles.getWorkProfilePresent()
&& listAdapter.getCount() == 0 && listAdapter.getPlaceholderCount() == 0) {
final TextView titleView = findViewById(com.android.internal.R.id.title);
if (titleView != null) {
titleView.setVisibility(View.GONE);
}
}
-
- CharSequence title = mResolverRequest.getTitle() != null
- ? mResolverRequest.getTitle()
- : getTitleForAction(mResolverRequest.getIntent(), 0);
+ ResolverRequest request = mViewModel.getRequest().getValue();
+ CharSequence title = mViewModel.getRequest().getValue().getTitle() != null
+ ? request.getTitle()
+ : getTitleForAction(request.getIntent(), 0);
if (!TextUtils.isEmpty(title)) {
final TextView titleView = findViewById(com.android.internal.R.id.title);
@@ -1811,7 +1778,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
// We only use the default app layout when the profile of the active user has a
// filtered item. We always show the same default app even in the inactive user profile.
return mMultiProfilePagerAdapter.getListAdapterForUserHandle(
- requireAnnotatedUserHandles().tabOwnerUserHandleForLaunch
+ mProfiles.getTabOwnerUserHandleForLaunch()
).hasFilteredItem();
}
@@ -1943,7 +1910,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
* {@link ResolverListController} configured for the provided {@code userHandle}.
*/
protected final UserHandle getQueryIntentsUser(UserHandle userHandle) {
- return requireAnnotatedUserHandles().getQueryIntentsUser(userHandle);
+ return mProfiles.getQueryIntentsHandle(userHandle);
}
/**
@@ -1963,9 +1930,9 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
// Add clonedProfileUserHandle to the list only if we are:
// a. Building the Personal Tab.
// b. CloneProfile exists on the device.
- if (userHandle.equals(requireAnnotatedUserHandles().personalProfileUserHandle)
- && hasCloneProfile()) {
- userList.add(requireAnnotatedUserHandles().cloneProfileUserHandle);
+ if (userHandle.equals(mProfiles.getPersonalHandle())
+ && mProfiles.getCloneUserPresent()) {
+ userList.add(mProfiles.getCloneHandle());
}
return userList;
}
diff --git a/java/src/com/android/intentresolver/v2/ResolverActivityLogic.kt b/java/src/com/android/intentresolver/v2/ResolverActivityLogic.kt
deleted file mode 100644
index 7eb63ab3..00000000
--- a/java/src/com/android/intentresolver/v2/ResolverActivityLogic.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.android.intentresolver.v2
-
-import androidx.activity.ComponentActivity
-import androidx.annotation.OpenForTesting
-
-/** Activity logic for [ResolverActivity]. */
-@OpenForTesting
-open class ResolverActivityLogic(
- tag: String,
- activity: ComponentActivity,
- onWorkProfileStatusUpdated: () -> Unit,
-) :
- ActivityLogic,
- CommonActivityLogic by CommonActivityLogicImpl(
- tag,
- activity,
- onWorkProfileStatusUpdated,
- )
diff --git a/java/src/com/android/intentresolver/v2/ResolverHelper.kt b/java/src/com/android/intentresolver/v2/ResolverHelper.kt
new file mode 100644
index 00000000..388b30a7
--- /dev/null
+++ b/java/src/com/android/intentresolver/v2/ResolverHelper.kt
@@ -0,0 +1,129 @@
+/*
+ * 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
+
+import android.app.Activity
+import android.os.UserHandle
+import android.util.Log
+import androidx.activity.ComponentActivity
+import androidx.activity.viewModels
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import com.android.intentresolver.inject.Background
+import com.android.intentresolver.v2.annotation.JavaInterop
+import com.android.intentresolver.v2.domain.interactor.UserInteractor
+import com.android.intentresolver.v2.ui.model.ResolverRequest
+import com.android.intentresolver.v2.ui.viewmodel.ResolverViewModel
+import com.android.intentresolver.v2.validation.Invalid
+import com.android.intentresolver.v2.validation.Valid
+import com.android.intentresolver.v2.validation.log
+import dagger.hilt.android.scopes.ActivityScoped
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+
+private const val TAG: String = "ResolverHelper"
+
+/**
+ * __Purpose__
+ *
+ * Cleanup aid. Provides a pathway to cleaner code.
+ *
+ * __Incoming References__
+ *
+ * ResolverHelper must not expose any properties or functions directly back to ResolverActivity. If
+ * a value or operation is required by ResolverActivity, then it must be added to
+ * ResolverInitializer (or a new interface as appropriate) with ResolverActivity supplying a
+ * callback to receive it at the appropriate point. This enforces unidirectional control flow.
+ *
+ * __Outgoing References__
+ *
+ * _ResolverActivity_
+ *
+ * This class must only reference it's host as Activity/ComponentActivity; no down-cast to
+ * [ResolverActivity]. Other components should be created here or supplied via Injection, and not
+ * referenced directly from the activity. This prevents circular dependencies from forming. If
+ * necessary, during cleanup the dependency can be supplied back to ChooserActivity as described
+ * above in 'Incoming References', see [ResolverInitializer].
+ *
+ * _Elsewhere_
+ *
+ * Where possible, Singleton and ActivityScoped dependencies should be injected here instead of
+ * referenced from an existing location. If not available for injection, the value should be
+ * constructed here, then provided to where it is needed.
+ */
+@ActivityScoped
+@JavaInterop
+class ResolverHelper
+@Inject
+constructor(
+ hostActivity: Activity,
+ private val userInteractor: UserInteractor,
+ @Background private val background: CoroutineDispatcher,
+) : DefaultLifecycleObserver {
+ // This is guaranteed by Hilt, since only a ComponentActivity is injectable.
+ private val activity: ComponentActivity = hostActivity as ComponentActivity
+ private val viewModel by activity.viewModels<ResolverViewModel>()
+
+ private lateinit var activityInitializer: Runnable
+
+ init {
+ activity.lifecycle.addObserver(this)
+ }
+
+ /**
+ * Set the initialization hook for the host activity.
+ *
+ * This _must_ be called from [ResolverActivity.onCreate].
+ */
+ fun setInitializer(initializer: Runnable) {
+ if (activity.lifecycle.currentState != Lifecycle.State.INITIALIZED) {
+ error("setInitializer must be called before onCreate returns")
+ }
+ activityInitializer = initializer
+ }
+
+ /** Invoked by Lifecycle, after Activity.onCreate() _returns_. */
+ override fun onCreate(owner: LifecycleOwner) {
+ Log.i(TAG, "CREATE")
+ Log.i(TAG, "${viewModel.activityModel}")
+
+ val callerUid: Int = viewModel.activityModel.launchedFromUid
+ if (callerUid < 0 || UserHandle.isIsolated(callerUid)) {
+ Log.e(TAG, "Can't start a resolver from uid $callerUid")
+ activity.finish()
+ return
+ }
+
+ when (val request = viewModel.initialRequest) {
+ is Valid -> initializeActivity(request)
+ is Invalid -> reportErrorsAndFinish(request)
+ }
+ }
+
+ private fun reportErrorsAndFinish(request: Invalid<ResolverRequest>) {
+ request.errors.forEach { it.log(TAG) }
+ activity.finish()
+ }
+
+ private fun initializeActivity(request: Valid<ResolverRequest>) {
+ Log.d(TAG, "initializeActivity")
+ request.warnings.forEach { it.log(TAG) }
+
+ activityInitializer.run()
+ }
+}
diff --git a/java/src/com/android/intentresolver/v2/emptystate/ResolverNoCrossProfileEmptyStateProvider.java b/java/src/com/android/intentresolver/v2/emptystate/ResolverNoCrossProfileEmptyStateProvider.java
deleted file mode 100644
index f133c31d..00000000
--- a/java/src/com/android/intentresolver/v2/emptystate/ResolverNoCrossProfileEmptyStateProvider.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2022 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.emptystate;
-
-import android.app.admin.DevicePolicyEventLogger;
-import android.app.admin.DevicePolicyManager;
-import android.content.Context;
-import android.os.UserHandle;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
-
-import com.android.intentresolver.ResolverListAdapter;
-import com.android.intentresolver.emptystate.CrossProfileIntentsChecker;
-import com.android.intentresolver.emptystate.EmptyState;
-import com.android.intentresolver.emptystate.EmptyStateProvider;
-
-/**
- * Empty state provider that does not allow cross profile sharing, it will return a blocker
- * in case if the profile of the current tab is not the same as the profile of the calling app.
- */
-public class ResolverNoCrossProfileEmptyStateProvider implements EmptyStateProvider {
-
- private final UserHandle mPersonalProfileUserHandle;
- private final EmptyState mNoWorkToPersonalEmptyState;
- private final EmptyState mNoPersonalToWorkEmptyState;
- private final CrossProfileIntentsChecker mCrossProfileIntentsChecker;
- private final UserHandle mTabOwnerUserHandleForLaunch;
-
- public ResolverNoCrossProfileEmptyStateProvider(UserHandle personalUserHandle,
- EmptyState noWorkToPersonalEmptyState,
- EmptyState noPersonalToWorkEmptyState,
- CrossProfileIntentsChecker crossProfileIntentsChecker,
- UserHandle tabOwnerUserHandleForLaunch) {
- mPersonalProfileUserHandle = personalUserHandle;
- mNoWorkToPersonalEmptyState = noWorkToPersonalEmptyState;
- mNoPersonalToWorkEmptyState = noPersonalToWorkEmptyState;
- mCrossProfileIntentsChecker = crossProfileIntentsChecker;
- mTabOwnerUserHandleForLaunch = tabOwnerUserHandleForLaunch;
- }
-
- @Nullable
- @Override
- public EmptyState getEmptyState(ResolverListAdapter resolverListAdapter) {
- boolean shouldShowBlocker =
- !mTabOwnerUserHandleForLaunch.equals(resolverListAdapter.getUserHandle())
- && !mCrossProfileIntentsChecker
- .hasCrossProfileIntents(resolverListAdapter.getIntents(),
- mTabOwnerUserHandleForLaunch.getIdentifier(),
- resolverListAdapter.getUserHandle().getIdentifier());
-
- if (!shouldShowBlocker) {
- return null;
- }
-
- if (resolverListAdapter.getUserHandle().equals(mPersonalProfileUserHandle)) {
- return mNoWorkToPersonalEmptyState;
- } else {
- return mNoPersonalToWorkEmptyState;
- }
- }
-
-
- /**
- * Empty state that gets strings from the device policy manager and tracks events into
- * event logger of the device policy events.
- */
- public static class DevicePolicyBlockerEmptyState implements EmptyState {
-
- @NonNull
- private final Context mContext;
- private final String mDevicePolicyStringTitleId;
- @StringRes
- private final int mDefaultTitleResource;
- private final String mDevicePolicyStringSubtitleId;
- @StringRes
- private final int mDefaultSubtitleResource;
- private final int mEventId;
- @NonNull
- private final String mEventCategory;
-
- public DevicePolicyBlockerEmptyState(@NonNull Context context,
- String devicePolicyStringTitleId, @StringRes int defaultTitleResource,
- String devicePolicyStringSubtitleId, @StringRes int defaultSubtitleResource,
- int devicePolicyEventId, @NonNull String devicePolicyEventCategory) {
- mContext = context;
- mDevicePolicyStringTitleId = devicePolicyStringTitleId;
- mDefaultTitleResource = defaultTitleResource;
- mDevicePolicyStringSubtitleId = devicePolicyStringSubtitleId;
- mDefaultSubtitleResource = defaultSubtitleResource;
- mEventId = devicePolicyEventId;
- mEventCategory = devicePolicyEventCategory;
- }
-
- @Nullable
- @Override
- public String getTitle() {
- return mContext.getSystemService(DevicePolicyManager.class).getResources().getString(
- mDevicePolicyStringTitleId,
- () -> mContext.getString(mDefaultTitleResource));
- }
-
- @Nullable
- @Override
- public String getSubtitle() {
- return mContext.getSystemService(DevicePolicyManager.class).getResources().getString(
- mDevicePolicyStringSubtitleId,
- () -> mContext.getString(mDefaultSubtitleResource));
- }
-
- @Override
- public void onEmptyStateShown() {
- DevicePolicyEventLogger.createEvent(mEventId)
- .setStrings(mEventCategory)
- .write();
- }
-
- @Override
- public boolean shouldSkipDataRebuild() {
- return true;
- }
- }
-}
diff --git a/java/src/com/android/intentresolver/v2/emptystate/ResolverWorkProfilePausedEmptyStateProvider.java b/java/src/com/android/intentresolver/v2/emptystate/ResolverWorkProfilePausedEmptyStateProvider.java
deleted file mode 100644
index eaed35a7..00000000
--- a/java/src/com/android/intentresolver/v2/emptystate/ResolverWorkProfilePausedEmptyStateProvider.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2022 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.emptystate;
-
-import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PAUSED_TITLE;
-
-import android.app.admin.DevicePolicyEventLogger;
-import android.app.admin.DevicePolicyManager;
-import android.content.Context;
-import android.os.UserHandle;
-import android.stats.devicepolicy.nano.DevicePolicyEnums;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.intentresolver.MultiProfilePagerAdapter.OnSwitchOnWorkSelectedListener;
-import com.android.intentresolver.R;
-import com.android.intentresolver.ResolverListAdapter;
-import com.android.intentresolver.WorkProfileAvailabilityManager;
-import com.android.intentresolver.emptystate.EmptyState;
-import com.android.intentresolver.emptystate.EmptyStateProvider;
-
-/**
- * ResolverActivity empty state provider that returns empty state which is shown when
- * work profile is paused and we need to show a button to enable it.
- */
-public class ResolverWorkProfilePausedEmptyStateProvider implements EmptyStateProvider {
-
- private final UserHandle mWorkProfileUserHandle;
- private final WorkProfileAvailabilityManager mWorkProfileAvailability;
- private final String mMetricsCategory;
- private final OnSwitchOnWorkSelectedListener mOnSwitchOnWorkSelectedListener;
- private final Context mContext;
-
- public ResolverWorkProfilePausedEmptyStateProvider(@NonNull Context context,
- @Nullable UserHandle workProfileUserHandle,
- @NonNull WorkProfileAvailabilityManager workProfileAvailability,
- @Nullable OnSwitchOnWorkSelectedListener onSwitchOnWorkSelectedListener,
- @NonNull String metricsCategory) {
- mContext = context;
- mWorkProfileUserHandle = workProfileUserHandle;
- mWorkProfileAvailability = workProfileAvailability;
- mMetricsCategory = metricsCategory;
- mOnSwitchOnWorkSelectedListener = onSwitchOnWorkSelectedListener;
- }
-
- @Nullable
- @Override
- public EmptyState getEmptyState(ResolverListAdapter resolverListAdapter) {
- if (!resolverListAdapter.getUserHandle().equals(mWorkProfileUserHandle)
- || !mWorkProfileAvailability.isQuietModeEnabled()
- || resolverListAdapter.getCount() == 0) {
- return null;
- }
-
- final String title = mContext.getSystemService(DevicePolicyManager.class)
- .getResources().getString(RESOLVER_WORK_PAUSED_TITLE,
- () -> mContext.getString(R.string.resolver_turn_on_work_apps));
-
- return new WorkProfileOffEmptyState(title, (tab) -> {
- tab.showSpinner();
- if (mOnSwitchOnWorkSelectedListener != null) {
- mOnSwitchOnWorkSelectedListener.onSwitchOnWorkSelected();
- }
- mWorkProfileAvailability.requestQuietModeEnabled(false);
- }, mMetricsCategory);
- }
-
- public static class WorkProfileOffEmptyState implements EmptyState {
-
- private final String mTitle;
- private final ClickListener mOnClick;
- private final String mMetricsCategory;
-
- public WorkProfileOffEmptyState(String title, @NonNull ClickListener onClick,
- @NonNull String metricsCategory) {
- mTitle = title;
- mOnClick = onClick;
- mMetricsCategory = metricsCategory;
- }
-
- @Nullable
- @Override
- public String getTitle() {
- return mTitle;
- }
-
- @Nullable
- @Override
- public ClickListener getButtonClickListener() {
- return mOnClick;
- }
-
- @Override
- public void onEmptyStateShown() {
- DevicePolicyEventLogger
- .createEvent(DevicePolicyEnums.RESOLVER_EMPTY_STATE_WORK_APPS_DISABLED)
- .setStrings(mMetricsCategory)
- .write();
- }
- }
-}
diff --git a/java/src/com/android/intentresolver/v2/ui/viewmodel/ResolverViewModel.kt b/java/src/com/android/intentresolver/v2/ui/viewmodel/ResolverViewModel.kt
new file mode 100644
index 00000000..eb6a1b96
--- /dev/null
+++ b/java/src/com/android/intentresolver/v2/ui/viewmodel/ResolverViewModel.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.util.Log
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import com.android.intentresolver.v2.ui.model.ActivityModel
+import com.android.intentresolver.v2.ui.model.ActivityModel.Companion.ACTIVITY_MODEL_KEY
+import com.android.intentresolver.v2.ui.model.ResolverRequest
+import com.android.intentresolver.v2.validation.Invalid
+import com.android.intentresolver.v2.validation.Valid
+import dagger.hilt.android.lifecycle.HiltViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+private const val TAG = "ResolverViewModel"
+
+@HiltViewModel
+class ResolverViewModel @Inject constructor(args: SavedStateHandle) : ViewModel() {
+
+ /** Parcelable-only references provided from the creating Activity */
+ val activityModel: ActivityModel =
+ requireNotNull(args[ACTIVITY_MODEL_KEY]) {
+ "ActivityModel missing in SavedStateHandle! ($ACTIVITY_MODEL_KEY)"
+ }
+
+ /**
+ * Provided only for the express purpose of early exit in the event of an invalid request.
+ *
+ * Note: [request] can only be safely accessed after checking if this value is [Valid].
+ */
+ internal val initialRequest = readResolverRequest(activityModel)
+
+ private lateinit var _request: MutableStateFlow<ResolverRequest>
+
+ /**
+ * A [StateFlow] of [ResolverRequest].
+ *
+ * Note: Only safe to access after checking if [initialRequest] is [Valid].
+ */
+ lateinit var request: StateFlow<ResolverRequest>
+ private set
+
+ init {
+ when (initialRequest) {
+ is Valid -> {
+ _request = MutableStateFlow(initialRequest.value)
+ request = _request.asStateFlow()
+ }
+ is Invalid -> Log.w(TAG, "initialRequest is Invalid, initialization failed")
+ }
+ }
+}
diff --git a/tests/activity/src/com/android/intentresolver/ResolverActivityTest.java b/tests/activity/src/com/android/intentresolver/ResolverActivityTest.java
index 05d397a2..81f6f5a6 100644
--- a/tests/activity/src/com/android/intentresolver/ResolverActivityTest.java
+++ b/tests/activity/src/com/android/intentresolver/ResolverActivityTest.java
@@ -49,9 +49,9 @@ import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
-import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.espresso.Espresso;
import androidx.test.espresso.NoMatchingViewException;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
diff --git a/tests/activity/src/com/android/intentresolver/v2/ResolverActivityTest.java b/tests/activity/src/com/android/intentresolver/v2/ResolverActivityTest.java
index 21fe2904..220a12cc 100644
--- a/tests/activity/src/com/android/intentresolver/v2/ResolverActivityTest.java
+++ b/tests/activity/src/com/android/intentresolver/v2/ResolverActivityTest.java
@@ -25,6 +25,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.intentresolver.MatcherUtils.first;
import static com.android.intentresolver.v2.ResolverWrapperActivity.sOverrides;
@@ -55,16 +56,23 @@ import androidx.test.espresso.NoMatchingViewException;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
-import com.android.intentresolver.AnnotatedUserHandles;
import com.android.intentresolver.R;
import com.android.intentresolver.ResolvedComponentInfo;
import com.android.intentresolver.ResolverDataProvider;
+import com.android.intentresolver.inject.ApplicationUser;
+import com.android.intentresolver.inject.ProfileParent;
+import com.android.intentresolver.v2.data.repository.FakeUserRepository;
+import com.android.intentresolver.v2.data.repository.UserRepository;
+import com.android.intentresolver.v2.data.repository.UserRepositoryModule;
+import com.android.intentresolver.v2.shared.model.User;
import com.android.intentresolver.widget.ResolverDrawerLayout;
import com.google.android.collect.Lists;
+import dagger.hilt.android.testing.BindValue;
import dagger.hilt.android.testing.HiltAndroidRule;
import dagger.hilt.android.testing.HiltAndroidTest;
+import dagger.hilt.android.testing.UninstallModules;
import org.junit.Before;
import org.junit.Ignore;
@@ -81,19 +89,15 @@ import java.util.List;
*/
@RunWith(AndroidJUnit4.class)
@HiltAndroidTest
+@UninstallModules(UserRepositoryModule.class)
public class ResolverActivityTest {
- private static final UserHandle PERSONAL_USER_HANDLE = androidx.test.platform.app
- .InstrumentationRegistry.getInstrumentation().getTargetContext().getUser();
+ private static final UserHandle PERSONAL_USER_HANDLE =
+ getInstrumentation().getTargetContext().getUser();
private static final UserHandle WORK_PROFILE_USER_HANDLE = UserHandle.of(10);
private static final UserHandle CLONE_PROFILE_USER_HANDLE = UserHandle.of(11);
-
- protected Intent getConcreteIntentForLaunch(Intent clientIntent) {
- clientIntent.setClass(
- androidx.test.platform.app.InstrumentationRegistry.getInstrumentation().getTargetContext(),
- ResolverWrapperActivity.class);
- return clientIntent;
- }
+ private static final User WORK_PROFILE_USER =
+ new User(WORK_PROFILE_USER_HANDLE.getIdentifier(), User.Role.WORK);
@Rule(order = 0)
public HiltAndroidRule mHiltAndroidRule = new HiltAndroidRule(this);
@@ -106,14 +110,30 @@ public class ResolverActivityTest {
public void setup() {
// TODO: use the other form of `adoptShellPermissionIdentity()` where we explicitly list the
// permissions we require (which we'll read from the manifest at runtime).
- androidx.test.platform.app.InstrumentationRegistry
- .getInstrumentation()
+ getInstrumentation()
.getUiAutomation()
.adoptShellPermissionIdentity();
sOverrides.reset();
}
+ @BindValue
+ @ApplicationUser
+ public final UserHandle mApplicationUser = PERSONAL_USER_HANDLE;
+
+ @BindValue
+ @ProfileParent
+ public final UserHandle mProfileParent = PERSONAL_USER_HANDLE;
+
+ /** For setup of test state, a mutable reference of mUserRepository */
+ private final FakeUserRepository mFakeUserRepo =
+ new FakeUserRepository(List.of(
+ new User(PERSONAL_USER_HANDLE.getIdentifier(), User.Role.PERSONAL)
+ ));
+
+ @BindValue
+ public final UserRepository mUserRepository = mFakeUserRepo;
+
@Test
public void twoOptionsAndUserSelectsOne() throws InterruptedException {
Intent sendIntent = createSendImageIntent();
@@ -404,15 +424,14 @@ public class ResolverActivityTest {
@Test
public void testWorkTab_workTabUsesExpectedAdapter() {
+ markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
PERSONAL_USER_HANDLE);
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
WORK_PROFILE_USER_HANDLE);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendImageIntent();
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
waitForIdle();
@@ -424,9 +443,9 @@ public class ResolverActivityTest {
@Test
public void testWorkTab_personalTabUsesExpectedAdapter() {
+ markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
List<ResolvedComponentInfo> personalResolvedComponentInfos =
createResolvedComponentsForTestWithOtherProfile(3, PERSONAL_USER_HANDLE);
- markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
WORK_PROFILE_USER_HANDLE);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
@@ -464,7 +483,8 @@ public class ResolverActivityTest {
public void testWorkTab_selectingWorkTabAppOpensAppInWorkProfile() throws InterruptedException {
markOtherProfileAvailability(/* workAvailable= */ true, /* cloneAvailable= */ false);
List<ResolvedComponentInfo> personalResolvedComponentInfos =
- createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10,
+ createResolvedComponentsForTestWithOtherProfile(3,
+ /* userId */ WORK_PROFILE_USER_HANDLE.getIdentifier(),
PERSONAL_USER_HANDLE);
List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4,
WORK_PROFILE_USER_HANDLE);
@@ -622,7 +642,7 @@ public class ResolverActivityTest {
PERSONAL_USER_HANDLE);
List<ResolvedComponentInfo> workResolvedComponentInfos =
createResolvedComponentsForTest(workProfileTargets, WORK_PROFILE_USER_HANDLE);
- sOverrides.isQuietModeEnabled = true;
+ mFakeUserRepo.updateState(WORK_PROFILE_USER, false);
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendImageIntent();
sendIntent.setType("TestType");
@@ -670,7 +690,7 @@ public class ResolverActivityTest {
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendImageIntent();
sendIntent.setType("TestType");
- sOverrides.isQuietModeEnabled = true;
+ mFakeUserRepo.updateState(WORK_PROFILE_USER, false);
sOverrides.hasCrossProfileIntents = false;
mActivityRule.launchActivity(sendIntent);
@@ -740,7 +760,7 @@ public class ResolverActivityTest {
setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
Intent sendIntent = createSendImageIntent();
sendIntent.setType("TestType");
- sOverrides.isQuietModeEnabled = true;
+ mFakeUserRepo.updateState(WORK_PROFILE_USER, false);
mActivityRule.launchActivity(sendIntent);
waitForIdle();
@@ -1068,18 +1088,14 @@ public class ResolverActivityTest {
}
private void markOtherProfileAvailability(boolean workAvailable, boolean cloneAvailable) {
- AnnotatedUserHandles.Builder handles = AnnotatedUserHandles.newBuilder();
- handles
- .setUserIdOfCallingApp(1234) // Must be non-negative.
- .setUserHandleSharesheetLaunchedAs(PERSONAL_USER_HANDLE)
- .setPersonalProfileUserHandle(PERSONAL_USER_HANDLE);
if (workAvailable) {
- handles.setWorkProfileUserHandle(WORK_PROFILE_USER_HANDLE);
+ mFakeUserRepo.addUser(
+ new User(WORK_PROFILE_USER_HANDLE.getIdentifier(), User.Role.WORK), true);
}
if (cloneAvailable) {
- handles.setCloneProfileUserHandle(CLONE_PROFILE_USER_HANDLE);
+ mFakeUserRepo.addUser(
+ new User(CLONE_PROFILE_USER_HANDLE.getIdentifier(), User.Role.CLONE), true);
}
- sOverrides.annotatedUserHandles = handles.build();
}
private void setupResolverControllers(
@@ -1095,21 +1111,14 @@ public class ResolverActivityTest {
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
- .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
- when(sOverrides.workResolverListController.getResolversForIntentAsUser(
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.anyBoolean(),
- Mockito.isA(List.class),
- eq(UserHandle.SYSTEM)))
+ eq(PERSONAL_USER_HANDLE)))
.thenReturn(new ArrayList<>(personalResolvedComponentInfos));
when(sOverrides.workResolverListController.getResolversForIntentAsUser(
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.anyBoolean(),
Mockito.isA(List.class),
- eq(UserHandle.of(10))))
+ eq(WORK_PROFILE_USER_HANDLE)))
.thenReturn(new ArrayList<>(workResolvedComponentInfos));
}
}
diff --git a/tests/activity/src/com/android/intentresolver/v2/ResolverWrapperActivity.java b/tests/activity/src/com/android/intentresolver/v2/ResolverWrapperActivity.java
index 2e29be11..e3d2edbb 100644
--- a/tests/activity/src/com/android/intentresolver/v2/ResolverWrapperActivity.java
+++ b/tests/activity/src/com/android/intentresolver/v2/ResolverWrapperActivity.java
@@ -24,7 +24,6 @@ import static org.mockito.Mockito.when;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -34,10 +33,8 @@ import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.test.espresso.idling.CountingIdlingResource;
-import com.android.intentresolver.AnnotatedUserHandles;
import com.android.intentresolver.ResolverListAdapter;
import com.android.intentresolver.ResolverListController;
-import com.android.intentresolver.WorkProfileAvailabilityManager;
import com.android.intentresolver.chooser.DisplayResolveInfo;
import com.android.intentresolver.chooser.SelectableTargetInfo;
import com.android.intentresolver.chooser.TargetInfo;
@@ -45,8 +42,6 @@ import com.android.intentresolver.emptystate.CrossProfileIntentsChecker;
import com.android.intentresolver.icons.LabelInfo;
import com.android.intentresolver.icons.TargetDataLoader;
-import kotlin.Unit;
-
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -60,19 +55,6 @@ public class ResolverWrapperActivity extends ResolverActivity {
private final CountingIdlingResource mLabelIdlingResource =
new CountingIdlingResource("LoadLabelTask");
- @Override
- protected final ResolverActivityLogic createActivityLogic() {
- return new TestResolverActivityLogic(
- "ResolverWrapper",
- this,
- () -> {
- onWorkProfileStatusUpdated();
- return Unit.INSTANCE;
- },
- sOverrides
- );
- }
-
public CountingIdlingResource getLabelIdlingResource() {
return mLabelIdlingResource;
}
@@ -154,12 +136,6 @@ public class ResolverWrapperActivity extends ResolverActivity {
super.startActivityAsUser(intent, options, user);
}
- @Override
- protected List<UserHandle> getResolverRankerServiceUserHandleListInternal(UserHandle
- userHandle) {
- return super.getResolverRankerServiceUserHandleListInternal(userHandle);
- }
-
/**
* We cannot directly mock the activity created since instrumentation creates it.
* <p>
@@ -167,58 +143,19 @@ public class ResolverWrapperActivity extends ResolverActivity {
*/
public static class OverrideData {
@SuppressWarnings("Since15")
- public Function<PackageManager, PackageManager> createPackageManager;
public Function<Pair<TargetInfo, UserHandle>, Boolean> onSafelyStartInternalCallback;
public ResolverListController resolverListController;
public ResolverListController workResolverListController;
public Boolean isVoiceInteraction;
- public AnnotatedUserHandles annotatedUserHandles;
- public Integer myUserId;
public boolean hasCrossProfileIntents;
- public boolean isQuietModeEnabled;
- public WorkProfileAvailabilityManager mWorkProfileAvailability;
public CrossProfileIntentsChecker mCrossProfileIntentsChecker;
public void reset() {
onSafelyStartInternalCallback = null;
isVoiceInteraction = null;
- createPackageManager = null;
resolverListController = mock(ResolverListController.class);
workResolverListController = mock(ResolverListController.class);
- annotatedUserHandles = AnnotatedUserHandles.newBuilder()
- .setUserIdOfCallingApp(1234) // Must be non-negative.
- .setUserHandleSharesheetLaunchedAs(UserHandle.SYSTEM)
- .setPersonalProfileUserHandle(UserHandle.SYSTEM)
- .build();
- myUserId = null;
hasCrossProfileIntents = true;
- isQuietModeEnabled = false;
-
- mWorkProfileAvailability = new WorkProfileAvailabilityManager(null, null, null) {
- @Override
- public boolean isQuietModeEnabled() {
- return isQuietModeEnabled;
- }
-
- @Override
- public boolean isWorkProfileUserUnlocked() {
- return true;
- }
-
- @Override
- public void requestQuietModeEnabled(boolean enabled) {
- isQuietModeEnabled = enabled;
- }
-
- @Override
- public void markWorkProfileEnabledBroadcastReceived() {}
-
- @Override
- public boolean isWaitingToEnableWorkProfile() {
- return false;
- }
- };
-
mCrossProfileIntentsChecker = mock(CrossProfileIntentsChecker.class);
when(mCrossProfileIntentsChecker.hasCrossProfileIntents(any(), anyInt(), anyInt()))
.thenAnswer(invocation -> hasCrossProfileIntents);
diff --git a/tests/activity/src/com/android/intentresolver/v2/TestResolverActivityLogic.kt b/tests/activity/src/com/android/intentresolver/v2/TestResolverActivityLogic.kt
deleted file mode 100644
index 6826f23d..00000000
--- a/tests/activity/src/com/android/intentresolver/v2/TestResolverActivityLogic.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.android.intentresolver.v2
-
-import androidx.activity.ComponentActivity
-import com.android.intentresolver.AnnotatedUserHandles
-import com.android.intentresolver.WorkProfileAvailabilityManager
-
-/** Activity logic for use when testing [ResolverActivity]. */
-class TestResolverActivityLogic(
- tag: String,
- activity: ComponentActivity,
- onWorkProfileStatusUpdated: () -> Unit,
- private val overrideData: ResolverWrapperActivity.OverrideData,
-) : ResolverActivityLogic(tag, activity, onWorkProfileStatusUpdated) {
-
- override val annotatedUserHandles: AnnotatedUserHandles? by lazy {
- overrideData.annotatedUserHandles
- }
-
- override val workProfileAvailabilityManager: WorkProfileAvailabilityManager by lazy {
- overrideData.mWorkProfileAvailability ?: super.workProfileAvailabilityManager
- }
-}