Loading core/java/android/app/admin/DevicePolicyResources.java +4 −2 Original line number Diff line number Diff line Loading @@ -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"; Loading core/java/android/content/pm/CrossProfileApps.java +38 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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<ResolveInfo> 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); } Loading @@ -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; } /** Loading core/res/res/values/strings.xml +6 −0 Original line number Diff line number Diff line Loading @@ -861,6 +861,12 @@ <!-- "Switch" is a verb; it means to change user profile by tapping another user profile name. --> <string name="managed_profile_label">Switch to work profile</string> <!-- "Switch" is a verb; it means to change user profile by tapping another user profile name. --> <string name="user_owner_app_label">Switch to personal <xliff:g id="app_name" example="Gmail">%1$s</xliff:g></string> <!-- "Switch" is a verb; it means to change user profile by tapping another user profile name. --> <string name="managed_profile_app_label">Switch to work <xliff:g id="app_name" example="Gmail">%1$s</xliff:g></string> <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgrouplab_contacts">Contacts</string> <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> Loading core/res/res/values/symbols.xml +2 −0 Original line number Diff line number Diff line Loading @@ -1068,7 +1068,9 @@ <java-symbol type="string" name="action_bar_home_subtitle_description_format" /> <java-symbol type="string" name="wireless_display_route_description" /> <java-symbol type="string" name="user_owner_label" /> <java-symbol type="string" name="user_owner_app_label" /> <java-symbol type="string" name="managed_profile_label" /> <java-symbol type="string" name="managed_profile_app_label" /> <java-symbol type="string" name="managed_profile_label_badge" /> <java-symbol type="string" name="managed_profile_label_badge_2" /> <java-symbol type="string" name="managed_profile_label_badge_3" /> Loading core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java +17 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading @@ -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 Loading @@ -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) Loading Loading
core/java/android/app/admin/DevicePolicyResources.java +4 −2 Original line number Diff line number Diff line Loading @@ -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"; Loading
core/java/android/content/pm/CrossProfileApps.java +38 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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<ResolveInfo> 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); } Loading @@ -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; } /** Loading
core/res/res/values/strings.xml +6 −0 Original line number Diff line number Diff line Loading @@ -861,6 +861,12 @@ <!-- "Switch" is a verb; it means to change user profile by tapping another user profile name. --> <string name="managed_profile_label">Switch to work profile</string> <!-- "Switch" is a verb; it means to change user profile by tapping another user profile name. --> <string name="user_owner_app_label">Switch to personal <xliff:g id="app_name" example="Gmail">%1$s</xliff:g></string> <!-- "Switch" is a verb; it means to change user profile by tapping another user profile name. --> <string name="managed_profile_app_label">Switch to work <xliff:g id="app_name" example="Gmail">%1$s</xliff:g></string> <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgrouplab_contacts">Contacts</string> <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> Loading
core/res/res/values/symbols.xml +2 −0 Original line number Diff line number Diff line Loading @@ -1068,7 +1068,9 @@ <java-symbol type="string" name="action_bar_home_subtitle_description_format" /> <java-symbol type="string" name="wireless_display_route_description" /> <java-symbol type="string" name="user_owner_label" /> <java-symbol type="string" name="user_owner_app_label" /> <java-symbol type="string" name="managed_profile_label" /> <java-symbol type="string" name="managed_profile_app_label" /> <java-symbol type="string" name="managed_profile_label_badge" /> <java-symbol type="string" name="managed_profile_label_badge_2" /> <java-symbol type="string" name="managed_profile_label_badge_3" /> Loading
core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java +17 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading @@ -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 Loading @@ -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) Loading