diff --git a/AndroidManifest.xml b/AndroidManifest.xml index eed87ed49ef..0fb6dc81334 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3051,11 +3051,12 @@ android:exported="false" android:authorities="${applicationId}.battery.usage.bugreport"/> - + diff --git a/res/drawable-sw600dp/ic_settings_about_device.xml b/res/drawable-sw600dp/ic_settings_about_device.xml new file mode 100644 index 00000000000..a7164823762 --- /dev/null +++ b/res/drawable-sw600dp/ic_settings_about_device.xml @@ -0,0 +1,25 @@ + + + + diff --git a/res/drawable/ic_homepage_about.xml b/res/drawable/ic_homepage_about.xml index ef2136eed8f..459ec934265 100644 --- a/res/drawable/ic_homepage_about.xml +++ b/res/drawable/ic_homepage_about.xml @@ -29,5 +29,5 @@ android:height="@dimen/dashboard_tile_foreground_image_size" android:start="@dimen/dashboard_tile_foreground_image_inset" android:top="@dimen/dashboard_tile_foreground_image_inset" - android:drawable="@drawable/ic_phone_info" /> + android:drawable="@drawable/ic_settings_about_device" /> diff --git a/res/drawable/ic_phone_info.xml b/res/drawable/ic_settings_about_device.xml similarity index 100% rename from res/drawable/ic_phone_info.xml rename to res/drawable/ic_settings_about_device.xml diff --git a/res/layout/locale_drag_cell.xml b/res/layout/locale_drag_cell.xml index 7b932f39a02..47bf70a473e 100644 --- a/res/layout/locale_drag_cell.xml +++ b/res/layout/locale_drag_cell.xml @@ -56,6 +56,17 @@ android:layout_toStartOf="@+id/dragHandle" android:layout_below="@id/label"/> + + + android:clipChildren="false" + android:layout_marginTop="@dimen/sfps_enroll_find_sensor_lottie_margin"> "나쁨" "매우 약함" - "양호" + "약함" "좋음" "우수함" diff --git a/res/values/dimens.xml b/res/values/dimens.xml index b086c1c928c..33f35a8d10d 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -407,4 +407,7 @@ 0dp 0dp 0dp + + + 0dp diff --git a/res/values/strings.xml b/res/values/strings.xml index c861ed9c3c8..0dc78dc3d1f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -312,6 +312,12 @@ Languages + + Preferred language order + + + System language + Remove @@ -325,13 +331,13 @@ Preferred Language - App Languages + App languages Set the language for each app - App Language + App language Suggested languages @@ -354,6 +360,18 @@ Only apps that support language selection are shown here. + + Your system, apps, and websites use the first supported language from your preferred languages. + + + To select a language for each app, go to app language settings. + + + Learn more about languages + + + https://support.google.com/android?p=per_language_app_settings + Regional preferences @@ -407,7 +425,7 @@ Keep at least one preferred language - May not be available in some apps + Not available as system language Move up @@ -808,6 +826,12 @@ How to set up your fingerprint It\u2019s on the back of your phone. Use your index finger. + + The fingerprint sensor is on the power button. It’s the flat button next to the raised volume button on the edge of the tablet. + + The fingerprint sensor is on the power button. It’s the flat button next to the raised volume button on the edge of the device. + + The fingerprint sensor is on the power button. It’s the flat button next to the raised volume button on the edge of the phone. The fingerprint sensor is on your screen. You\u2019ll capture your fingerprint on the next screen. @@ -2398,6 +2422,13 @@ %1$s is the only SIM in your device. Do you want to use this SIM for mobile data, calls, and SMS messages? + + Switch SIMs automatically? + + Allow your phone to automatically switch to %1$s for mobile data when it has better availability. + + \n\nCalls, messages, and network traffic may be visible to your organization. + Incorrect SIM PIN code you must now contact your carrier to unlock your device. diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml index 2bc1b87a50c..82d7e43e5d1 100644 --- a/res/xml/development_settings.xml +++ b/res/xml/development_settings.xml @@ -748,6 +748,7 @@ diff --git a/res/xml/languages.xml b/res/xml/languages.xml new file mode 100644 index 00000000000..0f455407645 --- /dev/null +++ b/res/xml/languages.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + diff --git a/res/xml/network_provider_internet.xml b/res/xml/network_provider_internet.xml index bbe5ace96df..ef6ed16f73f 100644 --- a/res/xml/network_provider_internet.xml +++ b/res/xml/network_provider_internet.xml @@ -52,7 +52,8 @@ settings:userRestriction="no_config_mobile_networks" settings:isPreferenceVisible="@bool/config_show_sim_info" settings:allowDividerAbove="true" - settings:useAdminDisabledSummary="true" /> + settings:useAdminDisabledSummary="true" + settings:searchable="@bool/config_show_sim_info"/> profileList = userManager.getProfiles(UserHandle.getCallingUserId()); + final List profileList = userManager.getProfiles(currentUser); // Only add non-Shell app as normal preference final List nonShellPackageList = bugreportAllowlistedPackages.stream() .filter(pkg -> !SHELL_APP_PACKAGE.equals(pkg)).collect(Collectors.toList()); @@ -228,11 +230,11 @@ public class BugReportHandlerUtil { private void setBugreportHandlerAppAndUser(Context context, String handlerApp, int handlerUser) { - Settings.Global.putString(context.getContentResolver(), - Settings.Global.CUSTOM_BUGREPORT_HANDLER_APP, + Settings.Secure.putString(context.getContentResolver(), + Settings.Secure.CUSTOM_BUGREPORT_HANDLER_APP, handlerApp); - Settings.Global.putInt(context.getContentResolver(), - Settings.Global.CUSTOM_BUGREPORT_HANDLER_USER, handlerUser); + Settings.Secure.putInt(context.getContentResolver(), + Settings.Secure.CUSTOM_BUGREPORT_HANDLER_USER, handlerUser); } /** diff --git a/src/com/android/settings/development/BugReportInPowerPreferenceController.java b/src/com/android/settings/development/BugReportInPowerPreferenceController.java index 99ced773255..5ad63e0ac96 100644 --- a/src/com/android/settings/development/BugReportInPowerPreferenceController.java +++ b/src/com/android/settings/development/BugReportInPowerPreferenceController.java @@ -59,7 +59,7 @@ public class BugReportInPowerPreferenceController extends public boolean onPreferenceChange(Preference preference, Object newValue) { final boolean isEnabled = (Boolean) newValue; Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Global.BUGREPORT_IN_POWER_MENU, + Settings.Secure.BUGREPORT_IN_POWER_MENU, isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF); return true; } @@ -67,7 +67,7 @@ public class BugReportInPowerPreferenceController extends @Override public void updateState(Preference preference) { final int mode = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Global.BUGREPORT_IN_POWER_MENU, SETTING_VALUE_OFF); + Settings.Secure.BUGREPORT_IN_POWER_MENU, SETTING_VALUE_OFF); ((SwitchPreference) mPreference).setChecked(mode != SETTING_VALUE_OFF); } @@ -75,7 +75,7 @@ public class BugReportInPowerPreferenceController extends protected void onDeveloperOptionsSwitchDisabled() { super.onDeveloperOptionsSwitchDisabled(); Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Global.BUGREPORT_IN_POWER_MENU, SETTING_VALUE_OFF); + Settings.Secure.BUGREPORT_IN_POWER_MENU, SETTING_VALUE_OFF); ((SwitchPreference) mPreference).setChecked(false); } } diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java index c151517d6b4..66e09a9cc3b 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java +++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java @@ -133,10 +133,15 @@ public interface PowerUsageFeatureProvider { boolean delayHourlyJobWhenBooting(); /** - * Gets a intent for one time bypass charge limited to resume charging. + * Gets an intent for one time bypass charge limited to resume charging. */ Intent getResumeChargeIntent(boolean isDockDefender); + /** + * Returns the intent action used to mark as the full charge start event. + */ + String getFullChargeIntentAction(); + /** * Returns {@link Set} for the system component ids which are combined into others */ diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java index 657e5c6018b..e0af88a694e 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java +++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java @@ -147,6 +147,11 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider return null; } + @Override + public String getFullChargeIntentAction() { + return Intent.ACTION_BATTERY_LEVEL_CHANGED; + } + @Override public boolean isExtraDefend() { return false; diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java index 817d367ca11..9c7ec35fb4a 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiver.java @@ -26,6 +26,7 @@ import android.util.Log; import androidx.annotation.VisibleForTesting; import com.android.settings.fuelgauge.BatteryUtils; +import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.fuelgauge.BatteryStatus; import java.time.Duration; @@ -36,6 +37,9 @@ public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver { /** An intent action to request Settings to clear cache data. */ public static final String ACTION_CLEAR_BATTERY_CACHE_DATA = "com.android.settings.battery.action.CLEAR_BATTERY_CACHE_DATA"; + /** An intent action to request Settings to clear cache data. */ + public static final String ACTION_BATTERY_UNPLUGGING = + "com.android.settings.battery.action.ACTION_BATTERY_UNPLUGGING"; @VisibleForTesting static long sBroadcastDelayFromBoot = Duration.ofMinutes(40).toMillis(); @@ -51,9 +55,27 @@ public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver { return; } Log.d(TAG, "onReceive:" + intent.getAction()); + final String fullChargeIntentAction = FeatureFactory.getFactory(context) + .getPowerUsageFeatureProvider(context) + .getFullChargeIntentAction(); switch (intent.getAction()) { case Intent.ACTION_BATTERY_LEVEL_CHANGED: - tryToFetchUsageData(context); + // Only when fullChargeIntentAction is ACTION_BATTERY_LEVEL_CHANGED, + // ACTION_BATTERY_LEVEL_CHANGED will be considered as the full charge event and then + // start usage events fetching. + if (Intent.ACTION_BATTERY_LEVEL_CHANGED.equals(fullChargeIntentAction)) { + Log.d(TAG, "fetch data because of event: ACTION_BATTERY_LEVEL_CHANGED"); + tryToFetchUsageData(context); + } + break; + case ACTION_BATTERY_UNPLUGGING: + // Only when fullChargeIntentAction is ACTION_POWER_DISCONNECTED, + // ACTION_BATTERY_UNPLUGGING will be considered as the full charge event and then + // start usage events fetching. + if (Intent.ACTION_POWER_DISCONNECTED.equals(fullChargeIntentAction)) { + Log.d(TAG, "fetch data because of event: ACTION_POWER_DISCONNECTED"); + tryToFetchUsageData(context); + } break; case ACTION_CLEAR_BATTERY_CACHE_DATA: if (sIsDebugMode) { @@ -74,7 +96,7 @@ public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver { final long broadcastDelay = sBroadcastDelayFromBoot - SystemClock.elapsedRealtime(); // If current boot time is smaller than expected delay, cancel sending the broadcast. if (broadcastDelay > 0) { - Log.d(TAG, "cancel sendBroadcastToFetchUsageData when broadcastDelay is" + Log.d(TAG, "cancel sendBroadcastToFetchUsageData when broadcastDelay is " + broadcastDelay + "ms."); return; } diff --git a/src/com/android/settings/fuelgauge/batteryusage/SpinnerPreference.java b/src/com/android/settings/fuelgauge/batteryusage/SpinnerPreference.java index eaac00a2588..800dc1426e6 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/SpinnerPreference.java +++ b/src/com/android/settings/fuelgauge/batteryusage/SpinnerPreference.java @@ -56,6 +56,10 @@ public class SpinnerPreference extends Preference { @Override public void onBindViewHolder(PreferenceViewHolder view) { + if (mSpinner != null) { + return; + } + mSpinner = (Spinner) view.findViewById(R.id.spinner); mSpinner.setAdapter(new SpinnerAdapter(getContext(), mItems)); mSpinner.setSelection(mSavedSpinnerPosition); diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java index ab9110d01ab..b3c2e3071af 100644 --- a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java +++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java @@ -43,7 +43,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; - class LocaleDragAndDropAdapter extends RecyclerView.Adapter { @@ -154,8 +153,10 @@ class LocaleDragAndDropAdapter final LocaleDragCell dragCell = holder.getLocaleDragCell(); final String label = feedItem.getFullNameNative(); final String description = feedItem.getFullNameInUiLanguage(); + dragCell.setLabelAndDescription(label, description); dragCell.setLocalized(feedItem.isTranslated()); + dragCell.setCurrentDefault(feedItem.getLocale().equals(Locale.getDefault())); dragCell.setMiniLabel(mNumberFormatter.format(i + 1)); dragCell.setShowCheckbox(mRemoveMode); dragCell.setShowMiniLabel(!mRemoveMode); diff --git a/src/com/android/settings/localepicker/LocaleDragCell.java b/src/com/android/settings/localepicker/LocaleDragCell.java index ea86189b018..2f4cfefefb1 100644 --- a/src/com/android/settings/localepicker/LocaleDragCell.java +++ b/src/com/android/settings/localepicker/LocaleDragCell.java @@ -33,6 +33,7 @@ class LocaleDragCell extends RelativeLayout { private CheckBox mCheckbox; private TextView mMiniLabel; private TextView mLocalized; + private TextView mCurrentDefault; private ImageView mDragHandle; public LocaleDragCell(Context context, AttributeSet attrs) { @@ -44,6 +45,7 @@ class LocaleDragCell extends RelativeLayout { super.onFinishInflate(); mLabel = (TextView) findViewById(R.id.label); mLocalized = (TextView) findViewById(R.id.l10nWarn); + mCurrentDefault = (TextView) findViewById(R.id.default_locale); mMiniLabel = (TextView) findViewById(R.id.miniLabel); mCheckbox = (CheckBox) findViewById(R.id.checkbox); mDragHandle = (ImageView) findViewById(R.id.dragHandle); @@ -100,6 +102,14 @@ class LocaleDragCell extends RelativeLayout { invalidate(); } + /** + * Indicate current locale is system default. + */ + public void setCurrentDefault(boolean isCurrentDefault) { + mCurrentDefault.setVisibility(isCurrentDefault ? VISIBLE : GONE); + invalidate(); + } + public ImageView getDragHandle() { return mDragHandle; } diff --git a/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java b/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java new file mode 100644 index 00000000000..05c740139cc --- /dev/null +++ b/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2022 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.localepicker; + +import android.content.Context; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settingslib.HelpUtils; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.widget.FooterPreference; + +/** + * A controller to update current locale information of application. + */ +public class LocaleHelperPreferenceController extends AbstractPreferenceController { + private static final String TAG = LocaleHelperPreferenceController.class.getSimpleName(); + + private static final String KEY_FOOTER_LANGUAGE_PICKER = "footer_languages_picker"; + + public LocaleHelperPreferenceController(Context context) { + super(context); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return KEY_FOOTER_LANGUAGE_PICKER; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + FooterPreference footerPreference = screen.findPreference(getPreferenceKey()); + updateFooterPreference(footerPreference); + } + + @VisibleForTesting + void updateFooterPreference(FooterPreference footerPreference) { + if (footerPreference != null) { + footerPreference.setLearnMoreAction(v -> openLocaleLearnMoreLink()); + footerPreference.setLearnMoreText(mContext.getString( + R.string.desc_locale_helper_footer_general)); + } + } + + private void openLocaleLearnMoreLink() { + mContext.startActivity( + HelpUtils.getHelpIntent( + mContext, + mContext.getString(R.string.link_locale_picker_footer_learn_more), + /*backupContext=*/"")); + } +} diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java index 1d6fb47d76a..75ea768e232 100644 --- a/src/com/android/settings/localepicker/LocaleListEditor.java +++ b/src/com/android/settings/localepicker/LocaleListEditor.java @@ -36,6 +36,7 @@ import android.widget.TextView; import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; +import androidx.preference.PreferenceScreen; import androidx.recyclerview.widget.RecyclerView; import com.android.internal.app.LocalePicker; @@ -47,6 +48,7 @@ import com.android.settings.search.BaseSearchIndexProvider; import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.search.SearchIndexableRaw; import com.android.settingslib.utils.StringUtil; +import com.android.settingslib.widget.LayoutPreference; import java.util.ArrayList; import java.util.List; @@ -65,6 +67,7 @@ public class LocaleListEditor extends RestrictedSettingsFragment { private static final int REQUEST_LOCALE_PICKER = 0; private static final String INDEX_KEY_ADD_LANGUAGE = "add_language"; + private static final String KEY_LANGUAGES_PICKER = "languages_picker"; private LocaleDragAndDropAdapter mAdapter; private Menu mMenu; @@ -73,6 +76,9 @@ public class LocaleListEditor extends RestrictedSettingsFragment { private boolean mShowingRemoveDialog; private boolean mIsUiRestricted; + private LayoutPreference mLocalePickerPreference; + private LocaleHelperPreferenceController mLocaleHelperPreferenceController; + public LocaleListEditor() { super(DISALLOW_CONFIG_LOCALE); } @@ -87,6 +93,14 @@ public class LocaleListEditor extends RestrictedSettingsFragment { super.onCreate(savedInstanceState); setHasOptionsMenu(true); + addPreferencesFromResource(R.xml.languages); + final Activity activity = getActivity(); + activity.setTitle(R.string.language_picker_title); + mLocaleHelperPreferenceController = new LocaleHelperPreferenceController(activity); + final PreferenceScreen screen = getPreferenceScreen(); + mLocalePickerPreference = screen.findPreference(KEY_LANGUAGES_PICKER); + mLocaleHelperPreferenceController.displayPreference(screen); + LocaleStore.fillCache(this.getContext()); final List feedsList = getUserLocaleList(); mAdapter = new LocaleDragAndDropAdapter(this.getContext(), feedsList); @@ -94,11 +108,8 @@ public class LocaleListEditor extends RestrictedSettingsFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstState) { - final View result = super.onCreateView(inflater, container, savedInstState); - final View myLayout = inflater.inflate(R.layout.locale_order_list, (ViewGroup) result); - - configureDragAndDrop(myLayout); - return result; + configureDragAndDrop(mLocalePickerPreference); + return super.onCreateView(inflater, container, savedInstState); } @Override @@ -288,8 +299,8 @@ public class LocaleListEditor extends RestrictedSettingsFragment { return result; } - private void configureDragAndDrop(View view) { - final RecyclerView list = view.findViewById(R.id.dragList); + private void configureDragAndDrop(LayoutPreference layout) { + final RecyclerView list = layout.findViewById(R.id.dragList); final LocaleLinearLayoutManager llm = new LocaleLinearLayoutManager(getContext(), mAdapter); llm.setAutoMeasureEnabled(true); list.setLayoutManager(llm); @@ -298,7 +309,7 @@ public class LocaleListEditor extends RestrictedSettingsFragment { mAdapter.setRecyclerView(list); list.setAdapter(mAdapter); - mAddLanguage = view.findViewById(R.id.add_language); + mAddLanguage = layout.findViewById(R.id.add_language); mAddLanguage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { diff --git a/src/com/android/settings/sim/EnableAutoDataSwitchDialogFragment.java b/src/com/android/settings/sim/EnableAutoDataSwitchDialogFragment.java new file mode 100644 index 00000000000..b1b5f8e69ed --- /dev/null +++ b/src/com/android/settings/sim/EnableAutoDataSwitchDialogFragment.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2022 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.sim; + +import android.app.Dialog; +import android.app.settings.SettingsEnums; +import android.content.DialogInterface; +import android.os.Bundle; +import android.os.UserHandle; +import android.os.UserManager; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.appcompat.app.AlertDialog; + +import com.android.settings.R; +import com.android.settings.network.SubscriptionUtil; + +import java.util.List; + +/** + * Show a dialog prompting the user to enable auto data switch following the dialog where user chose + * default data SIM. + */ +public class EnableAutoDataSwitchDialogFragment extends SimDialogFragment implements + DialogInterface.OnClickListener { + private static final String TAG = "EnableAutoDataSwitchDialogFragment"; + /** Sub Id of the non-default data SIM */ + private int mBackupDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + + /** @return a new instance of this fragment */ + public static EnableAutoDataSwitchDialogFragment newInstance() { + final EnableAutoDataSwitchDialogFragment fragment = + new EnableAutoDataSwitchDialogFragment(); + final Bundle args = initArguments(SimDialogActivity.ENABLE_AUTO_DATA_SWITCH, + R.string.enable_auto_data_switch_dialog_title); + fragment.setArguments(args); + return fragment; + } + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + final AlertDialog dialog = new AlertDialog.Builder(getContext()) + .setPositiveButton(R.string.yes, this) + .setNegativeButton(R.string.sim_action_no_thanks, null) + .create(); + updateDialog(dialog); + return dialog; + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.DIALOG_AUTO_DATA_SWITCH; + } + + /** update dialog */ + public void updateDialog(AlertDialog dialog) { + Log.d(TAG, "Dialog updated, dismiss status: " + mWasDismissed); + + if (mWasDismissed) { + return; + } + + if (dialog == null) { + Log.d(TAG, "Dialog is null."); + dismiss(); + return; + } + + // Set message + View content = LayoutInflater.from(getContext()).inflate( + R.layout.sim_confirm_dialog_multiple_enabled_profiles_supported, null); + TextView dialogMessage = content != null ? content.findViewById(R.id.msg) : null; + final String message = getMessage(); + if (TextUtils.isEmpty(message) || dialogMessage == null) { + onDismiss(dialog); + return; + } + dialogMessage.setText(message); + dialogMessage.setVisibility(View.VISIBLE); + dialog.setView(content); + + // Set title + View titleView = LayoutInflater.from(getContext()).inflate( + R.layout.sim_confirm_dialog_title_multiple_enabled_profiles_supported, null); + TextView titleTextView = titleView.findViewById(R.id.title); + titleTextView.setText(getContext().getString(getTitleResId())); + dialog.setCustomTitle(titleTextView); + } + + /** + * @return The message of the dialog. {@code null} if the dialog shouldn't be displayed. + */ + @VisibleForTesting + protected String getMessage() { + int ddsSubId = getDefaultDataSubId(); + if (ddsSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) return null; + Log.d(TAG, "DDS SubId: " + ddsSubId); + + SubscriptionManager subscriptionManager = getSubscriptionManager(); + List activeSubscriptions = subscriptionManager + .getActiveSubscriptionInfoList(); + if (activeSubscriptions == null) return null; + + // Find if a backup data sub exists. + SubscriptionInfo backupSubInfo = activeSubscriptions.stream() + .filter(subInfo -> subInfo.getSubscriptionId() != ddsSubId) + .findFirst() + .orElse(null); + if (backupSubInfo == null) return null; + mBackupDataSubId = backupSubInfo.getSubscriptionId(); + + // Check if auto data switch is already enabled + final TelephonyManager telephonyManager = getTelephonyManagerForSub(mBackupDataSubId); + if (telephonyManager == null) { + Log.d(TAG, "telephonyManager for " + mBackupDataSubId + " is null"); + return null; + } + if (telephonyManager.isMobileDataPolicyEnabled( + TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)) { + Log.d(TAG, "AUTO_DATA_SWITCH already enabled"); + return null; + } + + Log.d(TAG, "Backup data sub Id: " + mBackupDataSubId); + // The description of the feature + String message = + getContext().getString( + R.string.enable_auto_data_switch_dialog_message, + SubscriptionUtil.getUniqueSubscriptionDisplayName( + backupSubInfo, getContext())); + UserManager userManager = getUserManager(); + if (userManager == null) return message; + + // If one of the sub is dedicated to work profile(enterprise-managed), which means we might + // switching between personal & work profile, append a warning to the message. + UserHandle ddsUserHandle = subscriptionManager.getSubscriptionUserHandle(ddsSubId); + UserHandle nDdsUserHandle = subscriptionManager.getSubscriptionUserHandle(mBackupDataSubId); + boolean isDdsManaged = ddsUserHandle != null && userManager.isManagedProfile( + ddsUserHandle.getIdentifier()); + boolean isNDdsManaged = nDdsUserHandle != null && userManager.isManagedProfile( + nDdsUserHandle.getIdentifier()); + Log.d(TAG, "isDdsManaged= " + isDdsManaged + " isNDdsManaged=" + isNDdsManaged); + if (isDdsManaged ^ isNDdsManaged) { + message += getContext().getString( + R.string.auto_data_switch_dialog_managed_profile_warning); + } + + return message; + } + + @Override + public void updateDialog() { + updateDialog((AlertDialog) getDialog()); + } + + @Override + public void onClick(DialogInterface dialog, int buttonClicked) { + if (buttonClicked != DialogInterface.BUTTON_POSITIVE) { + return; + } + final SimDialogActivity activity = (SimDialogActivity) getActivity(); + if (mBackupDataSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + activity.onSubscriptionSelected(getDialogType(), mBackupDataSubId); + } + } + + private TelephonyManager getTelephonyManagerForSub(int subId) { + return getContext().getSystemService(TelephonyManager.class) + .createForSubscriptionId(subId); + } + + private SubscriptionManager getSubscriptionManager() { + return getContext().getSystemService(SubscriptionManager.class); + } + + @VisibleForTesting + protected int getDefaultDataSubId() { + return SubscriptionManager.getDefaultDataSubscriptionId(); + } + + private UserManager getUserManager() { + return getContext().getSystemService(UserManager.class); + } +} diff --git a/src/com/android/settings/sim/SelectSpecificDataSimDialogFragment.java b/src/com/android/settings/sim/SelectSpecificDataSimDialogFragment.java index b2ca6210a86..6ac0067d382 100644 --- a/src/com/android/settings/sim/SelectSpecificDataSimDialogFragment.java +++ b/src/com/android/settings/sim/SelectSpecificDataSimDialogFragment.java @@ -72,14 +72,31 @@ public class SelectSpecificDataSimDialogFragment extends SimDialogFragment imple } @Override - public void onClick(DialogInterface dialog, int buttonClicked) { - if (buttonClicked != DialogInterface.BUTTON_POSITIVE) { - return; + public void onDismiss(@NonNull DialogInterface dialog) { + Log.d(TAG, "Dialog onDismiss, dismiss status: " + mWasDismissed); + if (!mWasDismissed) { + // This dialog might be called onDismiss twice due to first time called by onDismiss() + // as a consequence of user action. We need this fragment alive so the activity + // doesn't end, which allows the following dialog to attach. Upon the second dialog + // dismiss, this fragment is removed from SimDialogActivity.onFragmentDismissed to + // end the activity. + mWasDismissed = true; + final SimDialogActivity activity = (SimDialogActivity) getActivity(); + activity.showEnableAutoDataSwitchDialog(); + // Not using super.onDismiss because it will result in an immediate end of the activity, + // before the second auto data switch dialog can attach. + if (getDialog() != null) getDialog().dismiss(); } + } + + @Override + public void onClick(DialogInterface dialog, int buttonClicked) { final SimDialogActivity activity = (SimDialogActivity) getActivity(); - final SubscriptionInfo info = getTargetSubscriptionInfo(); - if (info != null) { - activity.onSubscriptionSelected(getDialogType(), info.getSubscriptionId()); + if (buttonClicked == DialogInterface.BUTTON_POSITIVE) { + final SubscriptionInfo info = getTargetSubscriptionInfo(); + if (info != null) { + activity.onSubscriptionSelected(getDialogType(), info.getSubscriptionId()); + } } } diff --git a/src/com/android/settings/sim/SimDialogActivity.java b/src/com/android/settings/sim/SimDialogActivity.java index 464ba9b4a51..a1258be64b0 100644 --- a/src/com/android/settings/sim/SimDialogActivity.java +++ b/src/com/android/settings/sim/SimDialogActivity.java @@ -16,8 +16,6 @@ package com.android.settings.sim; -import static android.content.Context.MODE_PRIVATE; - import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; @@ -61,6 +59,8 @@ public class SimDialogActivity extends FragmentActivity { public static final int SMS_PICK_FOR_MESSAGE = 4; // Dismiss the current dialog and finish the activity. public static final int PICK_DISMISS = 5; + // Show auto data switch dialog(when user enables multi-SIM) + public static final int ENABLE_AUTO_DATA_SWITCH = 6; @Override protected void onCreate(Bundle savedInstanceState) { @@ -122,7 +122,7 @@ public class SimDialogActivity extends FragmentActivity { private SimDialogFragment createFragment(int dialogType) { switch (dialogType) { case DATA_PICK: - return getDataPickDialogFramgent(); + return getDataPickDialogFragment(); case CALLS_PICK: return CallsSimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_calls, @@ -141,12 +141,14 @@ public class SimDialogActivity extends FragmentActivity { return SimListDialogFragment.newInstance(dialogType, R.string.select_sim_for_sms, false /* includeAskEveryTime */, false /* isCancelItemShowed */); + case ENABLE_AUTO_DATA_SWITCH: + return EnableAutoDataSwitchDialogFragment.newInstance(); default: throw new IllegalArgumentException("Invalid dialog type " + dialogType + " sent."); } } - private SimDialogFragment getDataPickDialogFramgent() { + private SimDialogFragment getDataPickDialogFragment() { if (SubscriptionManager.getDefaultDataSubscriptionId() == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { return SimListDialogFragment.newInstance(DATA_PICK, R.string.select_sim_for_data, @@ -181,15 +183,40 @@ public class SimDialogActivity extends FragmentActivity { intent.putExtra(RESULT_SUB_ID, subId); setResult(Activity.RESULT_OK, intent); break; + case ENABLE_AUTO_DATA_SWITCH: + onEnableAutoDataSwitch(subId); + break; default: throw new IllegalArgumentException( "Invalid dialog type " + dialogType + " sent."); } } + /** + * Show dialog prompting the user to enable auto data switch + */ + public void showEnableAutoDataSwitchDialog() { + final FragmentManager fragmentManager = getSupportFragmentManager(); + SimDialogFragment fragment = createFragment(ENABLE_AUTO_DATA_SWITCH); + fragment.show(fragmentManager, Integer.toString(ENABLE_AUTO_DATA_SWITCH)); + } + + /** + * @param subId The sub Id to enable auto data switch + */ + public void onEnableAutoDataSwitch(int subId) { + Log.d(TAG, "onEnableAutoDataSwitch subId:" + subId); + final TelephonyManager telephonyManager = getSystemService( + TelephonyManager.class).createForSubscriptionId(subId); + telephonyManager.setMobileDataPolicyEnabled( + TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, true); + } + public void onFragmentDismissed(SimDialogFragment simDialogFragment) { final List fragments = getSupportFragmentManager().getFragments(); - if (fragments.size() == 1 && fragments.get(0) == simDialogFragment) { + if (fragments.size() == 1 && fragments.get(0) == simDialogFragment + || simDialogFragment.getDialogType() == ENABLE_AUTO_DATA_SWITCH) { + Log.d(TAG, "onFragmentDismissed dialogType:" + simDialogFragment.getDialogType()); finishAndRemoveTask(); } } @@ -200,7 +227,8 @@ public class SimDialogActivity extends FragmentActivity { TelephonyManager.class).createForSubscriptionId(subId); subscriptionManager.setDefaultDataSubId(subId); if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - telephonyManager.setDataEnabled(true); + telephonyManager.setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER, + true); Toast.makeText(this, R.string.data_switch_started, Toast.LENGTH_LONG).show(); } } diff --git a/src/com/android/settings/sim/SimListDialogFragment.java b/src/com/android/settings/sim/SimListDialogFragment.java index 245d31e8980..5b84d7b67c0 100644 --- a/src/com/android/settings/sim/SimListDialogFragment.java +++ b/src/com/android/settings/sim/SimListDialogFragment.java @@ -109,16 +109,18 @@ public class SimListDialogFragment extends SimDialogFragment { * @param selectionIndex the index of item in the list. */ public void onClick(int selectionIndex) { + final SimDialogActivity activity = (SimDialogActivity) getActivity(); if (selectionIndex >= 0 && selectionIndex < mSubscriptions.size()) { int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; final SubscriptionInfo subscription = mSubscriptions.get(selectionIndex); if (subscription != null) { subId = subscription.getSubscriptionId(); } - final SimDialogActivity activity = (SimDialogActivity) getActivity(); activity.onSubscriptionSelected(getDialogType(), subId); } - dismiss(); + Log.d(TAG, "Start showing auto data switch dialog"); + activity.showEnableAutoDataSwitchDialog(); + if (getDialog() != null) getDialog().dismiss(); } protected List getCurrentSubscriptions() { diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt index b4b4d6e09b5..55ea0c95861 100644 --- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt +++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt @@ -41,24 +41,27 @@ import com.android.settings.spa.system.SystemMainPageProvider import com.android.settingslib.spa.framework.common.SettingsPage import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository import com.android.settingslib.spa.framework.common.SpaEnvironment +import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListTemplate open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) { + open fun getTogglePermissionAppListProviders(): List { + return listOf( + AllFilesAccessAppListProvider, + DisplayOverOtherAppsAppListProvider, + MediaManagementAppsAppListProvider, + ModifySystemSettingsAppListProvider, + PictureInPictureListProvider, + InstallUnknownAppsListProvider, + AlarmsAndRemindersAppListProvider, + WifiControlAppListProvider, + ) + } + override val pageProviderRepository = lazy { - val togglePermissionAppListTemplate = - TogglePermissionAppListTemplate( - allProviders = - listOf( - AllFilesAccessAppListProvider, - DisplayOverOtherAppsAppListProvider, - MediaManagementAppsAppListProvider, - ModifySystemSettingsAppListProvider, - PictureInPictureListProvider, - InstallUnknownAppsListProvider, - AlarmsAndRemindersAppListProvider, - WifiControlAppListProvider, - ), - ) + val togglePermissionAppListTemplate = TogglePermissionAppListTemplate( + allProviders = getTogglePermissionAppListProviders() + ) SettingsPageProviderRepository( allPageProviders = listOf( HomePageProvider, diff --git a/src/com/android/settings/users/UserDetailsSettings.java b/src/com/android/settings/users/UserDetailsSettings.java index f9b42209755..b84fc323edd 100644 --- a/src/com/android/settings/users/UserDetailsSettings.java +++ b/src/com/android/settings/users/UserDetailsSettings.java @@ -307,8 +307,9 @@ public class UserDetailsSettings extends SettingsPreferenceFragment mSwitchUserPref.setSelectable(true); mSwitchUserPref.setOnPreferenceClickListener(this); } - //TODO(b/261700461): remove preference for supervised user - if (mUserInfo.isMain() || mUserInfo.isGuest() || !UserManager.isMultipleAdminEnabled()) { + if (mUserInfo.isMain() || mUserInfo.isGuest() || !UserManager.isMultipleAdminEnabled() + || mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_GRANT_ADMIN, + mUserInfo.getUserHandle())) { removePreference(KEY_GRANT_ADMIN); } if (!mUserManager.isAdminUser()) { // non admin users can't remove users and allow calls diff --git a/tests/robotests/src/com/android/settings/development/BugReportInPowerPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BugReportInPowerPreferenceControllerTest.java index 604dcb7a783..462ed16074b 100644 --- a/tests/robotests/src/com/android/settings/development/BugReportInPowerPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/development/BugReportInPowerPreferenceControllerTest.java @@ -99,7 +99,7 @@ public class BugReportInPowerPreferenceControllerTest { mController.onPreferenceChange(mPreference, false /* new value */); int mode = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Global.BUGREPORT_IN_POWER_MENU, -1 /* default */); + Settings.Secure.BUGREPORT_IN_POWER_MENU, -1 /* default */); assertThat(mode).isEqualTo(SETTING_VALUE_OFF); } @@ -111,7 +111,7 @@ public class BugReportInPowerPreferenceControllerTest { mController.onPreferenceChange(mPreference, true /* new value */); int mode = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Global.BUGREPORT_IN_POWER_MENU, -1 /* default */); + Settings.Secure.BUGREPORT_IN_POWER_MENU, -1 /* default */); assertThat(mode).isEqualTo(SETTING_VALUE_ON); } @@ -121,7 +121,7 @@ public class BugReportInPowerPreferenceControllerTest { public void updateState_settingsOn_preferenceShouldBeChecked() { when(mUserManager.hasUserRestriction(anyString())).thenReturn(false); Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Global.BUGREPORT_IN_POWER_MENU, SETTING_VALUE_ON); + Settings.Secure.BUGREPORT_IN_POWER_MENU, SETTING_VALUE_ON); mController.displayPreference(mScreen); mController.updateState(mPreference); @@ -133,7 +133,7 @@ public class BugReportInPowerPreferenceControllerTest { public void updateState_settingsOff_preferenceShouldNotBeChecked() { when(mUserManager.hasUserRestriction(anyString())).thenReturn(false); Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Global.BUGREPORT_IN_POWER_MENU, SETTING_VALUE_OFF); + Settings.Secure.BUGREPORT_IN_POWER_MENU, SETTING_VALUE_OFF); mController.displayPreference(mScreen); mController.updateState(mPreference); @@ -148,7 +148,7 @@ public class BugReportInPowerPreferenceControllerTest { mController.onDeveloperOptionsSwitchDisabled(); int mode = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Global.BUGREPORT_IN_POWER_MENU, -1 /* default */); + Settings.Secure.BUGREPORT_IN_POWER_MENU, -1 /* default */); assertThat(mode).isEqualTo(SETTING_VALUE_OFF); verify(mPreference).setChecked(false); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java index 104a6773f09..5f70a5e84ae 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizeUtilsTest.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; @@ -150,8 +151,9 @@ public class BatteryOptimizeUtilsTest { @Test public void testSetAppUsageState_Restricted_verifyAction() throws Exception { // Sets the current mode as MODE_UNRESTRICTED. - mBatteryOptimizeUtils.mAllowListed = false; - mBatteryOptimizeUtils.mMode = AppOpsManager.MODE_ALLOWED; + when(mMockBackend.isAllowlisted(anyString())).thenReturn(true); + when(mMockAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), anyString())) + .thenReturn(AppOpsManager.MODE_ALLOWED); mBatteryOptimizeUtils.setAppUsageState(MODE_RESTRICTED, Action.UNKNOWN); TimeUnit.SECONDS.sleep(1); @@ -161,6 +163,12 @@ public class BatteryOptimizeUtilsTest { @Test public void testSetAppUsageState_Unrestricted_verifyAction() throws Exception { + // Sets the current mode as MODE_RESTRICTED. + when(mMockBackend.isAllowlisted(anyString())).thenReturn(false); + when(mMockAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), anyString())) + .thenReturn(AppOpsManager.MODE_IGNORED); + + mBatteryOptimizeUtils.setAppUsageState(MODE_UNRESTRICTED, Action.UNKNOWN); TimeUnit.SECONDS.sleep(1); @@ -169,6 +177,11 @@ public class BatteryOptimizeUtilsTest { @Test public void testSetAppUsageState_Optimized_verifyAction() throws Exception { + // Sets the current mode as MODE_UNRESTRICTED. + when(mMockBackend.isAllowlisted(anyString())).thenReturn(true); + when(mMockAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), anyString())) + .thenReturn(AppOpsManager.MODE_ALLOWED); + mBatteryOptimizeUtils.setAppUsageState(MODE_OPTIMIZED, Action.UNKNOWN); TimeUnit.SECONDS.sleep(1); @@ -178,14 +191,16 @@ public class BatteryOptimizeUtilsTest { @Test public void testSetAppUsageState_sameUnrestrictedMode_verifyNoAction() throws Exception { // Sets the current mode as MODE_UNRESTRICTED. - mBatteryOptimizeUtils.mAllowListed = true; - mBatteryOptimizeUtils.mMode = AppOpsManager.MODE_ALLOWED; + when(mMockBackend.isAllowlisted(anyString())).thenReturn(true); + when(mMockAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), anyString())) + .thenReturn(AppOpsManager.MODE_ALLOWED); mBatteryOptimizeUtils.setAppUsageState(MODE_UNRESTRICTED, Action.UNKNOWN); TimeUnit.SECONDS.sleep(1); - verifyNoInteractions(mMockBackend); - verifyNoInteractions(mMockBatteryUtils); + verify(mMockBatteryUtils, never()).setForceAppStandby(anyInt(), anyString(), anyInt()); + verify(mMockBackend, never()).addApp(anyString()); + verify(mMockBackend, never()).removeApp(anyString()); } @Test diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java index addfd9bfdb9..60481975429 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBroadcastReceiverTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import android.content.Context; import android.content.Intent; @@ -29,6 +30,8 @@ import android.os.BatteryManager; import android.os.SystemClock; import android.text.format.DateUtils; +import com.android.settings.testutils.FakeFeatureFactory; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -42,6 +45,8 @@ public final class BatteryUsageBroadcastReceiverTest { private Context mContext; private BatteryUsageBroadcastReceiver mBatteryUsageBroadcastReceiver; + private FakeFeatureFactory mFakeFeatureFactory; + @Mock private PackageManager mPackageManager; @@ -49,6 +54,7 @@ public final class BatteryUsageBroadcastReceiverTest { public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); + mFakeFeatureFactory = FakeFeatureFactory.setupForTest(); mBatteryUsageBroadcastReceiver = new BatteryUsageBroadcastReceiver(); doReturn(mPackageManager).when(mContext).getPackageManager(); } @@ -62,6 +68,8 @@ public final class BatteryUsageBroadcastReceiverTest { @Test public void onReceive_actionBatteryLevelChanged_notFetchUsageData_notFullCharged() { + when(mFakeFeatureFactory.powerUsageFeatureProvider.getFullChargeIntentAction()) + .thenReturn(Intent.ACTION_BATTERY_LEVEL_CHANGED); doReturn(getBatteryIntent(/*level=*/ 20, BatteryManager.BATTERY_STATUS_UNKNOWN)) .when(mContext).registerReceiver(any(), any()); @@ -72,7 +80,9 @@ public final class BatteryUsageBroadcastReceiverTest { } @Test - public void onReceive_actionBatteryLevelChanged_cancelFetchUsageData() { + public void onReceive_actionBatteryLevelChanged_notFetchUsageData_nearBooting() { + when(mFakeFeatureFactory.powerUsageFeatureProvider.getFullChargeIntentAction()) + .thenReturn(Intent.ACTION_BATTERY_LEVEL_CHANGED); // Make sure isCharged returns true. doReturn(getBatteryIntent(/*level=*/ 100, BatteryManager.BATTERY_STATUS_FULL)) .when(mContext).registerReceiver(any(), any()); @@ -87,7 +97,25 @@ public final class BatteryUsageBroadcastReceiverTest { } @Test - public void onReceive_actionBatteryLevelChanged_notFetchUsageData() { + public void onReceive_actionBatteryLevelChanged_notFetchUsageData_wrongAction() { + when(mFakeFeatureFactory.powerUsageFeatureProvider.getFullChargeIntentAction()) + .thenReturn(Intent.ACTION_POWER_DISCONNECTED); + // Make sure isCharged returns true. + doReturn(getBatteryIntent(/*level=*/ 100, BatteryManager.BATTERY_STATUS_UNKNOWN)) + .when(mContext).registerReceiver(any(), any()); + BatteryUsageBroadcastReceiver.sBroadcastDelayFromBoot = + SystemClock.elapsedRealtime() - 5 * DateUtils.MINUTE_IN_MILLIS; + + mBatteryUsageBroadcastReceiver.onReceive(mContext, + new Intent(Intent.ACTION_BATTERY_LEVEL_CHANGED)); + + assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isFalse(); + } + + @Test + public void onReceive_actionBatteryLevelChanged_fetchUsageData() { + when(mFakeFeatureFactory.powerUsageFeatureProvider.getFullChargeIntentAction()) + .thenReturn(Intent.ACTION_BATTERY_LEVEL_CHANGED); // Make sure isCharged returns true. doReturn(getBatteryIntent(/*level=*/ 100, BatteryManager.BATTERY_STATUS_UNKNOWN)) .when(mContext).registerReceiver(any(), any()); @@ -100,6 +128,67 @@ public final class BatteryUsageBroadcastReceiverTest { assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isTrue(); } + @Test + public void onReceive_actionBatteryUnplugging_notFetchUsageData_notFullCharged() { + when(mFakeFeatureFactory.powerUsageFeatureProvider.getFullChargeIntentAction()) + .thenReturn(Intent.ACTION_POWER_DISCONNECTED); + doReturn(getBatteryIntent(/*level=*/ 20, BatteryManager.BATTERY_STATUS_UNKNOWN)) + .when(mContext).registerReceiver(any(), any()); + + mBatteryUsageBroadcastReceiver.onReceive(mContext, + new Intent(BatteryUsageBroadcastReceiver.ACTION_BATTERY_UNPLUGGING)); + + assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isFalse(); + } + + @Test + public void onReceive_actionBatteryUnplugging_notFetchUsageData_nearBooting() { + when(mFakeFeatureFactory.powerUsageFeatureProvider.getFullChargeIntentAction()) + .thenReturn(Intent.ACTION_POWER_DISCONNECTED); + // Make sure isCharged returns true. + doReturn(getBatteryIntent(/*level=*/ 100, BatteryManager.BATTERY_STATUS_FULL)) + .when(mContext).registerReceiver(any(), any()); + // Make sure broadcast will be sent with delay. + BatteryUsageBroadcastReceiver.sBroadcastDelayFromBoot = + SystemClock.elapsedRealtime() + 5 * DateUtils.MINUTE_IN_MILLIS; + + mBatteryUsageBroadcastReceiver.onReceive(mContext, + new Intent(BatteryUsageBroadcastReceiver.ACTION_BATTERY_UNPLUGGING)); + + assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isFalse(); + } + + @Test + public void onReceive_actionBatteryUnplugging_notFetchUsageData_wrongAction() { + when(mFakeFeatureFactory.powerUsageFeatureProvider.getFullChargeIntentAction()) + .thenReturn(Intent.ACTION_BATTERY_LEVEL_CHANGED); + // Make sure isCharged returns true. + doReturn(getBatteryIntent(/*level=*/ 100, BatteryManager.BATTERY_STATUS_UNKNOWN)) + .when(mContext).registerReceiver(any(), any()); + BatteryUsageBroadcastReceiver.sBroadcastDelayFromBoot = + SystemClock.elapsedRealtime() - 5 * DateUtils.MINUTE_IN_MILLIS; + + mBatteryUsageBroadcastReceiver.onReceive(mContext, + new Intent(BatteryUsageBroadcastReceiver.ACTION_BATTERY_UNPLUGGING)); + + assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isFalse(); + } + + @Test + public void onReceive_actionBatteryUnplugging_fetchUsageData() { + when(mFakeFeatureFactory.powerUsageFeatureProvider.getFullChargeIntentAction()) + .thenReturn(Intent.ACTION_POWER_DISCONNECTED); + // Make sure isCharged returns true. + doReturn(getBatteryIntent(/*level=*/ 100, BatteryManager.BATTERY_STATUS_UNKNOWN)) + .when(mContext).registerReceiver(any(), any()); + BatteryUsageBroadcastReceiver.sBroadcastDelayFromBoot = + SystemClock.elapsedRealtime() - 5 * DateUtils.MINUTE_IN_MILLIS; + + mBatteryUsageBroadcastReceiver.onReceive(mContext, + new Intent(BatteryUsageBroadcastReceiver.ACTION_BATTERY_UNPLUGGING)); + + assertThat(mBatteryUsageBroadcastReceiver.mFetchBatteryUsageData).isTrue(); + } @Test public void onReceive_clearCacheIntentInDebugMode_clearBatteryCacheData() { diff --git a/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java index 251b1d75c59..ed934733ea5 100644 --- a/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/MediaVolumePreferenceControllerTest.java @@ -216,12 +216,12 @@ public class MediaVolumePreferenceControllerTest { final Intent intent = new Intent(action); intent.setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME); return PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent, - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } private PendingIntent getActivityIntent(String action) { final Intent intent = new Intent(action); return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } } diff --git a/tests/robotests/src/com/android/settings/sim/EnableAutoDataSwitchDialogFragmentTest.java b/tests/robotests/src/com/android/settings/sim/EnableAutoDataSwitchDialogFragmentTest.java new file mode 100644 index 00000000000..ad60d06dc04 --- /dev/null +++ b/tests/robotests/src/com/android/settings/sim/EnableAutoDataSwitchDialogFragmentTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2022 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.sim; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +import android.content.Context; +import android.os.UserHandle; +import android.os.UserManager; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; + +import com.android.settings.R; +import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import java.util.Arrays; +import java.util.Collections; + +@RunWith(RobolectricTestRunner.class) +@Config(shadows = ShadowAlertDialogCompat.class) +public class EnableAutoDataSwitchDialogFragmentTest + extends SimDialogFragmentTestBase { + private static final String SUMMARY = "fake summary"; + private static final String WARNING = "fake warning"; + + // Mock + @Mock + private Context mContext; + @Mock + private SubscriptionManager mSubscriptionManager; + @Mock + private TelephonyManager mTelephonyManager; + @Mock + private UserManager mUserManager; + + @Before + public void setUp() { + super.setUp(); + mFragment = spy(EnableAutoDataSwitchDialogFragment.newInstance()); + doReturn(mContext).when(mFragment).getContext(); + + doReturn(mSubscriptionManager).when(mContext).getSystemService(SubscriptionManager.class); + doReturn(mTelephonyManager).when(mContext).getSystemService(TelephonyManager.class); + doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt()); + doReturn(mUserManager).when(mContext).getSystemService(UserManager.class); + + doReturn(SIM1_ID).when(mFragment).getDefaultDataSubId(); + doReturn(Arrays.asList(mSim1, mSim2)).when(mSubscriptionManager) + .getActiveSubscriptionInfoList(); + doReturn(true).when(mUserManager) + .isManagedProfile(UserHandle.MIN_SECONDARY_USER_ID); + + doReturn(SUMMARY).when(mContext).getString( + eq(R.string.enable_auto_data_switch_dialog_message), any()); + doReturn(WARNING).when(mContext).getString( + R.string.auto_data_switch_dialog_managed_profile_warning); + } + + @After + public void tearDown() { + mFragment = null; + } + + @Test + public void updateDialog_getMessage_noDdsExists() { + doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mFragment).getDefaultDataSubId(); + String msg = mFragment.getMessage(); + assertThat(msg).isEqualTo(null); + } + + @Test + public void updateDialog_getMessage_noBackupSubExists() { + doReturn(Collections.singletonList(mSim1)).when(mSubscriptionManager) + .getActiveSubscriptionInfoList(); + String msg = mFragment.getMessage(); + assertThat(msg).isEqualTo(null); + } + + @Test + public void updateDialog_getMessage_autoSwitchAlreadyEnabled() { + doReturn(true).when(mTelephonyManager).isMobileDataPolicyEnabled( + TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH); + String msg = mFragment.getMessage(); + assertThat(msg).isEqualTo(null); + } + + @Test + public void updateDialog_getMessage_noManagedProfile() { + UserHandle userHandle = UserHandle.of(UserHandle.USER_NULL); + UserHandle userHandle2 = UserHandle.of(UserHandle.USER_SYSTEM); + doReturn(userHandle).when(mSubscriptionManager).getSubscriptionUserHandle(SIM1_ID); + doReturn(userHandle2).when(mSubscriptionManager).getSubscriptionUserHandle(SIM2_ID); + String msg = mFragment.getMessage(); + assertThat(msg).contains(SUMMARY); + assertThat(msg).doesNotContain(WARNING); + } + + @Test + public void updateDialog_getMessage_hasManagedProfile() { + UserHandle userHandle = UserHandle.of(UserHandle.USER_NULL); + UserHandle userHandle2 = UserHandle.of(UserHandle.MIN_SECONDARY_USER_ID); + doReturn(userHandle).when(mSubscriptionManager).getSubscriptionUserHandle(SIM1_ID); + doReturn(userHandle2).when(mSubscriptionManager).getSubscriptionUserHandle(SIM2_ID); + String msg = mFragment.getMessage(); + assertThat(msg).contains(SUMMARY); + assertThat(msg).contains(WARNING); + } + + @Test + public void updateDialog_getMessage_BothManagedProfile() { + UserHandle userHandle = UserHandle.of(UserHandle.MIN_SECONDARY_USER_ID); + UserHandle userHandle2 = UserHandle.of(UserHandle.MIN_SECONDARY_USER_ID); + doReturn(userHandle).when(mSubscriptionManager).getSubscriptionUserHandle(SIM1_ID); + doReturn(userHandle2).when(mSubscriptionManager).getSubscriptionUserHandle(SIM2_ID); + String msg = mFragment.getMessage(); + assertThat(msg).contains(SUMMARY); + assertThat(msg).doesNotContain(WARNING); + } +} diff --git a/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java b/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java index 62846c1c87d..42c1e9f98b4 100644 --- a/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java +++ b/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java @@ -699,6 +699,16 @@ public class UserDetailsSettingsTest { verify(mFragment).removePreference(KEY_GRANT_ADMIN); } + @Test + public void initialize_restrictUserSelected_shouldNotShowGrantAdminPref_MultipleAdminEnabled() { + setupSelectedUser(); + ShadowUserManager.setIsMultipleAdminEnabled(true); + mUserManager.setUserRestriction(mUserInfo.getUserHandle(), + UserManager.DISALLOW_GRANT_ADMIN, true); + mFragment.initialize(mActivity, mArguments); + verify(mFragment).removePreference(KEY_GRANT_ADMIN); + } + @Test public void initialize_mainUserSelected_shouldShowGrantAdminPref_MultipleAdminEnabled() { setupSelectedMainUser(); diff --git a/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java b/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java new file mode 100644 index 00000000000..31b8e794480 --- /dev/null +++ b/tests/unit/src/com/android/settings/localepicker/LocaleHelperPreferenceControllerTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2022 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.localepicker; + +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.os.Looper; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.settingslib.widget.FooterPreference; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +public class LocaleHelperPreferenceControllerTest { + private Context mContext; + private LocaleHelperPreferenceController mLocaleHelperPreferenceController; + + @Mock + private FooterPreference mMockFooterPreference; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + if (Looper.myLooper() == null) { + Looper.prepare(); + } + mContext = ApplicationProvider.getApplicationContext(); + mLocaleHelperPreferenceController = new LocaleHelperPreferenceController(mContext); + } + + @Test + public void updateFooterPreference_setFooterPreference_hasClickAction() { + mLocaleHelperPreferenceController.updateFooterPreference(mMockFooterPreference); + verify(mMockFooterPreference).setLearnMoreText(anyString()); + } +}