From 39e62e94400459809f33e9d51524039e4daf4667 Mon Sep 17 00:00:00 2001 From: Anthony Alridge Date: Wed, 19 Apr 2023 10:45:02 +0000 Subject: [PATCH] Use mini-resolver instead of alert dialog for dialer work telephony We previously showed an alert dialog when redirecting the user to the work profile due to telephony being unavailable. This resulted in some inconsistent UIs, with the user sometimes seing the mini-resolver, sometimes seeing this dialog depending on what journey they use to make a phone call. This CL introduces consistency for the call case by redirecting to the intent forwarder when the user attempts to make a call in the primary profile, with telephony only available in the work profile. The previous dialog is modified to handle the error case when there is no dialer app to switch to in work, directing the user to install an app instead. A follow-up CL will address the case of ensuring messages show the mini-resolver. Tested manually, screenshots show that the mini-resolver is shown for both text and call in contacts app (previously calls would show the dialog). Bug: 271819927 Test: Manually, see screenshots in bug Change-Id: I98df65ab0a4a8acea41f2dc9581a07452f324539 --- .../app/admin/DevicePolicyResources.java | 45 +++++++++ .../internal/app/IntentForwarderActivity.java | 96 ++++++++++++++++++- core/res/res/layout/miniresolver.xml | 33 ++++++- core/res/res/values/strings.xml | 18 +++- core/res/res/values/symbols.xml | 8 ++ packages/SystemUI/res/values/strings.xml | 9 +- .../dagger/DefaultActivityBinder.java | 8 ++ .../SwitchToManagedProfileForCallActivity.kt | 37 ++++++- ...DefaultCrossProfileIntentFiltersUtils.java | 6 +- 9 files changed, 244 insertions(+), 16 deletions(-) diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java index f4dc6ba5e2ded..71dec457cc2e2 100644 --- a/core/java/android/app/admin/DevicePolicyResources.java +++ b/core/java/android/app/admin/DevicePolicyResources.java @@ -1851,9 +1851,54 @@ public final class DevicePolicyResources { public static final String WORK_PROFILE_TELEPHONY_PAUSED_TURN_ON_BUTTON = PREFIX + "TURN_ON_WORK_PROFILE_BUTTON_TEXT"; + /** + * Information section shown on a dialog when the user is unable to place a call in + * the personal profile due to admin restrictions, and must choose whether to place + * the call from the work profile or cancel. + */ + public static final String MINIRESOLVER_WORK_TELEPHONY_CALL_BLOCKED_INFORMATION = + PREFIX + "MINIRESOLVER_WORK_TELEPHONY_INFORMATION"; + + /** + * Information section shown on a dialog when the user is unable to send a text in + * the personal profile due to admin restrictions, and must choose whether to place + * the call from the work profile or cancel. + */ + public static final String MINIRESOLVER_WORK_TELEPHONY_TEXT_BLOCKED_INFORMATION = + PREFIX + "MINIRESOLVER_WORK_TELEPHONY_INFORMATION"; + + + /** + * Button for a dialog shown when the user is unable to place a call in the personal + * profile due to admin restrictions, and must choose whether to place the call from + * the work profile or cancel. + */ + public static final String MINIRESOLVER_CALL_FROM_WORK = + PREFIX + "MINIRESOLVER_CALL_FROM_WORK"; + + /** + * Button for a dialog shown when the user has no apps capable of handling an intent + * in the personal profile, and must choose whether to open the intent in a + * cross-profile app in the work profile, or cancel. + */ + public static final String MINIRESOLVER_SWITCH_TO_WORK = + PREFIX + "MINIRESOLVER_SWITCH_TO_WORK"; + + /** + * Title for a dialog shown when the user has no apps capable of handling an intent + * in the personal profile, and must choose whether to open the intent in a + * cross-profile app in the work profile, or open in the same profile browser. Accepts + * the app name as a param. + */ public static final String MINIRESOLVER_OPEN_IN_WORK = PREFIX + "MINIRESOLVER_OPEN_IN_WORK"; + /** + * Title for a dialog shown when the user has no apps capable of handling an intent + * in the personal profile, and must choose whether to open the intent in a + * cross-profile app in the personal profile, or open in the same profile browser. + * Accepts the app name as a param. + */ public static final String MINIRESOLVER_OPEN_IN_PERSONAL = PREFIX + "MINIRESOLVER_OPEN_IN_PERSONAL"; diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java index 904fb665335b9..db65cb37234b6 100644 --- a/core/java/com/android/internal/app/IntentForwarderActivity.java +++ b/core/java/com/android/internal/app/IntentForwarderActivity.java @@ -19,7 +19,11 @@ package com.android.internal.app; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_PERSONAL; import static android.app.admin.DevicePolicyResources.Strings.Core.FORWARD_INTENT_TO_WORK; +import static android.app.admin.DevicePolicyResources.Strings.Core.MINIRESOLVER_CALL_FROM_WORK; import static android.app.admin.DevicePolicyResources.Strings.Core.MINIRESOLVER_OPEN_WORK; +import static android.app.admin.DevicePolicyResources.Strings.Core.MINIRESOLVER_SWITCH_TO_WORK; +import static android.app.admin.DevicePolicyResources.Strings.Core.MINIRESOLVER_WORK_TELEPHONY_CALL_BLOCKED_INFORMATION; +import static android.app.admin.DevicePolicyResources.Strings.Core.MINIRESOLVER_WORK_TELEPHONY_TEXT_BLOCKED_INFORMATION; import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -32,6 +36,7 @@ import android.app.Activity; import android.app.ActivityThread; import android.app.AppGlobals; import android.app.admin.DevicePolicyManager; +import android.app.admin.ManagedSubscriptionsPolicy; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.ContentResolver; @@ -41,6 +46,7 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; +import android.graphics.drawable.Drawable; import android.metrics.LogMaker; import android.os.Build; import android.os.Bundle; @@ -48,6 +54,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.telecom.TelecomManager; import android.util.Slog; import android.view.View; import android.widget.Button; @@ -203,35 +210,116 @@ public class IntentForwarderActivity extends Activity { findViewById(R.id.title_container).setElevation(0); - ImageView icon = findViewById(R.id.icon); PackageManager packageManagerForTargetUser = createContextAsUser(UserHandle.of(targetUserId), /* flags= */ 0) .getPackageManager(); - icon.setImageDrawable(target.loadIcon(packageManagerForTargetUser)); + + ImageView icon = findViewById(R.id.icon); + icon.setImageDrawable( + getAppIcon(target, launchIntent, targetUserId, packageManagerForTargetUser)); View buttonContainer = findViewById(R.id.button_bar_container); buttonContainer.setPadding(0, 0, 0, buttonContainer.getPaddingBottom()); ((TextView) findViewById(R.id.open_cross_profile)).setText( - getOpenInWorkMessage(target.loadLabel(packageManagerForTargetUser))); + getOpenInWorkMessage(launchIntent, target.loadLabel(packageManagerForTargetUser))); // The mini-resolver's negative button is reused in this flow to cancel the intent ((Button) findViewById(R.id.use_same_profile_browser)).setText(R.string.cancel); findViewById(R.id.use_same_profile_browser).setOnClickListener(v -> finish()); + ((Button) findViewById(R.id.button_open)).setText(getOpenInWorkButtonString(launchIntent)); findViewById(R.id.button_open).setOnClickListener(v -> { startActivityAsCaller(launchIntent, targetUserId); finish(); }); + + + View telephonyInfo = findViewById(R.id.miniresolver_info_section); + DevicePolicyManager devicePolicyManager = + getSystemService(DevicePolicyManager.class); + // Additional information section is work telephony specific. Therefore, it is only shown + // for telephony related intents, when all sim subscriptions are in the work profile. + if ((isDialerIntent(launchIntent) || isTextMessageIntent(launchIntent)) + && devicePolicyManager.getManagedSubscriptionsPolicy().getPolicyType() + == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS) { + telephonyInfo.setVisibility(View.VISIBLE); + ((TextView) findViewById(R.id.miniresolver_info_section_text)) + .setText(getWorkTelephonyInfoSectionMessage(launchIntent)); + } else { + telephonyInfo.setVisibility(View.GONE); + } } - private String getOpenInWorkMessage(CharSequence targetLabel) { + private Drawable getAppIcon( + ResolveInfo target, + Intent launchIntent, + int targetUserId, + PackageManager packageManagerForTargetUser) { + if (isDialerIntent(launchIntent)) { + // The icon for the call intent will be a generic phone icon as the target will be + // the telecom call handler. From the user's perspective, they are being directed + // to the dialer app, so use the icon from that app instead. + TelecomManager telecomManager = + getApplicationContext().getSystemService(TelecomManager.class); + String defaultDialerPackageName = + telecomManager.getDefaultDialerPackage(UserHandle.of(targetUserId)); + try { + return packageManagerForTargetUser + .getApplicationInfo(defaultDialerPackageName, /* flags= */ 0) + .loadIcon(packageManagerForTargetUser); + } catch (PackageManager.NameNotFoundException e) { + // Allow to fall-through to the icon from the target if we can't find the default + // dialer icon. + Slog.w(TAG, "Cannot load icon for default dialer package"); + } + } + return target.loadIcon(packageManagerForTargetUser); + } + + private int getOpenInWorkButtonString(Intent launchIntent) { + if (isDialerIntent(launchIntent)) { + return R.string.miniresolver_call; + } + if (isTextMessageIntent(launchIntent)) { + return R.string.miniresolver_switch; + } + return R.string.whichViewApplicationLabel; + } + + private String getOpenInWorkMessage(Intent launchIntent, CharSequence targetLabel) { + if (isDialerIntent(launchIntent)) { + return getSystemService(DevicePolicyManager.class).getResources().getString( + MINIRESOLVER_CALL_FROM_WORK, + () -> getString(R.string.miniresolver_call_in_work)); + } + if (isTextMessageIntent(launchIntent)) { + return getSystemService(DevicePolicyManager.class).getResources().getString( + MINIRESOLVER_SWITCH_TO_WORK, + () -> getString(R.string.miniresolver_switch_to_work)); + } return getSystemService(DevicePolicyManager.class).getResources().getString( MINIRESOLVER_OPEN_WORK, () -> getString(R.string.miniresolver_open_work, targetLabel), targetLabel); } + private String getWorkTelephonyInfoSectionMessage(Intent launchIntent) { + if (isDialerIntent(launchIntent)) { + return getSystemService(DevicePolicyManager.class).getResources().getString( + MINIRESOLVER_WORK_TELEPHONY_CALL_BLOCKED_INFORMATION, + () -> getString(R.string.miniresolver_call_information)); + } + if (isTextMessageIntent(launchIntent)) { + return getSystemService(DevicePolicyManager.class).getResources().getString( + MINIRESOLVER_WORK_TELEPHONY_TEXT_BLOCKED_INFORMATION, + () -> getString(R.string.miniresolver_sms_information)); + } + return ""; + } + + + private String getForwardToPersonalMessage() { return getSystemService(DevicePolicyManager.class).getResources().getString( FORWARD_INTENT_TO_PERSONAL, diff --git a/core/res/res/layout/miniresolver.xml b/core/res/res/layout/miniresolver.xml index 1ad3acd7a3eaa..db0ea547fbd50 100644 --- a/core/res/res/layout/miniresolver.xml +++ b/core/res/res/layout/miniresolver.xml @@ -63,6 +63,37 @@ android:textColor="?android:textColorPrimary" /> + + + + + + + diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 027d4f8ab2b53..f8a7ece82a521 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -861,10 +861,10 @@ Switch to work profile - + Switch to personal %1$s - + Switch to work %1$s @@ -5918,10 +5918,24 @@ Open in personal %s? Open in work %s? + + Call from work app? + + Switch to work app? + + Your organization only allows you to make calls from work apps + + Your organization only allows you to send messages from work apps Use personal browser Use work browser + + Call + + Switch diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 73e3b417f67ae..cddcff0502ded 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1579,6 +1579,14 @@ + + + + + + + + diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 70fdc2070b7ab..fef7db436d773 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -3093,17 +3093,20 @@ - Can\'t call from this profile + Can\'t call from a personal app - Your work policy allows you to make phone calls only from the work profile + Your organization only allows you to make calls from work apps Switch to work profile + + Install a work phone app - Close + Cancel