From 649cddeb15ac32adbc4764b89b967cb0de99a9c8 Mon Sep 17 00:00:00 2001 From: hughchen Date: Wed, 25 Mar 2020 17:24:26 +0800 Subject: [PATCH 01/12] Get MediaDevice from devices list The TopDevice that we cached will not update device infomation when route is changed. We should get TopDevice from devices list every time to confirm the device infomation is updated. Bug: 151709228 Test: manual test to see ui is changed Change-Id: Iede279b424d070dc7a0b30c369bed316e7ac223d --- src/com/android/settings/media/MediaDeviceUpdateWorker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/settings/media/MediaDeviceUpdateWorker.java b/src/com/android/settings/media/MediaDeviceUpdateWorker.java index 281d23e9afb..b800c179859 100644 --- a/src/com/android/settings/media/MediaDeviceUpdateWorker.java +++ b/src/com/android/settings/media/MediaDeviceUpdateWorker.java @@ -151,7 +151,7 @@ public class MediaDeviceUpdateWorker extends SliceBackgroundWorker } MediaDevice getTopDevice() { - return mTopDevice; + return getMediaDeviceById(mTopDevice.getId()); } boolean addDeviceToPlayMedia(MediaDevice device) { From 2f4ebf947514558edc07df2f5af7ac61d7186696 Mon Sep 17 00:00:00 2001 From: Robert Luo Date: Mon, 30 Mar 2020 11:39:23 +0800 Subject: [PATCH 02/12] Refine NFC payment wording for "Set default" and "Update" scenarios. Fixes: 149270345 Test: manual and check if new string is applied Change-Id: I4d7591882489d3892020f8fa2f963f7c90ec79e4 --- res/values/strings.xml | 20 +++++++++++++++---- .../settings/nfc/PaymentDefaultDialog.java | 8 +++++--- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 0475597c927..484e89ad9a4 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -7035,7 +7035,7 @@ Except when another payment app is open - At a Tap & pay terminal, pay with: + At a contactless terminal, pay with: Paying at the terminal @@ -7046,9 +7046,21 @@ More... - Set as your preference? - Always use %1$s when you Tap & pay? - Always use %1$s instead of %2$s when you Tap & pay? + Set default payment app + + Update default payment app + At a contactless terminal, pay with + %1$s + + At a contactless terminal, pay with %1$s.\n\nThis replaces %2$s as your default + payment app. + + + Set default + + Update diff --git a/src/com/android/settings/nfc/PaymentDefaultDialog.java b/src/com/android/settings/nfc/PaymentDefaultDialog.java index 73b92e7b6ba..1aa8dca9bd1 100644 --- a/src/com/android/settings/nfc/PaymentDefaultDialog.java +++ b/src/com/android/settings/nfc/PaymentDefaultDialog.java @@ -111,21 +111,23 @@ public final class PaymentDefaultDialog extends AlertActivity implements mNewDefault = component; // Compose dialog; get final AlertController.AlertParams p = mAlertParams; - p.mTitle = getString(R.string.nfc_payment_set_default_label); if (defaultPaymentApp == null) { + p.mTitle = getString(R.string.nfc_payment_set_default_label); String formatString = getString(R.string.nfc_payment_set_default); String msg = String.format(formatString, sanitizePaymentAppCaption(requestedPaymentApp.label.toString())); p.mMessage = msg; + p.mPositiveButtonText = getString(R.string.nfc_payment_btn_text_set_deault); } else { + p.mTitle = getString(R.string.nfc_payment_update_default_label); String formatString = getString(R.string.nfc_payment_set_default_instead_of); String msg = String.format(formatString, sanitizePaymentAppCaption(requestedPaymentApp.label.toString()), sanitizePaymentAppCaption(defaultPaymentApp.label.toString())); p.mMessage = msg; + p.mPositiveButtonText = getString(R.string.nfc_payment_btn_text_update); } - p.mPositiveButtonText = getString(R.string.yes); - p.mNegativeButtonText = getString(R.string.no); + p.mNegativeButtonText = getString(R.string.cancel); p.mPositiveButtonListener = this; p.mNegativeButtonListener = this; setupAlert(); From 4d0edf369f515abff7d2b9ab3f9794d9ecfc7c76 Mon Sep 17 00:00:00 2001 From: kholoud mohamed Date: Wed, 18 Mar 2020 17:00:50 +0000 Subject: [PATCH 03/12] Show all apps that requested cross profile permission show all apps that requested the permission, show the admin restricted dialog if the app is not whitelisted by the admin, and show a link to install the app if the app is missing in one of the profiles. Bug: 149742043 Test: make RunSettingsRoboTests ROBOTEST_FILTER=InteractAcrossProfilesPreferenceControllerTest Test: make RunSettingsRoboTests ROBOTEST_FILTER=InteractAcrossProfilesControllerTest Test: make RunSettingsRoboTests ROBOTEST_FILTER=InteractAcrossProfilesSettingsTest Test: make RunSettingsRoboTests ROBOTEST_FILTER=InteractAcrossProfilesDetailsTest Change-Id: I2bc86b9966a2e14a12ee8f4b7f1b75f866ed98e3 --- res/drawable/ic_download_for_offline.xml | 21 ++ res/values/strings.xml | 25 +- ...ct_across_profiles_permissions_details.xml | 32 +- .../InteractAcrossProfilesDetails.java | 288 ++++++++++++++---- ...ssProfilesDetailsPreferenceController.java | 9 +- .../InteractAcrossProfilesSettings.java | 63 ++-- .../InteractAcrossProfilesDetailsTest.java | 47 ++- ...crossProfilesPreferenceControllerTest.java | 24 +- .../InteractAcrossProfilesSettingsTest.java | 67 ++-- 9 files changed, 428 insertions(+), 148 deletions(-) create mode 100644 res/drawable/ic_download_for_offline.xml diff --git a/res/drawable/ic_download_for_offline.xml b/res/drawable/ic_download_for_offline.xml new file mode 100644 index 00000000000..f9ea3050f55 --- /dev/null +++ b/res/drawable/ic_download_for_offline.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 46d75e82680..b340e0351b4 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8521,6 +8521,12 @@ When a user connects select work and personal apps, they can access work and personal data together. [CHAR LIMIT=50] --> Connected work & personal apps + + Connected + + + Not connected + No connected apps @@ -8587,27 +8593,18 @@ - Install %1$s in your work profile - - - To connect these apps, install the %1$s app in your work profile + Install work %1$s to connect these apps - Install %1$s in your personal profile + The placeholder would be the app name (e.g. Calendar). [CHAR LIMIT=NONE]--> + Install personal %1$s to connect these apps - To connect these apps, install the %1$s app in your personal profile - - - Get the app + Tap to get the app Do Not Disturb access diff --git a/res/xml/interact_across_profiles_permissions_details.xml b/res/xml/interact_across_profiles_permissions_details.xml index 8b1e043eee8..1baccf227e9 100644 --- a/res/xml/interact_across_profiles_permissions_details.xml +++ b/res/xml/interact_across_profiles_permissions_details.xml @@ -15,22 +15,28 @@ ~ limitations under the License. --> - - + - + - + - + + + diff --git a/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetails.java b/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetails.java index ff616268063..474a5b6ec79 100644 --- a/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetails.java +++ b/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetails.java @@ -15,15 +15,25 @@ */ package com.android.settings.applications.specialaccess.interactacrossprofiles; +import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; +import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; + import android.Manifest; +import android.annotation.UserIdInt; +import android.app.ActionBar; import android.app.AppOpsManager; +import android.app.admin.DevicePolicyManager; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; import android.content.PermissionChecker; import android.content.pm.CrossProfileApps; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.UserInfo; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; @@ -32,13 +42,15 @@ import android.view.View; import android.widget.ImageView; import android.widget.TextView; -import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.preference.Preference; -import androidx.preference.SwitchPreference; import com.android.settings.R; import com.android.settings.applications.AppInfoBase; +import com.android.settings.applications.AppStoreUtil; +import com.android.settings.widget.CardPreference; +import com.android.settingslib.RestrictedLockUtils; +import com.android.settingslib.RestrictedSwitchPreference; import com.android.settingslib.widget.LayoutPreference; public class InteractAcrossProfilesDetails extends AppInfoBase @@ -47,13 +59,21 @@ public class InteractAcrossProfilesDetails extends AppInfoBase private static final String INTERACT_ACROSS_PROFILES_SETTINGS_SWITCH = "interact_across_profiles_settings_switch"; private static final String INTERACT_ACROSS_PROFILES_HEADER = "interact_across_profiles_header"; + public static final String INSTALL_APP_BANNER_KEY = "install_app_banner"; private Context mContext; private CrossProfileApps mCrossProfileApps; private UserManager mUserManager; - private SwitchPreference mSwitchPref; + private RestrictedSwitchPreference mSwitchPref; private LayoutPreference mHeader; + private CardPreference mInstallBanner; private PackageManager mPackageManager; + private UserHandle mPersonalProfile; + private UserHandle mWorkProfile; + private boolean mInstalledInPersonal; + private boolean mInstalledInWork; + private String mAppLabel; + private Intent mInstallAppIntent; @Override public void onCreate(Bundle savedInstanceState) { @@ -64,19 +84,30 @@ public class InteractAcrossProfilesDetails extends AppInfoBase mUserManager = mContext.getSystemService(UserManager.class); mPackageManager = mContext.getPackageManager(); + mWorkProfile = InteractAcrossProfilesSettings.getWorkProfile(mUserManager); + mPersonalProfile = mUserManager.getProfileParent(mWorkProfile); + mInstalledInWork = isPackageInstalled(mPackageName, mWorkProfile.getIdentifier()); + mInstalledInPersonal = isPackageInstalled(mPackageName, mPersonalProfile.getIdentifier()); + + mAppLabel = mPackageInfo.applicationInfo.loadLabel(mPackageManager).toString(); + mInstallAppIntent = AppStoreUtil.getAppStoreLink(mContext, mPackageName); + addPreferencesFromResource(R.xml.interact_across_profiles_permissions_details); mSwitchPref = findPreference(INTERACT_ACROSS_PROFILES_SETTINGS_SWITCH); mSwitchPref.setOnPreferenceClickListener(this); + mHeader = findPreference(INTERACT_ACROSS_PROFILES_HEADER); + mInstallBanner = findPreference(INSTALL_APP_BANNER_KEY); + mInstallBanner.setOnPreferenceClickListener(this); + // refreshUi checks that the user can still configure the appOp, return to the // previous page if it can't. if (!refreshUi()) { setIntentAndFinish(true/* appChanged */); } - final UserHandle workProfile = getWorkProfile(); - final UserHandle personalProfile = mUserManager.getProfileParent(workProfile); - addAppTitleAndIcons(personalProfile, workProfile); + addAppTitleAndIcons(mPersonalProfile, mWorkProfile); + styleActionBar(); } private void addAppTitleAndIcons(UserHandle personalProfile, UserHandle workProfile) { @@ -89,70 +120,101 @@ public class InteractAcrossProfilesDetails extends AppInfoBase final ImageView personalIconView = mHeader.findViewById(R.id.entity_header_icon_personal); if (personalIconView != null) { - personalIconView.setImageDrawable(IconDrawableFactory.newInstance(mContext) - .getBadgedIcon(mPackageInfo.applicationInfo, personalProfile.getIdentifier())); + Drawable icon = IconDrawableFactory.newInstance(mContext) + .getBadgedIcon(mPackageInfo.applicationInfo, personalProfile.getIdentifier()) + .mutate(); + if (!mInstalledInPersonal) { + icon.setColorFilter(createSuspendedColorMatrix()); + } + personalIconView.setImageDrawable(icon); } - final ImageView workIconView2 = mHeader.findViewById(R.id.entity_header_icon_work); - if (workIconView2 != null) { - workIconView2.setImageDrawable(IconDrawableFactory.newInstance(mContext) - .getBadgedIcon(mPackageInfo.applicationInfo, workProfile.getIdentifier())); + + final ImageView workIconView = mHeader.findViewById(R.id.entity_header_icon_work); + if (workIconView != null) { + Drawable icon = IconDrawableFactory.newInstance(mContext) + .getBadgedIcon(mPackageInfo.applicationInfo, workProfile.getIdentifier()) + .mutate(); + if (!mInstalledInWork) { + icon.setColorFilter(createSuspendedColorMatrix()); + } + workIconView.setImageDrawable(icon); } } - @Nullable - private UserHandle getWorkProfile() { - for (UserInfo user : mUserManager.getProfiles(UserHandle.myUserId())) { - if (mUserManager.isManagedProfile(user.id)) { - return user.getUserHandle(); - } + private void styleActionBar() { + final ActionBar actionBar = getActivity().getActionBar(); + if (actionBar != null) { + actionBar.setElevation(0); } - return null; + } + + private ColorMatrixColorFilter createSuspendedColorMatrix() { + int grayValue = 127; + float scale = 0.5f; // half bright + + ColorMatrix tempBrightnessMatrix = new ColorMatrix(); + float[] mat = tempBrightnessMatrix.getArray(); + mat[0] = scale; + mat[6] = scale; + mat[12] = scale; + mat[4] = grayValue; + mat[9] = grayValue; + mat[14] = grayValue; + + ColorMatrix matrix = new ColorMatrix(); + matrix.setSaturation(0.0f); + matrix.preConcat(tempBrightnessMatrix); + return new ColorMatrixColorFilter(matrix); } @Override public boolean onPreferenceClick(Preference preference) { - if (preference != mSwitchPref) { - return false; - } // refreshUi checks that the user can still configure the appOp, return to the // previous page if it can't. if (!refreshUi()) { setIntentAndFinish(true/* appChanged */); } + if (preference == mSwitchPref) { + handleSwitchPreferenceClick(); + return true; + } + if (preference == mInstallBanner) { + handleInstallBannerClick(); + return true; + } + return false; + } + + private void handleSwitchPreferenceClick() { if (isInteractAcrossProfilesEnabled()) { enableInteractAcrossProfiles(false); refreshUi(); - return true; - } - if (!isInteractAcrossProfilesEnabled()) { + } else { showConsentDialog(); } - return true; } private void showConsentDialog() { - final String appLabel = mPackageInfo.applicationInfo.loadLabel(mPackageManager).toString(); - final View dialogView = getLayoutInflater().inflate( R.layout.interact_across_profiles_consent_dialog, null); final TextView dialogTitle = dialogView.findViewById( R.id.interact_across_profiles_consent_dialog_title); dialogTitle.setText( - getString(R.string.interact_across_profiles_consent_dialog_title, appLabel)); + getString(R.string.interact_across_profiles_consent_dialog_title, mAppLabel)); final TextView dialogSummary = dialogView.findViewById( R.id.interact_across_profiles_consent_dialog_summary); dialogSummary.setText( - getString(R.string.interact_across_profiles_consent_dialog_summary, appLabel)); + getString(R.string.interact_across_profiles_consent_dialog_summary, mAppLabel)); final TextView appDataSummary = dialogView.findViewById(R.id.app_data_summary); appDataSummary.setText(getString( - R.string.interact_across_profiles_consent_dialog_app_data_summary, appLabel)); + R.string.interact_across_profiles_consent_dialog_app_data_summary, mAppLabel)); final TextView permissionsSummary = dialogView.findViewById(R.id.permissions_summary); permissionsSummary.setText(getString( - R.string.interact_across_profiles_consent_dialog_permissions_summary, appLabel)); + R.string.interact_across_profiles_consent_dialog_permissions_summary, mAppLabel)); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setView(dialogView) @@ -171,18 +233,38 @@ public class InteractAcrossProfilesDetails extends AppInfoBase } private boolean isInteractAcrossProfilesEnabled() { - return isInteractAcrossProfilesEnabled( - mContext, mPackageName, mPackageInfo.applicationInfo.uid); + return isInteractAcrossProfilesEnabled(mContext, mPackageName); } - static boolean isInteractAcrossProfilesEnabled(Context context, String packageName, int uid) { + static boolean isInteractAcrossProfilesEnabled( + Context context, String packageName) { + UserManager userManager = context.getSystemService(UserManager.class); + UserHandle workProfile = InteractAcrossProfilesSettings.getWorkProfile(userManager); + UserHandle personalProfile = userManager.getProfileParent(workProfile); + return context.getSystemService( + CrossProfileApps.class).canConfigureInteractAcrossProfiles(packageName) + && isInteractAcrossProfilesEnabledInProfile(context, packageName, personalProfile) + && isInteractAcrossProfilesEnabledInProfile(context, packageName, workProfile); + + } + + private static boolean isInteractAcrossProfilesEnabledInProfile( + Context context, String packageName, UserHandle userHandle) { + final PackageManager packageManager = context.getPackageManager(); + final int uid; + try { + uid = packageManager.getApplicationInfoAsUser( + packageName, /* flags= */0, userHandle).uid; + } catch (PackageManager.NameNotFoundException e) { + return false; + } return PermissionChecker.PERMISSION_GRANTED == PermissionChecker.checkPermissionForPreflight( - context, - Manifest.permission.INTERACT_ACROSS_PROFILES, - PermissionChecker.PID_UNKNOWN, - uid, - packageName); + context, + Manifest.permission.INTERACT_ACROSS_PROFILES, + PermissionChecker.PID_UNKNOWN, + uid, + packageName); } private void enableInteractAcrossProfiles(boolean newState) { @@ -190,14 +272,28 @@ public class InteractAcrossProfilesDetails extends AppInfoBase mPackageName, newState ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); } + private void handleInstallBannerClick() { + if (mInstallAppIntent == null) { + return; + } + if (!mInstalledInWork) { + mContext.startActivityAsUser(mInstallAppIntent, mWorkProfile); + return; + } + if (!mInstalledInPersonal) { + mContext.startActivityAsUser(mInstallAppIntent, mPersonalProfile); + } + } + /** * @return the summary for the current state of whether the app associated with the given * {@code packageName} is allowed to interact across profiles. */ - public static CharSequence getPreferenceSummary(Context context, String packageName, int uid) { - return context.getString(isInteractAcrossProfilesEnabled(context, packageName, uid) - ? R.string.app_permission_summary_allowed - : R.string.app_permission_summary_not_allowed); + public static CharSequence getPreferenceSummary( + Context context, String packageName) { + return context.getString(isInteractAcrossProfilesEnabled(context, packageName) + ? R.string.interact_across_profiles_summary_allowed + : R.string.interact_across_profiles_summary_not_allowed); } @Override @@ -205,31 +301,99 @@ public class InteractAcrossProfilesDetails extends AppInfoBase if (mPackageInfo == null || mPackageInfo.applicationInfo == null) { return false; } - if (!mCrossProfileApps.canConfigureInteractAcrossProfiles(mPackageName)) { + if (!mCrossProfileApps.canUserAttemptToConfigureInteractAcrossProfiles(mPackageName)) { // Invalid app entry. Should not allow changing permission mSwitchPref.setEnabled(false); return false; } - - final ImageView horizontalArrowIcon = mHeader.findViewById(R.id.entity_header_swap_horiz); - if (isInteractAcrossProfilesEnabled()) { - mSwitchPref.setChecked(true); - mSwitchPref.setTitle(R.string.interact_across_profiles_switch_enabled); - if (horizontalArrowIcon != null) { - horizontalArrowIcon.setImageDrawable( - mContext.getDrawable(R.drawable.ic_swap_horiz_blue)); - } - } else { - mSwitchPref.setChecked(false); - mSwitchPref.setTitle(R.string.interact_across_profiles_switch_disabled); - if (horizontalArrowIcon != null) { - horizontalArrowIcon.setImageDrawable( - mContext.getDrawable(R.drawable.ic_swap_horiz_grey)); - } + if (!mCrossProfileApps.canConfigureInteractAcrossProfiles(mPackageName)) { + return refreshUiForNonConfigurableApps(); } + refreshUiForConfigurableApps(); return true; } + private boolean refreshUiForNonConfigurableApps() { + mSwitchPref.setChecked(false); + mSwitchPref.setTitle(R.string.interact_across_profiles_switch_disabled); + if (!isCrossProfilePackageWhitelisted(mPackageName)) { + mInstallBanner.setVisible(false); + mSwitchPref.setDisabledByAdmin(RestrictedLockUtils.getProfileOrDeviceOwner( + mContext, mWorkProfile)); + return true; + } + mSwitchPref.setEnabled(false); + if (!mInstalledInPersonal && !mInstalledInWork) { + return false; + } + if (!mInstalledInPersonal) { + mInstallBanner.setTitle(getString( + R.string.interact_across_profiles_install_personal_app_title, + mAppLabel)); + mInstallBanner.setSummary( + R.string.interact_across_profiles_install_app_summary); + mInstallBanner.setVisible(true); + return true; + } + if (!mInstalledInWork) { + mInstallBanner.setTitle(getString( + R.string.interact_across_profiles_install_work_app_title, + mAppLabel)); + mInstallBanner.setSummary( + R.string.interact_across_profiles_install_app_summary); + mInstallBanner.setVisible(true); + return true; + } + return false; + } + + private boolean isCrossProfilePackageWhitelisted(String packageName) { + return mContext.getSystemService(DevicePolicyManager.class) + .getAllCrossProfilePackages().contains(packageName); + } + + private boolean isPackageInstalled(String packageName, @UserIdInt int userId) { + final PackageInfo info; + try { + info = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */0) + .getPackageManager().getPackageInfo(packageName, + MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + return info != null; + } + + private void refreshUiForConfigurableApps() { + mInstallBanner.setVisible(false); + mSwitchPref.setEnabled(true); + if (isInteractAcrossProfilesEnabled()) { + enableSwitchPref(); + } else { + disableSwitchPref(); + } + } + + private void enableSwitchPref() { + mSwitchPref.setChecked(true); + mSwitchPref.setTitle(R.string.interact_across_profiles_switch_enabled); + final ImageView horizontalArrowIcon = mHeader.findViewById(R.id.entity_header_swap_horiz); + if (horizontalArrowIcon != null) { + horizontalArrowIcon.setImageDrawable( + mContext.getDrawable(R.drawable.ic_swap_horiz_blue)); + } + } + + private void disableSwitchPref() { + mSwitchPref.setChecked(false); + mSwitchPref.setTitle(R.string.interact_across_profiles_switch_disabled); + final ImageView horizontalArrowIcon = mHeader.findViewById(R.id.entity_header_swap_horiz); + if (horizontalArrowIcon != null) { + horizontalArrowIcon.setImageDrawable( + mContext.getDrawable(R.drawable.ic_swap_horiz_grey)); + } + } + @Override protected AlertDialog createDialog(int id, int errorCode) { return null; diff --git a/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsPreferenceController.java b/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsPreferenceController.java index 41e25a7d1d0..f57a6ed7c25 100644 --- a/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsPreferenceController.java +++ b/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsPreferenceController.java @@ -35,7 +35,7 @@ public class InteractAcrossProfilesDetailsPreferenceController @Override public int getAvailabilityStatus() { - return canConfigureInteractAcrossProfiles() ? AVAILABLE : DISABLED_FOR_USER; + return canUserAttemptToConfigureInteractAcrossProfiles() ? AVAILABLE : DISABLED_FOR_USER; } @Override @@ -49,13 +49,12 @@ public class InteractAcrossProfilesDetailsPreferenceController } private CharSequence getPreferenceSummary() { - return InteractAcrossProfilesDetails.getPreferenceSummary(mContext, mPackageName, - mParent.getPackageInfo().applicationInfo.uid); + return InteractAcrossProfilesDetails.getPreferenceSummary(mContext, mPackageName); } - private boolean canConfigureInteractAcrossProfiles() { + private boolean canUserAttemptToConfigureInteractAcrossProfiles() { return mContext.getSystemService(CrossProfileApps.class) - .canConfigureInteractAcrossProfiles(mPackageName); + .canUserAttemptToConfigureInteractAcrossProfiles(mPackageName); } public void setPackageName(String packageName) { diff --git a/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettings.java b/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettings.java index d686978fde4..c5d848af7a3 100644 --- a/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettings.java +++ b/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettings.java @@ -85,8 +85,8 @@ public class InteractAcrossProfilesSettings extends EmptyTextSettings { final Preference pref = new AppPreference(prefContext); pref.setIcon(mIconDrawableFactory.getBadgedIcon(appInfo, user.getIdentifier())); pref.setTitle(mPackageManager.getUserBadgedLabel(label, user)); - pref.setSummary(InteractAcrossProfilesDetails.getPreferenceSummary(prefContext, - packageName, appInfo.uid)); + pref.setSummary(InteractAcrossProfilesDetails.getPreferenceSummary( + prefContext, packageName)); pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { @@ -127,49 +127,78 @@ public class InteractAcrossProfilesSettings extends EmptyTextSettings { static ArrayList> collectConfigurableApps( PackageManager packageManager, UserManager userManager, CrossProfileApps crossProfileApps) { - final UserHandle personalProfile = getPersonalProfileForCallingUser(userManager); + final UserHandle workProfile = getWorkProfile(userManager); + if (workProfile == null) { + return new ArrayList<>(); + } + final UserHandle personalProfile = userManager.getProfileParent(workProfile); if (personalProfile == null) { return new ArrayList<>(); } final ArrayList> apps = new ArrayList<>(); - final List installedPackages = packageManager.getInstalledPackagesAsUser( - GET_ACTIVITIES, personalProfile.getIdentifier()); - for (PackageInfo packageInfo : installedPackages) { - if (crossProfileApps.canConfigureInteractAcrossProfiles(packageInfo.packageName)) { + for (PackageInfo packageInfo : getAllInstalledPackages( + packageManager, personalProfile, workProfile)) { + if (crossProfileApps.canUserAttemptToConfigureInteractAcrossProfiles( + packageInfo.packageName)) { apps.add(new Pair<>(packageInfo.applicationInfo, personalProfile)); } } return apps; } + private static List getAllInstalledPackages( + PackageManager packageManager, UserHandle personalProfile, UserHandle workProfile) { + List personalPackages = packageManager.getInstalledPackagesAsUser( + GET_ACTIVITIES, personalProfile.getIdentifier()); + List workPackages = packageManager.getInstalledPackagesAsUser( + GET_ACTIVITIES, workProfile.getIdentifier()); + List allPackages = new ArrayList<>(personalPackages); + for (PackageInfo workPackage : workPackages) { + if (allPackages.stream().noneMatch( + p -> workPackage.packageName.equals(p.packageName))) { + allPackages.add(workPackage); + } + } + return allPackages; + } + /** * @return the number of applications that can interact across profiles. */ static int getNumberOfEnabledApps( Context context, PackageManager packageManager, UserManager userManager, CrossProfileApps crossProfileApps) { + UserHandle workProfile = getWorkProfile(userManager); + if (workProfile == null) { + return 0; + } + UserHandle personalProfile = userManager.getProfileParent(workProfile); + if (personalProfile == null) { + return 0; + } final ArrayList> apps = collectConfigurableApps(packageManager, userManager, crossProfileApps); apps.removeIf( app -> !InteractAcrossProfilesDetails.isInteractAcrossProfilesEnabled( - context, app.first.packageName, app.first.uid)); + context, app.first.packageName) + || !crossProfileApps.canConfigureInteractAcrossProfiles( + app.first.packageName)); return apps.size(); } /** - * Returns the personal profile in the profile group of the calling user. - * Returns null if user is not in a profile group. + * Returns the work profile in the profile group of the calling user. + * Returns null if not found. */ @Nullable - private static UserHandle getPersonalProfileForCallingUser(UserManager userManager) { - final int callingUser = UserHandle.myUserId(); - if (userManager.getProfiles(callingUser).isEmpty()) { - return null; + static UserHandle getWorkProfile(UserManager userManager) { + for (UserInfo user : userManager.getProfiles(UserHandle.myUserId())) { + if (userManager.isManagedProfile(user.id)) { + return user.getUserHandle(); + } } - final UserInfo parentProfile = userManager.getProfileParent(callingUser); - return parentProfile == null - ? UserHandle.of(callingUser) : parentProfile.getUserHandle(); + return null; } public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsTest.java index 9e48b9ea3bb..bae9a12670b 100644 --- a/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsTest.java +++ b/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesDetailsTest.java @@ -22,14 +22,18 @@ import static org.robolectric.Shadows.shadowOf; import android.app.AppOpsManager; import android.content.Context; +import android.content.pm.CrossProfileApps; import android.content.pm.PackageManager; import android.content.pm.PermissionInfo; -import android.os.Process; +import android.content.pm.UserInfo; +import android.os.UserManager; import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; +import com.google.common.collect.ImmutableList; + import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -37,6 +41,9 @@ import org.robolectric.RobolectricTestRunner; @RunWith(RobolectricTestRunner.class) public class InteractAcrossProfilesDetailsTest { + private static final int PERSONAL_PROFILE_ID = 0; + private static final int WORK_PROFILE_ID = 10; + private static final int PACKAGE_UID = 0; private static final String CROSS_PROFILE_PACKAGE_NAME = "crossProfilePackage"; public static final String INTERACT_ACROSS_PROFILES_PERMISSION = "android.permission.INTERACT_ACROSS_PROFILES"; @@ -44,29 +51,53 @@ public class InteractAcrossProfilesDetailsTest { private final Context mContext = ApplicationProvider.getApplicationContext(); private final AppOpsManager mAppOpsManager = mContext.getSystemService(AppOpsManager.class); private final PackageManager mPackageManager = mContext.getPackageManager(); - private final InteractAcrossProfilesDetails mFragment = new InteractAcrossProfilesDetails(); + private final UserManager mUserManager = mContext.getSystemService(UserManager.class); + private final CrossProfileApps mCrossProfileApps = mContext.getSystemService( + CrossProfileApps.class); @Test public void getPreferenceSummary_appOpAllowed_returnsAllowed() { + shadowOf(mUserManager).addUser( + PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */); + shadowOf(mUserManager).addProfile( + PERSONAL_PROFILE_ID, WORK_PROFILE_ID, + "work-profile"/* profileName */, UserInfo.FLAG_MANAGED_PROFILE); + shadowOf(mPackageManager).setInstalledPackagesForUserId( + PERSONAL_PROFILE_ID, ImmutableList.of(CROSS_PROFILE_PACKAGE_NAME)); + shadowOf(mPackageManager).setInstalledPackagesForUserId( + WORK_PROFILE_ID, ImmutableList.of(CROSS_PROFILE_PACKAGE_NAME)); + shadowOf(mCrossProfileApps).addCrossProfilePackage( + CROSS_PROFILE_PACKAGE_NAME); String appOp = AppOpsManager.permissionToOp(INTERACT_ACROSS_PROFILES_PERMISSION); shadowOf(mAppOpsManager).setMode( - appOp, Process.myUid(), CROSS_PROFILE_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED); + appOp, PACKAGE_UID, CROSS_PROFILE_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED); shadowOf(mPackageManager).addPermissionInfo(createCrossProfilesPermissionInfo()); - assertThat(mFragment.getPreferenceSummary( - mContext, CROSS_PROFILE_PACKAGE_NAME, Process.myUid())) + assertThat(InteractAcrossProfilesDetails.getPreferenceSummary( + mContext, CROSS_PROFILE_PACKAGE_NAME)) .isEqualTo(mContext.getString(R.string.app_permission_summary_allowed)); } @Test public void getPreferenceSummary_appOpNotAllowed_returnsNotAllowed() { + shadowOf(mUserManager).addUser( + PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */); + shadowOf(mUserManager).addProfile( + PERSONAL_PROFILE_ID, WORK_PROFILE_ID, + "work-profile"/* profileName */, UserInfo.FLAG_MANAGED_PROFILE); + shadowOf(mPackageManager).setInstalledPackagesForUserId( + PERSONAL_PROFILE_ID, ImmutableList.of(CROSS_PROFILE_PACKAGE_NAME)); + shadowOf(mPackageManager).setInstalledPackagesForUserId( + WORK_PROFILE_ID, ImmutableList.of(CROSS_PROFILE_PACKAGE_NAME)); + shadowOf(mCrossProfileApps).addCrossProfilePackage( + CROSS_PROFILE_PACKAGE_NAME); String appOp = AppOpsManager.permissionToOp(INTERACT_ACROSS_PROFILES_PERMISSION); shadowOf(mAppOpsManager).setMode( - appOp, Process.myUid(), CROSS_PROFILE_PACKAGE_NAME, AppOpsManager.MODE_IGNORED); + appOp, PACKAGE_UID, CROSS_PROFILE_PACKAGE_NAME, AppOpsManager.MODE_IGNORED); shadowOf(mPackageManager).addPermissionInfo(createCrossProfilesPermissionInfo()); - assertThat(mFragment.getPreferenceSummary( - mContext, CROSS_PROFILE_PACKAGE_NAME, Process.myUid())) + assertThat(InteractAcrossProfilesDetails.getPreferenceSummary( + mContext, CROSS_PROFILE_PACKAGE_NAME)) .isEqualTo(mContext.getString(R.string.app_permission_summary_not_allowed)); } diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesPreferenceControllerTest.java index bac7437d81d..8479035804b 100644 --- a/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesPreferenceControllerTest.java @@ -21,12 +21,15 @@ import static com.google.common.truth.Truth.assertThat; import static org.robolectric.Shadows.shadowOf; import android.content.Context; -import android.content.pm.CrossProfileApps; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import androidx.test.core.app.ApplicationProvider; import com.android.settings.core.BasePreferenceController; +import com.google.common.collect.ImmutableList; + import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -36,25 +39,34 @@ public class InteractAcrossProfilesPreferenceControllerTest { private static final String CROSS_PROFILE_PACKAGE_NAME = "crossProfilePackage"; private static final String NOT_CROSS_PROFILE_PACKAGE_NAME = "notCrossProfilePackage"; + public static final String INTERACT_ACROSS_PROFILES_PERMISSION = + "android.permission.INTERACT_ACROSS_PROFILES"; + private static final int PROFILE_ID = 0; private final Context mContext = ApplicationProvider.getApplicationContext(); - private final CrossProfileApps mCrossProfileApps = - mContext.getSystemService(CrossProfileApps.class); + private final PackageManager mPackageManager = mContext.getPackageManager(); private final InteractAcrossProfilesDetailsPreferenceController mController = new InteractAcrossProfilesDetailsPreferenceController(mContext, "test_key"); @Test - public void getAvailabilityStatus_crossProfilePackage_returnsAvailable() { + public void getAvailabilityStatus_requestedCrossProfilePermission_returnsAvailable() { mController.setPackageName(CROSS_PROFILE_PACKAGE_NAME); - shadowOf(mCrossProfileApps).addCrossProfilePackage(CROSS_PROFILE_PACKAGE_NAME); + shadowOf(mPackageManager).setInstalledPackagesForUserId( + PROFILE_ID, ImmutableList.of(CROSS_PROFILE_PACKAGE_NAME)); + PackageInfo packageInfo = shadowOf(mPackageManager).getInternalMutablePackageInfo( + CROSS_PROFILE_PACKAGE_NAME); + packageInfo.requestedPermissions = new String[]{ + INTERACT_ACROSS_PROFILES_PERMISSION}; assertThat(mController.getAvailabilityStatus()) .isEqualTo(BasePreferenceController.AVAILABLE); } @Test - public void getAvailabilityStatus_notCrossProfilePackage_returnsDisabled() { + public void getAvailabilityStatus_notRequestedCrossProfilePermission_returnsDisabled() { mController.setPackageName(NOT_CROSS_PROFILE_PACKAGE_NAME); + shadowOf(mPackageManager).setInstalledPackagesForUserId( + PROFILE_ID, ImmutableList.of(NOT_CROSS_PROFILE_PACKAGE_NAME)); assertThat(mController.getAvailabilityStatus()) .isEqualTo(BasePreferenceController.DISABLED_FOR_USER); diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettingsTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettingsTest.java index 19ea50b6572..dac3e22913f 100644 --- a/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettingsTest.java +++ b/tests/robotests/src/com/android/settings/applications/specialaccess/interactacrossprofiles/InteractAcrossProfilesSettingsTest.java @@ -18,14 +18,17 @@ package com.android.settings.applications.specialaccess.interactacrossprofiles; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertTrue; import static org.robolectric.Shadows.shadowOf; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.CrossProfileApps; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PermissionInfo; +import android.content.pm.UserInfo; import android.os.UserHandle; import android.os.UserManager; import android.util.Pair; @@ -68,49 +71,56 @@ public class InteractAcrossProfilesSettingsTest { private final CrossProfileApps mCrossProfileApps = mContext.getSystemService(CrossProfileApps.class); private final AppOpsManager mAppOpsManager = mContext.getSystemService(AppOpsManager.class); - private final InteractAcrossProfilesSettings mFragment = new InteractAcrossProfilesSettings(); @Test - public void collectConfigurableApps_fromPersonal_returnsPersonalPackages() { + public void collectConfigurableApps_fromPersonal_returnsCombinedPackages() { shadowOf(mUserManager).addUser( PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */); shadowOf(mUserManager).addProfile( PERSONAL_PROFILE_ID, WORK_PROFILE_ID, - "work-profile"/* profileName */, 0/* profileFlags */); + "work-profile"/* profileName */, UserInfo.FLAG_MANAGED_PROFILE); shadowOf(mPackageManager).setInstalledPackagesForUserId( PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES); shadowOf(mPackageManager).setInstalledPackagesForUserId( WORK_PROFILE_ID, WORK_PROFILE_INSTALLED_PACKAGES); - shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_CROSS_PROFILE_PACKAGE); - shadowOf(mCrossProfileApps).addCrossProfilePackage(WORK_CROSS_PROFILE_PACKAGE); + installCrossProfilePackage(PERSONAL_PROFILE_ID, PERSONAL_CROSS_PROFILE_PACKAGE); + installCrossProfilePackage(WORK_PROFILE_ID, WORK_CROSS_PROFILE_PACKAGE); - List> apps = mFragment.collectConfigurableApps( - mPackageManager, mUserManager, mCrossProfileApps); + List> apps = + InteractAcrossProfilesSettings.collectConfigurableApps( + mPackageManager, mUserManager, mCrossProfileApps); - assertThat(apps.size()).isEqualTo(1); - assertThat(apps.get(0).first.packageName).isEqualTo(PERSONAL_CROSS_PROFILE_PACKAGE); + assertThat(apps.size()).isEqualTo(2); + assertTrue(apps.stream().anyMatch( + app -> app.first.packageName.equals(PERSONAL_CROSS_PROFILE_PACKAGE))); + assertTrue(apps.stream().anyMatch( + app -> app.first.packageName.equals(WORK_CROSS_PROFILE_PACKAGE))); } @Test - public void collectConfigurableApps_fromWork_returnsPersonalPackages() { + public void collectConfigurableApps_fromWork_returnsCombinedPackages() { shadowOf(mUserManager).addUser( PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */); shadowOf(mUserManager).addProfile( PERSONAL_PROFILE_ID, WORK_PROFILE_ID, - "work-profile"/* profileName */, 0/* profileFlags */); + "work-profile"/* profileName */, UserInfo.FLAG_MANAGED_PROFILE); ShadowProcess.setUid(WORK_UID); shadowOf(mPackageManager).setInstalledPackagesForUserId( PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES); shadowOf(mPackageManager).setInstalledPackagesForUserId( WORK_PROFILE_ID, WORK_PROFILE_INSTALLED_PACKAGES); - shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_CROSS_PROFILE_PACKAGE); - shadowOf(mCrossProfileApps).addCrossProfilePackage(WORK_CROSS_PROFILE_PACKAGE); + installCrossProfilePackage(PERSONAL_PROFILE_ID, PERSONAL_CROSS_PROFILE_PACKAGE); + installCrossProfilePackage(WORK_PROFILE_ID, WORK_CROSS_PROFILE_PACKAGE); - List> apps = mFragment.collectConfigurableApps( - mPackageManager, mUserManager, mCrossProfileApps); + List> apps = + InteractAcrossProfilesSettings.collectConfigurableApps( + mPackageManager, mUserManager, mCrossProfileApps); - assertThat(apps.size()).isEqualTo(1); - assertThat(apps.get(0).first.packageName).isEqualTo(PERSONAL_CROSS_PROFILE_PACKAGE); + assertThat(apps.size()).isEqualTo(2); + assertTrue(apps.stream().anyMatch( + app -> app.first.packageName.equals(PERSONAL_CROSS_PROFILE_PACKAGE))); + assertTrue(apps.stream().anyMatch( + app -> app.first.packageName.equals(WORK_CROSS_PROFILE_PACKAGE))); } @Test @@ -119,10 +129,11 @@ public class InteractAcrossProfilesSettingsTest { PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */); shadowOf(mPackageManager).setInstalledPackagesForUserId( PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES); - shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_CROSS_PROFILE_PACKAGE); + installCrossProfilePackage(PERSONAL_PROFILE_ID, PERSONAL_CROSS_PROFILE_PACKAGE); - List> apps = mFragment.collectConfigurableApps( - mPackageManager, mUserManager, mCrossProfileApps); + List> apps = + InteractAcrossProfilesSettings.collectConfigurableApps( + mPackageManager, mUserManager, mCrossProfileApps); assertThat(apps).isEmpty(); } @@ -133,11 +144,14 @@ public class InteractAcrossProfilesSettingsTest { PERSONAL_PROFILE_ID, "personal-profile"/* name */, 0/* flags */); shadowOf(mUserManager).addProfile( PERSONAL_PROFILE_ID, WORK_PROFILE_ID, - "work-profile"/* profileName */, 0/* profileFlags */); + "work-profile"/* profileName */, UserInfo.FLAG_MANAGED_PROFILE); shadowOf(mPackageManager).setInstalledPackagesForUserId( PERSONAL_PROFILE_ID, PERSONAL_PROFILE_INSTALLED_PACKAGES); + shadowOf(mPackageManager).setInstalledPackagesForUserId( + WORK_PROFILE_ID, WORK_PROFILE_INSTALLED_PACKAGES); + installCrossProfilePackage(PERSONAL_PROFILE_ID, PERSONAL_CROSS_PROFILE_PACKAGE); + installCrossProfilePackage(WORK_PROFILE_ID, WORK_CROSS_PROFILE_PACKAGE); shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_CROSS_PROFILE_PACKAGE); - shadowOf(mCrossProfileApps).addCrossProfilePackage(PERSONAL_NON_CROSS_PROFILE_PACKAGE); String appOp = AppOpsManager.permissionToOp(INTERACT_ACROSS_PROFILES_PERMISSION); shadowOf(mAppOpsManager).setMode( appOp, PACKAGE_UID, PERSONAL_CROSS_PROFILE_PACKAGE, AppOpsManager.MODE_ALLOWED); @@ -145,12 +159,19 @@ public class InteractAcrossProfilesSettingsTest { appOp, PACKAGE_UID, PERSONAL_NON_CROSS_PROFILE_PACKAGE, AppOpsManager.MODE_IGNORED); shadowOf(mPackageManager).addPermissionInfo(createCrossProfilesPermissionInfo()); - int numOfApps = mFragment.getNumberOfEnabledApps( + int numOfApps = InteractAcrossProfilesSettings.getNumberOfEnabledApps( mContext, mPackageManager, mUserManager, mCrossProfileApps); assertThat(numOfApps).isEqualTo(1); } + private void installCrossProfilePackage(int profileId, String packageName) { + PackageInfo personalPackageInfo = shadowOf(mPackageManager).getInternalMutablePackageInfo( + packageName); + personalPackageInfo.requestedPermissions = new String[]{ + INTERACT_ACROSS_PROFILES_PERMISSION}; + } + private PermissionInfo createCrossProfilesPermissionInfo() { PermissionInfo permissionInfo = new PermissionInfo(); permissionInfo.name = INTERACT_ACROSS_PROFILES_PERMISSION; From bef5c99802bac89755e6513462d6e17a7b425a5d Mon Sep 17 00:00:00 2001 From: Bonian Chen Date: Tue, 31 Mar 2020 09:08:16 +0800 Subject: [PATCH 04/12] [Settings] Remove internal ImsManager access within MobileNetworkUtils Remove the access of com.android.ims.ImsManager from MobileNetworkUtils. Bug: 140542283 Test: m RunSettingsRoboTests -j ROBOTEST_FILTER=WifiCallingSettingsForSubTest Test: m RunSettingsRoboTests -j ROBOTEST_FILTER=WifiCallingPreferenceControllerTest Test: m RunSettingsRoboTests -j ROBOTEST_FILTER=WifiCallingSettingsTest Test: m RunSettingsRoboTests -j ROBOTEST_FILTER=WifiCallingSliceHelperTest Change-Id: Id2550a11c185ac44c1f70c98929c4b1d659f9a0c --- .../network/telephony/MobileNetworkUtils.java | 25 ++----------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/src/com/android/settings/network/telephony/MobileNetworkUtils.java b/src/com/android/settings/network/telephony/MobileNetworkUtils.java index 076a87b1774..12b71e87120 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkUtils.java +++ b/src/com/android/settings/network/telephony/MobileNetworkUtils.java @@ -26,8 +26,8 @@ import static com.android.settings.network.telephony.TelephonyConstants.RadioAcc import static com.android.settings.network.telephony.TelephonyConstants.RadioAccessFamily.RAF_TD_SCDMA; import static com.android.settings.network.telephony.TelephonyConstants.RadioAccessFamily.RAF_UNKNOWN; import static com.android.settings.network.telephony.TelephonyConstants.RadioAccessFamily.WCDMA; -import static com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants.NETWORK_MODE_LTE_GSM_WCDMA; import static com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants.NETWORK_MODE_LTE_CDMA_EVDO; +import static com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants.NETWORK_MODE_LTE_GSM_WCDMA; import static com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO; import static com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA; @@ -54,7 +54,6 @@ import android.telephony.euicc.EuiccManager; import android.telephony.ims.ImsRcsManager; import android.telephony.ims.ProvisioningManager; import android.telephony.ims.RcsUceAdapter; -import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.text.TextUtils; @@ -63,8 +62,6 @@ import android.view.Gravity; import androidx.annotation.VisibleForTesting; -import com.android.ims.ImsException; -import com.android.ims.ImsManager; import com.android.internal.util.ArrayUtils; import com.android.settings.R; import com.android.settings.Utils; @@ -150,7 +147,6 @@ public class MobileNetworkUtils { final PhoneAccountHandle simCallManager = context.getSystemService(TelecomManager.class) .getSimCallManagerForSubscription(subId); - final int phoneId = SubscriptionManager.getSlotIndex(subId); boolean isWifiCallingEnabled; if (simCallManager != null) { @@ -161,9 +157,7 @@ public class MobileNetworkUtils { } else { final WifiCallingQueryImsState queryState = new WifiCallingQueryImsState(context, subId); - final ImsManager imsMgr = ImsManager.getInstance(context, phoneId); - isWifiCallingEnabled = queryState.isWifiCallingProvisioned() - && isImsServiceStateReady(imsMgr); + isWifiCallingEnabled = queryState.isReadyToWifiCalling(); } return isWifiCallingEnabled; @@ -280,21 +274,6 @@ public class MobileNetworkUtils { return intent; } - public static boolean isImsServiceStateReady(ImsManager imsMgr) { - boolean isImsServiceStateReady = false; - - try { - if (imsMgr != null && imsMgr.getImsServiceState() == ImsFeature.STATE_READY) { - isImsServiceStateReady = true; - } - } catch (ImsException ex) { - Log.e(TAG, "Exception when trying to get ImsServiceStatus: " + ex); - } - - Log.d(TAG, "isImsServiceStateReady=" + isImsServiceStateReady); - return isImsServiceStateReady; - } - /** * Whether to show the entry point to eUICC settings. * From 21475ae6c66ac43840178b475ff020722c55edb3 Mon Sep 17 00:00:00 2001 From: Bonian Chen Date: Mon, 30 Mar 2020 15:03:46 +0800 Subject: [PATCH 05/12] [Settings] Code Refactor Code refactor in order to support auto testing Bug: 140542283 Test: m RunSettingsRoboTests -j ROBOTEST_FILTER=WifiCallingSettingsForSubTest Change-Id: I2427732435b59d7359a102e0dd56317ac6a024b4 --- .../calling/WifiCallingSettingsForSub.java | 22 ++++++++++++++----- .../WifiCallingSettingsForSubTest.java | 8 +++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java index 96aae0ea4bb..6b279ed947d 100644 --- a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java +++ b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java @@ -119,8 +119,9 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment queryImsState(WifiCallingSettingsForSub.this.mSubId).isAllowUserControl(); final boolean isWfcEnabled = mSwitchBar.isChecked() && isNonTtyOrTtyOnVolteEnabled; - boolean isCallStateIdle = - mTelephonyManager.getCallState() == TelephonyManager.CALL_STATE_IDLE; + boolean isCallStateIdle = getTelephonyManagerForSub( + WifiCallingSettingsForSub.this.mSubId).getCallState() + == TelephonyManager.CALL_STATE_IDLE; mSwitchBar.setEnabled(isCallStateIdle && isNonTtyOrTtyOnVolteEnabled); @@ -201,7 +202,8 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment mSwitchBar.hide(); } - private void showAlert(Intent intent) { + @VisibleForTesting + void showAlert(Intent intent) { final Context context = getActivity(); final CharSequence title = intent.getCharSequenceExtra(Phone.EXTRA_KEY_ALERT_TITLE); @@ -247,6 +249,14 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment return 0; } + @VisibleForTesting + TelephonyManager getTelephonyManagerForSub(int subId) { + if (mTelephonyManager == null) { + mTelephonyManager = getContext().getSystemService(TelephonyManager.class); + } + return mTelephonyManager.createForSubscriptionId(subId); + } + @VisibleForTesting WifiCallingQueryImsState queryImsState(int subId) { return new WifiCallingQueryImsState(getContext(), subId); @@ -420,7 +430,8 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment updateBody(); if (queryImsState(mSubId).isWifiCallingSupported()) { - mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); + getTelephonyManagerForSub(mSubId).listen(mPhoneStateListener, + PhoneStateListener.LISTEN_CALL_STATE); mSwitchBar.addOnSwitchChangeListener(this); @@ -448,7 +459,8 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment if (mValidListener) { mValidListener = false; - mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); + getTelephonyManagerForSub(mSubId).listen(mPhoneStateListener, + PhoneStateListener.LISTEN_NONE); mSwitchBar.removeOnSwitchChangeListener(this); } diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java index 5b76adc84c5..a9df33d36c6 100644 --- a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java +++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java @@ -351,5 +351,13 @@ public class WifiCallingSettingsForSubTest { return null; } } + + @Override + TelephonyManager getTelephonyManagerForSub(int subId) { + return mTelephonyManager; + } + + @Override + void showAlert(Intent intent) {} } } From 7520adcceecf9e674c7a8d606a22f152fd5ed636 Mon Sep 17 00:00:00 2001 From: Bonian Chen Date: Tue, 31 Mar 2020 14:31:41 +0800 Subject: [PATCH 06/12] [Settings] Apply WifiCallingQueryImsState Apply design of WifiCallingQueryImsState to source code which accessing WifiCalling. Bug: 140542283 Test: m RunSettingsRoboTests -j ROBOTEST_FILTER=WifiCallingSettingsForSubTest Test: m RunSettingsRoboTests -j ROBOTEST_FILTER=WifiCallingPreferenceControllerTest Test: m RunSettingsRoboTests -j ROBOTEST_FILTER=WifiCallingSettingsTest Test: m RunSettingsRoboTests -j ROBOTEST_FILTER=WifiCallingSliceHelperTest Change-Id: I0db966742d63cfe9a1ef4e1b06f6b5c81f681927 --- .../network/ims/WifiCallingQueryImsState.java | 21 +++-- .../calling/WifiCallingSettingsForSub.java | 14 +--- .../WifiCallingSuggestionActivity.java | 10 +-- .../ims/MockWifiCallingQueryImsState.java | 21 +++-- .../WifiCallingPreferenceControllerTest.java | 14 ++-- .../WifiCallingSettingsForSubTest.java | 30 ++++--- .../wifi/calling/WifiCallingSettingsTest.java | 11 +-- .../calling/WifiCallingSliceHelperTest.java | 79 +++++++++---------- 8 files changed, 89 insertions(+), 111 deletions(-) diff --git a/src/com/android/settings/network/ims/WifiCallingQueryImsState.java b/src/com/android/settings/network/ims/WifiCallingQueryImsState.java index 6ed0d606590..efa93e5abe5 100644 --- a/src/com/android/settings/network/ims/WifiCallingQueryImsState.java +++ b/src/com/android/settings/network/ims/WifiCallingQueryImsState.java @@ -27,10 +27,6 @@ import android.util.Log; import androidx.annotation.VisibleForTesting; -import com.android.ims.ImsManager; -import com.android.settings.network.SubscriptionUtil; -import com.android.settings.network.telephony.MobileNetworkUtils; - /** * Controller class for querying Wifi calling status */ @@ -66,12 +62,6 @@ public class WifiCallingQueryImsState extends ImsQueryController { return (new ImsQueryWfcUserSetting(subId)).query(); } - @VisibleForTesting - ImsManager getImsManager(int subId) { - return ImsManager.getInstance(mContext, - SubscriptionUtil.getPhoneId(mContext, subId)); - } - /** * Check whether Wifi Calling is a supported feature on this subscription * @@ -107,8 +97,15 @@ public class WifiCallingQueryImsState extends ImsQueryController { if (!SubscriptionManager.isValidSubscriptionId(mSubId)) { return false; } - return isWifiCallingProvisioned() - && MobileNetworkUtils.isImsServiceStateReady(getImsManager(mSubId)); + if (!isWifiCallingProvisioned()) { + return false; + } + try { + return isServiceStateReady(mSubId); + } catch (InterruptedException | IllegalArgumentException | ImsException exception) { + Log.w(LOG_TAG, "fail to get WFC service status. subId=" + mSubId, exception); + } + return false; } /** diff --git a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java index 6b279ed947d..735fecc9c9a 100644 --- a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java +++ b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java @@ -54,9 +54,7 @@ import com.android.settings.SettingsActivity; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.Utils; import com.android.settings.core.SubSettingLauncher; -import com.android.settings.network.SubscriptionUtil; import com.android.settings.network.ims.WifiCallingQueryImsState; -import com.android.settings.network.telephony.MobileNetworkUtils; import com.android.settings.widget.SwitchBar; /** @@ -100,7 +98,6 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment private boolean mUseWfcHomeModeForRoaming = false; private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - private com.android.ims.ImsManager mImsManager; private ImsMmTelManager mImsMmTelManager; private ProvisioningManager mProvisioningManager; private TelephonyManager mTelephonyManager; @@ -270,12 +267,6 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment return ProvisioningManager.createForSubscriptionId(mSubId); } - @VisibleForTesting - com.android.ims.ImsManager getImsManager() { - return com.android.ims.ImsManager.getInstance(getActivity(), - SubscriptionUtil.getPhoneId(getActivity(), mSubId)); - } - @VisibleForTesting ImsMmTelManager getImsMmTelManager() { if (!SubscriptionManager.isValidSubscriptionId(mSubId)) { @@ -299,12 +290,9 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment FRAGMENT_BUNDLE_SUBID, SubscriptionManager.INVALID_SUBSCRIPTION_ID); } - mImsManager = getImsManager(); mProvisioningManager = getImsProvisioningManager(); mImsMmTelManager = getImsMmTelManager(); - mTelephonyManager = getActivity().getSystemService(TelephonyManager.class); - mButtonWfcMode = findPreference(BUTTON_WFC_MODE); mButtonWfcMode.setOnPreferenceChangeListener(this); @@ -341,7 +329,7 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment @VisibleForTesting boolean isWfcProvisionedOnDevice() { - return MobileNetworkUtils.isWfcProvisionedOnDevice(mSubId); + return queryImsState(mSubId).isWifiCallingProvisioned(); } private void updateBody() { diff --git a/src/com/android/settings/wifi/calling/WifiCallingSuggestionActivity.java b/src/com/android/settings/wifi/calling/WifiCallingSuggestionActivity.java index 0e3b8872d27..6b6eebfe3a0 100644 --- a/src/com/android/settings/wifi/calling/WifiCallingSuggestionActivity.java +++ b/src/com/android/settings/wifi/calling/WifiCallingSuggestionActivity.java @@ -19,10 +19,8 @@ package com.android.settings.wifi.calling; import android.content.Context; import android.telephony.SubscriptionManager; -import com.android.ims.ImsManager; import com.android.settings.SettingsActivity; import com.android.settings.network.ims.WifiCallingQueryImsState; -import com.android.settings.network.telephony.MobileNetworkUtils; public class WifiCallingSuggestionActivity extends SettingsActivity { @@ -30,11 +28,7 @@ public class WifiCallingSuggestionActivity extends SettingsActivity { final WifiCallingQueryImsState queryState = new WifiCallingQueryImsState(context, SubscriptionManager.getDefaultVoiceSubscriptionId()); - if (!ImsManager.isWfcEnabledByPlatform(context) || - !MobileNetworkUtils.isWfcProvisionedOnDevice( - SubscriptionManager.getDefaultVoiceSubscriptionId())) { - return true; - } - return queryState.isEnabledByUser() && queryState.isAllowUserControl(); + return (!queryState.isWifiCallingProvisioned()) + || (queryState.isEnabledByUser() && queryState.isAllowUserControl()); } } diff --git a/tests/robotests/src/com/android/settings/network/ims/MockWifiCallingQueryImsState.java b/tests/robotests/src/com/android/settings/network/ims/MockWifiCallingQueryImsState.java index af1031da07f..abea839791e 100644 --- a/tests/robotests/src/com/android/settings/network/ims/MockWifiCallingQueryImsState.java +++ b/tests/robotests/src/com/android/settings/network/ims/MockWifiCallingQueryImsState.java @@ -19,9 +19,6 @@ package com.android.settings.network.ims; import android.content.Context; import android.telephony.ims.ImsException; -import com.android.ims.ImsManager; - - /** * Controller class for mock Wifi calling status */ @@ -30,6 +27,7 @@ public class MockWifiCallingQueryImsState extends WifiCallingQueryImsState { private Boolean mIsTtyOnVolteEnabled; private Boolean mIsEnabledOnPlatform; private Boolean mIsProvisionedOnDevice; + private Boolean mIsServiceStateReady; private Boolean mIsEnabledByUser; /** @@ -42,10 +40,6 @@ public class MockWifiCallingQueryImsState extends WifiCallingQueryImsState { super(context, subId); } - public ImsManager getImsManager(int subId) { - return super.getImsManager(subId); - } - public void setIsTtyOnVolteEnabled(boolean enabled) { mIsTtyOnVolteEnabled = enabled; } @@ -84,6 +78,19 @@ public class MockWifiCallingQueryImsState extends WifiCallingQueryImsState { return super.isProvisionedOnDevice(subId); } + public void setServiceStateReady(boolean isReady) { + mIsServiceStateReady = isReady; + } + + @Override + boolean isServiceStateReady(int subId) throws InterruptedException, ImsException, + IllegalArgumentException { + if (mIsServiceStateReady != null) { + return mIsServiceStateReady; + } + return super.isServiceStateReady(subId); + } + public void setIsEnabledByUser(boolean enabled) { mIsEnabledByUser = enabled; } diff --git a/tests/robotests/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.java index b91f7188aef..dbfd3b24766 100644 --- a/tests/robotests/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.java @@ -37,7 +37,6 @@ import android.telephony.ims.ImsMmTelManager; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; -import com.android.ims.ImsManager; import com.android.internal.R; import com.android.settings.core.BasePreferenceController; import com.android.settings.network.ims.MockWifiCallingQueryImsState; @@ -60,8 +59,6 @@ public class WifiCallingPreferenceControllerTest { @Mock private TelephonyManager mTelephonyManager; @Mock - private ImsManager mImsManager; - @Mock private ImsMmTelManager mImsMmTelManager; @Mock private PreferenceScreen mPreferenceScreen; @@ -79,9 +76,8 @@ public class WifiCallingPreferenceControllerTest { mContext = spy(RuntimeEnvironment.application); - mQueryImsState = spy(new MockWifiCallingQueryImsState(mContext, SUB_ID)); - doReturn(true).when(mQueryImsState).isEnabledByUser(); - doReturn(mImsManager).when(mQueryImsState).getImsManager(anyInt()); + mQueryImsState = new MockWifiCallingQueryImsState(mContext, SUB_ID); + mQueryImsState.setIsEnabledByUser(true); mQueryImsState.setIsProvisionedOnDevice(true); mPreference = new Preference(mContext); @@ -109,7 +105,7 @@ public class WifiCallingPreferenceControllerTest { @Test public void updateState_noSimCallManager_setCorrectSummary() { mController.mSimCallManager = null; - doReturn(true).when(mQueryImsState).isEnabledByUser(); + mQueryImsState.setIsEnabledByUser(true); when(mImsMmTelManager.getVoWiFiRoamingModeSetting()).thenReturn( ImsMmTelManager.WIFI_MODE_WIFI_ONLY); when(mImsMmTelManager.getVoWiFiModeSetting()).thenReturn( @@ -149,7 +145,7 @@ public class WifiCallingPreferenceControllerTest { ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED); when(mImsMmTelManager.getVoWiFiModeSetting()).thenReturn( ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED); - doReturn(true).when(mQueryImsState).isEnabledByUser(); + mQueryImsState.setIsEnabledByUser(true); when(mTelephonyManager.isNetworkRoaming()).thenReturn(true); mController.updateState(mPreference); @@ -166,7 +162,7 @@ public class WifiCallingPreferenceControllerTest { ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED); when(mImsMmTelManager.getVoWiFiModeSetting()).thenReturn( ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED); - doReturn(true).when(mQueryImsState).isEnabledByUser(); + mQueryImsState.setIsEnabledByUser(true); when(mTelephonyManager.isNetworkRoaming()).thenReturn(true); mController.updateState(mPreference); diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java index a9df33d36c6..2e93faac27e 100644 --- a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java +++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java @@ -50,16 +50,15 @@ import android.widget.TextView; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; -import com.android.ims.ImsConfig; -import com.android.ims.ImsManager; import com.android.settings.R; import com.android.settings.SettingsActivity; +import com.android.settings.network.ims.MockWifiCallingQueryImsState; +import com.android.settings.network.ims.WifiCallingQueryImsState; import com.android.settings.testutils.shadow.ShadowFragment; import com.android.settings.widget.SwitchBar; import com.android.settings.widget.ToggleSwitch; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -73,6 +72,8 @@ import org.robolectric.util.ReflectionHelpers; @Config(shadows = ShadowFragment.class) @RunWith(RobolectricTestRunner.class) public class WifiCallingSettingsForSubTest { + private static final int SUB_ID = 2; + private static final String BUTTON_WFC_MODE = "wifi_calling_mode"; private static final String BUTTON_WFC_ROAMING_MODE = "wifi_calling_roaming_mode"; private static final String TEST_EMERGENCY_ADDRESS_CARRIER_APP = @@ -83,9 +84,10 @@ public class WifiCallingSettingsForSubTest { private TextView mEmptyView; private final PersistableBundle mBundle = new PersistableBundle(); + private MockWifiCallingQueryImsState mQueryImsState; + @Mock private static CarrierConfigManager sCarrierConfigManager; @Mock private CarrierConfigManager mMockConfigManager; - @Mock private ImsManager mImsManager; @Mock private ImsMmTelManager mImsMmTelManager; @Mock private TelephonyManager mTelephonyManager; @Mock private PreferenceScreen mPreferenceScreen; @@ -93,7 +95,6 @@ public class WifiCallingSettingsForSubTest { @Mock private SwitchBar mSwitchBar; @Mock private ToggleSwitch mToggleSwitch; @Mock private View mView; - @Mock private ImsConfig mImsConfig; @Mock private ListWithEntrySummaryPreference mButtonWfcMode; @Mock private ListWithEntrySummaryPreference mButtonWfcRoamingMode; @Mock private Preference mUpdateAddress; @@ -127,12 +128,13 @@ public class WifiCallingSettingsForSubTest { ReflectionHelpers.setField(mSwitchBar, "mSwitch", mToggleSwitch); doReturn(mSwitchBar).when(mView).findViewById(R.id.switch_bar); - doReturn(mImsManager).when(mFragment).getImsManager(); + mQueryImsState = new MockWifiCallingQueryImsState(mContext, SUB_ID); + doReturn(mImsMmTelManager).when(mFragment).getImsMmTelManager(); - doReturn(mImsConfig).when(mImsManager).getConfigInterface(); - doReturn(true).when(mFragment).isWfcProvisionedOnDevice(); - doReturn(true).when(mImsManager).isWfcEnabledByUser(); - doReturn(true).when(mImsManager).isNonTtyOrTtyOnVolteEnabled(); + mQueryImsState.setIsProvisionedOnDevice(true); + mQueryImsState.setIsEnabledByPlatform(true); + mQueryImsState.setIsEnabledByUser(true); + mQueryImsState.setIsTtyOnVolteEnabled(true); doReturn(ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED) .when(mImsMmTelManager).getVoWiFiModeSetting(); doReturn(ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED) @@ -178,7 +180,7 @@ public class WifiCallingSettingsForSubTest { @Test public void onResume_provisioningDisallowed_shouldFinish() { // Call onResume while provisioning is disallowed. - doReturn(false).when(mFragment).isWfcProvisionedOnDevice(); + mQueryImsState.setIsProvisionedOnDevice(false); mFragment.onResume(); // Verify that finish() is called @@ -197,7 +199,6 @@ public class WifiCallingSettingsForSubTest { } @Test - @Ignore public void onResume_useWfcHomeModeConfigFalseAndEditable_shouldShowWfcRoaming() { // Call onResume to update the WFC roaming preference. mFragment.onResume(); @@ -357,6 +358,11 @@ public class WifiCallingSettingsForSubTest { return mTelephonyManager; } + @Override + WifiCallingQueryImsState queryImsState(int subId) { + return mQueryImsState; + } + @Override void showAlert(Intent intent) {} } diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsTest.java index 90633cb8d6b..1ba8e7a44fa 100644 --- a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsTest.java +++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsTest.java @@ -18,7 +18,6 @@ package com.android.settings.wifi.calling; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -72,12 +71,10 @@ public class WifiCallingSettingsTest { mContext = spy(RuntimeEnvironment.application); - mQueryImsState1 = spy(new MockWifiCallingQueryImsState(mContext, SUB_ID1)); - mQueryImsState2 = spy(new MockWifiCallingQueryImsState(mContext, SUB_ID2)); - doReturn(true).when(mQueryImsState1).isEnabledByUser(); - doReturn(true).when(mQueryImsState2).isEnabledByUser(); - doReturn(mImsManager).when(mQueryImsState1).getImsManager(anyInt()); - doReturn(mImsManager).when(mQueryImsState2).getImsManager(anyInt()); + mQueryImsState1 = new MockWifiCallingQueryImsState(mContext, SUB_ID1); + mQueryImsState2 = new MockWifiCallingQueryImsState(mContext, SUB_ID2); + mQueryImsState1.setIsEnabledByUser(true); + mQueryImsState2.setIsEnabledByUser(true); mQueryImsState1.setIsEnabledByPlatform(true); mQueryImsState2.setIsEnabledByPlatform(true); mQueryImsState1.setIsProvisionedOnDevice(true); diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java index f537be32c3e..0013234c950 100644 --- a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java +++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java @@ -47,7 +47,7 @@ import androidx.slice.widget.SliceLiveData; import com.android.ims.ImsManager; import com.android.settings.R; -import com.android.settings.network.ims.WifiCallingQueryImsState; +import com.android.settings.network.ims.MockWifiCallingQueryImsState; import com.android.settings.slices.CustomSliceRegistry; import com.android.settings.slices.SettingsSliceProvider; import com.android.settings.slices.SliceBroadcastReceiver; @@ -81,7 +81,7 @@ public class WifiCallingSliceHelperTest { @Mock private ImsMmTelManager mMockImsMmTelManager; - private WifiCallingQueryImsState mQueryImsState; + private MockWifiCallingQueryImsState mQueryImsState; private FakeWifiCallingSliceHelper mWfcSliceHelper; private SettingsSliceProvider mProvider; @@ -105,9 +105,10 @@ public class WifiCallingSliceHelperTest { mFeatureFactory = FakeFeatureFactory.setupForTest(); mSlicesFeatureProvider = mFeatureFactory.getSlicesFeatureProvider(); - mQueryImsState = spy(new WifiCallingQueryImsState(mContext, SUB_ID)); - doReturn(true).when(mQueryImsState).isEnabledByUser(); - doReturn(true).when(mQueryImsState).isWifiCallingProvisioned(); + mQueryImsState = new MockWifiCallingQueryImsState(mContext, SUB_ID); + mQueryImsState.setIsEnabledByUser(true); + mQueryImsState.setIsEnabledByPlatform(true); + mQueryImsState.setIsProvisionedOnDevice(true); mWfcSliceHelper = spy(new FakeWifiCallingSliceHelper(mContext)); doReturn(mQueryImsState).when(mWfcSliceHelper).queryImsState(anyInt()); @@ -118,8 +119,8 @@ public class WifiCallingSliceHelperTest { @Test public void test_CreateWifiCallingSlice_invalidSubId() { - doReturn(true).when(mQueryImsState).isEnabledByUser(); - doReturn(false).when(mQueryImsState).isWifiCallingProvisioned(); + mQueryImsState.setIsEnabledByUser(true); + mQueryImsState.setIsProvisionedOnDevice(false); mWfcSliceHelper.setDefaultVoiceSubId(-1); final Slice slice = mWfcSliceHelper.createWifiCallingSlice( @@ -130,7 +131,7 @@ public class WifiCallingSliceHelperTest { @Test public void test_CreateWifiCallingSlice_wfcNotSupported() { - doReturn(false).when(mQueryImsState).isWifiCallingProvisioned(); + mQueryImsState.setIsProvisionedOnDevice(false); final Slice slice = mWfcSliceHelper.createWifiCallingSlice( CustomSliceRegistry.WIFI_CALLING_URI); @@ -146,9 +147,9 @@ public class WifiCallingSliceHelperTest { turned off) we need to guide the user to wifi calling settings activity so the user can perform the activation there.(PrimaryAction) */ - doReturn(true).when(mQueryImsState).isWifiCallingProvisioned(); - doReturn(false).when(mQueryImsState).isEnabledByUser(); - when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(false); + mQueryImsState.setIsProvisionedOnDevice(true); + mQueryImsState.setIsEnabledByUser(false); + mQueryImsState.setIsTtyOnVolteEnabled(false); when(mMockCarrierConfigManager.getConfigForSubId(1)).thenReturn(null); mWfcSliceHelper.setActivationAppIntent(new Intent()); // dummy Intent @@ -163,9 +164,9 @@ public class WifiCallingSliceHelperTest { @Test public void test_CreateWifiCallingSlice_success() { - doReturn(true).when(mQueryImsState).isWifiCallingProvisioned(); - doReturn(true).when(mQueryImsState).isEnabledByUser(); - when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true); + mQueryImsState.setIsProvisionedOnDevice(true); + mQueryImsState.setIsEnabledByUser(true); + mQueryImsState.setIsTtyOnVolteEnabled(true); when(mMockCarrierConfigManager.getConfigForSubId(1)).thenReturn(null); final Slice slice = mWfcSliceHelper.createWifiCallingSlice( @@ -177,9 +178,9 @@ public class WifiCallingSliceHelperTest { @Test public void test_SettingSliceProvider_getsRightSliceWifiCalling() { - doReturn(true).when(mQueryImsState).isWifiCallingProvisioned(); - doReturn(true).when(mQueryImsState).isEnabledByUser(); - when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true); + mQueryImsState.setIsProvisionedOnDevice(true); + mQueryImsState.setIsEnabledByUser(true); + mQueryImsState.setIsTtyOnVolteEnabled(true); when(mMockCarrierConfigManager.getConfigForSubId(1)).thenReturn(null); when(mSlicesFeatureProvider.getNewWifiCallingSliceHelper(mContext)) .thenReturn(mWfcSliceHelper); @@ -192,9 +193,9 @@ public class WifiCallingSliceHelperTest { @Test public void test_SliceBroadcastReceiver_toggleOnWifiCalling() { - doReturn(true).when(mQueryImsState).isWifiCallingProvisioned(); - doReturn(false).when(mQueryImsState).isEnabledByUser(); - when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true); + mQueryImsState.setIsProvisionedOnDevice(true); + mQueryImsState.setIsEnabledByUser(false); + mQueryImsState.setIsTtyOnVolteEnabled(true); when(mSlicesFeatureProvider.getNewWifiCallingSliceHelper(mContext)) .thenReturn(mWfcSliceHelper); mWfcSliceHelper.setActivationAppIntent(null); @@ -216,9 +217,9 @@ public class WifiCallingSliceHelperTest { @Test public void test_CreateWifiCallingPreferenceSlice_prefNotEditable() { - doReturn(true).when(mQueryImsState).isWifiCallingProvisioned(); - doReturn(true).when(mQueryImsState).isEnabledByUser(); - when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true); + mQueryImsState.setIsProvisionedOnDevice(true); + mQueryImsState.setIsEnabledByUser(true); + mQueryImsState.setIsTtyOnVolteEnabled(true); mWfcSliceHelper.setIsWifiCallingPrefEditable(false); final Slice slice = mWfcSliceHelper.createWifiCallingPreferenceSlice( @@ -230,9 +231,9 @@ public class WifiCallingSliceHelperTest { @Test public void test_CreateWifiCallingPreferenceSlice_wfcOff() { - doReturn(true).when(mQueryImsState).isWifiCallingProvisioned(); - doReturn(false).when(mQueryImsState).isEnabledByUser(); - when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true); + mQueryImsState.setIsProvisionedOnDevice(true); + mQueryImsState.setIsEnabledByUser(false); + mQueryImsState.setIsTtyOnVolteEnabled(true); mWfcSliceHelper.setIsWifiCallingPrefEditable(true); final Slice slice = mWfcSliceHelper.createWifiCallingPreferenceSlice( @@ -246,9 +247,9 @@ public class WifiCallingSliceHelperTest { @Test public void test_CreateWifiCallingPreferenceSlice_success() { - doReturn(true).when(mQueryImsState).isWifiCallingProvisioned(); - doReturn(true).when(mQueryImsState).isEnabledByUser(); - when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true); + mQueryImsState.setIsProvisionedOnDevice(true); + mQueryImsState.setIsEnabledByUser(true); + mQueryImsState.setIsTtyOnVolteEnabled(true); when(mMockImsMmTelManager.getVoWiFiModeSetting()).thenReturn( ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED); mWfcSliceHelper.setIsWifiCallingPrefEditable(true); @@ -263,9 +264,9 @@ public class WifiCallingSliceHelperTest { @Test public void test_SettingsSliceProvider_getWfcPreferenceSlice() { - doReturn(true).when(mQueryImsState).isWifiCallingProvisioned(); - doReturn(true).when(mQueryImsState).isEnabledByUser(); - when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true); + mQueryImsState.setIsProvisionedOnDevice(true); + mQueryImsState.setIsEnabledByUser(true); + mQueryImsState.setIsTtyOnVolteEnabled(true); when(mMockImsMmTelManager.getVoWiFiModeSetting()).thenReturn( ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED); when(mSlicesFeatureProvider.getNewWifiCallingSliceHelper(mContext)) @@ -281,9 +282,9 @@ public class WifiCallingSliceHelperTest { } @Test public void test_SliceBroadcastReceiver_setWfcPrefCellularPref() { - doReturn(true).when(mQueryImsState).isWifiCallingProvisioned(); - doReturn(true).when(mQueryImsState).isEnabledByUser(); - when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true); + mQueryImsState.setIsProvisionedOnDevice(true); + mQueryImsState.setIsEnabledByUser(true); + mQueryImsState.setIsTtyOnVolteEnabled(true); when(mMockImsMmTelManager.getVoWiFiModeSetting()).thenReturn( ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED); when(mSlicesFeatureProvider.getNewWifiCallingSliceHelper(mContext)) @@ -447,14 +448,6 @@ public class WifiCallingSliceHelperTest { mSubId = id; } - boolean isWfcProvisionedOnDevice(int subId) { - return true; - } - - WifiCallingQueryImsState queryImsState(int subId) { - return super.queryImsState(subId); - } - @Override protected Intent getWifiCallingCarrierActivityIntent(int subId) { return mActivationAppIntent; From 3bb3808043b0f1b7b581c02c1e509ded5f9b8103 Mon Sep 17 00:00:00 2001 From: Arc Wang Date: Tue, 31 Mar 2020 17:01:22 +0800 Subject: [PATCH 07/12] [Wi-Fi] Fix Wi-Fi picker context menu 'Connect' button 1. Display the Wi-Fi dialog with MODE_CONNECT. 2. Connect with the WifiConfiguration from WifiConfigController2#getConfig() if it's available. Bug: 152826354 Test: make RunSettingsRoboTests ROBOTEST_FILTER=WifiSettings2 Change-Id: Ifbe2ac30b1c37c8eab3227ac0561fdd2de5cb706 --- .../android/settings/wifi/WifiDialog2.java | 4 ++ .../android/settings/wifi/WifiSettings2.java | 27 ++++++--- .../settings/wifi/WifiSettings2Test.java | 58 +++++++++++++++++++ 3 files changed, 82 insertions(+), 7 deletions(-) diff --git a/src/com/android/settings/wifi/WifiDialog2.java b/src/com/android/settings/wifi/WifiDialog2.java index acd6f17d36b..dc546f3c231 100644 --- a/src/com/android/settings/wifi/WifiDialog2.java +++ b/src/com/android/settings/wifi/WifiDialog2.java @@ -214,4 +214,8 @@ public class WifiDialog2 extends AlertDialog implements WifiConfigUiBase2, public void setCancelButton(CharSequence text) { setButton(BUTTON_NEGATIVE, text, this); } + + public WifiEntry getWifiEntry() { + return mWifiEntry; + } } diff --git a/src/com/android/settings/wifi/WifiSettings2.java b/src/com/android/settings/wifi/WifiSettings2.java index 13ccdc3695b..51bdcede318 100644 --- a/src/com/android/settings/wifi/WifiSettings2.java +++ b/src/com/android/settings/wifi/WifiSettings2.java @@ -954,18 +954,30 @@ public class WifiSettings2 extends RestrictedSettingsFragment @Override public void onForget(WifiDialog2 dialog) { - forget(mDialogWifiEntry); + forget(dialog.getWifiEntry()); } @Override public void onSubmit(WifiDialog2 dialog) { - final int dialogMode = mDialog.getController().getMode(); + final int dialogMode = dialog.getMode(); + final WifiConfiguration config = dialog.getController().getConfig(); + final WifiEntry wifiEntry = dialog.getWifiEntry(); if (dialogMode == WifiConfigUiBase2.MODE_MODIFY) { - mWifiManager.save(mDialogWifiEntry.getWifiConfiguration(), mSaveListener); + if (config == null) { + Toast.makeText(getContext(), R.string.wifi_failed_save_message, + Toast.LENGTH_SHORT).show(); + } else { + mWifiManager.save(config, mSaveListener); + } } else if (dialogMode == WifiConfigUiBase2.MODE_CONNECT - || (dialogMode == WifiConfigUiBase2.MODE_VIEW && mDialogWifiEntry.canConnect())) { - connect(mDialogWifiEntry, false /* editIfNoConfig */, false /* fullScreenEdit*/); + || (dialogMode == WifiConfigUiBase2.MODE_VIEW && wifiEntry.canConnect())) { + if (config == null) { + connect(wifiEntry, false /* editIfNoConfig */, + false /* fullScreenEdit*/); + } else { + mWifiManager.connect(config, new WifiConnectActionListener()); + } } } @@ -981,7 +993,8 @@ public class WifiSettings2 extends RestrictedSettingsFragment wifiEntry.forget(null /* callback */); } - private void connect(WifiEntry wifiEntry, boolean editIfNoConfig, boolean fullScreenEdit) { + @VisibleForTesting + void connect(WifiEntry wifiEntry, boolean editIfNoConfig, boolean fullScreenEdit) { mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_WIFI_CONNECT, wifiEntry.isSaved()); @@ -1034,7 +1047,7 @@ public class WifiSettings2 extends RestrictedSettingsFragment if (mFullScreenEdit) { launchConfigNewNetworkFragment(mConnectWifiEntry); } else { - showDialog(mConnectWifiEntry, WifiConfigUiBase2.MODE_MODIFY); + showDialog(mConnectWifiEntry, WifiConfigUiBase2.MODE_CONNECT); } } } else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) { diff --git a/tests/robotests/src/com/android/settings/wifi/WifiSettings2Test.java b/tests/robotests/src/com/android/settings/wifi/WifiSettings2Test.java index bd6b20f71d0..b259cc39b4f 100644 --- a/tests/robotests/src/com/android/settings/wifi/WifiSettings2Test.java +++ b/tests/robotests/src/com/android/settings/wifi/WifiSettings2Test.java @@ -15,6 +15,9 @@ */ package com.android.settings.wifi; +import static com.android.settings.wifi.WifiConfigUiBase2.MODE_CONNECT; +import static com.android.settings.wifi.WifiConfigUiBase2.MODE_MODIFY; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -34,6 +37,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.res.Resources; +import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.PowerManager; @@ -64,6 +68,7 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowToast; @RunWith(RobolectricTestRunner.class) public class WifiSettings2Test { @@ -303,4 +308,57 @@ public class WifiSettings2Test { mWifiSettings2.onNumSavedSubscriptionsChanged(); } + + @Test + public void onSubmit_modeModifyNoConfig_toastErrorMessage() { + WifiDialog2 dialog = createWifiDialog2(MODE_MODIFY, null /* config */); + + mWifiSettings2.onSubmit(dialog); + + assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo( + mContext.getString(R.string.wifi_failed_save_message)); + } + + @Test + public void onSubmit_modeModifyHasConfig_saveWifiManager() { + final WifiConfiguration config = mock(WifiConfiguration.class); + WifiDialog2 dialog = createWifiDialog2(MODE_MODIFY, config); + + mWifiSettings2.onSubmit(dialog); + + verify(mWifiManager).save(eq(config), any()); + } + + @Test + public void onSubmit_modeConnectNoConfig_connectWifiEntry() { + WifiDialog2 dialog = createWifiDialog2(MODE_CONNECT, null /* config */); + final WifiEntry wifiEntry = dialog.getWifiEntry(); + + mWifiSettings2.onAttach(mContext); + mWifiSettings2.onSubmit(dialog); + + verify(mWifiSettings2).connect(wifiEntry, false /* editIfNoConfig */, + false /* fullScreenEdit*/); + } + + @Test + public void onSubmit_modeConnectHasConfig_connectWifiManager() { + final WifiConfiguration config = mock(WifiConfiguration.class); + WifiDialog2 dialog = createWifiDialog2(MODE_CONNECT, config); + + mWifiSettings2.onSubmit(dialog); + + verify(mWifiManager).connect(eq(config), any(WifiManager.ActionListener.class)); + } + + private WifiDialog2 createWifiDialog2(int mode, WifiConfiguration config) { + final WifiEntry wifiEntry = mock(WifiEntry.class); + when(wifiEntry.canConnect()).thenReturn(true); + final WifiConfigController2 controller = mock(WifiConfigController2.class); + when(controller.getConfig()).thenReturn(config); + final WifiDialog2 wifiDialog2 = spy(WifiDialog2.createModal(mContext, null /* listener */, + wifiEntry, mode)); + when(wifiDialog2.getController()).thenReturn(controller); + return wifiDialog2; + } } From a76669656f5adc598f56887261d7bc22095b45b4 Mon Sep 17 00:00:00 2001 From: Arc Wang Date: Tue, 31 Mar 2020 11:49:07 +0800 Subject: [PATCH 08/12] [Wi-Fi] Wi-Fi picker scrolls to top after a user clicked to connect After a connect success callback, Wi-Fi picker scrolls to the connected Wi-Fi network after it's added in. Bug: 152576795 Test: make RunSettingsRoboTests ROBOTEST_FILTER=WifiSettings2 manual Click a Wi-Fi network or it's context menu 'Connect' button to connect and see if UI scroll to the top. Change-Id: Ia96af86c9ed552324089f97db104975e28f6c1c7 --- src/com/android/settings/wifi/WifiSettings2.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/wifi/WifiSettings2.java b/src/com/android/settings/wifi/WifiSettings2.java index 51bdcede318..f73891dfb53 100644 --- a/src/com/android/settings/wifi/WifiSettings2.java +++ b/src/com/android/settings/wifi/WifiSettings2.java @@ -647,6 +647,7 @@ public class WifiSettings2 extends RestrictedSettingsFragment setOffMessage(); setAdditionalSettingsSummaries(); setProgressBarVisible(false); + mClickedConnect = false; break; } } @@ -739,6 +740,11 @@ public class WifiSettings2 extends RestrictedSettingsFragment pref.setOnGearClickListener(preference -> { launchNetworkDetailsFragment(pref); }); + + if (mClickedConnect) { + mClickedConnect = false; + scrollToPreference(mConnectedWifiEntryPreferenceCategory); + } } } else { mConnectedWifiEntryPreferenceCategory.removeAll(); @@ -1007,7 +1013,7 @@ public class WifiSettings2 extends RestrictedSettingsFragment private class WifiConnectActionListener implements WifiManager.ActionListener { @Override public void onSuccess() { - // Do nothing. + mClickedConnect = true; } @Override @@ -1041,7 +1047,9 @@ public class WifiSettings2 extends RestrictedSettingsFragment return; } - if (status == ConnectCallback.CONNECT_STATUS_FAILURE_NO_CONFIG) { + if (status == ConnectCallback.CONNECT_STATUS_SUCCESS) { + mClickedConnect = true; + } else if (status == ConnectCallback.CONNECT_STATUS_FAILURE_NO_CONFIG) { if (mEditIfNoConfig) { // Edit an unsaved secure Wi-Fi network. if (mFullScreenEdit) { From c364f5d95d343efa98cad809268ec119b13b9a1d Mon Sep 17 00:00:00 2001 From: Alex Kershaw Date: Tue, 31 Mar 2020 11:37:53 +0100 Subject: [PATCH 09/12] Remove policy transparency for cross-profile calendar setting We will likely deprecate this setting in the next release, to be replaced by usage of 'connected work and personal apps'. However, we are keeping it in this release as it's still possible that a lightweight calendar access API could be useful, if the admin and user approvals are combined. For now, the error message that refers to cross-profile calendar when the setting is disabled is confusing admins, since we're not recommending usage of this API at this time. Remove the setting when it is disabled, rather than showing policy transparency. Fixes: 143477431 Test: manual Change-Id: I65573a5ba892e12a4dabcdf23541f33516db0a8a --- res/values/strings.xml | 3 - res/xml/managed_profile_settings.xml | 8 -- ...eCalendarDisabledPreferenceController.java | 47 --------- .../accounts/ManagedProfileSettings.java | 1 - ...endarDisabledPreferenceControllerTest.java | 99 ------------------- tests/uitests/assets/search_results_list | 1 - 6 files changed, 159 deletions(-) delete mode 100644 src/com/android/settings/accounts/CrossProfileCalendarDisabledPreferenceController.java delete mode 100644 tests/robotests/src/com/android/settings/accounts/CrossProfileCalendarDisabledPreferenceControllerTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index fd869d425e4..b4bca607954 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -10545,9 +10545,6 @@ Cross-profile calendar Show work events on your personal calendar - - Your organization doesn\u2019t allow personal apps to access your work calendar - diff --git a/res/xml/managed_profile_settings.xml b/res/xml/managed_profile_settings.xml index 7b65a3d30b0..20f6d3dc3f3 100644 --- a/res/xml/managed_profile_settings.xml +++ b/res/xml/managed_profile_settings.xml @@ -32,18 +32,10 @@ settings:useAdditionalSummary="true" settings:controller="com.android.settings.accounts.ContactSearchPreferenceController"/> - - \ No newline at end of file diff --git a/src/com/android/settings/accounts/CrossProfileCalendarDisabledPreferenceController.java b/src/com/android/settings/accounts/CrossProfileCalendarDisabledPreferenceController.java deleted file mode 100644 index c0879823792..00000000000 --- a/src/com/android/settings/accounts/CrossProfileCalendarDisabledPreferenceController.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2019 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.settings.accounts; -import static com.android.settings.accounts.CrossProfileCalendarPreferenceController.isCrossProfileCalendarDisallowedByAdmin; - -import android.content.Context; -import android.os.UserHandle; - -import com.android.settings.core.BasePreferenceController; - -public class CrossProfileCalendarDisabledPreferenceController extends BasePreferenceController { - private UserHandle mManagedUser; - - public void setManagedUser(UserHandle managedUser) { - mManagedUser = managedUser; - } - - public CrossProfileCalendarDisabledPreferenceController(Context context, - String preferenceKey) { - super(context, preferenceKey); - } - - @Override - public int getAvailabilityStatus() { - if (mManagedUser != null - && isCrossProfileCalendarDisallowedByAdmin( - mContext, mManagedUser.getIdentifier())) { - return AVAILABLE; - } - - return DISABLED_FOR_USER; - } -} diff --git a/src/com/android/settings/accounts/ManagedProfileSettings.java b/src/com/android/settings/accounts/ManagedProfileSettings.java index 6591be488a3..f16bc3f49b6 100644 --- a/src/com/android/settings/accounts/ManagedProfileSettings.java +++ b/src/com/android/settings/accounts/ManagedProfileSettings.java @@ -79,7 +79,6 @@ public class ManagedProfileSettings extends DashboardFragment { use(WorkModePreferenceController.class).setManagedUser(mManagedUser); use(ContactSearchPreferenceController.class).setManagedUser(mManagedUser); use(CrossProfileCalendarPreferenceController.class).setManagedUser(mManagedUser); - use(CrossProfileCalendarDisabledPreferenceController.class).setManagedUser(mManagedUser); } @Override diff --git a/tests/robotests/src/com/android/settings/accounts/CrossProfileCalendarDisabledPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/CrossProfileCalendarDisabledPreferenceControllerTest.java deleted file mode 100644 index 2226e2c4e1d..00000000000 --- a/tests/robotests/src/com/android/settings/accounts/CrossProfileCalendarDisabledPreferenceControllerTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2018 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.settings.accounts; - -import static com.android.settings.core.BasePreferenceController.AVAILABLE; -import static com.android.settings.core.BasePreferenceController.DISABLED_FOR_USER; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.robolectric.RuntimeEnvironment.application; - -import android.app.admin.DevicePolicyManager; -import android.content.ComponentName; -import android.content.Context; -import android.os.UserHandle; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.Shadows; -import org.robolectric.shadows.ShadowDevicePolicyManager; - -import java.util.Collections; - -@RunWith(RobolectricTestRunner.class) -public class CrossProfileCalendarDisabledPreferenceControllerTest { - - private static final String PREF_KEY = "cross_profile_calendar_disabled"; - private static final int MANAGED_USER_ID = 10; - private static final String TEST_PACKAGE_NAME = "com.test"; - private static final ComponentName TEST_COMPONENT_NAME = new ComponentName("test", "test"); - - @Mock - private UserHandle mManagedUser; - - private Context mContext; - private CrossProfileCalendarDisabledPreferenceController mController; - private ShadowDevicePolicyManager mDpm; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - mContext = spy(RuntimeEnvironment.application); - mController = new CrossProfileCalendarDisabledPreferenceController(mContext, PREF_KEY); - mController.setManagedUser(mManagedUser); - mDpm = Shadows.shadowOf(application.getSystemService(DevicePolicyManager.class)); - - when(mManagedUser.getIdentifier()).thenReturn(MANAGED_USER_ID); - doReturn(mContext).when(mContext).createPackageContextAsUser( - any(String.class), anyInt(), any(UserHandle.class)); - } - - @Test - public void getAvailabilityStatus_noPackageAllowed_shouldBeAvailable() { - mDpm.setProfileOwner(TEST_COMPONENT_NAME); - - assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); - } - - @Test - public void getAvailabilityStatus_somePackagesAllowed_shouldBeDisabledForUser() { - mDpm.setProfileOwner(TEST_COMPONENT_NAME); - mDpm.setCrossProfileCalendarPackages(TEST_COMPONENT_NAME, - Collections.singleton(TEST_PACKAGE_NAME)); - - assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_USER); - } - - @Test - public void getAvailabilityStatus_allPackagesAllowed_shouldBeDisabledForUser() { - mDpm.setProfileOwner(TEST_COMPONENT_NAME); - mDpm.setCrossProfileCalendarPackages(TEST_COMPONENT_NAME, null); - - assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_USER); - } -} \ No newline at end of file diff --git a/tests/uitests/assets/search_results_list b/tests/uitests/assets/search_results_list index a6dafe2e98e..f8694636767 100644 --- a/tests/uitests/assets/search_results_list +++ b/tests/uitests/assets/search_results_list @@ -150,7 +150,6 @@ Cool color temperature;color_temperature Copyright;copyright Correction mode;type Cross-profile calendar;cross_profile_calendar -Cross-profile calendar;cross_profile_calendar_disabled Current screen saver;current_screensaver Custom;zen_custom Custom restrictions;zen_mode_block_settings_page From 82e7bc46cfbb7d7ae148495aa423d14c5b021899 Mon Sep 17 00:00:00 2001 From: Beverly Date: Tue, 31 Mar 2020 11:09:50 -0400 Subject: [PATCH 10/12] Use DEFAULT_LIST contact intent Test: manual Fixes: 152733367 Change-Id: Ib2addc353298e593160500347d0a9bbfa85d1e0b --- .../zen/ZenModePrioritySendersPreferenceController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/settings/notification/zen/ZenModePrioritySendersPreferenceController.java b/src/com/android/settings/notification/zen/ZenModePrioritySendersPreferenceController.java index da7b24d7d33..226ce155af3 100644 --- a/src/com/android/settings/notification/zen/ZenModePrioritySendersPreferenceController.java +++ b/src/com/android/settings/notification/zen/ZenModePrioritySendersPreferenceController.java @@ -58,7 +58,7 @@ public class ZenModePrioritySendersPreferenceController @VisibleForTesting static final String KEY_NONE = "senders_none"; private static final Intent ALL_CONTACTS_INTENT = - new Intent(Contacts.Intents.UI.LIST_ALL_CONTACTS_ACTION); + new Intent(Contacts.Intents.UI.LIST_DEFAULT); private static final Intent STARRED_CONTACTS_INTENT = new Intent(Contacts.Intents.UI.LIST_STARRED_ACTION); private static final Intent FALLBACK_INTENT = new Intent(Intent.ACTION_MAIN); From 8a575029fc2329610ba19986e2f32e24f4752b3e Mon Sep 17 00:00:00 2001 From: Steve Elliott Date: Wed, 5 Feb 2020 14:39:55 -0500 Subject: [PATCH 11/12] Disable conversation strip setting. Test: manual, robotests Fixes: 149070144 Fixes: 149001624 Change-Id: I422872e38f7adead81938c39b6084bbc7ed911ae --- res/xml/configure_notification_settings.xml | 5 - ...cationPeopleStripPreferenceController.java | 117 ----------------- ...onPeopleStripPreferenceControllerTest.java | 124 ------------------ 3 files changed, 246 deletions(-) delete mode 100644 src/com/android/settings/notification/NotificationPeopleStripPreferenceController.java delete mode 100644 tests/robotests/src/com/android/settings/notification/NotificationPeopleStripPreferenceControllerTest.java diff --git a/res/xml/configure_notification_settings.xml b/res/xml/configure_notification_settings.xml index 4078e824819..3dcddc8eb19 100644 --- a/res/xml/configure_notification_settings.xml +++ b/res/xml/configure_notification_settings.xml @@ -140,11 +140,6 @@ android:ringtoneType="notification" settings:searchable="false"/> - - contentResolver.unregisterContentObserver(observer); - contentResolver.registerContentObserver(mPeopleStripUri, false, observer); - } - - @Override - public void onPause() { - if (mUnregisterOnPropertiesChangedListener != null) { - mUnregisterOnPropertiesChangedListener.run(); - mUnregisterOnPropertiesChangedListener = null; - } - } - - @Override - public int getAvailabilityStatus() { - return AVAILABLE; - } - - @Override - public boolean isSliceable() { - return false; - } - - @Override - public boolean isChecked() { - int value = Settings.Secure.getInt( - mContext.getContentResolver(), - Settings.Secure.PEOPLE_STRIP, - OFF); - return value != OFF; - } - - @Override - public boolean setChecked(boolean isChecked) { - return Settings.Secure.putInt( - mContext.getContentResolver(), - Settings.Secure.PEOPLE_STRIP, - isChecked ? ON : OFF); - } -} diff --git a/tests/robotests/src/com/android/settings/notification/NotificationPeopleStripPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/NotificationPeopleStripPreferenceControllerTest.java deleted file mode 100644 index 786ffb37ae7..00000000000 --- a/tests/robotests/src/com/android/settings/notification/NotificationPeopleStripPreferenceControllerTest.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2019 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.settings.notification; - -import static android.provider.Settings.Secure.PEOPLE_STRIP; - -import static com.android.settings.notification.NotificationPeopleStripPreferenceController.OFF; -import static com.android.settings.notification.NotificationPeopleStripPreferenceController.ON; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.provider.Settings; - -import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; -import androidx.preference.TwoStatePreference; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Answers; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -@RunWith(RobolectricTestRunner.class) -public class NotificationPeopleStripPreferenceControllerTest { - - private static final String KEY_PEOPLE_STRIP = "notification_people_strip"; - - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private Context mContext; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private PreferenceScreen mScreen; - - private NotificationPeopleStripPreferenceController mController; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - mController = new NotificationPeopleStripPreferenceController(mContext, KEY_PEOPLE_STRIP); - Preference preference = new Preference(RuntimeEnvironment.application); - preference.setKey(mController.getPreferenceKey()); - when(mScreen.findPreference(preference.getKey())).thenReturn(preference); - } - - @Test - public void updateState_preferenceSetCheckedWhenSettingIsOn() { - final TwoStatePreference preference = mock(TwoStatePreference.class); - final Context context = RuntimeEnvironment.application; - Settings.Secure.putInt(context.getContentResolver(), PEOPLE_STRIP, ON); - - mController = new NotificationPeopleStripPreferenceController(context, KEY_PEOPLE_STRIP); - mController.updateState(preference); - - verify(preference).setChecked(true); - } - - @Test - public void updateState_preferenceSetUncheckedWhenSettingIsOff() { - final TwoStatePreference preference = mock(TwoStatePreference.class); - final Context context = RuntimeEnvironment.application; - Settings.Secure.putInt(context.getContentResolver(), PEOPLE_STRIP, OFF); - - mController = new NotificationPeopleStripPreferenceController(context, KEY_PEOPLE_STRIP); - mController.updateState(preference); - - verify(preference).setChecked(false); - } - - @Test - public void isChecked_settingIsOff_shouldReturnFalse() { - Settings.Secure.putInt(mContext.getContentResolver(), PEOPLE_STRIP, OFF); - - assertThat(mController.isChecked()).isFalse(); - } - - @Test - public void isChecked_settingIsOn_shouldReturnTrue() { - Settings.Secure.putInt(mContext.getContentResolver(), PEOPLE_STRIP, ON); - - assertThat(mController.isChecked()).isTrue(); - } - - @Test - public void setChecked_setFalse_disablesSetting() { - Settings.Secure.putInt(mContext.getContentResolver(), PEOPLE_STRIP, ON); - - mController.setChecked(false); - int updatedValue = Settings.Secure.getInt(mContext.getContentResolver(), PEOPLE_STRIP, -1); - - assertThat(updatedValue).isEqualTo(OFF); - } - - @Test - public void setChecked_setTrue_enablesSetting() { - Settings.Secure.putInt(mContext.getContentResolver(), PEOPLE_STRIP, OFF); - - mController.setChecked(true); - int updatedValue = Settings.Secure.getInt(mContext.getContentResolver(), PEOPLE_STRIP, -1); - - assertThat(updatedValue).isEqualTo(ON); - } -} From 5a4fb7986f0a2272d0247cdc49d0359f7d420dc9 Mon Sep 17 00:00:00 2001 From: Beverly Date: Tue, 31 Mar 2020 13:29:35 -0400 Subject: [PATCH 12/12] Add dnd messages + calls senders image resource Add a visual representation of the call or message senders that can bypass DND. Test: manual Bug: 151845457 Change-Id: Ie7d3598d5993e4c1cd62294ab9e17da47f2b6f76 --- res/drawable/zen_calls_any.xml | 54 +++++++++ res/drawable/zen_calls_contacts.xml | 81 +++++++++++++ res/drawable/zen_calls_none.xml | 39 +++++++ res/drawable/zen_calls_starred.xml | 69 +++++++++++ res/drawable/zen_messages_any.xml | 45 ++++++++ res/drawable/zen_messages_contacts.xml | 72 ++++++++++++ res/drawable/zen_messages_none.xml | 30 +++++ res/drawable/zen_messages_starred.xml | 60 ++++++++++ res/layout/zen_mode_senders_image.xml | 25 ++++ res/xml/zen_mode_calls_settings.xml | 5 + res/xml/zen_mode_messages_settings.xml | 5 + .../zen/ZenModeCallsSettings.java | 2 + .../zen/ZenModeMessagesSettings.java | 2 + ...nModeSendersImagePreferenceController.java | 108 ++++++++++++++++++ 14 files changed, 597 insertions(+) create mode 100644 res/drawable/zen_calls_any.xml create mode 100644 res/drawable/zen_calls_contacts.xml create mode 100644 res/drawable/zen_calls_none.xml create mode 100644 res/drawable/zen_calls_starred.xml create mode 100644 res/drawable/zen_messages_any.xml create mode 100644 res/drawable/zen_messages_contacts.xml create mode 100644 res/drawable/zen_messages_none.xml create mode 100644 res/drawable/zen_messages_starred.xml create mode 100644 res/layout/zen_mode_senders_image.xml create mode 100644 src/com/android/settings/notification/zen/ZenModeSendersImagePreferenceController.java diff --git a/res/drawable/zen_calls_any.xml b/res/drawable/zen_calls_any.xml new file mode 100644 index 00000000000..546fd93395a --- /dev/null +++ b/res/drawable/zen_calls_any.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + diff --git a/res/drawable/zen_calls_contacts.xml b/res/drawable/zen_calls_contacts.xml new file mode 100644 index 00000000000..a4c064a76e5 --- /dev/null +++ b/res/drawable/zen_calls_contacts.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/drawable/zen_calls_none.xml b/res/drawable/zen_calls_none.xml new file mode 100644 index 00000000000..e89da49d7b6 --- /dev/null +++ b/res/drawable/zen_calls_none.xml @@ -0,0 +1,39 @@ + + + + + + + + + diff --git a/res/drawable/zen_calls_starred.xml b/res/drawable/zen_calls_starred.xml new file mode 100644 index 00000000000..25e9a6193eb --- /dev/null +++ b/res/drawable/zen_calls_starred.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + diff --git a/res/drawable/zen_messages_any.xml b/res/drawable/zen_messages_any.xml new file mode 100644 index 00000000000..edeaea2726c --- /dev/null +++ b/res/drawable/zen_messages_any.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + diff --git a/res/drawable/zen_messages_contacts.xml b/res/drawable/zen_messages_contacts.xml new file mode 100644 index 00000000000..8da80b94a0b --- /dev/null +++ b/res/drawable/zen_messages_contacts.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/res/drawable/zen_messages_none.xml b/res/drawable/zen_messages_none.xml new file mode 100644 index 00000000000..c72cc4f133c --- /dev/null +++ b/res/drawable/zen_messages_none.xml @@ -0,0 +1,30 @@ + + + + + + diff --git a/res/drawable/zen_messages_starred.xml b/res/drawable/zen_messages_starred.xml new file mode 100644 index 00000000000..75feb80d711 --- /dev/null +++ b/res/drawable/zen_messages_starred.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + diff --git a/res/layout/zen_mode_senders_image.xml b/res/layout/zen_mode_senders_image.xml new file mode 100644 index 00000000000..bc7cdae2627 --- /dev/null +++ b/res/layout/zen_mode_senders_image.xml @@ -0,0 +1,25 @@ + + + + + + + diff --git a/res/xml/zen_mode_calls_settings.xml b/res/xml/zen_mode_calls_settings.xml index cbadc7d7ce4..0b44875a3ec 100644 --- a/res/xml/zen_mode_calls_settings.xml +++ b/res/xml/zen_mode_calls_settings.xml @@ -25,6 +25,11 @@ android:key="zen_mode_settings_category_calls" android:title="@string/zen_mode_calls_header" settings:allowDividerBelow="true"> + + + diff --git a/res/xml/zen_mode_messages_settings.xml b/res/xml/zen_mode_messages_settings.xml index c302b02b72e..31cad2a5347 100644 --- a/res/xml/zen_mode_messages_settings.xml +++ b/res/xml/zen_mode_messages_settings.xml @@ -23,6 +23,11 @@ + + + diff --git a/src/com/android/settings/notification/zen/ZenModeCallsSettings.java b/src/com/android/settings/notification/zen/ZenModeCallsSettings.java index 1b5412e0ec8..82d5cf62765 100644 --- a/src/com/android/settings/notification/zen/ZenModeCallsSettings.java +++ b/src/com/android/settings/notification/zen/ZenModeCallsSettings.java @@ -45,6 +45,8 @@ public class ZenModeCallsSettings extends ZenModeSettingsBase { List controllers = new ArrayList<>(); controllers.add(new ZenModePrioritySendersPreferenceController(context, "zen_mode_settings_category_calls", lifecycle, false)); + controllers.add(new ZenModeSendersImagePreferenceController(context, + "zen_mode_calls_image", lifecycle, false)); controllers.add(new ZenModeRepeatCallersPreferenceController(context, lifecycle, context.getResources().getInteger(com.android.internal.R.integer .config_zen_repeat_callers_threshold))); diff --git a/src/com/android/settings/notification/zen/ZenModeMessagesSettings.java b/src/com/android/settings/notification/zen/ZenModeMessagesSettings.java index d15a4aa428b..f8e4548f53f 100644 --- a/src/com/android/settings/notification/zen/ZenModeMessagesSettings.java +++ b/src/com/android/settings/notification/zen/ZenModeMessagesSettings.java @@ -44,6 +44,8 @@ public class ZenModeMessagesSettings extends ZenModeSettingsBase { private static List buildPreferenceControllers(Context context, Lifecycle lifecycle) { List controllers = new ArrayList<>(); + controllers.add(new ZenModeSendersImagePreferenceController(context, + "zen_mode_messages_image", lifecycle, true)); controllers.add(new ZenModePrioritySendersPreferenceController(context, "zen_mode_settings_category_messages", lifecycle, true)); controllers.add(new ZenModeBehaviorFooterPreferenceController( diff --git a/src/com/android/settings/notification/zen/ZenModeSendersImagePreferenceController.java b/src/com/android/settings/notification/zen/ZenModeSendersImagePreferenceController.java new file mode 100644 index 00000000000..e8cd40db63a --- /dev/null +++ b/src/com/android/settings/notification/zen/ZenModeSendersImagePreferenceController.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2020 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.settings.notification.zen; + +import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY; +import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS; +import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_STARRED; + +import android.content.Context; +import android.widget.ImageView; + +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.widget.LayoutPreference; + +/** + * Common preference controller functionality shared by + * ZenModeCallsSettings and ZenModeMessagesSettings. + * + * Changes the image resource based on the selected senders allowed to bypass DND option for + * calls or messages. + */ +public class ZenModeSendersImagePreferenceController + extends AbstractZenModePreferenceController { + + private final boolean mIsMessages; // if this is false, then this preference is for calls + + private ImageView mImageView; + + public ZenModeSendersImagePreferenceController(Context context, String key, + Lifecycle lifecycle, boolean isMessages) { + super(context, key, lifecycle); + mIsMessages = isMessages; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + LayoutPreference pref = (LayoutPreference) screen.findPreference(KEY); + mImageView = (ImageView) pref.findViewById(R.id.zen_mode_settings_senders_image); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return KEY; + } + + @Override + public void updateState(Preference preference) { + final int currSetting = getPrioritySenders(); + int newImageRes; + CharSequence newContentDescription = ""; + if (PRIORITY_SENDERS_ANY == currSetting) { + newImageRes = mIsMessages + ? R.drawable.zen_messages_any + : R.drawable.zen_calls_any; + newContentDescription = mContext.getString(R.string.zen_mode_from_anyone); + } else if (PRIORITY_SENDERS_CONTACTS == currSetting) { + newImageRes = mIsMessages + ? R.drawable.zen_messages_contacts + : R.drawable.zen_calls_contacts; + newContentDescription = mContext.getString(R.string.zen_mode_from_contacts); + } else if (PRIORITY_SENDERS_STARRED == currSetting) { + newImageRes = mIsMessages + ? R.drawable.zen_messages_starred + : R.drawable.zen_calls_starred; + newContentDescription = mContext.getString(R.string.zen_mode_from_starred); + } else { + newImageRes = mIsMessages + ? R.drawable.zen_messages_none + : R.drawable.zen_calls_none; + newContentDescription = mContext.getString(R.string.zen_mode_from_none); + } + + mImageView.setImageResource(newImageRes); + mImageView.setContentDescription(newContentDescription); + } + + private int getPrioritySenders() { + if (mIsMessages) { + return mBackend.getPriorityMessageSenders(); + } else { + return mBackend.getPriorityCallSenders(); + } + } +}