summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Mark Renouf <mrenouf@google.com> 2024-04-17 17:26:13 -0400
committer Mark Renouf <mrenouf@google.com> 2024-04-19 10:47:08 -0400
commit4ca5dc54a57d61b8bd116a2259f51a7cdc5ea184 (patch)
tree5c6ca057bb0c64b667aa277ab4487656a5af5ee7
parent56bae808e5af2bde54a69f95aa0c9ba19edc051c (diff)
Fix "NoAppsAvailableEmptyStateProvider" message for Private
Updates the logic to simplify and correct correct labels for private profile. When there is only a single visible profile (personal), the default empty state is returned, otherwise present the more informative "No work apps", "No private apps" or "No personal apps" is shown. To send an intent to share via Sharesheet which matches no installed apps: adb shell am start -a android.intent.action.CHOOSER --eu android.intent.extra.INTENT data:text/plain;base64,SGVsbG8sIFdvcmxkIQ== Explanation: ChooserActivity accepts EXTRA_INTENT containing either an Intent or a URI. In this case the data: uri is decoded and results in a string -as- the intent which doesn't match any apps. Bug: 334039327 Test: manual, see adb command Test: With work profile paused, and unpaused Test: With private profile locked, and unlocked Change-Id: I1e2565686fb8f7bcf15611541e905cf903e64704
-rw-r--r--java/res/values/strings.xml3
-rw-r--r--java/src/com/android/intentresolver/ChooserActivity.java7
-rw-r--r--java/src/com/android/intentresolver/ProfileAvailability.kt18
-rw-r--r--java/src/com/android/intentresolver/ResolverActivity.java11
-rw-r--r--java/src/com/android/intentresolver/data/repository/DevicePolicyResources.kt43
-rw-r--r--java/src/com/android/intentresolver/emptystate/NoAppsAvailableEmptyStateProvider.java101
-rw-r--r--java/src/com/android/intentresolver/ui/ProfilePagerResources.kt8
7 files changed, 112 insertions, 79 deletions
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 5c1210b7..17a514d7 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -295,6 +295,9 @@
<!-- Error message. This text lets the user know that their current personal apps don't support the specific content. [CHAR LIMIT=NONE] -->
<string name="resolver_no_personal_apps_available">No personal apps</string>
+ <!-- Error message. This text lets the user know that their current private apps don't support the specific content. [CHAR LIMIT=NONE] -->
+ <string name="resolver_no_private_apps_available">No private apps</string>
+
<!-- Dialog title. User must choose between opening content in a cross-profile app or same-profile browser. [CHAR LIMIT=NONE] -->
<string name="miniresolver_open_in_personal">Open <xliff:g id="app" example="YouTube">%s</xliff:g> in your personal profile?</string>
<!-- Dialog title. User must choose between opening content in a cross-profile app or same-profile browser. [CHAR LIMIT=NONE] -->
diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java
index 7e2c9c5a..43d28761 100644
--- a/java/src/com/android/intentresolver/ChooserActivity.java
+++ b/java/src/com/android/intentresolver/ChooserActivity.java
@@ -1051,11 +1051,10 @@ public class ChooserActivity extends Hilt_ChooserActivity implements
getMetricsCategory());
EmptyStateProvider noAppsEmptyStateProvider = new NoAppsAvailableEmptyStateProvider(
- this,
- profileHelper.getWorkHandle(),
- profileHelper.getPersonalHandle(),
+ mProfiles,
+ mProfileAvailability,
getMetricsCategory(),
- profileHelper.getTabOwnerUserHandleForLaunch()
+ mProfilePagerResources
);
// Return composite provider, the order matters (the higher, the more priority)
diff --git a/java/src/com/android/intentresolver/ProfileAvailability.kt b/java/src/com/android/intentresolver/ProfileAvailability.kt
index cf3e566e..c8e78552 100644
--- a/java/src/com/android/intentresolver/ProfileAvailability.kt
+++ b/java/src/com/android/intentresolver/ProfileAvailability.kt
@@ -53,6 +53,24 @@ class ProfileAvailability(
}
}
+ /**
+ * The number of profiles which are visible. All profiles count except for private which is
+ * hidden when locked.
+ */
+ fun visibleProfileCount() =
+ runBlocking(background) {
+ val availability = userInteractor.availability.first()
+ val profiles = userInteractor.profiles.first()
+ profiles
+ .filter {
+ when (it.type) {
+ Profile.Type.PRIVATE -> availability[it] == true
+ else -> true
+ }
+ }
+ .size
+ }
+
/** Used by WorkProfilePausedEmptyStateProvider */
fun requestQuietModeState(profile: Profile, quietMode: Boolean) {
val enableProfile = !quietMode
diff --git a/java/src/com/android/intentresolver/ResolverActivity.java b/java/src/com/android/intentresolver/ResolverActivity.java
index 2f1f8ee5..4e763f94 100644
--- a/java/src/com/android/intentresolver/ResolverActivity.java
+++ b/java/src/com/android/intentresolver/ResolverActivity.java
@@ -112,6 +112,7 @@ import com.android.intentresolver.profiles.ResolverMultiProfilePagerAdapter;
import com.android.intentresolver.profiles.TabConfig;
import com.android.intentresolver.shared.model.Profile;
import com.android.intentresolver.ui.ActionTitle;
+import com.android.intentresolver.ui.ProfilePagerResources;
import com.android.intentresolver.ui.model.ActivityModel;
import com.android.intentresolver.ui.model.ResolverRequest;
import com.android.intentresolver.ui.viewmodel.ResolverViewModel;
@@ -153,6 +154,7 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
@Inject public ResolverHelper mResolverHelper;
@Inject public PackageManager mPackageManager;
@Inject public DevicePolicyResources mDevicePolicyResources;
+ @Inject public ProfilePagerResources mProfilePagerResources;
@Inject public IntentForwarding mIntentForwarding;
@Inject public FeatureFlags mFeatureFlags;
@@ -963,12 +965,11 @@ public class ResolverActivity extends Hilt_ResolverActivity implements
},
getMetricsCategory());
- final EmptyStateProvider noAppsEmptyStateProvider = new NoAppsAvailableEmptyStateProvider(
- this,
- workProfileUserHandle,
- mProfiles.getPersonalHandle(),
+ EmptyStateProvider noAppsEmptyStateProvider = new NoAppsAvailableEmptyStateProvider(
+ mProfiles,
+ mProfileAvailability,
getMetricsCategory(),
- mProfiles.getTabOwnerUserHandleForLaunch()
+ mProfilePagerResources
);
// Return composite provider, the order matters (the higher, the more priority)
diff --git a/java/src/com/android/intentresolver/data/repository/DevicePolicyResources.kt b/java/src/com/android/intentresolver/data/repository/DevicePolicyResources.kt
index c396b720..75faa068 100644
--- a/java/src/com/android/intentresolver/data/repository/DevicePolicyResources.kt
+++ b/java/src/com/android/intentresolver/data/repository/DevicePolicyResources.kt
@@ -18,6 +18,9 @@ package com.android.intentresolver.data.repository
import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL
import android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK
+import android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE
+import android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_PERSONAL_APPS
+import android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_WORK_APPS
import android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB
import android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_PERSONAL_TAB_ACCESSIBILITY
import android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_WORK_PROFILE_NOT_SUPPORTED
@@ -83,6 +86,46 @@ constructor(
)
}
+ val noPersonalApps by lazy {
+ requireNotNull(
+ policyResources.getString(RESOLVER_NO_PERSONAL_APPS) {
+ resources.getString(R.string.resolver_no_personal_apps_available)
+ }
+ )
+ }
+
+ val noWorkApps by lazy {
+ requireNotNull(
+ policyResources.getString(RESOLVER_NO_WORK_APPS) {
+ resources.getString(R.string.resolver_no_work_apps_available)
+ }
+ )
+ }
+
+ val crossProfileBlocked by lazy {
+ requireNotNull(
+ policyResources.getString(RESOLVER_CROSS_PROFILE_BLOCKED_TITLE) {
+ resources.getString(R.string.resolver_cross_profile_blocked)
+ }
+ )
+ }
+
+ fun toPersonalBlockedByPolicyMessage(sendAction: Boolean): String {
+ return if (sendAction) {
+ resources.getString(R.string.resolver_cant_share_with_personal_apps_explanation)
+ } else {
+ resources.getString(R.string.resolver_cant_access_personal_apps_explanation)
+ }
+ }
+
+ fun toWorkBlockedByPolicyMessage(sendAction: Boolean): String {
+ return if (sendAction) {
+ resources.getString(R.string.resolver_cant_share_with_work_apps_explanation)
+ } else {
+ resources.getString(R.string.resolver_cant_access_work_apps_explanation)
+ }
+ }
+
fun getWorkProfileNotSupportedMessage(launcherName: String): String {
return requireNotNull(
policyResources.getString(
diff --git a/java/src/com/android/intentresolver/emptystate/NoAppsAvailableEmptyStateProvider.java b/java/src/com/android/intentresolver/emptystate/NoAppsAvailableEmptyStateProvider.java
index 7bfea4f8..af9d56d1 100644
--- a/java/src/com/android/intentresolver/emptystate/NoAppsAvailableEmptyStateProvider.java
+++ b/java/src/com/android/intentresolver/emptystate/NoAppsAvailableEmptyStateProvider.java
@@ -16,24 +16,22 @@
package com.android.intentresolver.emptystate;
-import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_PERSONAL_APPS;
-import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_NO_WORK_APPS;
+
+import static com.android.intentresolver.shared.model.Profile.Type.PERSONAL;
+
+import static java.util.Objects.requireNonNull;
import android.app.admin.DevicePolicyEventLogger;
-import android.app.admin.DevicePolicyManager;
-import android.content.Context;
-import android.content.pm.ResolveInfo;
import android.os.UserHandle;
import android.stats.devicepolicy.nano.DevicePolicyEnums;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import com.android.intentresolver.R;
-import com.android.intentresolver.ResolvedComponentInfo;
+import com.android.intentresolver.ProfileAvailability;
+import com.android.intentresolver.ProfileHelper;
import com.android.intentresolver.ResolverListAdapter;
-
-import java.util.List;
+import com.android.intentresolver.shared.model.Profile;
+import com.android.intentresolver.ui.ProfilePagerResources;
/**
* Chooser/ResolverActivity empty state provider that returns empty state which is shown when
@@ -41,77 +39,40 @@ import java.util.List;
*/
public class NoAppsAvailableEmptyStateProvider implements EmptyStateProvider {
- @NonNull
- private final Context mContext;
- @Nullable
- private final UserHandle mWorkProfileUserHandle;
- @Nullable
- private final UserHandle mPersonalProfileUserHandle;
- @NonNull
- private final String mMetricsCategory;
- @NonNull
- private final UserHandle mTabOwnerUserHandleForLaunch;
-
- public NoAppsAvailableEmptyStateProvider(@NonNull Context context,
- @Nullable UserHandle workProfileUserHandle,
- @Nullable UserHandle personalProfileUserHandle, @NonNull String metricsCategory,
- @NonNull UserHandle tabOwnerUserHandleForLaunch) {
- mContext = context;
- mWorkProfileUserHandle = workProfileUserHandle;
- mPersonalProfileUserHandle = personalProfileUserHandle;
+ @NonNull private final String mMetricsCategory;
+ private final ProfilePagerResources mProfilePagerResources;
+ private final ProfileHelper mProfileHelper;
+ private final ProfileAvailability mProfileAvailability;
+
+ public NoAppsAvailableEmptyStateProvider(
+ ProfileHelper profileHelper,
+ ProfileAvailability profileAvailability,
+ @NonNull String metricsCategory,
+ ProfilePagerResources profilePagerResources) {
+ mProfileHelper = profileHelper;
+ mProfileAvailability = profileAvailability;
mMetricsCategory = metricsCategory;
- mTabOwnerUserHandleForLaunch = tabOwnerUserHandleForLaunch;
+ mProfilePagerResources = profilePagerResources;
}
- @Nullable
+ @NonNull
@Override
- @SuppressWarnings("ReferenceEquality")
public EmptyState getEmptyState(ResolverListAdapter resolverListAdapter) {
UserHandle listUserHandle = resolverListAdapter.getUserHandle();
-
- if (mWorkProfileUserHandle != null
- && (mTabOwnerUserHandleForLaunch.equals(listUserHandle)
- || !hasAppsInOtherProfile(resolverListAdapter))) {
-
- String title;
- if (listUserHandle == mPersonalProfileUserHandle) {
- title = mContext.getSystemService(
- DevicePolicyManager.class).getResources().getString(
- RESOLVER_NO_PERSONAL_APPS,
- () -> mContext.getString(R.string.resolver_no_personal_apps_available));
- } else {
- title = mContext.getSystemService(
- DevicePolicyManager.class).getResources().getString(
- RESOLVER_NO_WORK_APPS,
- () -> mContext.getString(R.string.resolver_no_work_apps_available));
- }
-
+ if (mProfileAvailability.visibleProfileCount() == 1) {
+ return new DefaultEmptyState();
+ } else {
+ Profile.Type profileType =
+ requireNonNull(mProfileHelper.findProfileType(listUserHandle));
+ String title = mProfilePagerResources.noAppsMessage(profileType);
return new NoAppsAvailableEmptyState(
- title, mMetricsCategory,
- /* isPersonalProfile= */ listUserHandle == mPersonalProfileUserHandle
+ title,
+ mMetricsCategory,
+ /* isPersonalProfile= */ profileType == PERSONAL
);
- } else if (mWorkProfileUserHandle == null) {
- // Return default empty state without tracking
- return new DefaultEmptyState();
}
-
- return null;
}
- private boolean hasAppsInOtherProfile(ResolverListAdapter adapter) {
- if (mWorkProfileUserHandle == null) {
- return false;
- }
- List<ResolvedComponentInfo> resolversForIntent =
- adapter.getResolversForUser(mTabOwnerUserHandleForLaunch);
- for (ResolvedComponentInfo info : resolversForIntent) {
- ResolveInfo resolveInfo = info.getResolveInfoAt(0);
- if (resolveInfo.targetUserId != UserHandle.USER_CURRENT) {
- return true;
- }
- }
- return false;
- }
public static class DefaultEmptyState implements EmptyState {
@Override
diff --git a/java/src/com/android/intentresolver/ui/ProfilePagerResources.kt b/java/src/com/android/intentresolver/ui/ProfilePagerResources.kt
index baab9a4c..0d07af8f 100644
--- a/java/src/com/android/intentresolver/ui/ProfilePagerResources.kt
+++ b/java/src/com/android/intentresolver/ui/ProfilePagerResources.kt
@@ -50,4 +50,12 @@ constructor(
Profile.Type.PRIVATE -> privateTabAccessibilityLabel
}
}
+
+ fun noAppsMessage(type: Profile.Type): String {
+ return when (type) {
+ Profile.Type.PERSONAL -> devicePolicyResources.noPersonalApps
+ Profile.Type.WORK -> devicePolicyResources.noWorkApps
+ Profile.Type.PRIVATE -> resources.getString(R.string.resolver_no_private_apps_available)
+ }
+ }
}