From 67d2ec38abfb7705188db71c0826f896029be09e Mon Sep 17 00:00:00 2001 From: Anthony Alridge Date: Fri, 3 Feb 2023 11:06:22 +0000 Subject: [PATCH] Update UI for account switcher The text is updated to include the app name. (strings will be updated following go/android-late-strings) The personal profile icon is tinted with the same color as the work badge (blue). Tints are set using setColorFilter(...) so that they are not overridden by callers using setTint. Bug: 261717754 Test: Manual - screenshots in bug Change-Id: I0eac34e908bb0fe6430abb2a9c05e6d5e9b414e0 --- .../app/admin/DevicePolicyResources.java | 6 ++- .../android/content/pm/CrossProfileApps.java | 45 ++++++++++++++++--- core/res/res/values/strings.xml | 6 +++ core/res/res/values/symbols.xml | 2 + .../content/pm/CrossProfileAppsTest.java | 20 +++++++-- 5 files changed, 67 insertions(+), 12 deletions(-) diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java index 052f670b71bd6..77ba560f29a19 100644 --- a/core/java/android/app/admin/DevicePolicyResources.java +++ b/core/java/android/app/admin/DevicePolicyResources.java @@ -1662,14 +1662,16 @@ public final class DevicePolicyResources { /** * Label returned from * {@link android.content.pm.CrossProfileApps#getProfileSwitchingLabel(UserHandle)} - * that calling app can show to user for the semantic of switching to work profile. + * that calling app can show to user for the semantic of switching to work profile, and + * accepts the app name as a param. */ public static final String SWITCH_TO_WORK_LABEL = PREFIX + "SWITCH_TO_WORK_LABEL"; /** * Label returned from * {@link android.content.pm.CrossProfileApps#getProfileSwitchingLabel(UserHandle)} - * that calling app can show to user for the semantic of switching to personal profile. + * that calling app can show to user for the semantic of switching to personal profile, + * and accepts the app name as a param. */ public static final String SWITCH_TO_PERSONAL_LABEL = PREFIX + "SWITCH_TO_PERSONAL_LABEL"; diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index d6951eec904f4..7ac8f37c645e4 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -18,6 +18,7 @@ package android.content.pm; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_PERSONAL_LABEL; import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_WORK_LABEL; +import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY; import android.annotation.NonNull; import android.annotation.Nullable; @@ -40,6 +41,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.text.TextUtils; import com.android.internal.R; import com.android.internal.util.UserIcons; @@ -329,19 +331,40 @@ public class CrossProfileApps { final boolean isManagedProfile = mUserManager.isManagedProfile(userHandle.getIdentifier()); final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); + final String callingAppLabel = getCallingApplicationLabel().toString(); return dpm.getResources().getString( getUpdatableProfileSwitchingLabelId(isManagedProfile), - () -> getDefaultProfileSwitchingLabel(isManagedProfile)); + () -> getDefaultProfileSwitchingLabel(isManagedProfile, callingAppLabel), + callingAppLabel); + } + + private CharSequence getCallingApplicationLabel() { + PackageManager pm = mContext.getPackageManager(); + // If there is a label for the launcher intent, then use that as it is typically shorter. + // Otherwise, just use the top-level application name. + Intent launchIntent = pm.getLaunchIntentForPackage(mContext.getPackageName()); + List infos = + pm.queryIntentActivities( + launchIntent, PackageManager.ResolveInfoFlags.of(MATCH_DEFAULT_ONLY)); + if (infos.size() > 0) { + return infos.get(0).loadLabel(pm); + } + return mContext.getApplicationInfo() + .loadSafeLabel( + pm, + /* ellipsizeDip= */ 0, + TextUtils.SAFE_STRING_FLAG_SINGLE_LINE + | TextUtils.SAFE_STRING_FLAG_TRIM); } private String getUpdatableProfileSwitchingLabelId(boolean isManagedProfile) { return isManagedProfile ? SWITCH_TO_WORK_LABEL : SWITCH_TO_PERSONAL_LABEL; } - private String getDefaultProfileSwitchingLabel(boolean isManagedProfile) { + private String getDefaultProfileSwitchingLabel(boolean isManagedProfile, String label) { final int stringRes = isManagedProfile - ? R.string.managed_profile_label : R.string.user_owner_label; - return mResources.getString(stringRes); + ? R.string.managed_profile_app_label : R.string.user_owner_app_label; + return mResources.getString(stringRes, label); } @@ -366,10 +389,18 @@ public class CrossProfileApps { if (isManagedProfile) { return mContext.getPackageManager().getUserBadgeForDensityNoBackground( userHandle, /* density= */ 0); - } else { - return UserIcons.getDefaultUserIcon( - mResources, UserHandle.USER_SYSTEM, true /* light */); } + Drawable personalProfileIcon = UserIcons.getDefaultUserIcon( + mResources, UserHandle.USER_SYSTEM, /* light= */ true); + // Using the same colors as the managed profile icon. + int colorId = mContext.getResources().getConfiguration().isNightModeActive() + ? R.color.profile_badge_1_dark + : R.color.profile_badge_1; + // First set the color filter to null so that it does not override + // the tint. + personalProfileIcon.setColorFilter(null); + personalProfileIcon.setTint(mResources.getColor(colorId, /* theme= */ null)); + return personalProfileIcon; } /** diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index c6462f15ec509..a49ea0bd55fb0 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -861,6 +861,12 @@ Switch to work profile + + Switch to personal %1$s + + + Switch to work %1$s + Contacts diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index b7df6a482983c..ae107fd648d3b 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1068,7 +1068,9 @@ + + diff --git a/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java index 5e076076d2f45..661b210f3e185 100644 --- a/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java +++ b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java @@ -20,15 +20,18 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_PER import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_WORK_LABEL; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyResourcesManager; import android.content.Context; +import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.UserHandle; @@ -76,15 +79,21 @@ public class CrossProfileAppsTest { private Drawable mDrawable; @Mock private PackageManager mPackageManager; + + @Mock private ApplicationInfo mApplicationInfo; + + private Configuration mConfiguration; + private CrossProfileApps mCrossProfileApps; @Before public void initCrossProfileApps() { - mCrossProfileApps = new CrossProfileApps(mContext, mService); + mCrossProfileApps = spy(new CrossProfileApps(mContext, mService)); } @Before public void mockContext() { + mConfiguration = new Configuration(); when(mContext.getPackageName()).thenReturn(MY_PACKAGE); when(mContext.getSystemServiceName(UserManager.class)).thenReturn(Context.USER_SERVICE); when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); @@ -94,6 +103,8 @@ public class CrossProfileAppsTest { mDevicePolicyManager); when(mDevicePolicyManager.getResources()).thenReturn(mDevicePolicyResourcesManager); when(mContext.getPackageManager()).thenReturn(mPackageManager); + when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo); + when(mResources.getConfiguration()).thenReturn(mConfiguration); } @Before @@ -115,17 +126,20 @@ public class CrossProfileAppsTest { @Test public void getProfileSwitchingLabel_managedProfile() { setValidTargetProfile(MANAGED_PROFILE); + when(mApplicationInfo.loadSafeLabel(any(), anyFloat(), anyInt())).thenReturn("app"); mCrossProfileApps.getProfileSwitchingLabel(MANAGED_PROFILE); - verify(mDevicePolicyResourcesManager).getString(eq(SWITCH_TO_WORK_LABEL), any()); + verify(mDevicePolicyResourcesManager).getString(eq(SWITCH_TO_WORK_LABEL), any(), eq("app")); } @Test public void getProfileSwitchingLabel_personalProfile() { setValidTargetProfile(PERSONAL_PROFILE); + when(mApplicationInfo.loadSafeLabel(any(), anyFloat(), anyInt())).thenReturn("app"); mCrossProfileApps.getProfileSwitchingLabel(PERSONAL_PROFILE); - verify(mDevicePolicyResourcesManager).getString(eq(SWITCH_TO_PERSONAL_LABEL), any()); + verify(mDevicePolicyResourcesManager) + .getString(eq(SWITCH_TO_PERSONAL_LABEL), any(), eq("app")); } @Test(expected = SecurityException.class)