diff --git a/res/xml/power_background_usage_detail.xml b/res/xml/power_background_usage_detail.xml index 5c7b6a5235a..32d80b52ecb 100644 --- a/res/xml/power_background_usage_detail.xml +++ b/res/xml/power_background_usage_detail.xml @@ -25,22 +25,25 @@ android:layout="@layout/settings_entity_header" android:selectable="false"/> - + - + - + + + + + android:key="background_usage_allowability_category" + settings:controller="com.android.settings.fuelgauge.BackgroundUsageAllowabilityPreferenceController"> + android:key="background_usage_allowability_switch" + android:title="@string/manager_battery_usage_allow_background_usage_title"/> diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java index e922f7058be..28d7d58bee3 100644 --- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java @@ -26,13 +26,10 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.UserHandle; -import android.text.TextUtils; import android.util.Log; import android.view.View; -import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.SettingsActivity; @@ -48,7 +45,6 @@ import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry; import com.android.settings.fuelgauge.batteryusage.BatteryEntry; import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.EntityHeaderController; -import com.android.settingslib.PrimarySwitchPreference; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.core.AbstractPreferenceController; @@ -67,9 +63,7 @@ import java.util.concurrent.Executors; * 2. Battery related controls for app(i.e uninstall, force stop) */ public class AdvancedPowerUsageDetail extends DashboardFragment - implements ButtonActionDialogFragment.AppButtonsDialogListener, - Preference.OnPreferenceClickListener, - Preference.OnPreferenceChangeListener { + implements ButtonActionDialogFragment.AppButtonsDialogListener { public static final String TAG = "AdvancedPowerDetail"; public static final String EXTRA_UID = "extra_uid"; public static final String EXTRA_PACKAGE_NAME = "extra_package_name"; @@ -86,7 +80,8 @@ public class AdvancedPowerUsageDetail extends DashboardFragment public static final String EXTRA_POWER_USAGE_AMOUNT = "extra_power_usage_amount"; private static final String KEY_PREF_HEADER = "header_view"; - private static final String KEY_ALLOW_BACKGROUND_USAGE = "allow_background_usage"; + private static final String KEY_BACKGROUND_USAGE_ALLOWABILITY_CATEGORY = + "background_usage_allowability_category"; private static final int REQUEST_UNINSTALL = 0; private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1; @@ -96,11 +91,9 @@ public class AdvancedPowerUsageDetail extends DashboardFragment private AppButtonsPreferenceController mAppButtonsPreferenceController; private PowerUsageTimeController mPowerUsageTimeController; - @VisibleForTesting LayoutPreference mHeaderPreference; @VisibleForTesting ApplicationsState mState; @VisibleForTesting ApplicationsState.AppEntry mAppEntry; @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils; - @VisibleForTesting PrimarySwitchPreference mAllowBackgroundUsagePreference; @VisibleForTesting @BatteryOptimizeUtils.OptimizationMode int mOptimizationMode = BatteryOptimizeUtils.MODE_UNKNOWN; @@ -242,17 +235,11 @@ public class AdvancedPowerUsageDetail extends DashboardFragment public void onAttach(Activity activity) { super.onAttach(activity); + final Bundle bundle = getArguments(); + final int uid = bundle.getInt(EXTRA_UID, 0); + final String packageName = bundle.getString(EXTRA_PACKAGE_NAME); + mBatteryOptimizeUtils = new BatteryOptimizeUtils(getContext(), uid, packageName); mState = ApplicationsState.getInstance(getActivity().getApplication()); - } - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - final String packageName = getArguments().getString(EXTRA_PACKAGE_NAME); - onCreateBackgroundUsageState(packageName); - mHeaderPreference = findPreference(KEY_PREF_HEADER); - if (packageName != null) { mAppEntry = mState.getEntry(packageName, UserHandle.myUserId()); } @@ -264,7 +251,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment initHeader(); mOptimizationMode = mBatteryOptimizeUtils.getAppOptimizationMode(); - initFooter(); mLogStringBuilder = new StringBuilder("onResume mode = ").append(mOptimizationMode); } @@ -299,7 +285,8 @@ public class AdvancedPowerUsageDetail extends DashboardFragment @VisibleForTesting void initHeader() { - final View appSnippet = mHeaderPreference.findViewById(R.id.entity_header); + final LayoutPreference headerPreference = findPreference(KEY_PREF_HEADER); + final View appSnippet = headerPreference.findViewById(R.id.entity_header); final Activity context = getActivity(); final Bundle bundle = getArguments(); EntityHeaderController controller = @@ -340,31 +327,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment controller.done(true /* rebindActions */); } - @VisibleForTesting - void initFooter() { - final String stateString; - final String detailInfoString; - final Context context = getContext(); - - if (mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()) { - // Present optimized only string when the package name is invalid. - stateString = context.getString(R.string.manager_battery_usage_optimized_only); - detailInfoString = - context.getString(R.string.manager_battery_usage_footer_limited, stateString); - } else if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) { - // Present unrestricted only string when the package is system or default active app. - stateString = context.getString(R.string.manager_battery_usage_unrestricted_only); - detailInfoString = - context.getString(R.string.manager_battery_usage_footer_limited, stateString); - } else { - // Present default string to normal app. - detailInfoString = - context.getString( - R.string.manager_battery_usage_allow_background_usage_summary); - } - mAllowBackgroundUsagePreference.setSummary(detailInfoString); - } - @Override public int getMetricsCategory() { return SettingsEnums.FUELGAUGE_POWER_USAGE_DETAIL; @@ -384,7 +346,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment protected List createPreferenceControllers(Context context) { final List controllers = new ArrayList<>(); final Bundle bundle = getArguments(); - final int uid = bundle.getInt(EXTRA_UID, 0); final String packageName = bundle.getString(EXTRA_PACKAGE_NAME); mAppButtonsPreferenceController = @@ -401,7 +362,12 @@ public class AdvancedPowerUsageDetail extends DashboardFragment controllers.add(mPowerUsageTimeController); } controllers.add(mAppButtonsPreferenceController); - controllers.add(new AllowBackgroundPreferenceController(context, uid, packageName)); + controllers.add( + new BackgroundUsageAllowabilityPreferenceController( + context, + /* dashboardFragment= */ this, + KEY_BACKGROUND_USAGE_ALLOWABILITY_CATEGORY, + mBatteryOptimizeUtils)); return controllers; } @@ -421,34 +387,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment } } - @Override - public boolean onPreferenceClick(Preference preference) { - if (!(preference instanceof PrimarySwitchPreference) - || !TextUtils.equals(preference.getKey(), KEY_ALLOW_BACKGROUND_USAGE)) { - return false; - } - PowerBackgroundUsageDetail.startPowerBackgroundUsageDetailPage( - getContext(), getArguments()); - return true; - } - - @Override - public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) { - if (!(preference instanceof PrimarySwitchPreference) - || !TextUtils.equals(preference.getKey(), KEY_ALLOW_BACKGROUND_USAGE)) { - return false; - } - if (newValue instanceof Boolean) { - final boolean isAllowBackgroundUsage = (boolean) newValue; - mBatteryOptimizeUtils.setAppUsageState( - isAllowBackgroundUsage - ? BatteryOptimizeUtils.MODE_OPTIMIZED - : BatteryOptimizeUtils.MODE_RESTRICTED, - Action.APPLY); - } - return true; - } - private void logMetricCategory(int currentOptimizeMode) { if (currentOptimizeMode == mOptimizationMode) { return; @@ -482,16 +420,4 @@ public class AdvancedPowerUsageDetail extends DashboardFragment getArguments().getInt(EXTRA_POWER_USAGE_AMOUNT)); }); } - - private void onCreateBackgroundUsageState(String packageName) { - mAllowBackgroundUsagePreference = findPreference(KEY_ALLOW_BACKGROUND_USAGE); - if (mAllowBackgroundUsagePreference != null) { - mAllowBackgroundUsagePreference.setOnPreferenceClickListener(this); - mAllowBackgroundUsagePreference.setOnPreferenceChangeListener(this); - } - - mBatteryOptimizeUtils = - new BatteryOptimizeUtils( - getContext(), getArguments().getInt(EXTRA_UID), packageName); - } } diff --git a/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceController.java b/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceController.java deleted file mode 100644 index 52cec79599a..00000000000 --- a/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceController.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2023 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.fuelgauge; - -import android.content.Context; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; - -import com.android.settings.core.PreferenceControllerMixin; -import com.android.settingslib.PrimarySwitchPreference; -import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.widget.MainSwitchPreference; - -/** Controller to update the app background usage state */ -public class AllowBackgroundPreferenceController extends AbstractPreferenceController - implements PreferenceControllerMixin { - - private static final String TAG = "AllowBackgroundPreferenceController"; - - @VisibleForTesting static final String KEY_ALLOW_BACKGROUND_USAGE = "allow_background_usage"; - - @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils; - - public AllowBackgroundPreferenceController(Context context, int uid, String packageName) { - super(context); - mBatteryOptimizeUtils = new BatteryOptimizeUtils(context, uid, packageName); - } - - private void setChecked(Preference preference, boolean checked) { - if (preference instanceof PrimarySwitchPreference) { - ((PrimarySwitchPreference) preference).setChecked(checked); - } else if (preference instanceof MainSwitchPreference) { - ((MainSwitchPreference) preference).setChecked(checked); - } - } - - private void setEnabled(Preference preference, boolean enabled) { - if (preference instanceof PrimarySwitchPreference) { - ((PrimarySwitchPreference) preference).setEnabled(enabled); - ((PrimarySwitchPreference) preference).setSwitchEnabled(enabled); - } else if (preference instanceof MainSwitchPreference) { - ((MainSwitchPreference) preference).setEnabled(enabled); - } - } - - @Override - public void updateState(Preference preference) { - setEnabled(preference, mBatteryOptimizeUtils.isOptimizeModeMutable()); - - final boolean isAllowBackground = - mBatteryOptimizeUtils.getAppOptimizationMode() - != BatteryOptimizeUtils.MODE_RESTRICTED; - setChecked(preference, isAllowBackground); - } - - @Override - public boolean isAvailable() { - return true; - } - - @Override - public String getPreferenceKey() { - return KEY_ALLOW_BACKGROUND_USAGE; - } - - @Override - public boolean handlePreferenceTreeClick(Preference preference) { - return getPreferenceKey().equals(preference.getKey()); - } -} diff --git a/src/com/android/settings/fuelgauge/BackgroundUsageAllowabilityPreferenceController.java b/src/com/android/settings/fuelgauge/BackgroundUsageAllowabilityPreferenceController.java new file mode 100644 index 00000000000..bce439bad1f --- /dev/null +++ b/src/com/android/settings/fuelgauge/BackgroundUsageAllowabilityPreferenceController.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2024 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.fuelgauge; + +import android.content.Context; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settingslib.PrimarySwitchPreference; + +/** Controller to update the manage battery usage preference in App Battery Usage page */ +public class BackgroundUsageAllowabilityPreferenceController extends BasePreferenceController + implements PreferenceControllerMixin { + + @VisibleForTesting + static final String KEY_BACKGROUND_USAGE_ALLOWABILITY_SWITCH = + "background_usage_allowability_switch"; + + private final BatteryOptimizeUtils mBatteryOptimizeUtils; + private final DashboardFragment mDashboardFragment; + @Nullable @VisibleForTesting PrimarySwitchPreference mBackgroundUsageAllowabilityPreference; + + public BackgroundUsageAllowabilityPreferenceController( + @NonNull Context context, + @NonNull DashboardFragment dashboardFragment, + @NonNull String preferenceKey, + @NonNull BatteryOptimizeUtils batteryOptimizeUtils) { + super(context, preferenceKey); + mDashboardFragment = dashboardFragment; + mBatteryOptimizeUtils = batteryOptimizeUtils; + } + + @Override + public int getAvailabilityStatus() { + return AVAILABLE; + } + + @Override + public void updateState(@NonNull Preference preference) { + updatePreferences(mBatteryOptimizeUtils.getAppOptimizationMode()); + } + + @Override + public void displayPreference(@NonNull PreferenceScreen screen) { + super.displayPreference(screen); + mBackgroundUsageAllowabilityPreference = + screen.findPreference(KEY_BACKGROUND_USAGE_ALLOWABILITY_SWITCH); + initPreferences(); + } + + @VisibleForTesting + void initPreferences() { + if (mBackgroundUsageAllowabilityPreference == null) { + return; + } + final String stateString; + final String detailInfoString; + boolean isPreferenceEnabled = true; + if (mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()) { + // Present "Optimized" only string if the package name is invalid. + stateString = mContext.getString(R.string.manager_battery_usage_optimized_only); + detailInfoString = + mContext.getString(R.string.manager_battery_usage_footer_limited, stateString); + isPreferenceEnabled = false; + } else if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) { + // Present "Unrestricted" only string if the package is system important apps. + stateString = mContext.getString(R.string.manager_battery_usage_unrestricted_only); + detailInfoString = + mContext.getString(R.string.manager_battery_usage_footer_limited, stateString); + isPreferenceEnabled = false; + } else { + // Present default string to normal app. + detailInfoString = + mContext.getString( + R.string.manager_battery_usage_allow_background_usage_summary); + } + mBackgroundUsageAllowabilityPreference.setEnabled(isPreferenceEnabled); + mBackgroundUsageAllowabilityPreference.setSwitchEnabled(isPreferenceEnabled); + mBackgroundUsageAllowabilityPreference.setSummary(detailInfoString); + if (isPreferenceEnabled) { + mBackgroundUsageAllowabilityPreference.setOnPreferenceClickListener( + preference -> { + PowerBackgroundUsageDetail.startPowerBackgroundUsageDetailPage( + mContext, mDashboardFragment.getArguments()); + return true; + }); + mBackgroundUsageAllowabilityPreference.setOnPreferenceChangeListener( + (preference, isAllowBackground) -> { + handleBatteryOptimizeModeUpdated( + (boolean) isAllowBackground + ? BatteryOptimizeUtils.MODE_OPTIMIZED + : BatteryOptimizeUtils.MODE_RESTRICTED); + return true; + }); + } + } + + @VisibleForTesting + void handleBatteryOptimizeModeUpdated(int newBatteryOptimizeMode) { + if (mBatteryOptimizeUtils.getAppOptimizationMode() == newBatteryOptimizeMode) { + Log.w(TAG, "ignore same mode for: " + mBatteryOptimizeUtils.getPackageName()); + return; + } + mBatteryOptimizeUtils.setAppUsageState( + newBatteryOptimizeMode, BatteryOptimizeHistoricalLogEntry.Action.APPLY); + updatePreferences(newBatteryOptimizeMode); + } + + @VisibleForTesting + void updatePreferences(int optimizationMode) { + if (mBackgroundUsageAllowabilityPreference == null) { + return; + } + mBackgroundUsageAllowabilityPreference.setChecked( + optimizationMode != BatteryOptimizeUtils.MODE_RESTRICTED); + } +} diff --git a/src/com/android/settings/fuelgauge/BatteryOptimizationModePreferenceController.java b/src/com/android/settings/fuelgauge/BatteryOptimizationModePreferenceController.java new file mode 100644 index 00000000000..0a4cbac4dd6 --- /dev/null +++ b/src/com/android/settings/fuelgauge/BatteryOptimizationModePreferenceController.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2024 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.fuelgauge; + +import android.content.Context; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.widget.MainSwitchPreference; +import com.android.settingslib.widget.SelectorWithWidgetPreference; + +/** Controller to update the app background usage mode state in Allow background usage page */ +public class BatteryOptimizationModePreferenceController extends BasePreferenceController + implements PreferenceControllerMixin { + + @VisibleForTesting + static final String KEY_BACKGROUND_USAGE_ALLOWABILITY_SWITCH = + "background_usage_allowability_switch"; + + @VisibleForTesting static final String KEY_OPTIMIZED_PREF = "optimized_preference"; + @VisibleForTesting static final String KEY_UNRESTRICTED_PREF = "unrestricted_preference"; + + private final BatteryOptimizeUtils mBatteryOptimizeUtils; + @Nullable @VisibleForTesting MainSwitchPreference mBackgroundUsageAllowabilityPreference; + @Nullable @VisibleForTesting SelectorWithWidgetPreference mOptimizedPreference; + @Nullable @VisibleForTesting SelectorWithWidgetPreference mUnrestrictedPreference; + + public BatteryOptimizationModePreferenceController( + @NonNull Context context, + @NonNull String preferenceKey, + @NonNull BatteryOptimizeUtils batteryOptimizeUtils) { + super(context, preferenceKey); + mBatteryOptimizeUtils = batteryOptimizeUtils; + } + + @Override + public int getAvailabilityStatus() { + return AVAILABLE; + } + + @Override + public void updateState(@NonNull Preference preference) { + updatePreferences(mBatteryOptimizeUtils.getAppOptimizationMode()); + } + + @Override + public void displayPreference(@NonNull PreferenceScreen screen) { + super.displayPreference(screen); + mBackgroundUsageAllowabilityPreference = + screen.findPreference(KEY_BACKGROUND_USAGE_ALLOWABILITY_SWITCH); + mOptimizedPreference = screen.findPreference(KEY_OPTIMIZED_PREF); + mUnrestrictedPreference = screen.findPreference(KEY_UNRESTRICTED_PREF); + initPreferences(); + } + + @VisibleForTesting + void initPreferences() { + if (mBackgroundUsageAllowabilityPreference == null + || mOptimizedPreference == null + || mUnrestrictedPreference == null) { + return; + } + final boolean isEnabled = mBatteryOptimizeUtils.isOptimizeModeMutable(); + mBackgroundUsageAllowabilityPreference.setEnabled(isEnabled); + mOptimizedPreference.setEnabled(isEnabled); + mUnrestrictedPreference.setEnabled(isEnabled); + if (isEnabled) { + mBackgroundUsageAllowabilityPreference.setOnPreferenceChangeListener( + (preference, isAllowBackground) -> { + handleBatteryOptimizeModeUpdated( + (boolean) isAllowBackground + ? BatteryOptimizeUtils.MODE_OPTIMIZED + : BatteryOptimizeUtils.MODE_RESTRICTED); + return true; + }); + mOptimizedPreference.setOnPreferenceClickListener( + preference -> { + handleBatteryOptimizeModeUpdated(BatteryOptimizeUtils.MODE_OPTIMIZED); + return true; + }); + mUnrestrictedPreference.setOnPreferenceClickListener( + preference -> { + handleBatteryOptimizeModeUpdated(BatteryOptimizeUtils.MODE_UNRESTRICTED); + return true; + }); + } + } + + @VisibleForTesting + void updatePreferences(int optimizationMode) { + if (mBackgroundUsageAllowabilityPreference == null + || mOptimizedPreference == null + || mUnrestrictedPreference == null) { + return; + } + final boolean isAllowBackground = optimizationMode != BatteryOptimizeUtils.MODE_RESTRICTED; + mBackgroundUsageAllowabilityPreference.setChecked(isAllowBackground); + mOptimizedPreference.setEnabled(isAllowBackground); + mUnrestrictedPreference.setEnabled(isAllowBackground); + mOptimizedPreference.setChecked(optimizationMode == BatteryOptimizeUtils.MODE_OPTIMIZED); + mUnrestrictedPreference.setChecked( + optimizationMode == BatteryOptimizeUtils.MODE_UNRESTRICTED); + } + + @VisibleForTesting + void handleBatteryOptimizeModeUpdated(int newBatteryOptimizeMode) { + if (mBatteryOptimizeUtils.getAppOptimizationMode() == newBatteryOptimizeMode) { + Log.w(TAG, "ignore same mode for: " + mBatteryOptimizeUtils.getPackageName()); + return; + } + mBatteryOptimizeUtils.setAppUsageState( + newBatteryOptimizeMode, BatteryOptimizeHistoricalLogEntry.Action.APPLY); + updatePreferences(newBatteryOptimizeMode); + } +} diff --git a/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java b/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java deleted file mode 100644 index a26ab7a58e4..00000000000 --- a/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2021 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.fuelgauge; - -import android.content.Context; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; - -import com.android.settings.core.PreferenceControllerMixin; -import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.widget.SelectorWithWidgetPreference; - -public class OptimizedPreferenceController extends AbstractPreferenceController - implements PreferenceControllerMixin { - - private static final String TAG = "OPTIMIZED_PREF"; - - @VisibleForTesting static final String KEY_OPTIMIZED_PREF = "optimized_preference"; - @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils; - - public OptimizedPreferenceController(Context context, int uid, String packageName) { - super(context); - mBatteryOptimizeUtils = new BatteryOptimizeUtils(context, uid, packageName); - } - - @Override - public boolean isAvailable() { - return true; - } - - @Override - public void updateState(Preference preference) { - preference.setEnabled(mBatteryOptimizeUtils.isSelectorPreferenceEnabled()); - - final boolean isOptimized = - mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly() - || mBatteryOptimizeUtils.getAppOptimizationMode() - == BatteryOptimizeUtils.MODE_OPTIMIZED; - ((SelectorWithWidgetPreference) preference).setChecked(isOptimized); - } - - @Override - public String getPreferenceKey() { - return KEY_OPTIMIZED_PREF; - } - - @Override - public boolean handlePreferenceTreeClick(Preference preference) { - return getPreferenceKey().equals(preference.getKey()); - } -} diff --git a/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java b/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java index e59cc4add46..dadf2e89fbe 100644 --- a/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java +++ b/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java @@ -24,11 +24,8 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.UserHandle; -import android.text.TextUtils; import android.util.Log; import android.view.View; -import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; import androidx.annotation.VisibleForTesting; @@ -44,8 +41,6 @@ import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.widget.FooterPreference; import com.android.settingslib.widget.LayoutPreference; -import com.android.settingslib.widget.MainSwitchPreference; -import com.android.settingslib.widget.SelectorWithWidgetPreference; import java.util.ArrayList; import java.util.List; @@ -53,8 +48,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** Allow background usage fragment for each app */ -public class PowerBackgroundUsageDetail extends DashboardFragment - implements SelectorWithWidgetPreference.OnClickListener, OnCheckedChangeListener { +public class PowerBackgroundUsageDetail extends DashboardFragment { private static final String TAG = "PowerBackgroundUsageDetail"; public static final String EXTRA_UID = "extra_uid"; @@ -63,21 +57,15 @@ public class PowerBackgroundUsageDetail extends DashboardFragment public static final String EXTRA_POWER_USAGE_AMOUNT = "extra_power_usage_amount"; public static final String EXTRA_ICON_ID = "extra_icon_id"; private static final String KEY_PREF_HEADER = "header_view"; - private static final String KEY_PREF_UNRESTRICTED = "unrestricted_preference"; - private static final String KEY_PREF_OPTIMIZED = "optimized_preference"; - private static final String KEY_ALLOW_BACKGROUND_USAGE = "allow_background_usage"; private static final String KEY_FOOTER_PREFERENCE = "app_usage_footer_preference"; + private static final String KEY_BATTERY_OPTIMIZATION_MODE_CATEGORY = + "battery_optimization_mode_category"; private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); - @VisibleForTesting LayoutPreference mHeaderPreference; @VisibleForTesting ApplicationsState mState; @VisibleForTesting ApplicationsState.AppEntry mAppEntry; @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils; - @VisibleForTesting SelectorWithWidgetPreference mOptimizePreference; - @VisibleForTesting SelectorWithWidgetPreference mUnrestrictedPreference; - @VisibleForTesting MainSwitchPreference mMainSwitchPreference; - @VisibleForTesting FooterPreference mFooterPreference; @VisibleForTesting StringBuilder mLogStringBuilder; @VisibleForTesting @BatteryOptimizeUtils.OptimizationMode @@ -87,17 +75,11 @@ public class PowerBackgroundUsageDetail extends DashboardFragment public void onAttach(Activity activity) { super.onAttach(activity); + final Bundle bundle = getArguments(); + final int uid = bundle.getInt(EXTRA_UID, 0); + final String packageName = bundle.getString(EXTRA_PACKAGE_NAME); + mBatteryOptimizeUtils = new BatteryOptimizeUtils(getContext(), uid, packageName); mState = ApplicationsState.getInstance(getActivity().getApplication()); - } - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - final String packageName = getArguments().getString(EXTRA_PACKAGE_NAME); - onCreateBackgroundUsageState(packageName); - mHeaderPreference = findPreference(KEY_PREF_HEADER); - if (packageName != null) { mAppEntry = mState.getEntry(packageName, UserHandle.myUserId()); } @@ -107,8 +89,8 @@ public class PowerBackgroundUsageDetail extends DashboardFragment public void onResume() { super.onResume(); initHeader(); - mOptimizationMode = mBatteryOptimizeUtils.getAppOptimizationMode(); initFooter(); + mOptimizationMode = mBatteryOptimizeUtils.getAppOptimizationMode(); mLogStringBuilder = new StringBuilder("onResume mode = ").append(mOptimizationMode); } @@ -136,20 +118,6 @@ public class PowerBackgroundUsageDetail extends DashboardFragment Log.d(TAG, "Leave with mode: " + currentOptimizeMode); } - @Override - public void onRadioButtonClicked(SelectorWithWidgetPreference selected) { - final String selectedKey = selected == null ? null : selected.getKey(); - updateSelectorPreferenceState(mUnrestrictedPreference, selectedKey); - updateSelectorPreferenceState(mOptimizePreference, selectedKey); - mBatteryOptimizeUtils.setAppUsageState(getSelectedPreference(), Action.APPLY); - } - - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - mMainSwitchPreference.setChecked(isChecked); - updateSelectorPreference(isChecked); - } - @Override public int getMetricsCategory() { return SettingsEnums.FUELGAUGE_POWER_USAGE_MANAGE_BACKGROUND; @@ -157,14 +125,10 @@ public class PowerBackgroundUsageDetail extends DashboardFragment @Override protected List createPreferenceControllers(Context context) { - final List controllers = new ArrayList<>(); - final Bundle bundle = getArguments(); - final int uid = bundle.getInt(EXTRA_UID, 0); - final String packageName = bundle.getString(EXTRA_PACKAGE_NAME); - - controllers.add(new AllowBackgroundPreferenceController(context, uid, packageName)); - controllers.add(new OptimizedPreferenceController(context, uid, packageName)); - controllers.add(new UnrestrictedPreferenceController(context, uid, packageName)); + final List controllers = new ArrayList<>(1); + controllers.add( + new BatteryOptimizationModePreferenceController( + context, KEY_BATTERY_OPTIMIZATION_MODE_CATEGORY, mBatteryOptimizeUtils)); return controllers; } @@ -179,26 +143,6 @@ public class PowerBackgroundUsageDetail extends DashboardFragment return TAG; } - @VisibleForTesting - void updateSelectorPreference(boolean isEnabled) { - mOptimizePreference.setEnabled(isEnabled); - mUnrestrictedPreference.setEnabled(isEnabled); - onRadioButtonClicked(isEnabled ? mOptimizePreference : null); - } - - @VisibleForTesting - int getSelectedPreference() { - if (!mMainSwitchPreference.isChecked()) { - return BatteryOptimizeUtils.MODE_RESTRICTED; - } else if (mUnrestrictedPreference.isChecked()) { - return BatteryOptimizeUtils.MODE_UNRESTRICTED; - } else if (mOptimizePreference.isChecked()) { - return BatteryOptimizeUtils.MODE_OPTIMIZED; - } else { - return BatteryOptimizeUtils.MODE_UNKNOWN; - } - } - static void startPowerBackgroundUsageDetailPage(Context context, Bundle args) { new SubSettingLauncher(context) .setDestination(PowerBackgroundUsageDetail.class.getName()) @@ -209,7 +153,11 @@ public class PowerBackgroundUsageDetail extends DashboardFragment @VisibleForTesting void initHeader() { - final View appSnippet = mHeaderPreference.findViewById(R.id.entity_header); + final LayoutPreference headerPreference = findPreference(KEY_PREF_HEADER); + if (headerPreference == null) { + return; + } + final View appSnippet = headerPreference.findViewById(R.id.entity_header); final Activity context = getActivity(); final Bundle bundle = getArguments(); EntityHeaderController controller = @@ -239,58 +187,25 @@ public class PowerBackgroundUsageDetail extends DashboardFragment @VisibleForTesting void initFooter() { - final String stateString; - final String footerString; - final Context context = getContext(); - - if (mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()) { - // Present optimized only string when the package name is invalid. - stateString = context.getString(R.string.manager_battery_usage_optimized_only); - footerString = - context.getString(R.string.manager_battery_usage_footer_limited, stateString); - } else if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) { - // Present unrestricted only string when the package is system or default active app. - stateString = context.getString(R.string.manager_battery_usage_unrestricted_only); - footerString = - context.getString(R.string.manager_battery_usage_footer_limited, stateString); - } else { - // Present default string to normal app. - footerString = context.getString(R.string.manager_battery_usage_footer); + final FooterPreference footerPreference = findPreference(KEY_FOOTER_PREFERENCE); + if (footerPreference == null) { + return; } - mFooterPreference.setTitle(footerString); + final Context context = getContext(); + footerPreference.setTitle(context.getString(R.string.manager_battery_usage_footer)); final Intent helpIntent = HelpUtils.getHelpIntent( context, context.getString(R.string.help_url_app_usage_settings), /* backupContext= */ ""); if (helpIntent != null) { - mFooterPreference.setLearnMoreAction( + footerPreference.setLearnMoreAction( v -> startActivityForResult(helpIntent, /* requestCode= */ 0)); - mFooterPreference.setLearnMoreText( + footerPreference.setLearnMoreText( context.getString(R.string.manager_battery_usage_link_a11y)); } } - private void onCreateBackgroundUsageState(String packageName) { - mOptimizePreference = findPreference(KEY_PREF_OPTIMIZED); - mUnrestrictedPreference = findPreference(KEY_PREF_UNRESTRICTED); - mMainSwitchPreference = findPreference(KEY_ALLOW_BACKGROUND_USAGE); - mFooterPreference = findPreference(KEY_FOOTER_PREFERENCE); - - mOptimizePreference.setOnClickListener(this); - mUnrestrictedPreference.setOnClickListener(this); - mMainSwitchPreference.addOnSwitchChangeListener(this); - - mBatteryOptimizeUtils = - new BatteryOptimizeUtils( - getContext(), getArguments().getInt(EXTRA_UID), packageName); - } - - private void updateSelectorPreferenceState( - SelectorWithWidgetPreference preference, String selectedKey) { - preference.setChecked(TextUtils.equals(selectedKey, preference.getKey())); - } - private void logMetricCategory(int currentOptimizeMode) { if (currentOptimizeMode == mOptimizationMode) { return; diff --git a/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java b/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java deleted file mode 100644 index 652941b2544..00000000000 --- a/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2021 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.fuelgauge; - -import android.content.Context; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; - -import com.android.settings.core.PreferenceControllerMixin; -import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.widget.SelectorWithWidgetPreference; - -public class UnrestrictedPreferenceController extends AbstractPreferenceController - implements PreferenceControllerMixin { - - private static final String TAG = "UNRESTRICTED_PREF"; - - @VisibleForTesting static final String KEY_UNRESTRICTED_PREF = "unrestricted_preference"; - - @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils; - - public UnrestrictedPreferenceController(Context context, int uid, String packageName) { - super(context); - mBatteryOptimizeUtils = new BatteryOptimizeUtils(context, uid, packageName); - } - - @Override - public void updateState(Preference preference) { - preference.setEnabled(mBatteryOptimizeUtils.isSelectorPreferenceEnabled()); - - final boolean isUnrestricted = - mBatteryOptimizeUtils.getAppOptimizationMode() - == BatteryOptimizeUtils.MODE_UNRESTRICTED; - ((SelectorWithWidgetPreference) preference).setChecked(isUnrestricted); - } - - @Override - public boolean isAvailable() { - return true; - } - - @Override - public String getPreferenceKey() { - return KEY_UNRESTRICTED_PREF; - } - - @Override - public boolean handlePreferenceTreeClick(Preference preference) { - return getPreferenceKey().equals(preference.getKey()); - } -} diff --git a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java index cfaee006d39..9b4da46a98d 100644 --- a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java +++ b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java @@ -243,7 +243,9 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment i protected boolean setDefaultKey(String key) { setCurrentSystemNavigationMode(mOverlayManager, key); setIllustrationVideo(mVideoPreference, key); - setGestureNavigationTutorialDialog(key); + if (!android.provider.Flags.a11yStandaloneGestureEnabled()) { + setGestureNavigationTutorialDialog(key); + } setIllustrationClickListener(mVideoPreference, key); return true; } diff --git a/src/com/android/settings/network/NetworkProviderSettings.java b/src/com/android/settings/network/NetworkProviderSettings.java index 69183ff25c0..b268461a0ec 100644 --- a/src/com/android/settings/network/NetworkProviderSettings.java +++ b/src/com/android/settings/network/NetworkProviderSettings.java @@ -64,8 +64,8 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.settings.AirplaneModeEnabler; import com.android.settings.R; -import com.android.settings.RestrictedSettingsFragment; import com.android.settings.core.SubSettingLauncher; +import com.android.settings.dashboard.RestrictedDashboardFragment; import com.android.settings.datausage.DataUsagePreference; import com.android.settings.datausage.DataUsageUtils; import com.android.settings.location.WifiScanningFragment; @@ -104,7 +104,7 @@ import java.util.Optional; * UI for Mobile network and Wi-Fi network settings. */ @SearchIndexable -public class NetworkProviderSettings extends RestrictedSettingsFragment +public class NetworkProviderSettings extends RestrictedDashboardFragment implements Indexable, WifiPickerTracker.WifiPickerTrackerCallback, WifiDialog2.WifiDialog2Listener, DialogInterface.OnDismissListener, AirplaneModeEnabler.OnAirplaneModeChangedListener, InternetUpdater.InternetChangeListener { @@ -356,9 +356,17 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment mIsGuest = userManager.isGuestUser(); } - private void addPreferences() { - addPreferencesFromResource(R.xml.network_provider_settings); + @Override + protected String getLogTag() { + return TAG; + } + @Override + protected int getPreferenceScreenResId() { + return R.xml.network_provider_settings; + } + + private void addPreferences() { mAirplaneModeMsgPreference = findPreference(PREF_KEY_AIRPLANE_MODE_MSG); updateAirplaneModeMsgPreference(mAirplaneModeEnabler.isAirplaneModeOn() /* visible */); mConnectedWifiEntryPreferenceCategory = findPreference(PREF_KEY_CONNECTED_ACCESS_POINTS); diff --git a/src/com/android/settings/network/apn/ApnEditor.java b/src/com/android/settings/network/apn/ApnEditor.java index dc9741d67bc..533fd292289 100644 --- a/src/com/android/settings/network/apn/ApnEditor.java +++ b/src/com/android/settings/network/apn/ApnEditor.java @@ -16,153 +16,33 @@ package com.android.settings.network.apn; -import android.app.Dialog; +import static com.android.settings.network.apn.ApnEditPageProviderKt.EDIT_URL; +import static com.android.settings.network.apn.ApnEditPageProviderKt.INSERT_URL; + import android.app.settings.SettingsEnums; -import android.content.ContentValues; -import android.content.Context; import android.content.Intent; -import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.PersistableBundle; import android.os.UserManager; import android.provider.Telephony; import android.telephony.CarrierConfigManager; -import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.text.TextUtils; import android.util.Log; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.View.OnKeyListener; -import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import androidx.appcompat.app.AlertDialog; -import androidx.preference.EditTextPreference; -import androidx.preference.ListPreference; -import androidx.preference.MultiSelectListPreference; -import androidx.preference.Preference; -import androidx.preference.Preference.OnPreferenceChangeListener; -import androidx.preference.TwoStatePreference; import com.android.internal.util.ArrayUtils; -import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.core.instrumentation.InstrumentedDialogFragment; -import com.android.settings.network.ProxySubscriptionManager; -import com.android.settingslib.utils.ThreadUtils; +import com.android.settings.spa.SpaActivity; import java.util.Arrays; -import java.util.HashSet; import java.util.List; -import java.util.Objects; -import java.util.Set; /** Use to edit apn settings. */ -public class ApnEditor extends SettingsPreferenceFragment - implements OnPreferenceChangeListener, OnKeyListener { +public class ApnEditor extends SettingsPreferenceFragment { private static final String TAG = ApnEditor.class.getSimpleName(); - private static final boolean VDBG = false; // STOPSHIP if true - - private static final String KEY_AUTH_TYPE = "auth_type"; - private static final String KEY_APN_TYPE = "apn_type"; - private static final String KEY_PROTOCOL = "apn_protocol"; - private static final String KEY_ROAMING_PROTOCOL = "apn_roaming_protocol"; - private static final String KEY_CARRIER_ENABLED = "carrier_enabled"; - private static final String KEY_BEARER_MULTI = "bearer_multi"; - private static final String KEY_MVNO_TYPE = "mvno_type"; - private static final String KEY_PASSWORD = "apn_password"; - - @VisibleForTesting - static final int MENU_DELETE = Menu.FIRST; - private static final int MENU_SAVE = Menu.FIRST + 1; - private static final int MENU_CANCEL = Menu.FIRST + 2; - - @VisibleForTesting - static String sNotSet; - @VisibleForTesting - EditTextPreference mName; - @VisibleForTesting - EditTextPreference mApn; - @VisibleForTesting - EditTextPreference mProxy; - @VisibleForTesting - EditTextPreference mPort; - @VisibleForTesting - EditTextPreference mUser; - @VisibleForTesting - EditTextPreference mServer; - @VisibleForTesting - EditTextPreference mPassword; - @VisibleForTesting - EditTextPreference mMmsc; - @VisibleForTesting - EditTextPreference mMcc; - @VisibleForTesting - EditTextPreference mMnc; - @VisibleForTesting - EditTextPreference mMmsProxy; - @VisibleForTesting - EditTextPreference mMmsPort; - @VisibleForTesting - ListPreference mAuthType; - @VisibleForTesting - EditTextPreference mApnType; - @VisibleForTesting - ListPreference mProtocol; - @VisibleForTesting - ListPreference mRoamingProtocol; - @VisibleForTesting - TwoStatePreference mCarrierEnabled; - @VisibleForTesting - MultiSelectListPreference mBearerMulti; - @VisibleForTesting - ListPreference mMvnoType; - @VisibleForTesting - EditTextPreference mMvnoMatchData; - - @VisibleForTesting - ApnData mApnData; - - private String mCurMnc; - private String mCurMcc; - - private boolean mNewApn; - private int mSubId; - @VisibleForTesting - ProxySubscriptionManager mProxySubscriptionMgr; - private int mBearerInitialVal = 0; - private String mMvnoTypeStr; - private String mMvnoMatchDataStr; - @VisibleForTesting - String[] mReadOnlyApnTypes; - @VisibleForTesting - String[] mDefaultApnTypes; - @VisibleForTesting - String mDefaultApnProtocol; - @VisibleForTesting - String mDefaultApnRoamingProtocol; - private String[] mReadOnlyApnFields; - private boolean mReadOnlyApn; - /** - * The APN deletion feature within menu is aligned with the APN adding feature. - * Having only one of them could lead to a UX which not that make sense from user's - * perspective. - * - * mIsAddApnAllowed stores the configuration value reading from - * CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL to support the presentation - * control of the menu options. When false, delete option would be invisible to - * the end user. - */ - private boolean mIsAddApnAllowed; - private Uri mCarrierUri; - private boolean mIsCarrierIdApn; /** * APN types for data connections. These are usage categories for an APN @@ -225,150 +105,41 @@ public class ApnEditor extends SettingsPreferenceFragment APN_TYPE_OEM_PRIVATE, }; - /** - * Standard projection for the interesting columns of a normal note. - */ - private static final String[] sProjection = new String[] { - Telephony.Carriers._ID, // 0 - Telephony.Carriers.NAME, // 1 - Telephony.Carriers.APN, // 2 - Telephony.Carriers.PROXY, // 3 - Telephony.Carriers.PORT, // 4 - Telephony.Carriers.USER, // 5 - Telephony.Carriers.SERVER, // 6 - Telephony.Carriers.PASSWORD, // 7 - Telephony.Carriers.MMSC, // 8 - Telephony.Carriers.MCC, // 9 - Telephony.Carriers.MNC, // 10 - Telephony.Carriers.NUMERIC, // 11 - Telephony.Carriers.MMSPROXY, // 12 - Telephony.Carriers.MMSPORT, // 13 - Telephony.Carriers.AUTH_TYPE, // 14 - Telephony.Carriers.TYPE, // 15 - Telephony.Carriers.PROTOCOL, // 16 - Telephony.Carriers.CARRIER_ENABLED, // 17 - Telephony.Carriers.BEARER, // 18 - Telephony.Carriers.BEARER_BITMASK, // 19 - Telephony.Carriers.ROAMING_PROTOCOL, // 20 - Telephony.Carriers.MVNO_TYPE, // 21 - Telephony.Carriers.MVNO_MATCH_DATA, // 22 - Telephony.Carriers.EDITED_STATUS, // 23 - Telephony.Carriers.USER_EDITABLE, // 24 - Telephony.Carriers.CARRIER_ID // 25 - }; - - private static final int ID_INDEX = 0; - @VisibleForTesting - static final int NAME_INDEX = 1; - @VisibleForTesting - static final int APN_INDEX = 2; - private static final int PROXY_INDEX = 3; - private static final int PORT_INDEX = 4; - private static final int USER_INDEX = 5; - private static final int SERVER_INDEX = 6; - private static final int PASSWORD_INDEX = 7; - private static final int MMSC_INDEX = 8; - @VisibleForTesting - static final int MCC_INDEX = 9; - @VisibleForTesting - static final int MNC_INDEX = 10; - private static final int MMSPROXY_INDEX = 12; - private static final int MMSPORT_INDEX = 13; - private static final int AUTH_TYPE_INDEX = 14; - @VisibleForTesting - static final int TYPE_INDEX = 15; - @VisibleForTesting - static final int PROTOCOL_INDEX = 16; - @VisibleForTesting - static final int CARRIER_ENABLED_INDEX = 17; - private static final int BEARER_INDEX = 18; - private static final int BEARER_BITMASK_INDEX = 19; - @VisibleForTesting - static final int ROAMING_PROTOCOL_INDEX = 20; - private static final int MVNO_TYPE_INDEX = 21; - private static final int MVNO_MATCH_DATA_INDEX = 22; - private static final int EDITED_INDEX = 23; - private static final int USER_EDITABLE_INDEX = 24; - private static final int CARRIER_ID_INDEX = 25; - @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); + maybeRedirectToNewPage(); + finish(); + } + + private void maybeRedirectToNewPage() { if (isUserRestricted()) { Log.e(TAG, "This setting isn't available due to user restriction."); - finish(); return; } - setLifecycleForAllControllers(); - final Intent intent = getIntent(); final String action = intent.getAction(); - if (TextUtils.isEmpty(action)) { - finish(); - return; - } - mSubId = intent.getIntExtra(ApnSettings.SUB_ID, - SubscriptionManager.INVALID_SUBSCRIPTION_ID); - initApnEditorUi(); - getCarrierCustomizedConfig(getContext()); + int subId = + intent.getIntExtra(ApnSettings.SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID); - Uri uri = null; - if (action.equals(Intent.ACTION_EDIT)) { - uri = intent.getData(); + Uri uri = intent.getData(); + if (Intent.ACTION_EDIT.equals(action)) { if (!uri.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) { Log.e(TAG, "Edit request not for carrier table. Uri: " + uri); - finish(); - return; + } else { + String route = ApnEditPageProvider.INSTANCE.getRoute(EDIT_URL, uri, subId); + SpaActivity.startSpaActivity(requireContext(), route); } - } else if (action.equals(Intent.ACTION_INSERT)) { - mCarrierUri = intent.getData(); - if (!mCarrierUri.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) { - Log.e(TAG, "Insert request not for carrier table. Uri: " + mCarrierUri); - finish(); - return; + } else if (Intent.ACTION_INSERT.equals(action)) { + if (!uri.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) { + Log.e(TAG, "Insert request not for carrier table. Uri: " + uri); + } else { + String route = ApnEditPageProvider.INSTANCE.getRoute( + INSERT_URL, Telephony.Carriers.CONTENT_URI, subId); + SpaActivity.startSpaActivity(getContext(), route); } - mNewApn = true; - mMvnoTypeStr = intent.getStringExtra(ApnSettings.MVNO_TYPE); - mMvnoMatchDataStr = intent.getStringExtra(ApnSettings.MVNO_MATCH_DATA); - } else { - finish(); - return; - } - - // Creates an ApnData to store the apn data temporary, so that we don't need the cursor to - // get the apn data. The uri is null if the action is ACTION_INSERT, that mean there is no - // record in the database, so create a empty ApnData to represent a empty row of database. - if (uri != null) { - mApnData = getApnDataFromUri(uri); - } else { - mApnData = new ApnData(sProjection.length); - } - final int carrierId = mApnData.getInteger(CARRIER_ID_INDEX, - TelephonyManager.UNKNOWN_CARRIER_ID); - mIsCarrierIdApn = (carrierId > TelephonyManager.UNKNOWN_CARRIER_ID); - - final boolean isUserEdited = mApnData.getInteger(EDITED_INDEX, - Telephony.Carriers.USER_EDITED) == Telephony.Carriers.USER_EDITED; - - Log.d(TAG, "onCreate: EDITED " + isUserEdited); - // if it's not a USER_EDITED apn, check if it's read-only - if (!isUserEdited && (mApnData.getInteger(USER_EDITABLE_INDEX, 1) == 0 - || apnTypesMatch(mReadOnlyApnTypes, mApnData.getString(TYPE_INDEX)))) { - Log.d(TAG, "onCreate: apnTypesMatch; read-only APN"); - mReadOnlyApn = true; - disableAllFields(); - } else if (!ArrayUtils.isEmpty(mReadOnlyApnFields)) { - disableFields(mReadOnlyApnFields); - } - // Make sure that a user cannot break carrier id APN matching - if (mIsCarrierIdApn) { - disableFieldsForCarrieridApn(); - } - - for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) { - getPreferenceScreen().getPreference(i).setOnPreferenceChangeListener(this); } } @@ -384,44 +155,6 @@ public class ApnEditor extends SettingsPreferenceFragment return ArrayUtils.concat(String.class, carrierReadOnlyApnTypes, ALWAYS_READ_ONLY_APN_TYPES); } - /** - * Enable ProxySubscriptionMgr with Lifecycle support for all controllers - * live within this fragment - */ - private void setLifecycleForAllControllers() { - if (mProxySubscriptionMgr == null) { - mProxySubscriptionMgr = ProxySubscriptionManager.getInstance(getContext()); - } - mProxySubscriptionMgr.setLifecycle(getLifecycle()); - } - - @Override - public void onViewStateRestored(@Nullable Bundle savedInstanceState) { - super.onViewStateRestored(savedInstanceState); - fillUI(savedInstanceState == null); - setCarrierCustomizedConfigToUi(); - } - - @VisibleForTesting - static String formatInteger(String value) { - try { - final int intValue = Integer.parseInt(value); - return String.format(getCorrectDigitsFormat(value), intValue); - } catch (NumberFormatException e) { - return value; - } - } - - /** - * Get the digits format so we preserve leading 0's. - * MCCs are 3 digits and MNCs are either 2 or 3. - */ - static String getCorrectDigitsFormat(String value) { - if (value.length() == 2) return "%02d"; - else return "%03d"; - } - - /** * Check if passed in array of APN types indicates all APN types * @param apnTypes array of APN types. "*" indicates all types. @@ -447,1045 +180,11 @@ public class ApnEditor extends SettingsPreferenceFragment return true; } - /** - * Check if APN types overlap. - * @param apnTypesArray1 array of APNs. Empty array indicates no APN type; "*" indicates all - * types - * @param apnTypes2 comma separated string of APN types. Empty string represents all types. - * @return if any apn type matches return true, otherwise return false - */ - private boolean apnTypesMatch(String[] apnTypesArray1, String apnTypes2) { - if (ArrayUtils.isEmpty(apnTypesArray1)) { - return false; - } - - final String[] apnTypesArray1LowerCase = new String[apnTypesArray1.length]; - for (int i = 0; i < apnTypesArray1.length; i++) { - apnTypesArray1LowerCase[i] = apnTypesArray1[i].toLowerCase(); - } - - if (hasAllApns(apnTypesArray1LowerCase) || TextUtils.isEmpty(apnTypes2)) { - return true; - } - - final List apnTypesList1 = Arrays.asList(apnTypesArray1LowerCase); - final String[] apnTypesArray2 = apnTypes2.split(","); - - for (String apn : apnTypesArray2) { - if (apnTypesList1.contains(apn.trim().toLowerCase())) { - Log.d(TAG, "apnTypesMatch: true because match found for " + apn.trim()); - return true; - } - } - - Log.d(TAG, "apnTypesMatch: false"); - return false; - } - - /** - * Function to get Preference obj corresponding to an apnField - * @param apnField apn field name for which pref is needed - * @return Preference obj corresponding to passed in apnField - */ - private Preference getPreferenceFromFieldName(String apnField) { - switch (apnField) { - case Telephony.Carriers.NAME: - return mName; - case Telephony.Carriers.APN: - return mApn; - case Telephony.Carriers.PROXY: - return mProxy; - case Telephony.Carriers.PORT: - return mPort; - case Telephony.Carriers.USER: - return mUser; - case Telephony.Carriers.SERVER: - return mServer; - case Telephony.Carriers.PASSWORD: - return mPassword; - case Telephony.Carriers.MMSPROXY: - return mMmsProxy; - case Telephony.Carriers.MMSPORT: - return mMmsPort; - case Telephony.Carriers.MMSC: - return mMmsc; - case Telephony.Carriers.MCC: - return mMcc; - case Telephony.Carriers.MNC: - return mMnc; - case Telephony.Carriers.TYPE: - return mApnType; - case Telephony.Carriers.AUTH_TYPE: - return mAuthType; - case Telephony.Carriers.PROTOCOL: - return mProtocol; - case Telephony.Carriers.ROAMING_PROTOCOL: - return mRoamingProtocol; - case Telephony.Carriers.CARRIER_ENABLED: - return mCarrierEnabled; - case Telephony.Carriers.BEARER: - case Telephony.Carriers.BEARER_BITMASK: - return mBearerMulti; - case Telephony.Carriers.MVNO_TYPE: - return mMvnoType; - case Telephony.Carriers.MVNO_MATCH_DATA: - return mMvnoMatchData; - } - return null; - } - - /** - * Disables given fields so that user cannot modify them - * - * @param apnFields fields to be disabled - */ - private void disableFields(String[] apnFields) { - for (String apnField : apnFields) { - final Preference preference = getPreferenceFromFieldName(apnField); - if (preference != null) { - preference.setEnabled(false); - } - } - } - - /** - * Disables all fields so that user cannot modify the APN - */ - private void disableAllFields() { - mName.setEnabled(false); - mApn.setEnabled(false); - mProxy.setEnabled(false); - mPort.setEnabled(false); - mUser.setEnabled(false); - mServer.setEnabled(false); - mPassword.setEnabled(false); - mMmsProxy.setEnabled(false); - mMmsPort.setEnabled(false); - mMmsc.setEnabled(false); - mMcc.setEnabled(false); - mMnc.setEnabled(false); - mApnType.setEnabled(false); - mAuthType.setEnabled(false); - mProtocol.setEnabled(false); - mRoamingProtocol.setEnabled(false); - mCarrierEnabled.setEnabled(false); - mBearerMulti.setEnabled(false); - mMvnoType.setEnabled(false); - mMvnoMatchData.setEnabled(false); - } - - /** - * Disables fields for a carrier id APN to avoid breaking the match criteria - */ - private void disableFieldsForCarrieridApn() { - mMcc.setEnabled(false); - mMnc.setEnabled(false); - mMvnoType.setEnabled(false); - mMvnoMatchData.setEnabled(false); - } - @Override public int getMetricsCategory() { return SettingsEnums.APN_EDITOR; } - @VisibleForTesting - void fillUI(boolean firstTime) { - if (firstTime) { - // Fill in all the values from the db in both text editor and summary - mName.setText(mApnData.getString(NAME_INDEX)); - mApn.setText(mApnData.getString(APN_INDEX)); - mProxy.setText(mApnData.getString(PROXY_INDEX)); - mPort.setText(mApnData.getString(PORT_INDEX)); - mUser.setText(mApnData.getString(USER_INDEX)); - mServer.setText(mApnData.getString(SERVER_INDEX)); - mPassword.setText(mApnData.getString(PASSWORD_INDEX)); - mMmsProxy.setText(mApnData.getString(MMSPROXY_INDEX)); - mMmsPort.setText(mApnData.getString(MMSPORT_INDEX)); - mMmsc.setText(mApnData.getString(MMSC_INDEX)); - mMcc.setText(mApnData.getString(MCC_INDEX)); - mMnc.setText(mApnData.getString(MNC_INDEX)); - mApnType.setText(mApnData.getString(TYPE_INDEX)); - if (mNewApn) { - final SubscriptionInfo subInfo = - mProxySubscriptionMgr.getAccessibleSubscriptionInfo(mSubId); - - // Country code - final String mcc = (subInfo == null) ? null : subInfo.getMccString(); - // Network code - final String mnc = (subInfo == null) ? null : subInfo.getMncString(); - - if (!TextUtils.isEmpty(mcc)) { - // Auto populate MNC and MCC for new entries, based on what SIM reports - mMcc.setText(mcc); - mMnc.setText(mnc); - mCurMnc = mnc; - mCurMcc = mcc; - } - } - final int authVal = mApnData.getInteger(AUTH_TYPE_INDEX, -1); - if (authVal != -1) { - mAuthType.setValueIndex(authVal); - } else { - mAuthType.setValue(null); - } - - mProtocol.setValue(mApnData.getString(PROTOCOL_INDEX)); - mRoamingProtocol.setValue(mApnData.getString(ROAMING_PROTOCOL_INDEX)); - mCarrierEnabled.setChecked(mApnData.getInteger(CARRIER_ENABLED_INDEX, 1) == 1); - mBearerInitialVal = mApnData.getInteger(BEARER_INDEX, 0); - - final HashSet bearers = new HashSet(); - int bearerBitmask = mApnData.getInteger(BEARER_BITMASK_INDEX, 0); - if (bearerBitmask == 0) { - if (mBearerInitialVal == 0) { - bearers.add("" + 0); - } - } else { - int i = 1; - while (bearerBitmask != 0) { - if ((bearerBitmask & 1) == 1) { - bearers.add("" + i); - } - bearerBitmask >>= 1; - i++; - } - } - - if (mBearerInitialVal != 0 && !bearers.contains("" + mBearerInitialVal)) { - // add mBearerInitialVal to bearers - bearers.add("" + mBearerInitialVal); - } - mBearerMulti.setValues(bearers); - - mMvnoType.setValue(mApnData.getString(MVNO_TYPE_INDEX)); - mMvnoMatchData.setEnabled(false); - mMvnoMatchData.setText(mApnData.getString(MVNO_MATCH_DATA_INDEX)); - if (mNewApn && mMvnoTypeStr != null && mMvnoMatchDataStr != null) { - mMvnoType.setValue(mMvnoTypeStr); - mMvnoMatchData.setText(mMvnoMatchDataStr); - } - } - - mName.setSummary(checkNull(mName.getText())); - mApn.setSummary(checkNull(mApn.getText())); - mProxy.setSummary(checkNull(mProxy.getText())); - mPort.setSummary(checkNull(mPort.getText())); - mUser.setSummary(checkNull(mUser.getText())); - mServer.setSummary(checkNull(mServer.getText())); - mPassword.setSummary(starify(mPassword.getText())); - mMmsProxy.setSummary(checkNull(mMmsProxy.getText())); - mMmsPort.setSummary(checkNull(mMmsPort.getText())); - mMmsc.setSummary(checkNull(mMmsc.getText())); - mMcc.setSummary(formatInteger(checkNull(mMcc.getText()))); - mMnc.setSummary(formatInteger(checkNull(mMnc.getText()))); - mApnType.setSummary(checkNull(mApnType.getText())); - - final String authVal = mAuthType.getValue(); - if (authVal != null) { - final int authValIndex = Integer.parseInt(authVal); - mAuthType.setValueIndex(authValIndex); - - final String[] values = getResources().getStringArray(R.array.apn_auth_entries); - mAuthType.setSummary(values[authValIndex]); - } else { - mAuthType.setSummary(sNotSet); - } - - mProtocol.setSummary(checkNull(protocolDescription(mProtocol.getValue(), mProtocol))); - mRoamingProtocol.setSummary( - checkNull(protocolDescription(mRoamingProtocol.getValue(), mRoamingProtocol))); - mBearerMulti.setSummary( - checkNull(bearerMultiDescription(mBearerMulti.getValues()))); - mMvnoType.setSummary( - checkNull(mvnoDescription(mMvnoType.getValue()))); - mMvnoMatchData.setSummary(checkNullforMvnoValue(mMvnoMatchData.getText())); - // allow user to edit carrier_enabled for some APN - final boolean ceEditable = getResources().getBoolean( - R.bool.config_allow_edit_carrier_enabled); - if (ceEditable) { - mCarrierEnabled.setEnabled(true); - } else { - mCarrierEnabled.setEnabled(false); - } - } - - /** - * Returns the UI choice (e.g., "IPv4/IPv6") corresponding to the given - * raw value of the protocol preference (e.g., "IPV4V6"). If unknown, - * return null. - */ - private String protocolDescription(String raw, ListPreference protocol) { - String uRaw = checkNull(raw).toUpperCase(); - uRaw = uRaw.equals("IPV4") ? "IP" : uRaw; - final int protocolIndex = protocol.findIndexOfValue(uRaw); - if (protocolIndex == -1) { - return null; - } else { - final String[] values = getResources().getStringArray(R.array.apn_protocol_entries); - try { - return values[protocolIndex]; - } catch (ArrayIndexOutOfBoundsException e) { - return null; - } - } - } - - private String bearerMultiDescription(Set raw) { - final String[] values = getResources().getStringArray(R.array.bearer_entries); - final StringBuilder retVal = new StringBuilder(); - boolean first = true; - for (String bearer : raw) { - int bearerIndex = mBearerMulti.findIndexOfValue(bearer); - try { - if (first) { - retVal.append(values[bearerIndex]); - first = false; - } else { - retVal.append(", " + values[bearerIndex]); - } - } catch (ArrayIndexOutOfBoundsException e) { - // ignore - } - } - final String val = retVal.toString(); - if (!TextUtils.isEmpty(val)) { - return val; - } - return null; - } - - private String mvnoDescription(String newValue) { - final int mvnoIndex = mMvnoType.findIndexOfValue(newValue); - final String oldValue = mMvnoType.getValue(); - - if (mvnoIndex == -1) { - return null; - } else { - final String[] values = getResources().getStringArray(R.array.mvno_type_entries); - final boolean mvnoMatchDataUneditable = - mReadOnlyApn || (mReadOnlyApnFields != null - && Arrays.asList(mReadOnlyApnFields) - .contains(Telephony.Carriers.MVNO_MATCH_DATA)); - mMvnoMatchData.setEnabled(!mvnoMatchDataUneditable && mvnoIndex != 0); - if (newValue != null && !newValue.equals(oldValue)) { - if (values[mvnoIndex].equals("SPN")) { - TelephonyManager telephonyManager = (TelephonyManager) - getContext().getSystemService(TelephonyManager.class); - final TelephonyManager telephonyManagerForSubId = - telephonyManager.createForSubscriptionId(mSubId); - if (telephonyManagerForSubId != null) { - telephonyManager = telephonyManagerForSubId; - } - mMvnoMatchData.setText(telephonyManager.getSimOperatorName()); - } else if (values[mvnoIndex].equals("IMSI")) { - final SubscriptionInfo subInfo = - mProxySubscriptionMgr.getAccessibleSubscriptionInfo(mSubId); - final String mcc = (subInfo == null) ? "" : - Objects.toString(subInfo.getMccString(), ""); - final String mnc = (subInfo == null) ? "" : - Objects.toString(subInfo.getMncString(), ""); - mMvnoMatchData.setText(mcc + mnc + "x"); - } else if (values[mvnoIndex].equals("GID")) { - TelephonyManager telephonyManager = (TelephonyManager) - getContext().getSystemService(TelephonyManager.class); - final TelephonyManager telephonyManagerForSubId = - telephonyManager.createForSubscriptionId(mSubId); - if (telephonyManagerForSubId != null) { - telephonyManager = telephonyManagerForSubId; - } - mMvnoMatchData.setText(telephonyManager.getGroupIdLevel1()); - } else { - // mvno type 'none' case. At this time, mvnoIndex should be 0. - mMvnoMatchData.setText(""); - } - } - - try { - return values[mvnoIndex]; - } catch (ArrayIndexOutOfBoundsException e) { - return null; - } - } - } - /** - * Callback when preference status changed. - */ - public boolean onPreferenceChange(Preference preference, Object newValue) { - String key = preference.getKey(); - if (KEY_AUTH_TYPE.equals(key)) { - try { - final int index = Integer.parseInt((String) newValue); - mAuthType.setValueIndex(index); - - final String[] values = getResources().getStringArray(R.array.apn_auth_entries); - mAuthType.setSummary(values[index]); - } catch (NumberFormatException e) { - return false; - } - } else if (KEY_APN_TYPE.equals(key)) { - String data = (TextUtils.isEmpty((String) newValue) - && !ArrayUtils.isEmpty(mDefaultApnTypes)) - ? getEditableApnType(mDefaultApnTypes) : (String) newValue; - if (!TextUtils.isEmpty(data)) { - mApnType.setSummary(data); - } - } else if (KEY_PROTOCOL.equals(key)) { - final String protocol = protocolDescription((String) newValue, mProtocol); - if (protocol == null) { - return false; - } - mProtocol.setSummary(protocol); - mProtocol.setValue((String) newValue); - } else if (KEY_ROAMING_PROTOCOL.equals(key)) { - final String protocol = protocolDescription((String) newValue, mRoamingProtocol); - if (protocol == null) { - return false; - } - mRoamingProtocol.setSummary(protocol); - mRoamingProtocol.setValue((String) newValue); - } else if (KEY_BEARER_MULTI.equals(key)) { - final String bearer = bearerMultiDescription((Set) newValue); - if (bearer == null) { - return false; - } - mBearerMulti.setValues((Set) newValue); - mBearerMulti.setSummary(bearer); - } else if (KEY_MVNO_TYPE.equals(key)) { - final String mvno = mvnoDescription((String) newValue); - if (mvno == null) { - return false; - } - mMvnoType.setValue((String) newValue); - mMvnoType.setSummary(mvno); - mMvnoMatchData.setSummary(checkNullforMvnoValue(mMvnoMatchData.getText())); - } else if (KEY_PASSWORD.equals(key)) { - mPassword.setSummary(starify(newValue != null ? String.valueOf(newValue) : "")); - } else if (KEY_CARRIER_ENABLED.equals(key)) { - // do nothing - } else { - preference.setSummary(checkNull(newValue != null ? String.valueOf(newValue) : null)); - } - return true; - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - // If it's a new APN, then cancel will delete the new entry in onPause - // If APN add is not allowed, delete might lead to issue regarding recovery - if (!mNewApn && !mReadOnlyApn && mIsAddApnAllowed) { - menu.add(0, MENU_DELETE, 0, R.string.menu_delete) - .setIcon(R.drawable.ic_delete); - } - if (!mReadOnlyApn) { - menu.add(0, MENU_SAVE, 0, R.string.menu_save) - .setIcon(android.R.drawable.ic_menu_save); - } - menu.add(0, MENU_CANCEL, 0, R.string.menu_cancel) - .setIcon(android.R.drawable.ic_menu_close_clear_cancel); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case MENU_DELETE: - deleteApn(); - finish(); - return true; - case MENU_SAVE: - if (validateAndSaveApnData()) { - finish(); - } - return true; - case MENU_CANCEL: - finish(); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - view.setOnKeyListener(this); - view.setFocusableInTouchMode(true); - view.requestFocus(); - } - - /** - * Try to save the apn data when pressed the back button. An error message will be displayed if - * the apn data is invalid. - * - * TODO(b/77339593): Try to keep the same behavior between back button and up navigate button. - * We will save the valid apn data to the database when pressed the back button, but discard all - * user changed when pressed the up navigate button. - */ - @Override - public boolean onKey(View v, int keyCode, KeyEvent event) { - if (event.getAction() != KeyEvent.ACTION_DOWN) return false; - switch (keyCode) { - case KeyEvent.KEYCODE_BACK: { - if (validateAndSaveApnData()) { - finish(); - } - return true; - } - } - return false; - } - - /** - * Add key, value to {@code cv} and compare the value against the value at index in - * {@link #mApnData}. - * - *

- * The key, value will not add to {@code cv} if value is null. - * - * @return true if values are different. {@code assumeDiff} indicates if values can be assumed - * different in which case no comparison is needed. - */ - boolean setStringValueAndCheckIfDiff( - ContentValues cv, String key, String value, boolean assumeDiff, int index) { - final String valueFromLocalCache = mApnData.getString(index); - if (VDBG) { - Log.d(TAG, "setStringValueAndCheckIfDiff: assumeDiff: " + assumeDiff - + " key: " + key - + " value: '" + value - + "' valueFromDb: '" + valueFromLocalCache + "'"); - } - final boolean isDiff = assumeDiff - || !((TextUtils.isEmpty(value) && TextUtils.isEmpty(valueFromLocalCache)) - || (value != null && value.equals(valueFromLocalCache))); - - if (isDiff && value != null) { - cv.put(key, value); - } - return isDiff; - } - - /** - * Add key, value to {@code cv} and compare the value against the value at index in - * {@link #mApnData}. - * - * @return true if values are different. {@code assumeDiff} indicates if values can be assumed - * different in which case no comparison is needed. - */ - boolean setIntValueAndCheckIfDiff( - ContentValues cv, String key, int value, boolean assumeDiff, int index) { - final Integer valueFromLocalCache = mApnData.getInteger(index); - if (VDBG) { - Log.d(TAG, "setIntValueAndCheckIfDiff: assumeDiff: " + assumeDiff - + " key: " + key - + " value: '" + value - + "' valueFromDb: '" + valueFromLocalCache + "'"); - } - - final boolean isDiff = assumeDiff || value != valueFromLocalCache; - if (isDiff) { - cv.put(key, value); - } - return isDiff; - } - - /** - * Validates the apn data and save it to the database if it's valid. - * - *

- * A dialog with error message will be displayed if the APN data is invalid. - * - * @return true if there is no error - */ - @VisibleForTesting - boolean validateAndSaveApnData() { - // Nothing to do if it's a read only APN - if (mReadOnlyApn) { - return true; - } - - final String name = checkNotSet(mName.getText()); - final String apn = checkNotSet(mApn.getText()); - final String mcc = checkNotSet(mMcc.getText()); - final String mnc = checkNotSet(mMnc.getText()); - - final String errorMsg = validateApnData(); - if (errorMsg != null) { - showError(); - return false; - } - - final ContentValues values = new ContentValues(); - // call update() if it's a new APN. If not, check if any field differs from the db value; - // if any diff is found update() should be called - boolean callUpdate = mNewApn; - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.NAME, - name, - callUpdate, - NAME_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.APN, - apn, - callUpdate, - APN_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.PROXY, - checkNotSet(mProxy.getText()), - callUpdate, - PROXY_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.PORT, - checkNotSet(mPort.getText()), - callUpdate, - PORT_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.MMSPROXY, - checkNotSet(mMmsProxy.getText()), - callUpdate, - MMSPROXY_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.MMSPORT, - checkNotSet(mMmsPort.getText()), - callUpdate, - MMSPORT_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.USER, - checkNotSet(mUser.getText()), - callUpdate, - USER_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.SERVER, - checkNotSet(mServer.getText()), - callUpdate, - SERVER_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.PASSWORD, - checkNotSet(mPassword.getText()), - callUpdate, - PASSWORD_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.MMSC, - checkNotSet(mMmsc.getText()), - callUpdate, - MMSC_INDEX); - - final String authVal = mAuthType.getValue(); - if (authVal != null) { - callUpdate = setIntValueAndCheckIfDiff(values, - Telephony.Carriers.AUTH_TYPE, - Integer.parseInt(authVal), - callUpdate, - AUTH_TYPE_INDEX); - } - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.PROTOCOL, - checkNotSet(mProtocol.getValue()), - callUpdate, - PROTOCOL_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.ROAMING_PROTOCOL, - checkNotSet(mRoamingProtocol.getValue()), - callUpdate, - ROAMING_PROTOCOL_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.TYPE, - checkNotSet(getUserEnteredApnType()), - callUpdate, - TYPE_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.MCC, - mcc, - callUpdate, - MCC_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.MNC, - mnc, - callUpdate, - MNC_INDEX); - - values.put(Telephony.Carriers.NUMERIC, mcc + mnc); - - if (mCurMnc != null && mCurMcc != null) { - if (mCurMnc.equals(mnc) && mCurMcc.equals(mcc)) { - values.put(Telephony.Carriers.CURRENT, 1); - } - } - - final Set bearerSet = mBearerMulti.getValues(); - int bearerBitmask = 0; - for (String bearer : bearerSet) { - if (Integer.parseInt(bearer) == 0) { - bearerBitmask = 0; - break; - } else { - bearerBitmask |= getBitmaskForTech(Integer.parseInt(bearer)); - } - } - callUpdate = setIntValueAndCheckIfDiff(values, - Telephony.Carriers.BEARER_BITMASK, - bearerBitmask, - callUpdate, - BEARER_BITMASK_INDEX); - - int bearerVal; - if (bearerBitmask == 0 || mBearerInitialVal == 0) { - bearerVal = 0; - } else if (bitmaskHasTech(bearerBitmask, mBearerInitialVal)) { - bearerVal = mBearerInitialVal; - } else { - // bearer field was being used but bitmask has changed now and does not include the - // initial bearer value -- setting bearer to 0 but maybe better behavior is to choose a - // random tech from the new bitmask?? - bearerVal = 0; - } - callUpdate = setIntValueAndCheckIfDiff(values, - Telephony.Carriers.BEARER, - bearerVal, - callUpdate, - BEARER_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.MVNO_TYPE, - checkNotSet(mMvnoType.getValue()), - callUpdate, - MVNO_TYPE_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.MVNO_MATCH_DATA, - checkNotSet(mMvnoMatchData.getText()), - callUpdate, - MVNO_MATCH_DATA_INDEX); - - callUpdate = setIntValueAndCheckIfDiff(values, - Telephony.Carriers.CARRIER_ENABLED, - mCarrierEnabled.isChecked() ? 1 : 0, - callUpdate, - CARRIER_ENABLED_INDEX); - - values.put(Telephony.Carriers.EDITED_STATUS, Telephony.Carriers.USER_EDITED); - - if (callUpdate) { - final Uri uri = mApnData.getUri() == null ? mCarrierUri : mApnData.getUri(); - updateApnDataToDatabase(uri, values); - } else { - if (VDBG) Log.d(TAG, "validateAndSaveApnData: not calling update()"); - } - - return true; - } - - private void updateApnDataToDatabase(Uri uri, ContentValues values) { - ThreadUtils.postOnBackgroundThread(() -> { - if (uri.equals(mCarrierUri)) { - // Add a new apn to the database - final Uri newUri = getContentResolver().insert(mCarrierUri, values); - if (newUri == null) { - Log.e(TAG, "Can't add a new apn to database " + mCarrierUri); - } - } else { - // Update the existing apn - getContentResolver().update( - uri, values, null /* where */, null /* selection Args */); - } - }); - } - - /** - * Validates whether the apn data is valid. - * - * @return An error message if the apn data is invalid, otherwise return null. - */ - @VisibleForTesting - String validateApnData() { - String errorMsg = null; - - final String name = checkNotSet(mName.getText()); - final String apn = checkNotSet(mApn.getText()); - final String mcc = checkNotSet(mMcc.getText()); - final String mnc = checkNotSet(mMnc.getText()); - boolean doNotCheckMccMnc = mIsCarrierIdApn && TextUtils.isEmpty(mcc) - && TextUtils.isEmpty(mnc); - if (TextUtils.isEmpty(name)) { - errorMsg = getResources().getString(R.string.error_name_empty); - } else if (TextUtils.isEmpty(apn)) { - errorMsg = getResources().getString(R.string.error_apn_empty); - } else if (doNotCheckMccMnc) { - Log.d(TAG, "validateApnData: carrier id APN does not have mcc/mnc defined"); - // no op, skip mcc mnc null check - } else if (mcc == null || mcc.length() != 3) { - errorMsg = getResources().getString(R.string.error_mcc_not3); - } else if ((mnc == null || (mnc.length() & 0xFFFE) != 2)) { - errorMsg = getResources().getString(R.string.error_mnc_not23); - } - - if (errorMsg == null) { - // if carrier does not allow editing certain apn types, make sure type does not include - // those - if (!ArrayUtils.isEmpty(mReadOnlyApnTypes) - && apnTypesMatch(mReadOnlyApnTypes, getUserEnteredApnType())) { - final StringBuilder stringBuilder = new StringBuilder(); - for (String type : mReadOnlyApnTypes) { - stringBuilder.append(type).append(", "); - Log.d(TAG, "validateApnData: appending type: " + type); - } - // remove last ", " - if (stringBuilder.length() >= 2) { - stringBuilder.delete(stringBuilder.length() - 2, stringBuilder.length()); - } - errorMsg = String.format(getResources().getString(R.string.error_adding_apn_type), - stringBuilder); - } - } - - return errorMsg; - } - - @VisibleForTesting - void showError() { - ErrorDialog.showError(this); - } - - private void deleteApn() { - if (mApnData.getUri() != null) { - getContentResolver().delete(mApnData.getUri(), null, null); - mApnData = new ApnData(sProjection.length); - } - } - - private String starify(String value) { - if (value == null || value.length() == 0) { - return sNotSet; - } else { - final char[] password = new char[value.length()]; - for (int i = 0; i < password.length; i++) { - password[i] = '*'; - } - return new String(password); - } - } - - /** - * Returns {@link #sNotSet} if the given string {@code value} is null or empty. The string - * {@link #sNotSet} typically used as the default display when an entry in the preference is - * null or empty. - */ - private String checkNull(String value) { - return TextUtils.isEmpty(value) ? sNotSet : value; - } - - /** - * To make traslation be diversity, use another string id for MVNO value. - */ - private String checkNullforMvnoValue(String value) { - String notSetForMvnoValue = getResources().getString(R.string.apn_not_set_for_mvno); - return TextUtils.isEmpty(value) ? notSetForMvnoValue : value; - } - - /** - * Returns null if the given string {@code value} equals to {@link #sNotSet}. This method - * should be used when convert a string value from preference to database. - */ - private String checkNotSet(String value) { - return sNotSet.equals(value) ? null : value; - } - - @VisibleForTesting - String getUserEnteredApnType() { - // if user has not specified a type, map it to "ALL APN TYPES THAT ARE NOT READ-ONLY" - // but if user enter empty type, map it just for default - String userEnteredApnType = mApnType.getText(); - if (userEnteredApnType != null) userEnteredApnType = userEnteredApnType.trim(); - if ((TextUtils.isEmpty(userEnteredApnType) - || APN_TYPE_ALL.equals(userEnteredApnType))) { - userEnteredApnType = getEditableApnType(APN_TYPES); - } - Log.d(TAG, "getUserEnteredApnType: changed apn type to editable apn types: " - + userEnteredApnType); - return userEnteredApnType; - } - - private String getEditableApnType(String[] apnTypeList) { - final StringBuilder editableApnTypes = new StringBuilder(); - final List readOnlyApnTypes = Arrays.asList(mReadOnlyApnTypes); - boolean first = true; - for (String apnType : apnTypeList) { - // add APN type if it is not read-only and is not wild-cardable - if (!readOnlyApnTypes.contains(apnType) - && !apnType.equals(APN_TYPE_IA) - && !apnType.equals(APN_TYPE_EMERGENCY) - && !apnType.equals(APN_TYPE_MCX) - && !apnType.equals(APN_TYPE_IMS)) { - if (first) { - first = false; - } else { - editableApnTypes.append(","); - } - editableApnTypes.append(apnType); - } - } - return editableApnTypes.toString(); - } - - private void initApnEditorUi() { - addPreferencesFromResource(R.xml.apn_editor); - - sNotSet = getResources().getString(R.string.apn_not_set); - mName = (EditTextPreference) findPreference("apn_name"); - mApn = (EditTextPreference) findPreference("apn_apn"); - mProxy = (EditTextPreference) findPreference("apn_http_proxy"); - mPort = (EditTextPreference) findPreference("apn_http_port"); - mUser = (EditTextPreference) findPreference("apn_user"); - mServer = (EditTextPreference) findPreference("apn_server"); - mPassword = (EditTextPreference) findPreference(KEY_PASSWORD); - mMmsProxy = (EditTextPreference) findPreference("apn_mms_proxy"); - mMmsPort = (EditTextPreference) findPreference("apn_mms_port"); - mMmsc = (EditTextPreference) findPreference("apn_mmsc"); - mMcc = (EditTextPreference) findPreference("apn_mcc"); - mMnc = (EditTextPreference) findPreference("apn_mnc"); - mApnType = (EditTextPreference) findPreference("apn_type"); - mAuthType = (ListPreference) findPreference(KEY_AUTH_TYPE); - mProtocol = (ListPreference) findPreference(KEY_PROTOCOL); - mRoamingProtocol = (ListPreference) findPreference(KEY_ROAMING_PROTOCOL); - mCarrierEnabled = (TwoStatePreference) findPreference(KEY_CARRIER_ENABLED); - mBearerMulti = (MultiSelectListPreference) findPreference(KEY_BEARER_MULTI); - mMvnoType = (ListPreference) findPreference(KEY_MVNO_TYPE); - mMvnoMatchData = (EditTextPreference) findPreference("mvno_match_data"); - } - - @VisibleForTesting - protected void getCarrierCustomizedConfig(Context context) { - mReadOnlyApn = false; - mReadOnlyApnTypes = null; - mReadOnlyApnFields = null; - mIsAddApnAllowed = true; - - final CarrierConfigManager configManager = (CarrierConfigManager) - context.getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (configManager != null) { - final PersistableBundle b = configManager.getConfigForSubId(mSubId); - if (b != null) { - mReadOnlyApnTypes = getReadOnlyApnTypes(b); - if (!ArrayUtils.isEmpty(mReadOnlyApnTypes)) { - Log.d(TAG, - "onCreate: read only APN type: " + Arrays.toString(mReadOnlyApnTypes)); - } - mReadOnlyApnFields = b.getStringArray( - CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY); - - mDefaultApnTypes = b.getStringArray( - CarrierConfigManager.KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY); - - if (!ArrayUtils.isEmpty(mDefaultApnTypes)) { - Log.d(TAG, "onCreate: default apn types: " + Arrays.toString(mDefaultApnTypes)); - } - - mDefaultApnProtocol = b.getString( - CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_PROTOCOL_STRING); - if (!TextUtils.isEmpty(mDefaultApnProtocol)) { - Log.d(TAG, "onCreate: default apn protocol: " + mDefaultApnProtocol); - } - - mDefaultApnRoamingProtocol = b.getString( - CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING); - if (!TextUtils.isEmpty(mDefaultApnRoamingProtocol)) { - Log.d(TAG, "onCreate: default apn roaming protocol: " - + mDefaultApnRoamingProtocol); - } - - mIsAddApnAllowed = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL); - if (!mIsAddApnAllowed) { - Log.d(TAG, "onCreate: not allow to add new APN"); - } - } - } - } - - private void setCarrierCustomizedConfigToUi() { - if (TextUtils.isEmpty(mApnType.getText()) && !ArrayUtils.isEmpty(mDefaultApnTypes)) { - String value = getEditableApnType(mDefaultApnTypes); - mApnType.setText(value); - mApnType.setSummary(value); - } - - String protocol = protocolDescription(mDefaultApnProtocol, mProtocol); - if (TextUtils.isEmpty(mProtocol.getValue()) && !TextUtils.isEmpty(protocol)) { - mProtocol.setValue(mDefaultApnProtocol); - mProtocol.setSummary(protocol); - } - - String roamingProtocol = protocolDescription(mDefaultApnRoamingProtocol, mRoamingProtocol); - if (TextUtils.isEmpty(mRoamingProtocol.getValue()) && !TextUtils.isEmpty(roamingProtocol)) { - mRoamingProtocol.setValue(mDefaultApnRoamingProtocol); - mRoamingProtocol.setSummary(roamingProtocol); - } - } - - /** - * Dialog of error message. - */ - public static class ErrorDialog extends InstrumentedDialogFragment { - /** - * Show error dialog. - */ - public static void showError(ApnEditor editor) { - final ErrorDialog dialog = new ErrorDialog(); - dialog.setTargetFragment(editor, 0); - dialog.show(editor.getFragmentManager(), "error"); - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final String msg = ((ApnEditor) getTargetFragment()).validateApnData(); - - return new AlertDialog.Builder(getContext()) - .setTitle(R.string.error_title) - .setPositiveButton(android.R.string.ok, null) - .setMessage(msg) - .create(); - } - - @Override - public int getMetricsCategory() { - return SettingsEnums.DIALOG_APN_EDITOR_ERROR; - } - } - - @VisibleForTesting - ApnData getApnDataFromUri(Uri uri) { - ApnData apnData = null; - try (Cursor cursor = getContentResolver().query( - uri, - sProjection, - null /* selection */, - null /* selectionArgs */, - null /* sortOrder */)) { - if (cursor != null && cursor.moveToFirst()) { - apnData = new ApnData(uri, cursor); - } - } - - if (apnData == null) { - Log.d(TAG, "Can't get apnData from Uri " + uri); - } - - return apnData; - } - @VisibleForTesting boolean isUserRestricted() { UserManager userManager = getContext().getSystemService(UserManager.class); @@ -1502,80 +201,4 @@ public class ApnEditor extends SettingsPreferenceFragment } return false; } - - @VisibleForTesting - static class ApnData { - /** - * The uri correspond to a database row of the apn data. This should be null if the apn - * is not in the database. - */ - Uri mUri; - - /** Each element correspond to a column of the database row. */ - Object[] mData; - - ApnData(int numberOfField) { - mData = new Object[numberOfField]; - } - - ApnData(Uri uri, Cursor cursor) { - mUri = uri; - mData = new Object[cursor.getColumnCount()]; - for (int i = 0; i < mData.length; i++) { - switch (cursor.getType(i)) { - case Cursor.FIELD_TYPE_FLOAT: - mData[i] = cursor.getFloat(i); - break; - case Cursor.FIELD_TYPE_INTEGER: - mData[i] = cursor.getInt(i); - break; - case Cursor.FIELD_TYPE_STRING: - mData[i] = cursor.getString(i); - break; - case Cursor.FIELD_TYPE_BLOB: - mData[i] = cursor.getBlob(i); - break; - default: - mData[i] = null; - } - } - } - - Uri getUri() { - return mUri; - } - - void setUri(Uri uri) { - mUri = uri; - } - - Integer getInteger(int index) { - return (Integer) mData[index]; - } - - Integer getInteger(int index, Integer defaultValue) { - final Integer val = getInteger(index); - return val == null ? defaultValue : val; - } - - String getString(int index) { - return (String) mData[index]; - } - } - - private static int getBitmaskForTech(int radioTech) { - if (radioTech >= 1) { - return (1 << (radioTech - 1)); - } - return 0; - } - - private static boolean bitmaskHasTech(int bearerBitmask, int radioTech) { - if (bearerBitmask == 0) { - return true; - } else if (radioTech >= 1) { - return ((bearerBitmask & (1 << (radioTech - 1))) != 0); - } - return false; - } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/BluetoothDashboardScreenTest.kt b/tests/robotests/src/com/android/settings/connecteddevice/BluetoothDashboardScreenTest.kt index 39c029467b4..16db8413fa7 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/BluetoothDashboardScreenTest.kt +++ b/tests/robotests/src/com/android/settings/connecteddevice/BluetoothDashboardScreenTest.kt @@ -15,7 +15,12 @@ */ package com.android.settings.connecteddevice +import android.content.Intent +import android.provider.Settings.Global +import androidx.preference.PreferenceFragmentCompat +import androidx.test.core.app.ActivityScenario import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settings.Settings.BluetoothDashboardActivity import com.android.settings.flags.Flags import com.android.settingslib.preference.CatalystScreenTestCase import com.google.common.truth.Truth.assertThat @@ -34,6 +39,18 @@ class BluetoothDashboardScreenTest : CatalystScreenTestCase() { assertThat(preferenceScreenCreator.key).isEqualTo(BluetoothDashboardScreen.KEY) } - override fun migration() { + override fun launchFragment( + fragmentClass: Class, + action: (PreferenceFragmentCompat) -> Unit, + ) { + Global.putInt(appContext.contentResolver, Global.DEVICE_PROVISIONED, 1) + val intent = Intent(appContext, BluetoothDashboardActivity::class.java) + ActivityScenario.launch(intent).use { + it.onActivity { activity -> + val fragment = activity.supportFragmentManager.fragments[0] + assertThat(fragment.javaClass).isEqualTo(fragmentClass) + action(fragment as PreferenceFragmentCompat) + } + } } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java index 1bc00a1fdff..f3848b3fbb2 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java @@ -17,10 +17,10 @@ package com.android.settings.fuelgauge; import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS; -import static com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; 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.anyString; import static org.mockito.ArgumentMatchers.nullable; @@ -49,13 +49,13 @@ import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; import com.android.settings.SettingsActivity; +import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry; import com.android.settings.fuelgauge.batteryusage.BatteryEntry; import com.android.settings.fuelgauge.batteryusage.ConvertUtils; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowEntityHeaderController; import com.android.settings.widget.EntityHeaderController; -import com.android.settingslib.PrimarySwitchPreference; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.instantapps.InstantAppDataProvider; @@ -87,12 +87,12 @@ import java.util.concurrent.TimeUnit; }) public class AdvancedPowerUsageDetailTest { - @Rule - public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); private static final String APP_LABEL = "app label"; private static final String SUMMARY = "summary"; - private static final String[] PACKAGE_NAME = {"com.android.app"}; + private static final String PACKAGE_NAME = "com.android.app"; + private static final String INITIATING_PACKAGE_NAME = "com.android.vending"; private static final String USAGE_PERCENT = "16%"; private static final int ICON_ID = 123; private static final int UID = 1; @@ -100,39 +100,43 @@ public class AdvancedPowerUsageDetailTest { private static final long FOREGROUND_SERVICE_TIME_MS = 123; private static final long BACKGROUND_TIME_MS = 100; private static final long SCREEN_ON_TIME_MS = 321; - private static final String KEY_ALLOW_BACKGROUND_USAGE = "allow_background_usage"; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private FragmentActivity mActivity; @Mock private EntityHeaderController mEntityHeaderController; - @Mock private LayoutPreference mHeaderPreference; @Mock private ApplicationsState mState; @Mock private ApplicationsState.AppEntry mAppEntry; @Mock private BatteryEntry mBatteryEntry; @Mock private PackageManager mPackageManager; @Mock private InstallSourceInfo mInstallSourceInfo; + @Mock private LayoutPreference mLayoutPreference; @Mock private AppOpsManager mAppOpsManager; @Mock private LoaderManager mLoaderManager; - @Mock private BatteryOptimizeUtils mBatteryOptimizeUtils; + private int mTestMode; private Context mContext; - private PrimarySwitchPreference mAllowBackgroundUsagePreference; private AdvancedPowerUsageDetail mFragment; private SettingsActivity mTestActivity; private FakeFeatureFactory mFeatureFactory; private MetricsFeatureProvider mMetricsFeatureProvider; private BatteryDiffEntry mBatteryDiffEntry; private Bundle mBundle; + private BatteryOptimizeUtils mBatteryOptimizeUtils; @Before - public void setUp() { + public void setUp() throws Exception { mContext = spy(ApplicationProvider.getApplicationContext()); - when(mContext.getPackageName()).thenReturn("foo"); + when(mContext.getPackageName()).thenReturn(PACKAGE_NAME); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(mInstallSourceInfo); mFeatureFactory = FakeFeatureFactory.setupForTest(); mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider; + prepareTestBatteryOptimizationUtils(); mFragment = spy(new AdvancedPowerUsageDetail()); + mFragment.mBatteryOptimizeUtils = mBatteryOptimizeUtils; + doReturn(mLayoutPreference).when(mFragment).findPreference(any()); mBundle = spy(new Bundle()); doReturn(mContext).when(mFragment).getContext(); doReturn(mActivity).when(mFragment).getActivity(); @@ -195,7 +199,6 @@ public class AdvancedPowerUsageDetailTest { when(mBatteryDiffEntry.getAppLabel()).thenReturn(APP_LABEL); when(mBatteryDiffEntry.getAppIconId()).thenReturn(ICON_ID); - mFragment.mHeaderPreference = mHeaderPreference; mFragment.mState = mState; mFragment.mBatteryOptimizeUtils = mBatteryOptimizeUtils; mFragment.mLogStringBuilder = new StringBuilder(); @@ -219,10 +222,6 @@ public class AdvancedPowerUsageDetailTest { .startActivityAsUser(captor.capture(), nullable(UserHandle.class)); doAnswer(callable).when(mActivity).startActivity(captor.capture()); doAnswer(callable).when(mContext).startActivity(captor.capture()); - - mAllowBackgroundUsagePreference = new PrimarySwitchPreference(mContext); - mAllowBackgroundUsagePreference.setKey(KEY_ALLOW_BACKGROUND_USAGE); - mFragment.mAllowBackgroundUsagePreference = mAllowBackgroundUsagePreference; } @After @@ -320,17 +319,15 @@ public class AdvancedPowerUsageDetailTest { .isEqualTo(SCREEN_ON_TIME_MS); assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT)) .isEqualTo(USAGE_PERCENT); - assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_SLOT_TIME)) - .isEqualTo(null); + assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_SLOT_TIME)).isNull(); } - @Test public void startBatteryDetailPage_noBatteryUsage_hasBasicData() { final ArgumentCaptor captor = ArgumentCaptor.forClass(Intent.class); AdvancedPowerUsageDetail.startBatteryDetailPage( - mActivity, mFragment, PACKAGE_NAME[0], UserHandle.OWNER); + mActivity, mFragment, PACKAGE_NAME, UserHandle.OWNER); verify(mActivity).startActivity(captor.capture()); @@ -338,7 +335,7 @@ public class AdvancedPowerUsageDetailTest { captor.getValue() .getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS) .getString(AdvancedPowerUsageDetail.EXTRA_PACKAGE_NAME)) - .isEqualTo(PACKAGE_NAME[0]); + .isEqualTo(PACKAGE_NAME); assertThat( captor.getValue() @@ -351,62 +348,21 @@ public class AdvancedPowerUsageDetailTest { public void startBatteryDetailPage_batteryEntryNotExisted_extractUidFromPackageName() throws PackageManager.NameNotFoundException { mBundle.clear(); - doReturn(UID).when(mPackageManager).getPackageUid(PACKAGE_NAME[0], 0 /* no flag */); + doReturn(UID).when(mPackageManager).getPackageUid(PACKAGE_NAME, 0 /* no flag */); AdvancedPowerUsageDetail.startBatteryDetailPage( - mActivity, mFragment, PACKAGE_NAME[0], UserHandle.OWNER); + mActivity, mFragment, PACKAGE_NAME, UserHandle.OWNER); assertThat(mBundle.getInt(AdvancedPowerUsageDetail.EXTRA_UID)).isEqualTo(UID); } @Test - public void initFooter_isValidPackageName_hasCorrectString() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); + public void onPause_optimizationModeIsChanged_logPreference() throws Exception { + mFragment.mOptimizationMode = BatteryOptimizeUtils.MODE_RESTRICTED; + when(mBatteryOptimizeUtils.getPackageName()).thenReturn(PACKAGE_NAME); + when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn(INITIATING_PACKAGE_NAME); - mFragment.initFooter(); - - assertThat(mAllowBackgroundUsagePreference.getSummary().toString()) - .isEqualTo("This app requires optimized battery usage."); - } - - @Test - public void initFooter_isSystemOrDefaultApp_hasCorrectString() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - - mFragment.initFooter(); - - assertThat(mAllowBackgroundUsagePreference.getSummary().toString()) - .isEqualTo("This app requires unrestricted battery usage."); - } - - @Test - public void initFooter_hasCorrectString() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); - - mFragment.initFooter(); - - assertThat(mAllowBackgroundUsagePreference.getSummary().toString()) - .isEqualTo("Enable for real-time updates, disable to save battery"); - } - - @Test - public void onPause_optimizationModeChanged_logPreference() - throws PackageManager.NameNotFoundException, InterruptedException { - final String packageName = "testPackageName"; - final int restrictedMode = BatteryOptimizeUtils.MODE_RESTRICTED; - final int optimizedMode = BatteryOptimizeUtils.MODE_OPTIMIZED; - mFragment.mOptimizationMode = restrictedMode; - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(restrictedMode); - when(mBatteryOptimizeUtils.getPackageName()).thenReturn(packageName); - when(mContext.getPackageManager()).thenReturn(mPackageManager); - when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(mInstallSourceInfo); - when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn("com.android.vending"); - - mFragment.onPreferenceChange(mAllowBackgroundUsagePreference, true); - verify(mBatteryOptimizeUtils).setAppUsageState(optimizedMode, Action.APPLY); - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(optimizedMode); + mBatteryOptimizeUtils.setAppUsageState(BatteryOptimizeUtils.MODE_OPTIMIZED, Action.APPLY); mFragment.onPause(); TimeUnit.SECONDS.sleep(1); @@ -415,27 +371,18 @@ public class AdvancedPowerUsageDetailTest { SettingsEnums.LEAVE_APP_BATTERY_USAGE, SettingsEnums.ACTION_APP_BATTERY_USAGE_ALLOW_BACKGROUND, SettingsEnums.FUELGAUGE_POWER_USAGE_DETAIL, - packageName, + PACKAGE_NAME, /* consumed battery */ 0); } @Test - public void onPause_optimizationModeIsNotChanged_notInvokeLogging() - throws PackageManager.NameNotFoundException, InterruptedException { - final int restrictedMode = BatteryOptimizeUtils.MODE_RESTRICTED; - final int optimizedMode = BatteryOptimizeUtils.MODE_OPTIMIZED; - mFragment.mOptimizationMode = restrictedMode; - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(restrictedMode); - when(mContext.getPackageManager()).thenReturn(mPackageManager); - when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(mInstallSourceInfo); - when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn("com.android.vending"); + public void onPause_optimizationModeIsNotChanged_notInvokeLogging() throws Exception { + mFragment.mOptimizationMode = BatteryOptimizeUtils.MODE_RESTRICTED; + when(mBatteryOptimizeUtils.getPackageName()).thenReturn(PACKAGE_NAME); + when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn(INITIATING_PACKAGE_NAME); - mFragment.onPreferenceChange(mAllowBackgroundUsagePreference, true); - verify(mBatteryOptimizeUtils).setAppUsageState(optimizedMode, Action.APPLY); - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(optimizedMode); - mFragment.onPreferenceChange(mAllowBackgroundUsagePreference, false); - verify(mBatteryOptimizeUtils).setAppUsageState(restrictedMode, Action.APPLY); - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(restrictedMode); + mBatteryOptimizeUtils.setAppUsageState(BatteryOptimizeUtils.MODE_OPTIMIZED, Action.APPLY); + mBatteryOptimizeUtils.setAppUsageState(BatteryOptimizeUtils.MODE_RESTRICTED, Action.APPLY); mFragment.onPause(); TimeUnit.SECONDS.sleep(1); @@ -446,4 +393,16 @@ public class AdvancedPowerUsageDetailTest { public void shouldSkipForInitialSUW_returnTrue() { assertThat(mFragment.shouldSkipForInitialSUW()).isTrue(); } + + private void prepareTestBatteryOptimizationUtils() { + mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(mContext, UID, PACKAGE_NAME)); + Answer setTestMode = + invocation -> { + mTestMode = invocation.getArgument(0); + return null; + }; + doAnswer(setTestMode).when(mBatteryOptimizeUtils).setAppUsageState(anyInt(), any()); + Answer getTestMode = invocation -> mTestMode; + doAnswer(getTestMode).when(mBatteryOptimizeUtils).getAppOptimizationMode(); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceControllerTest.java deleted file mode 100644 index 261a3151531..00000000000 --- a/tests/robotests/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceControllerTest.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2023 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.fuelgauge; - -import static com.google.common.truth.Truth.assertThat; - -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.pm.PackageManager; - -import com.android.settingslib.widget.MainSwitchPreference; - -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; - -@RunWith(RobolectricTestRunner.class) -public class AllowBackgroundPreferenceControllerTest { - private static final int UID = 12345; - private static final String PACKAGE_NAME = "com.android.app"; - - private AllowBackgroundPreferenceController mController; - private MainSwitchPreference mMainSwitchPreference; - private BatteryOptimizeUtils mBatteryOptimizeUtils; - - @Mock private PackageManager mMockPackageManager; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - Context context = spy(RuntimeEnvironment.application); - BatteryUtils.getInstance(context).reset(); - doReturn(UID) - .when(mMockPackageManager) - .getPackageUid(PACKAGE_NAME, PackageManager.GET_META_DATA); - - mController = new AllowBackgroundPreferenceController(context, UID, PACKAGE_NAME); - mMainSwitchPreference = new MainSwitchPreference(RuntimeEnvironment.application); - mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(context, UID, PACKAGE_NAME)); - mController.mBatteryOptimizeUtils = mBatteryOptimizeUtils; - } - - @Test - public void testUpdateState_isValidPackage_prefEnabled() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); - - mController.updateState(mMainSwitchPreference); - - assertThat(mBatteryOptimizeUtils.isOptimizeModeMutable()).isTrue(); - assertThat(mMainSwitchPreference.isEnabled()).isTrue(); - } - - @Test - public void testUpdateState_invalidPackage_prefDisabled() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); - - mController.updateState(mMainSwitchPreference); - - assertThat(mBatteryOptimizeUtils.isOptimizeModeMutable()).isFalse(); - assertThat(mMainSwitchPreference.isEnabled()).isFalse(); - } - - @Test - public void testUpdateState_isSystemOrDefaultAppAndRestrictedStates_prefChecked() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_RESTRICTED); - - mController.updateState(mMainSwitchPreference); - - assertThat(mMainSwitchPreference.isEnabled()).isFalse(); - assertThat(mMainSwitchPreference.isChecked()).isFalse(); - } - - @Test - public void testUpdateState_isSystemOrDefaultApp_prefUnchecked() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_OPTIMIZED); - - mController.updateState(mMainSwitchPreference); - - assertThat(mMainSwitchPreference.isEnabled()).isFalse(); - assertThat(mMainSwitchPreference.isChecked()).isTrue(); - } - - @Test - public void testUpdateState_isRestrictedStates_prefChecked() { - when(mBatteryOptimizeUtils.isOptimizeModeMutable()).thenReturn(true); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_RESTRICTED); - - mController.updateState(mMainSwitchPreference); - - assertThat(mMainSwitchPreference.isEnabled()).isTrue(); - assertThat(mMainSwitchPreference.isChecked()).isFalse(); - } - - @Test - public void testUpdateState_prefUnchecked() { - when(mBatteryOptimizeUtils.isOptimizeModeMutable()).thenReturn(true); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_OPTIMIZED); - - mController.updateState(mMainSwitchPreference); - - assertThat(mMainSwitchPreference.isEnabled()).isTrue(); - assertThat(mMainSwitchPreference.isChecked()).isTrue(); - } - - @Test - public void testHandlePreferenceTreeClick_samePrefKey_verifyAction() { - mMainSwitchPreference.setKey( - AllowBackgroundPreferenceController.KEY_ALLOW_BACKGROUND_USAGE); - mController.handlePreferenceTreeClick(mMainSwitchPreference); - - assertThat(mController.handlePreferenceTreeClick(mMainSwitchPreference)).isTrue(); - } - - @Test - public void testHandlePreferenceTreeClick_incorrectPrefKey_noAction() { - assertThat(mController.handlePreferenceTreeClick(mMainSwitchPreference)).isFalse(); - } -} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BackgroundUsageAllowabilityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundUsageAllowabilityPreferenceControllerTest.java new file mode 100644 index 00000000000..190446ed116 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundUsageAllowabilityPreferenceControllerTest.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2024 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.fuelgauge; + +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.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.Context; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; +import com.android.settingslib.PrimarySwitchPreference; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class BackgroundUsageAllowabilityPreferenceControllerTest { + private static final int UID = 12345; + private static final String PACKAGE_NAME = "com.android.app"; + + private int mTestMode; + private Context mContext; + private BackgroundUsageAllowabilityPreferenceController mBackgroundUsageController; + private BatteryOptimizeUtils mBatteryOptimizeUtils; + + @Mock DashboardFragment mDashboardFragment; + @Mock PrimarySwitchPreference mBackgroundUsageAllowabilityPreference; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mContext = spy(ApplicationProvider.getApplicationContext()); + prepareTestBatteryOptimizationUtils(); + mBackgroundUsageController = + spy( + new BackgroundUsageAllowabilityPreferenceController( + mContext, + mDashboardFragment, + /* preferenceKey= */ "test", + mBatteryOptimizeUtils)); + mBackgroundUsageController.mBackgroundUsageAllowabilityPreference = + mBackgroundUsageAllowabilityPreference; + } + + @Test + public void initPreferences_immutableOptimized_setExpectedContent() { + doReturn(false).when(mBatteryOptimizeUtils).isOptimizeModeMutable(); + doReturn(true).when(mBatteryOptimizeUtils).isDisabledForOptimizeModeOnly(); + + mBackgroundUsageController.initPreferences(); + + verify(mBackgroundUsageAllowabilityPreference).setEnabled(false); + verify(mBackgroundUsageAllowabilityPreference).setSwitchEnabled(false); + verify(mBackgroundUsageAllowabilityPreference) + .setSummary( + mContext.getString( + R.string.manager_battery_usage_footer_limited, + mContext.getString(R.string.manager_battery_usage_optimized_only))); + verify(mBackgroundUsageAllowabilityPreference, never()) + .setOnPreferenceChangeListener(any()); + verify(mBackgroundUsageAllowabilityPreference, never()).setOnPreferenceClickListener(any()); + } + + @Test + public void initPreferences_immutableUnrestricted_setExpectedContent() { + doReturn(false).when(mBatteryOptimizeUtils).isOptimizeModeMutable(); + doReturn(false).when(mBatteryOptimizeUtils).isDisabledForOptimizeModeOnly(); + doReturn(true).when(mBatteryOptimizeUtils).isSystemOrDefaultApp(); + + mBackgroundUsageController.initPreferences(); + + verify(mBackgroundUsageAllowabilityPreference).setEnabled(false); + verify(mBackgroundUsageAllowabilityPreference).setSwitchEnabled(false); + verify(mBackgroundUsageAllowabilityPreference) + .setSummary( + mContext.getString( + R.string.manager_battery_usage_footer_limited, + mContext.getString( + R.string.manager_battery_usage_unrestricted_only))); + verify(mBackgroundUsageAllowabilityPreference, never()) + .setOnPreferenceChangeListener(any()); + verify(mBackgroundUsageAllowabilityPreference, never()).setOnPreferenceClickListener(any()); + } + + @Test + public void initPreferences_mutableMode_setExpectedContent() { + doReturn(true).when(mBatteryOptimizeUtils).isOptimizeModeMutable(); + doReturn(false).when(mBatteryOptimizeUtils).isDisabledForOptimizeModeOnly(); + doReturn(false).when(mBatteryOptimizeUtils).isSystemOrDefaultApp(); + + mBackgroundUsageController.initPreferences(); + + verify(mBackgroundUsageAllowabilityPreference).setEnabled(true); + verify(mBackgroundUsageAllowabilityPreference).setSwitchEnabled(true); + verify(mBackgroundUsageAllowabilityPreference) + .setSummary( + mContext.getString( + R.string.manager_battery_usage_allow_background_usage_summary)); + verify(mBackgroundUsageAllowabilityPreference).setOnPreferenceChangeListener(any()); + verify(mBackgroundUsageAllowabilityPreference).setOnPreferenceClickListener(any()); + } + + @Test + public void updatePreferences_setIntoUnrestrictedMode_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_UNRESTRICTED; + + mBackgroundUsageController.updatePreferences(mTestMode); + + verifyPreferences(mTestMode); + } + + @Test + public void updatePreferences_setIntoOptimizedMode_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_OPTIMIZED; + + mBackgroundUsageController.updatePreferences(mTestMode); + + verifyPreferences(mTestMode); + } + + @Test + public void updatePreferences_setIntoRestrictedMode_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_RESTRICTED; + + mBackgroundUsageController.updatePreferences(mTestMode); + + verifyPreferences(mTestMode); + } + + @Test + public void handleBatteryOptimizeModeUpdated_modeChange_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_RESTRICTED; + + mBackgroundUsageController.handleBatteryOptimizeModeUpdated( + BatteryOptimizeUtils.MODE_OPTIMIZED); + + verify(mBatteryOptimizeUtils) + .setAppUsageState(BatteryOptimizeUtils.MODE_OPTIMIZED, Action.APPLY); + assertThat(mTestMode).isEqualTo(BatteryOptimizeUtils.MODE_OPTIMIZED); + verifyPreferences(mTestMode); + } + + @Test + public void handleBatteryOptimizeModeUpdated_modeNotChange_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_RESTRICTED; + + mBackgroundUsageController.handleBatteryOptimizeModeUpdated( + BatteryOptimizeUtils.MODE_RESTRICTED); + + verify(mBatteryOptimizeUtils, never()).setAppUsageState(anyInt(), any()); + assertThat(mTestMode).isEqualTo(BatteryOptimizeUtils.MODE_RESTRICTED); + verify(mBackgroundUsageController, never()).updatePreferences(mTestMode); + } + + private void prepareTestBatteryOptimizationUtils() { + mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(mContext, UID, PACKAGE_NAME)); + Answer setTestMode = + invocation -> { + mTestMode = invocation.getArgument(0); + return null; + }; + doAnswer(setTestMode).when(mBatteryOptimizeUtils).setAppUsageState(anyInt(), any()); + Answer getTestMode = invocation -> mTestMode; + doAnswer(getTestMode).when(mBatteryOptimizeUtils).getAppOptimizationMode(); + } + + private void verifyPreferences(int mode) { + boolean isAllowBackground = mode != BatteryOptimizeUtils.MODE_RESTRICTED; + verify(mBackgroundUsageAllowabilityPreference).setChecked(isAllowBackground); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizationModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizationModePreferenceControllerTest.java new file mode 100644 index 00000000000..2ddc7ebbc72 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizationModePreferenceControllerTest.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2024 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.fuelgauge; + +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.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.Context; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.settingslib.widget.MainSwitchPreference; +import com.android.settingslib.widget.SelectorWithWidgetPreference; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class BatteryOptimizationModePreferenceControllerTest { + private static final int UID = 12345; + private static final String PACKAGE_NAME = "com.android.app"; + + private int mTestMode; + private Context mContext; + private BatteryOptimizationModePreferenceController mBackgroundUsageController; + private BatteryOptimizeUtils mBatteryOptimizeUtils; + + @Mock MainSwitchPreference mBackgroundUsageAllowabilityPreference; + @Mock SelectorWithWidgetPreference mOptimizedPreference; + @Mock SelectorWithWidgetPreference mUnrestrictedPreference; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mContext = spy(ApplicationProvider.getApplicationContext()); + prepareTestBatteryOptimizationUtils(); + mBackgroundUsageController = + spy( + new BatteryOptimizationModePreferenceController( + mContext, "test", mBatteryOptimizeUtils)); + mBackgroundUsageController.mBackgroundUsageAllowabilityPreference = + mBackgroundUsageAllowabilityPreference; + mBackgroundUsageController.mOptimizedPreference = mOptimizedPreference; + mBackgroundUsageController.mUnrestrictedPreference = mUnrestrictedPreference; + } + + @Test + public void initPreferences_mutableMode_setEnabled() { + doReturn(true).when(mBatteryOptimizeUtils).isOptimizeModeMutable(); + + mBackgroundUsageController.initPreferences(); + + verify(mBackgroundUsageAllowabilityPreference).setEnabled(true); + verify(mOptimizedPreference).setEnabled(true); + verify(mUnrestrictedPreference).setEnabled(true); + verify(mBackgroundUsageAllowabilityPreference, never()).setOnPreferenceClickListener(any()); + verify(mBackgroundUsageAllowabilityPreference).setOnPreferenceChangeListener(any()); + verify(mOptimizedPreference).setOnPreferenceClickListener(any()); + verify(mUnrestrictedPreference).setOnPreferenceClickListener(any()); + } + + @Test + public void initPreferences_immutableMode_setDisabledAndSkipSetListeners() { + doReturn(false).when(mBatteryOptimizeUtils).isOptimizeModeMutable(); + + mBackgroundUsageController.initPreferences(); + + verify(mBackgroundUsageAllowabilityPreference).setEnabled(false); + verify(mOptimizedPreference).setEnabled(false); + verify(mUnrestrictedPreference).setEnabled(false); + verify(mBackgroundUsageAllowabilityPreference, never()).setOnPreferenceClickListener(any()); + verify(mBackgroundUsageAllowabilityPreference, never()) + .setOnPreferenceChangeListener(any()); + verify(mOptimizedPreference, never()).setOnPreferenceClickListener(any()); + verify(mUnrestrictedPreference, never()).setOnPreferenceClickListener(any()); + } + + @Test + public void updatePreferences_setIntoUnrestrictedMode_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_UNRESTRICTED; + + mBackgroundUsageController.updatePreferences(mTestMode); + + verifyPreferences(mTestMode); + } + + @Test + public void updatePreferences_setIntoOptimizedMode_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_OPTIMIZED; + + mBackgroundUsageController.updatePreferences(mTestMode); + + verifyPreferences(mTestMode); + } + + @Test + public void updatePreferences_setIntoRestrictedMode_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_RESTRICTED; + + mBackgroundUsageController.updatePreferences(mTestMode); + + verifyPreferences(mTestMode); + } + + @Test + public void handleBatteryOptimizeModeUpdated_modeChange_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_RESTRICTED; + + mBackgroundUsageController.handleBatteryOptimizeModeUpdated( + BatteryOptimizeUtils.MODE_OPTIMIZED); + + verify(mBatteryOptimizeUtils) + .setAppUsageState( + BatteryOptimizeUtils.MODE_OPTIMIZED, + BatteryOptimizeHistoricalLogEntry.Action.APPLY); + assertThat(mTestMode).isEqualTo(BatteryOptimizeUtils.MODE_OPTIMIZED); + verifyPreferences(mBatteryOptimizeUtils.getAppOptimizationMode()); + } + + @Test + public void handleBatteryOptimizeModeUpdated_modeNotChange_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_RESTRICTED; + + mBackgroundUsageController.handleBatteryOptimizeModeUpdated( + BatteryOptimizeUtils.MODE_RESTRICTED); + + verify(mBatteryOptimizeUtils, never()).setAppUsageState(anyInt(), any()); + assertThat(mTestMode).isEqualTo(BatteryOptimizeUtils.MODE_RESTRICTED); + verify(mBackgroundUsageController, never()).updatePreferences(anyInt()); + } + + private void prepareTestBatteryOptimizationUtils() { + mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(mContext, UID, PACKAGE_NAME)); + Answer setTestMode = + invocation -> { + mTestMode = invocation.getArgument(0); + return null; + }; + doAnswer(setTestMode).when(mBatteryOptimizeUtils).setAppUsageState(anyInt(), any()); + Answer getTestMode = invocation -> mTestMode; + doAnswer(getTestMode).when(mBatteryOptimizeUtils).getAppOptimizationMode(); + } + + private void verifyPreferences(int mode) { + boolean isAllowBackground = mode != BatteryOptimizeUtils.MODE_RESTRICTED; + verify(mBackgroundUsageAllowabilityPreference).setChecked(isAllowBackground); + verify(mOptimizedPreference).setEnabled(isAllowBackground); + verify(mUnrestrictedPreference).setEnabled(isAllowBackground); + verify(mOptimizedPreference).setChecked(mode == BatteryOptimizeUtils.MODE_OPTIMIZED); + verify(mUnrestrictedPreference).setChecked(mode == BatteryOptimizeUtils.MODE_UNRESTRICTED); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java deleted file mode 100644 index 66564718d0e..00000000000 --- a/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2021 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.fuelgauge; - -import static com.google.common.truth.Truth.assertThat; - -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.pm.PackageManager; - -import com.android.settingslib.widget.SelectorWithWidgetPreference; - -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; - -@RunWith(RobolectricTestRunner.class) -public class OptimizedPreferenceControllerTest { - private static final int UID = 12345; - private static final String PACKAGE_NAME = "com.android.app"; - - private OptimizedPreferenceController mController; - private SelectorWithWidgetPreference mPreference; - private BatteryOptimizeUtils mBatteryOptimizeUtils; - - @Mock PackageManager mMockPackageManager; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - Context context = spy(RuntimeEnvironment.application); - BatteryUtils.getInstance(context).reset(); - doReturn(UID) - .when(mMockPackageManager) - .getPackageUid(PACKAGE_NAME, PackageManager.GET_META_DATA); - - mController = new OptimizedPreferenceController(context, UID, PACKAGE_NAME); - mPreference = new SelectorWithWidgetPreference(RuntimeEnvironment.application); - mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(context, UID, PACKAGE_NAME)); - mController.mBatteryOptimizeUtils = mBatteryOptimizeUtils; - } - - @Test - public void testUpdateState_invalidPackage_prefEnabled() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); - - mController.updateState(mPreference); - - assertThat(mPreference.isEnabled()).isFalse(); - assertThat(mPreference.isChecked()).isTrue(); - } - - @Test - public void testUpdateState_isSystemOrDefaultAppAndOptimizeStates_prefChecked() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_OPTIMIZED); - - mController.updateState(mPreference); - - assertThat(mPreference.isChecked()).isTrue(); - } - - @Test - public void testUpdateState_isSystemOrDefaultApp_prefUnchecked() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - - mController.updateState(mPreference); - - assertThat(mPreference.isChecked()).isFalse(); - assertThat(mPreference.isEnabled()).isFalse(); - } - - @Test - public void testUpdateState_isOptimizedStates_prefChecked() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_OPTIMIZED); - - mController.updateState(mPreference); - - assertThat(mPreference.isChecked()).isTrue(); - } - - @Test - public void testUpdateState_prefUnchecked() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - - mController.updateState(mPreference); - - assertThat(mPreference.isChecked()).isFalse(); - } - - @Test - public void testHandlePreferenceTreeClick_samePrefKey_verifyAction() { - mPreference.setKey(mController.KEY_OPTIMIZED_PREF); - mController.handlePreferenceTreeClick(mPreference); - - assertThat(mController.handlePreferenceTreeClick(mPreference)).isTrue(); - } - - @Test - public void testHandlePreferenceTreeClick_incorrectPrefKey_noAction() { - assertThat(mController.handlePreferenceTreeClick(mPreference)).isFalse(); - } -} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetailTest.java index 90611178fdb..9f98d78541e 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetailTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetailTest.java @@ -17,10 +17,10 @@ package com.android.settings.fuelgauge; import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS; -import static com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; 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.anyString; import static org.mockito.ArgumentMatchers.nullable; @@ -42,16 +42,17 @@ import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.UserHandle; -import android.widget.CompoundButton; import androidx.fragment.app.FragmentActivity; import androidx.loader.app.LoaderManager; import androidx.test.core.app.ApplicationProvider; +import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.fuelgauge.batteryusage.BatteryEntry; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowEntityHeaderController; +import com.android.settings.testutils.shadow.ShadowHelpUtils; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; @@ -59,8 +60,6 @@ import com.android.settingslib.applications.instantapps.InstantAppDataProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.widget.FooterPreference; import com.android.settingslib.widget.LayoutPreference; -import com.android.settingslib.widget.MainSwitchPreference; -import com.android.settingslib.widget.SelectorWithWidgetPreference; import org.junit.After; import org.junit.Before; @@ -83,36 +82,33 @@ import java.util.concurrent.TimeUnit; @Config( shadows = { ShadowEntityHeaderController.class, + ShadowHelpUtils.class, com.android.settings.testutils.shadow.ShadowFragment.class, }) public class PowerBackgroundUsageDetailTest { - @Rule - public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); private static final String APP_LABEL = "app label"; private static final String SUMMARY = "summary"; private static final int ICON_ID = 123; private static final int UID = 1; - private static final String KEY_PREF_UNRESTRICTED = "unrestricted_preference"; - private static final String KEY_PREF_OPTIMIZED = "optimized_preference"; - private static final String KEY_ALLOW_BACKGROUND_USAGE = "allow_background_usage"; + private static final String PACKAGE_NAME = "com.android.app"; + private static final String KEY_PREF_HEADER = "header_view"; + private static final String KEY_FOOTER_PREFERENCE = "app_usage_footer_preference"; + private static final String INITIATING_PACKAGE_NAME = "com.android.vending"; + private int mTestMode; private Context mContext; private PowerBackgroundUsageDetail mFragment; - private FooterPreference mFooterPreference; - private MainSwitchPreference mMainSwitchPreference; private MetricsFeatureProvider mMetricsFeatureProvider; - private SelectorWithWidgetPreference mOptimizePreference; - private SelectorWithWidgetPreference mUnrestrictedPreference; private SettingsActivity mTestActivity; + private BatteryOptimizeUtils mBatteryOptimizeUtils; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private FragmentActivity mActivity; @Mock private EntityHeaderController mEntityHeaderController; - @Mock private BatteryOptimizeUtils mBatteryOptimizeUtils; - @Mock private LayoutPreference mHeaderPreference; @Mock private ApplicationsState mState; @Mock private Bundle mBundle; @Mock private LoaderManager mLoaderManager; @@ -120,21 +116,26 @@ public class PowerBackgroundUsageDetailTest { @Mock private BatteryEntry mBatteryEntry; @Mock private PackageManager mPackageManager; @Mock private AppOpsManager mAppOpsManager; - @Mock private CompoundButton mMockSwitch; @Mock private InstallSourceInfo mInstallSourceInfo; + @Mock private LayoutPreference mLayoutPreference; + @Mock private FooterPreference mFooterPreference; @Before public void setUp() throws Exception { mContext = spy(ApplicationProvider.getApplicationContext()); - when(mContext.getPackageName()).thenReturn("foo"); + when(mContext.getPackageName()).thenReturn(PACKAGE_NAME); when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(mInstallSourceInfo); final FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest(); mMetricsFeatureProvider = fakeFeatureFactory.metricsFeatureProvider; + prepareTestBatteryOptimizationUtils(); mFragment = spy(new PowerBackgroundUsageDetail()); + mFragment.mBatteryOptimizeUtils = mBatteryOptimizeUtils; mFragment.mLogStringBuilder = new StringBuilder(); + doReturn(mLayoutPreference).when(mFragment).findPreference(KEY_PREF_HEADER); + doReturn(mFooterPreference).when(mFragment).findPreference(KEY_FOOTER_PREFERENCE); doReturn(mContext).when(mFragment).getContext(); doReturn(mActivity).when(mFragment).getActivity(); doReturn(SUMMARY).when(mFragment).getString(anyInt()); @@ -169,9 +170,7 @@ public class PowerBackgroundUsageDetailTest { when(mBatteryEntry.getLabel()).thenReturn(APP_LABEL); mBatteryEntry.mIconId = ICON_ID; - mFragment.mHeaderPreference = mHeaderPreference; mFragment.mState = mState; - mFragment.mBatteryOptimizeUtils = mBatteryOptimizeUtils; mAppEntry.info = mock(ApplicationInfo.class); mTestActivity = spy(new SettingsActivity()); @@ -191,23 +190,12 @@ public class PowerBackgroundUsageDetailTest { .when(mActivity) .startActivityAsUser(captor.capture(), nullable(UserHandle.class)); doAnswer(callable).when(mActivity).startActivity(captor.capture()); - - mFooterPreference = spy(new FooterPreference(mContext)); - mMainSwitchPreference = spy(new MainSwitchPreference(mContext)); - mMainSwitchPreference.setKey(KEY_ALLOW_BACKGROUND_USAGE); - mOptimizePreference = spy(new SelectorWithWidgetPreference(mContext)); - mOptimizePreference.setKey(KEY_PREF_OPTIMIZED); - mUnrestrictedPreference = spy(new SelectorWithWidgetPreference(mContext)); - mUnrestrictedPreference.setKey(KEY_PREF_UNRESTRICTED); - mFragment.mFooterPreference = mFooterPreference; - mFragment.mMainSwitchPreference = mMainSwitchPreference; - mFragment.mOptimizePreference = mOptimizePreference; - mFragment.mUnrestrictedPreference = mUnrestrictedPreference; } @After public void reset() { ShadowEntityHeaderController.reset(); + ShadowHelpUtils.reset(); } @Test @@ -258,91 +246,64 @@ public class PowerBackgroundUsageDetailTest { } @Test - public void initFooter_hasCorrectString() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); - + public void initFooter_setExpectedFooterContent() { mFragment.initFooter(); - assertThat(mFooterPreference.getTitle().toString()) - .isEqualTo("Changing how an app uses your battery can affect its performance."); + verify(mFooterPreference) + .setTitle(mContext.getString(R.string.manager_battery_usage_footer)); + verify(mFooterPreference).setLearnMoreAction(any()); + verify(mFooterPreference) + .setLearnMoreText(mContext.getString(R.string.manager_battery_usage_link_a11y)); } @Test - public void onSwitchChanged_fromUnrestrictedModeSetDisabled_becomeRestrictedMode() { - final int restrictedMode = BatteryOptimizeUtils.MODE_RESTRICTED; - final int optimizedMode = BatteryOptimizeUtils.MODE_OPTIMIZED; - mFragment.mOptimizationMode = optimizedMode; + public void onPause_optimizationModeIsChanged_logPreference() throws Exception { + mFragment.mOptimizationMode = BatteryOptimizeUtils.MODE_OPTIMIZED; + when(mBatteryOptimizeUtils.getPackageName()).thenReturn(PACKAGE_NAME); + when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn(INITIATING_PACKAGE_NAME); - mFragment.onCheckedChanged(mMockSwitch, /* isChecked= */ false); - - verify(mOptimizePreference).setEnabled(false); - verify(mUnrestrictedPreference).setEnabled(false); - verify(mFragment).onRadioButtonClicked(null); - verify(mMainSwitchPreference).setChecked(false); - assertThat(mFragment.getSelectedPreference()).isEqualTo(restrictedMode); - verify(mBatteryOptimizeUtils).setAppUsageState(restrictedMode, Action.APPLY); - } - - @Test - public void onSwitchChanged_fromRestrictedModeSetEnabled_becomeOptimizedMode() { - final int restrictedMode = BatteryOptimizeUtils.MODE_RESTRICTED; - final int optimizedMode = BatteryOptimizeUtils.MODE_OPTIMIZED; - mFragment.mOptimizationMode = restrictedMode; - - mFragment.onCheckedChanged(mMockSwitch, /* isChecked= */ true); - - verify(mOptimizePreference).setEnabled(true); - verify(mUnrestrictedPreference).setEnabled(true); - verify(mFragment).onRadioButtonClicked(mOptimizePreference); - verify(mMainSwitchPreference).setChecked(true); - verify(mOptimizePreference).setChecked(true); - assertThat(mFragment.getSelectedPreference()).isEqualTo(optimizedMode); - verify(mBatteryOptimizeUtils).setAppUsageState(optimizedMode, Action.APPLY); - } - - @Test - public void onPause_optimizationModeChanged_logPreference() throws Exception { - final String packageName = "testPackageName"; - final int restrictedMode = BatteryOptimizeUtils.MODE_RESTRICTED; - final int optimizedMode = BatteryOptimizeUtils.MODE_OPTIMIZED; - mFragment.mOptimizationMode = restrictedMode; - when(mBatteryOptimizeUtils.getPackageName()).thenReturn(packageName); - when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn("com.android.vending"); - - mFragment.onCheckedChanged(mMockSwitch, /* isChecked= */ true); - verify(mBatteryOptimizeUtils).setAppUsageState(optimizedMode, Action.APPLY); - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(optimizedMode); + mTestMode = BatteryOptimizeUtils.MODE_UNRESTRICTED; + assertThat(mBatteryOptimizeUtils.getAppOptimizationMode()) + .isEqualTo(BatteryOptimizeUtils.MODE_UNRESTRICTED); mFragment.onPause(); TimeUnit.SECONDS.sleep(1); verify(mMetricsFeatureProvider) .action( SettingsEnums.LEAVE_POWER_USAGE_MANAGE_BACKGROUND, - SettingsEnums.ACTION_APP_BATTERY_USAGE_OPTIMIZED, + SettingsEnums.ACTION_APP_BATTERY_USAGE_UNRESTRICTED, SettingsEnums.FUELGAUGE_POWER_USAGE_MANAGE_BACKGROUND, - packageName, + PACKAGE_NAME, /* consumed battery */ 0); } @Test public void onPause_optimizationModeIsNotChanged_notInvokeLogging() throws Exception { - final String packageName = "testPackageName"; - final int restrictedMode = BatteryOptimizeUtils.MODE_RESTRICTED; - final int optimizedMode = BatteryOptimizeUtils.MODE_OPTIMIZED; - mFragment.mOptimizationMode = restrictedMode; - when(mBatteryOptimizeUtils.getPackageName()).thenReturn(packageName); - when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn("com.android.vending"); + mFragment.mOptimizationMode = BatteryOptimizeUtils.MODE_OPTIMIZED; + when(mBatteryOptimizeUtils.getPackageName()).thenReturn(PACKAGE_NAME); + when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn(INITIATING_PACKAGE_NAME); - mFragment.onCheckedChanged(mMockSwitch, /* isChecked= */ true); - verify(mBatteryOptimizeUtils).setAppUsageState(optimizedMode, Action.APPLY); - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(optimizedMode); - mFragment.onCheckedChanged(mMockSwitch, /* isChecked= */ false); - verify(mBatteryOptimizeUtils).setAppUsageState(restrictedMode, Action.APPLY); - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(restrictedMode); + mTestMode = BatteryOptimizeUtils.MODE_UNRESTRICTED; + assertThat(mBatteryOptimizeUtils.getAppOptimizationMode()) + .isEqualTo(BatteryOptimizeUtils.MODE_UNRESTRICTED); + mTestMode = BatteryOptimizeUtils.MODE_OPTIMIZED; + assertThat(mBatteryOptimizeUtils.getAppOptimizationMode()) + .isEqualTo(BatteryOptimizeUtils.MODE_OPTIMIZED); mFragment.onPause(); TimeUnit.SECONDS.sleep(1); verifyNoInteractions(mMetricsFeatureProvider); } + + private void prepareTestBatteryOptimizationUtils() { + mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(mContext, UID, PACKAGE_NAME)); + Answer setTestMode = + invocation -> { + mTestMode = invocation.getArgument(0); + return null; + }; + doAnswer(setTestMode).when(mBatteryOptimizeUtils).setAppUsageState(anyInt(), any()); + Answer getTestMode = invocation -> mTestMode; + doAnswer(getTestMode).when(mBatteryOptimizeUtils).getAppOptimizationMode(); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java deleted file mode 100644 index 0c6f7da2109..00000000000 --- a/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2021 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.fuelgauge; - -import static com.google.common.truth.Truth.assertThat; - -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.pm.PackageManager; - -import com.android.settingslib.widget.SelectorWithWidgetPreference; - -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; - -@RunWith(RobolectricTestRunner.class) -public class UnrestrictedPreferenceControllerTest { - private static final int UID = 12345; - private static final String PACKAGE_NAME = "com.android.app"; - - private UnrestrictedPreferenceController mController; - private SelectorWithWidgetPreference mPreference; - private BatteryOptimizeUtils mBatteryOptimizeUtils; - - @Mock PackageManager mMockPackageManager; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - Context context = spy(RuntimeEnvironment.application); - BatteryUtils.getInstance(context).reset(); - doReturn(UID) - .when(mMockPackageManager) - .getPackageUid(PACKAGE_NAME, PackageManager.GET_META_DATA); - - mController = new UnrestrictedPreferenceController(context, UID, PACKAGE_NAME); - mPreference = new SelectorWithWidgetPreference(RuntimeEnvironment.application); - mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(context, UID, PACKAGE_NAME)); - mController.mBatteryOptimizeUtils = mBatteryOptimizeUtils; - } - - @Test - public void testUpdateState_isValidPackage_prefEnabled() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); - - mController.updateState(mPreference); - - assertThat(mBatteryOptimizeUtils.isOptimizeModeMutable()).isTrue(); - assertThat(mPreference.isEnabled()).isTrue(); - } - - @Test - public void testUpdateState_invalidPackage_prefDisabled() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); - - mController.updateState(mPreference); - - assertThat(mBatteryOptimizeUtils.isOptimizeModeMutable()).isFalse(); - assertThat(mPreference.isEnabled()).isFalse(); - } - - @Test - public void testUpdateState_isSystemOrDefaultAppAndUnrestrictedStates_prefChecked() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_UNRESTRICTED); - - mController.updateState(mPreference); - - assertThat(mPreference.isChecked()).isTrue(); - } - - @Test - public void testUpdateState_isSystemOrDefaultApp_prefUnchecked() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_OPTIMIZED); - - mController.updateState(mPreference); - - assertThat(mPreference.isEnabled()).isFalse(); - assertThat(mPreference.isChecked()).isFalse(); - } - - @Test - public void testUpdateState_isUnrestrictedStates_prefChecked() { - when(mBatteryOptimizeUtils.isOptimizeModeMutable()).thenReturn(true); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_UNRESTRICTED); - - mController.updateState(mPreference); - - assertThat(mPreference.isEnabled()).isTrue(); - assertThat(mPreference.isChecked()).isTrue(); - } - - @Test - public void testUpdateState_prefUnchecked() { - when(mBatteryOptimizeUtils.isOptimizeModeMutable()).thenReturn(true); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_OPTIMIZED); - - mController.updateState(mPreference); - - assertThat(mPreference.isEnabled()).isTrue(); - assertThat(mPreference.isChecked()).isFalse(); - } - - @Test - public void testHandlePreferenceTreeClick_samePrefKey_verifyAction() { - mPreference.setKey(mController.KEY_UNRESTRICTED_PREF); - mController.handlePreferenceTreeClick(mPreference); - - assertThat(mController.handlePreferenceTreeClick(mPreference)).isTrue(); - } - - @Test - public void testHandlePreferenceTreeClick_incorrectPrefKey_noAction() { - assertThat(mController.handlePreferenceTreeClick(mPreference)).isFalse(); - } -} diff --git a/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java b/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java index 400f73f7f56..df399d779e1 100644 --- a/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java +++ b/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java @@ -70,6 +70,7 @@ import androidx.test.core.app.ApplicationProvider; import com.android.settings.AirplaneModeEnabler; import com.android.settings.R; +import com.android.settings.dashboard.DashboardFeatureProvider; import com.android.settings.datausage.DataUsagePreference; import com.android.settings.testutils.shadow.ShadowDataUsageUtils; import com.android.settings.testutils.shadow.ShadowFragment; @@ -98,6 +99,7 @@ import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.shadows.ShadowToast; +import org.robolectric.util.ReflectionHelpers; import java.util.List; @@ -190,6 +192,9 @@ public class NetworkProviderSettingsTest { .when(mFirstWifiEntryPreferenceCategory).getKey(); mNetworkProviderSettings.mFirstWifiEntryPreferenceCategory = mFirstWifiEntryPreferenceCategory; + + ReflectionHelpers.setField(mNetworkProviderSettings, "mDashboardFeatureProvider", + mock(DashboardFeatureProvider.class)); } @Test diff --git a/tests/robotests/src/com/android/settings/network/apn/ApnEditorTest.java b/tests/robotests/src/com/android/settings/network/apn/ApnEditorTest.java deleted file mode 100644 index d8895d5ddd6..00000000000 --- a/tests/robotests/src/com/android/settings/network/apn/ApnEditorTest.java +++ /dev/null @@ -1,625 +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.network.apn; - -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.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.database.Cursor; -import android.net.Uri; -import android.os.PersistableBundle; -import android.os.UserManager; -import android.telephony.CarrierConfigManager; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; - -import androidx.fragment.app.FragmentActivity; -import androidx.preference.EditTextPreference; -import androidx.preference.ListPreference; -import androidx.preference.MultiSelectListPreference; -import androidx.preference.SwitchPreference; - -import com.android.settings.R; -import com.android.settings.network.ProxySubscriptionManager; -import com.android.settings.network.apn.ApnEditor.ApnData; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; - -@RunWith(RobolectricTestRunner.class) -@Config(shadows = { - com.android.settings.testutils.shadow.ShadowFragment.class, -}) -public class ApnEditorTest { - - private static final Object[] APN_DATA = { - 0, /* ID */ - "apn_name" /* apn name */, - "apn.com" /* apn */, - "" /* proxy */, - "" /* port */, - "" /* username */, - "" /* server */, - "" /* password */, - "" /* MMSC */, - "123" /* MCC */, - "456" /* MNC */, - "123456" /* operator numeric */, - "" /* MMS proxy */, - "" /* MMS port */, - 0 /* Authentication type */, - "default,supl,ia" /* APN type */, - "IP" /* APN protocol */, - 1 /* APN enable/disable */, - 0 /* Bearer */, - 0 /* Bearer BITMASK*/, - "IPV6" /* APN roaming protocol */, - "None" /* MVNO type */, - "", /* MVNO value */ - }; - - private static final int CURSOR_INTEGER_INDEX = 0; - private static final int CURSOR_STRING_INDEX = 1; - - private static final Uri APN_URI = Uri.parse("Apn://row/1"); - - @Mock - private Cursor mCursor; - - @Mock - private FragmentActivity mActivity; - @Mock - private UserManager mUserManager; - @Mock - private ProxySubscriptionManager mProxySubscriptionMgr; - @Mock - private CarrierConfigManager mCarrierConfigManager; - @Captor - private ArgumentCaptor mUriCaptor; - - private ApnEditor mApnEditorUT; - private Context mContext; - private Resources mResources; - private PersistableBundle mBundle = new PersistableBundle(); - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mContext = spy(RuntimeEnvironment.application); - - mResources = mContext.getResources(); - mApnEditorUT = spy(new ApnEditor()); - - doReturn(mActivity).when(mApnEditorUT).getActivity(); - doReturn(mResources).when(mApnEditorUT).getResources(); - doNothing().when(mApnEditorUT).finish(); - doNothing().when(mApnEditorUT).showError(); - doReturn(mContext).when(mApnEditorUT).getContext(); - doReturn(mContext.getTheme()).when(mActivity).getTheme(); - doReturn(mContext.getContentResolver()).when(mActivity).getContentResolver(); - - doReturn(mUserManager).when(mContext).getSystemService(UserManager.class); - doReturn(true).when(mUserManager).isAdminUser(); - doReturn(false).when(mUserManager) - .hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); - doReturn(mCarrierConfigManager).when(mContext) - .getSystemService(Context.CARRIER_CONFIG_SERVICE); - doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt()); - - setMockPreference(mContext); - mApnEditorUT.mApnData = new FakeApnData(APN_DATA); - mApnEditorUT.sNotSet = "Not Set"; - } - - @Test - public void testApnEditor_doesNotUseManagedQuery() { - mApnEditorUT.getApnDataFromUri(Mockito.mock(Uri.class)); - - verify(mActivity, never()).managedQuery( - any(Uri.class), - any(String[].class), - any(String.class), - any(String.class)); - - verify(mActivity, never()).managedQuery( - any(Uri.class), - any(String[].class), - any(String.class), - any(String[].class), - any(String.class)); - } - - @Test - public void getApnDataFromUri_emptyCursor_returnsNull() { - var mockContentResolver = mock(ContentResolver.class); - var mockCursor = mock(Cursor.class); - doReturn(mockContentResolver).when(mActivity).getContentResolver(); - when(mockContentResolver.query(any(), any(), any(), any(), any())).thenReturn(mockCursor); - when(mockCursor.moveToFirst()).thenReturn(false); - - var apnData = mApnEditorUT.getApnDataFromUri(mock(Uri.class)); - - assertThat(apnData).isNull(); - } - - @Test - public void testSetStringValue_valueChanged_shouldSetValue() { - // GIVEN an APN value which is different than the APN value in database - final String apnKey = "apn"; - final String apnValue = "testing.com"; - final ContentValues cv = new ContentValues(); - - // WHEN try to check and set the apn value - final boolean isDiff = mApnEditorUT.setStringValueAndCheckIfDiff( - cv, apnKey, apnValue, false /* assumeDiff */, ApnEditor.APN_INDEX); - - // THEN the APN value is different than the one in database, and it has been stored in the - // given ContentValues - assertThat(isDiff).isTrue(); - assertThat(apnValue).isEqualTo(cv.getAsString(apnKey)); - } - - @Test - public void testSetStringValue_valueNotChanged_shouldNotSetValue() { - // GIVEN an APN value which is same as the APN value in database - final String apnKey = "apn"; - final String apnValue = (String) APN_DATA[ApnEditor.APN_INDEX]; - final ContentValues cv = new ContentValues(); - - // WHEN try to check and set the apn value - final boolean isDiff = mApnEditorUT.setStringValueAndCheckIfDiff( - cv, apnKey, apnValue, false /* assumeDiff */, ApnEditor.APN_INDEX); - - // THEN the APN value is same as the one in database, and the new APN value is not stored - // in the given ContentValues - assertThat(isDiff).isFalse(); - assertThat(cv.get(apnKey)).isNull(); - } - - @Test - public void testSetStringValue_nullValue_shouldNotSetValue_shouldNotSetValue() { - // GIVEN a null APN value - final String apnKey = "apn"; - final String apnValue = null; - final ContentValues cv = new ContentValues(); - - // WHEN try to check and set the apn value - final boolean isDiff = mApnEditorUT.setStringValueAndCheckIfDiff( - cv, apnKey, apnValue, false /* assumeDiff */, ApnEditor.APN_INDEX); - - // THEN the APN value is different than the one in database, but the null value is not - // stored in the given ContentValues - assertThat(isDiff).isTrue(); - assertThat(cv.get(apnKey)).isNull(); - } - - @Test - public void testSetIntValue_valueChanged_shouldSetValue() { - // GIVEN a value indicated whether the apn is enabled, and it's different than the value in - // the database - final String apnEnabledKey = "apn_enabled"; - final int apnEnabledValue = 0; - final ContentValues cv = new ContentValues(); - - // WHEN try to check and set the apn enabled - final boolean isDiff = mApnEditorUT.setIntValueAndCheckIfDiff( - cv, - apnEnabledKey, - apnEnabledValue, - false /* assumeDiff */, - ApnEditor.CARRIER_ENABLED_INDEX); - - // THEN the apn enabled field is different than the one in database, and it has been stored - // in the given ContentValues - assertThat(isDiff).isTrue(); - assertThat(cv.getAsInteger(apnEnabledKey)).isEqualTo(apnEnabledValue); - } - - @Test - public void testSetIntValue_valueNotChanged_shouldNotSetValue() { - // GIVEN a value indicated whether the apn is enabled, and it's same as the one in the - // database - final String apnEnabledKey = "apn_enabled"; - final int apnEnabledValue = (int) APN_DATA[ApnEditor.CARRIER_ENABLED_INDEX]; - final ContentValues cv = new ContentValues(); - - // WHEN try to check and set the apn enabled - final boolean isDiff = mApnEditorUT.setIntValueAndCheckIfDiff( - cv, - apnEnabledKey, - apnEnabledValue, - false /* assumeDiff */, - ApnEditor.CARRIER_ENABLED_INDEX); - - // THEN the apn enabled field is same as the one in the database, and the filed is not - // stored in the given ContentValues - assertThat(isDiff).isFalse(); - assertThat(cv.get(apnEnabledKey)).isNull(); - } - - @Test - public void testValidateApnData_validData_shouldReturnNull() { - // GIVEN a valid apn data - mApnEditorUT.fillUI(true /* firstTime */); - - // WHEN validate the apn data - final String errMsg = mApnEditorUT.validateApnData(); - - // THEN the error message should be null - assertThat(errMsg).isNull(); - } - - @Test - public void testValidateApn_apnNameNotSet_shouldReturnErrorMessage() { - // GIVEN a apn data without the apn name - mApnEditorUT.mApnData.mData[ApnEditor.NAME_INDEX] = ""; - mApnEditorUT.fillUI(true /* firstTime */); - - // THEN validate the apn data - final String errMsg = mApnEditorUT.validateApnData(); - - // THEN the error message indicated the apn name not set is returned - assertThat(errMsg).isEqualTo(mResources.getString(R.string.error_name_empty)); - } - - @Test - public void testValidateApnData_apnNotSet_shouldReturnErrorMessage() { - // GIVEN a apn data without the apn - mApnEditorUT.mApnData.mData[ApnEditor.APN_INDEX] = ""; - mApnEditorUT.fillUI(true /* firstTime */); - - // THEN validate the apn data - final String errMsg = mApnEditorUT.validateApnData(); - - // THEN the error message indicated the apn not set is returned - assertThat(errMsg).isEqualTo(mResources.getString(R.string.error_apn_empty)); - } - - @Test - public void testValidateApnData_mccInvalid_shouldReturnErrorMessage() { - // The length of the mcc should be 3 - mApnEditorUT.mApnData.mData[ApnEditor.MCC_INDEX] = "1324"; - mApnEditorUT.fillUI(true /* firstTime */); - - // WHEN validate the apn data - final String errMsg = mApnEditorUT.validateApnData(); - - // THEN the error message indicated the mcc invalid is returned - assertThat(errMsg).isEqualTo(mResources.getString(R.string.error_mcc_not3)); - } - - @Test - public void testValidateApnData_mncInvalid_shouldReturnErrorMessage() { - // GIVEN an apn data with invalid mnc - // The length of the mnc should be 2 or 3 - mApnEditorUT.mApnData.mData[ApnEditor.MNC_INDEX] = "1324"; - mApnEditorUT.fillUI(true /* firstTime */); - - // WHEN validate the apn data - final String errMsg = mApnEditorUT.validateApnData(); - - // THEN the error message indicated the mnc invalid is returned - assertThat(errMsg).isEqualTo(mResources.getString(R.string.error_mnc_not23)); - } - - @Test - public void testSaveApnData_pressBackButtonWithValidApnData_shouldSaveApnData() { - // GIVEN a valid apn data - mApnEditorUT.fillUI(true /* firstTime */); - - // WHEN press the back button - final KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK); - mApnEditorUT.onKey(new View(mContext), KeyEvent.KEYCODE_BACK, event); - - // THEN the apn data is saved and the apn editor is closed - verify(mApnEditorUT).validateAndSaveApnData(); - verify(mApnEditorUT).finish(); - } - - @Test - public void testSaveApnData_pressSaveButtonWithValidApnData_shouldSaveApnData() { - // GIVEN a valid apn data - mApnEditorUT.fillUI(true /* firstTime */); - - // WHEN press the save button - MenuItem item = Mockito.mock(MenuItem.class); - // Menu.FIRST + 1 indicated the SAVE option in ApnEditor - doReturn(Menu.FIRST + 1).when(item).getItemId(); - mApnEditorUT.onOptionsItemSelected(item); - - // THEN the apn data is saved and the apn editor is closed - verify(mApnEditorUT).validateAndSaveApnData(); - verify(mApnEditorUT).finish(); - } - - @Test - public void testSaveApnData_apnDataInvalid_shouldNotSaveApnData() { - // GIVEN an invalid apn data - // The valid apn data should contains a non-empty apn name - mApnEditorUT.mApnData.mData[ApnEditor.NAME_INDEX] = ""; - mApnEditorUT.fillUI(true /* firstTime */); - - // WHEN press the save button - final MenuItem item = Mockito.mock(MenuItem.class); - // Menu.FIRST + 1 indicated the SAVE option in ApnEditor - doReturn(Menu.FIRST + 1).when(item).getItemId(); - mApnEditorUT.onOptionsItemSelected(item); - - // THEN the error dialog is shown - verify(mApnEditorUT).validateAndSaveApnData(); - verify(mApnEditorUT).showError(); - } - - @Test - public void testDeleteApnData_shouldDeleteData() { - // GIVEN a valid apn data correspond a row in database - final Uri apnUri = Uri.parse("content://telephony/carriers/1"); - mApnEditorUT.mApnData = new FakeApnData(APN_DATA, apnUri); - mApnEditorUT.fillUI(true /* firstTime */); - ContentResolver mockContentResolver = Mockito.mock(ContentResolver.class); - doReturn(mockContentResolver).when(mActivity).getContentResolver(); - - // WHEN press the save button - final MenuItem item = Mockito.mock(MenuItem.class); - // Menu.FIRST indicated the DELETE option in ApnEditor - doReturn(Menu.FIRST).when(item).getItemId(); - mApnEditorUT.onOptionsItemSelected(item); - - // THEN the apn data is deleted and the apn editor is closed - verify(mockContentResolver).delete(mUriCaptor.capture(), any(), any()); - assertThat(apnUri).isEqualTo(mUriCaptor.getValue()); - verify(mApnEditorUT).finish(); - } - - @Test - public void testDeleteApnData_shouldNotPresentMenuWhenNotSupportAdding() { - mBundle.putBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL, false); - - MenuItem item = Mockito.mock(MenuItem.class); - Menu menu = Mockito.mock(Menu.class); - doReturn(item).when(menu).add(anyInt(), anyInt(), anyInt(), anyInt()); - - mApnEditorUT.getCarrierCustomizedConfig(mContext); - mApnEditorUT.onCreateOptionsMenu(menu, null); - - verify(menu, times(0)).add(anyInt(), eq(ApnEditor.MENU_DELETE), anyInt(), anyInt()); - } - - @Test(expected = ClassCastException.class) - public void testApnData_invalidIntegerType_throwsInvalidTypeException() { - // GIVEN a ApnData constructed from cursor - initCursor(); - final ApnData data = new ApnData(APN_URI, mCursor); - - // WHEN get a string from an integer column - // THEN the InvalidTypeException is threw - data.getString(CURSOR_INTEGER_INDEX); - } - - @Test(expected = ClassCastException.class) - public void testApnData_invalidStringType_throwsInvalidTypeException() { - // GIVEN a ApnData constructed from cursor - initCursor(); - final ApnData data = new ApnData(APN_URI, mCursor); - - // WHEN get a integer from a string column - // THEN the InvalidTypeException is threw - data.getInteger(CURSOR_STRING_INDEX); - } - - @Test - public void testApnData_validIntegerType_returnCorrectValue() { - // GIVEN a ApnData constructed from cursor - initCursor(); - final ApnData data = new ApnData(APN_URI, mCursor); - - // WHEN get integer from an integer column - final int val = data.getInteger(CURSOR_INTEGER_INDEX); - - // THEN the integer is returned correctly - assertThat(val).isEqualTo(mCursor.getInt(CURSOR_INTEGER_INDEX)); - } - - @Test - public void testApnData_validStringType_returnCorrectValue() { - // GIVEN a ApnData constructed from cursor - initCursor(); - final ApnData data = new ApnData(APN_URI, mCursor); - - // WHEN get string from a string column - final String str = data.getString(CURSOR_STRING_INDEX); - - // THEN the integer is returned correctly - assertThat(str).isEqualTo(mCursor.getString(CURSOR_STRING_INDEX)); - } - - @Test - public void testApnData_nullValueColumn_returnNull() { - // GIVEN a empty ApnData - final ApnData data = new ApnData(3); - - // WHEN get string value from a null column - final String str = data.getString(0); - - // THEN the null value is returned - assertThat(str).isNull(); - } - - @Test - public void formatInteger_shouldParseString() { - assertThat(ApnEditor.formatInteger("42")).isEqualTo("42"); - assertThat(ApnEditor.formatInteger("01")).isEqualTo("01"); - assertThat(ApnEditor.formatInteger("001")).isEqualTo("001"); - } - - @Test - public void formatInteger_shouldIgnoreNonIntegers() { - assertThat(ApnEditor.formatInteger("not an int")).isEqualTo("not an int"); - } - - @Test - public void onCreate_notAdminUser_shouldFinish() { - doReturn(false).when(mUserManager).isAdminUser(); - - mApnEditorUT.onCreate(null); - - verify(mApnEditorUT).finish(); - } - - @Test - public void onCreate_hasUserRestriction_shouldFinish() { - doReturn(true).when(mUserManager) - .hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); - - mApnEditorUT.onCreate(null); - - verify(mApnEditorUT).finish(); - } - - @Test - public void onCreate_noAction_shouldFinishAndNoCrash() { - ProxySubscriptionManager proxySubscriptionMgr = mock(ProxySubscriptionManager.class); - mApnEditorUT.mProxySubscriptionMgr = proxySubscriptionMgr; - doReturn(new Intent()).when(mActivity).getIntent(); - doNothing().when(mApnEditorUT).addPreferencesFromResource(anyInt()); - - mApnEditorUT.onCreate(null); - - verify(mApnEditorUT).finish(); - } - - @Test - public void testOnViewStateRestored_customizedValueWithoutDefault_shouldShowCustomized() { - mApnEditorUT.mDefaultApnProtocol = "IP"; - mApnEditorUT.mApnData.mData[ApnEditor.PROTOCOL_INDEX] = null; - mApnEditorUT.mProtocol.setEntryValues(new CharSequence[]{"IP", "IPV6", "IPV4V6"}); - - mApnEditorUT.onViewStateRestored(null); - - assertThat(mApnEditorUT.mProtocol.getSummary()).isEqualTo("IPv4"); - } - - @Test - public void testOnViewStateRestored_customizedValueWithDefault_shouldShowDefault() { - mApnEditorUT.mDefaultApnProtocol = "IP"; - mApnEditorUT.mApnData.mData[ApnEditor.PROTOCOL_INDEX] = "IPV6"; - mApnEditorUT.mProtocol.setEntryValues(new CharSequence[]{"IP", "IPV6", "IPV4V6"}); - - mApnEditorUT.onViewStateRestored(null); - - assertThat(mApnEditorUT.mProtocol.getSummary()).isEqualTo("IPv6"); - } - - @Test - public void getUserEnteredApnType_emptyApnType_shouldReturnDefault() { - // case 1 - // GIVEN read only APN types with DUN - mApnEditorUT.mReadOnlyApnTypes = new String [] {"dun"}; - // GIVEN read specificApnTypeForEmptyInput with DEFAULT,DUN - mApnEditorUT.mDefaultApnTypes = new String [] {"default", "dun"}; - - // Input empty in TYPE - mApnEditorUT.mApnData.mData[ApnEditor.TYPE_INDEX] = ""; - mApnEditorUT.onViewStateRestored(null); - - // THEN APN type should be default - assertThat(mApnEditorUT.getUserEnteredApnType()).isEqualTo("default"); - - // case 2 - // GIVEN read only APN types with DUN - mApnEditorUT.mReadOnlyApnTypes = new String [] {"dun"}; - // GIVEN read specificApnTypeForEmptyInput with DEFAULT - mApnEditorUT.mDefaultApnTypes = new String [] {"default"}; - - // Input empty in TYPE - mApnEditorUT.mApnData.mData[ApnEditor.TYPE_INDEX] = ""; - mApnEditorUT.onViewStateRestored(null); - - // THEN APN type should be default - assertThat(mApnEditorUT.getUserEnteredApnType()).isEqualTo("default"); - } - - private void initCursor() { - doReturn(2).when(mCursor).getColumnCount(); - doReturn(2).when(mCursor).getInt(CURSOR_INTEGER_INDEX); - doReturn("str").when(mCursor).getString(CURSOR_STRING_INDEX); - doReturn(Cursor.FIELD_TYPE_INTEGER).when(mCursor).getType(CURSOR_INTEGER_INDEX); - doReturn(Cursor.FIELD_TYPE_STRING).when(mCursor).getType(CURSOR_STRING_INDEX); - } - - private void setMockPreference(Context context) { - mApnEditorUT.mName = new EditTextPreference(context); - mApnEditorUT.mApn = new EditTextPreference(context); - mApnEditorUT.mProxy = new EditTextPreference(context); - mApnEditorUT.mPort = new EditTextPreference(context); - mApnEditorUT.mUser = new EditTextPreference(context); - mApnEditorUT.mServer = new EditTextPreference(context); - mApnEditorUT.mPassword = new EditTextPreference(context); - mApnEditorUT.mMmsc = new EditTextPreference(context); - mApnEditorUT.mMcc = new EditTextPreference(context); - mApnEditorUT.mMnc = new EditTextPreference(context); - mApnEditorUT.mMmsProxy = new EditTextPreference(context); - mApnEditorUT.mMmsPort = new EditTextPreference(context); - mApnEditorUT.mAuthType = new ListPreference(context); - mApnEditorUT.mApnType = new EditTextPreference(context); - mApnEditorUT.mProtocol = new ListPreference(context); - mApnEditorUT.mRoamingProtocol = new ListPreference(context); - mApnEditorUT.mCarrierEnabled = new SwitchPreference(context); - mApnEditorUT.mBearerMulti = new MultiSelectListPreference(context); - mApnEditorUT.mMvnoType = new ListPreference(context); - mApnEditorUT.mMvnoMatchData = new EditTextPreference(context); - } - - private final class FakeApnData extends ApnData { - FakeApnData(Object[] data) { - super(data.length); - System.arraycopy(data, 0, mData, 0, data.length); - } - - FakeApnData(Object[] data, Uri uri) { - this(data); - mUri = uri; - } - } -}