diff --git a/aconfig/accessibility/accessibility_flags.aconfig b/aconfig/accessibility/accessibility_flags.aconfig index c9e1ece1b3a..2c92547474d 100644 --- a/aconfig/accessibility/accessibility_flags.aconfig +++ b/aconfig/accessibility/accessibility_flags.aconfig @@ -3,6 +3,13 @@ container: "system_ext" # NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors. +flag { + name: "add_brightness_settings_in_suw" + namespace: "accessibility" + description: "Whether to add brightness preference in SUW Vision Settings" + bug: "332974327" +} + flag { name: "edit_shortcuts_in_full_screen" namespace: "accessibility" diff --git a/aconfig/settings_device_diagnostics_declarations.aconfig b/aconfig/settings_device_diagnostics_declarations.aconfig new file mode 100644 index 00000000000..90a12dbb9ab --- /dev/null +++ b/aconfig/settings_device_diagnostics_declarations.aconfig @@ -0,0 +1,9 @@ +package: "com.android.settings.flags" +container: "system_ext" + +flag { + name: "enable_device_diagnostics_in_settings" + namespace: "phoenix" + description: "Enable the Device Diagnostics app in Settings" + bug: "309886423" +} diff --git a/color-check-baseline.xml b/color-check-baseline.xml index ecc795ee4c6..bd90b981ff5 100644 --- a/color-check-baseline.xml +++ b/color-check-baseline.xml @@ -1,6 +1,18 @@ + + + + + + + + + + + + + + + + diff --git a/res/drawable/ic_brightness_medium.xml b/res/drawable/ic_brightness_medium.xml new file mode 100644 index 00000000000..3e778857127 --- /dev/null +++ b/res/drawable/ic_brightness_medium.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/res/drawable/ic_device_diagnostics.xml b/res/drawable/ic_device_diagnostics.xml new file mode 100644 index 00000000000..26953a70314 --- /dev/null +++ b/res/drawable/ic_device_diagnostics.xml @@ -0,0 +1,10 @@ + + + diff --git a/res/drawable/ic_suw_brightness_auto.xml b/res/drawable/ic_suw_brightness_auto.xml new file mode 100644 index 00000000000..a4221c540d1 --- /dev/null +++ b/res/drawable/ic_suw_brightness_auto.xml @@ -0,0 +1,26 @@ + + + + + + + + \ No newline at end of file diff --git a/res/drawable/ic_suw_brightness_level.xml b/res/drawable/ic_suw_brightness_level.xml new file mode 100644 index 00000000000..57bd1883849 --- /dev/null +++ b/res/drawable/ic_suw_brightness_level.xml @@ -0,0 +1,26 @@ + + + + + + + + \ No newline at end of file diff --git a/res/values/config.xml b/res/values/config.xml index 4d3a23348e4..4b638b25552 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -836,4 +836,7 @@ + + + com.android.devicediagnostics diff --git a/res/values/strings.xml b/res/values/strings.xml index 1a83b79296e..593282fc93e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1257,8 +1257,8 @@ You can lock your private space automatically if you haven\’t used your device for a period of time Every time device locks - - After 5 minutes of inactivity + + 5 minutes after screen timeout Only after device restarts @@ -2732,6 +2732,10 @@ Adaptive + + brightness + + auto_brightness_entry Brightness level @@ -7889,6 +7893,9 @@ Do Not Disturb + + Priority Modes + Only get notified by important people and apps @@ -12947,17 +12954,17 @@ Give your watch the same app permissions that you’ve allowed on this phone - Audio Device Type - - Unknown + Audio device type + + Not set Speaker Headphones - - Hearing Aid + + Hearing device - Car Kit + Car Other @@ -13258,4 +13265,9 @@ Sync across devices + + + + + Device diagnostics diff --git a/res/xml/accessibility_settings_for_setup_wizard.xml b/res/xml/accessibility_settings_for_setup_wizard.xml index 0926d2d3008..a69dc26ce3f 100644 --- a/res/xml/accessibility_settings_for_setup_wizard.xml +++ b/res/xml/accessibility_settings_for_setup_wizard.xml @@ -29,6 +29,22 @@ settings:keywords="text_reading_options" settings:controller="com.android.settings.accessibility.TextReadingFragmentForSuwController"/> + + + + + + + + + + + + + + + diff --git a/res/xml/modes_rule_settings.xml b/res/xml/modes_rule_settings.xml new file mode 100644 index 00000000000..d7e26946829 --- /dev/null +++ b/res/xml/modes_rule_settings.xml @@ -0,0 +1,25 @@ + + + + + + + + + \ No newline at end of file diff --git a/res/xml/system_dashboard_fragment.xml b/res/xml/system_dashboard_fragment.xml index 628eab94e4c..6225f4f8382 100644 --- a/res/xml/system_dashboard_fragment.xml +++ b/res/xml/system_dashboard_fragment.xml @@ -97,6 +97,13 @@ android:order="-40" settings:controller="com.android.settings.system.DeveloperOptionsController"/> + + createPreferenceControllers(Context context) { + final List controllers = new ArrayList<>(); + BrightnessLevelPreferenceController brightnessLevelPreferenceController = + new BrightnessLevelPreferenceController(context, getSettingsLifecycle()); + brightnessLevelPreferenceController.setInSetupWizard(true); + controllers.add(brightnessLevelPreferenceController); + String autoBrightnessKey = context.getString(R.string.preference_key_auto_brightness); + AutoBrightnessPreferenceController autoBrightnessPreferenceController = + new AutoBrightnessPreferenceController(context, autoBrightnessKey); + autoBrightnessPreferenceController.setInSetupWizard(true); + controllers.add(autoBrightnessPreferenceController); + return controllers; + } + /** * Returns accessibility service info by given package name and service name. * diff --git a/src/com/android/settings/accessibility/AutoBrightnessPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/AutoBrightnessPreferenceFragmentForSetupWizard.java new file mode 100644 index 00000000000..ad1ae96b03b --- /dev/null +++ b/src/com/android/settings/accessibility/AutoBrightnessPreferenceFragmentForSetupWizard.java @@ -0,0 +1,81 @@ +/* + * 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.accessibility; + +import static android.app.Activity.RESULT_CANCELED; + +import android.app.settings.SettingsEnums; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.settings.R; +import com.android.settings.display.AutoBrightnessSettings; +import com.android.settingslib.Utils; + +import com.google.android.setupcompat.template.FooterBarMixin; +import com.google.android.setupdesign.GlifPreferenceLayout; + +/** + * Fragment for adaptive brightness settings in the SetupWizard. + */ +public class AutoBrightnessPreferenceFragmentForSetupWizard extends AutoBrightnessSettings { + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + if (view instanceof GlifPreferenceLayout) { + final GlifPreferenceLayout layout = (GlifPreferenceLayout) view; + final String title = getContext().getString( + R.string.auto_brightness_title); + final Drawable icon = getContext().getDrawable(R.drawable.ic_accessibility_visibility); + icon.setTintList(Utils.getColorAttr(getContext(), android.R.attr.colorPrimary)); + AccessibilitySetupWizardUtils.updateGlifPreferenceLayout(getContext(), layout, title, + /* description= */ null, icon); + + final FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class); + AccessibilitySetupWizardUtils.setPrimaryButton(getContext(), mixin, R.string.done, + () -> { + setResult(RESULT_CANCELED); + finish(); + }); + } + } + + @NonNull + @Override + public RecyclerView onCreateRecyclerView(@NonNull LayoutInflater inflater, + @NonNull ViewGroup parent, @Nullable Bundle savedInstanceState) { + if (parent instanceof GlifPreferenceLayout) { + final GlifPreferenceLayout layout = (GlifPreferenceLayout) parent; + return layout.onCreateRecyclerView(inflater, parent, savedInstanceState); + } + return super.onCreateRecyclerView(inflater, parent, savedInstanceState); + } + + @Override + public int getMetricsCategory() { + return SettingsEnums.SUW_ACCESSIBILITY_AUTO_BRIGHTNESS; + } +} diff --git a/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java b/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java index c806c0bc5a8..7455eea51ce 100644 --- a/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java +++ b/src/com/android/settings/accessibility/RestrictedPreferenceHelper.java @@ -16,6 +16,7 @@ package com.android.settings.accessibility; +import static com.android.settings.accessibility.AccessibilitySettings.VOICE_ACCESS_SERVICE; import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_MEDIUM; import android.accessibilityservice.AccessibilityServiceInfo; @@ -37,6 +38,7 @@ import androidx.core.content.ContextCompat; import com.android.settings.R; import com.android.settings.Utils; +import com.android.settings.development.Enable16kUtils; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtilsInternal; @@ -89,6 +91,11 @@ public class RestrictedPreferenceHelper { final AccessibilityServiceInfo info = installedServices.get(i); final ResolveInfo resolveInfo = info.getResolveInfo(); final String packageName = resolveInfo.serviceInfo.packageName; + // TODO(b/335443194) Voice access is not available in 16kB mode. + if (packageName.contains(VOICE_ACCESS_SERVICE) + && Enable16kUtils.isPageAgnosticModeOn(mContext)) { + continue; + } final ComponentName componentName = new ComponentName(packageName, resolveInfo.serviceInfo.name); diff --git a/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java b/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java index fccd969bece..57cd1d2d8da 100644 --- a/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java +++ b/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java @@ -20,16 +20,20 @@ import android.content.Context; import android.content.Intent; import android.credentials.CredentialManager; import android.credentials.CredentialProviderInfo; +import android.credentials.SetEnabledProvidersException; import android.graphics.drawable.Drawable; +import android.os.OutcomeReceiver; import android.os.UserHandle; import android.provider.Settings; import android.service.autofill.AutofillService; import android.service.autofill.AutofillServiceInfo; import android.text.TextUtils; +import android.util.Log; import android.view.autofill.AutofillManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; import androidx.preference.Preference; import com.android.internal.annotations.VisibleForTesting; @@ -41,6 +45,7 @@ import com.android.settingslib.widget.TwoTargetPreference; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; public class DefaultCombinedPreferenceController extends DefaultAppPreferenceController { @@ -49,10 +54,12 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon private final AutofillManager mAutofillManager; private final CredentialManager mCredentialManager; + private final Executor mExecutor; public DefaultCombinedPreferenceController(Context context) { super(context); + mExecutor = ContextCompat.getMainExecutor(context); mAutofillManager = mContext.getSystemService(AutofillManager.class); if (CredentialManager.isServiceEnabled(context)) { @@ -158,6 +165,9 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon // Apply device admin restrictions to top provider. if (topProvider != null && topProvider.getDeviceAdminRestrictions(mContext, userId) != null) { + // This case means, the provider is blocked by device admin, but settings' storage has + // not be cleared correctly. So clean the storage here. + removePrimaryProvider(); return null; } @@ -209,4 +219,28 @@ public class DefaultCombinedPreferenceController extends DefaultAppPreferenceCon mContext.createContextAsUser(UserHandle.of(getUser()), /* flags= */ 0); return new Intent(context, CredentialsPickerActivity.class); } + + private void removePrimaryProvider() { + // Commit using the CredMan API. + if (mCredentialManager == null) { + return; + } + + mCredentialManager.setEnabledProviders( + List.of(), // empty primary provider. + List.of(), // empty enabled providers. + getUser(), + mExecutor, + new OutcomeReceiver() { + @Override + public void onResult(Void result) { + Log.i(TAG, "setEnabledProviders success"); + } + + @Override + public void onError(SetEnabledProvidersException e) { + Log.e(TAG, "setEnabledProviders error: " + e.toString()); + } + }); + } } diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index 4d43d7964d5..7c601c0d613 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -28,6 +28,7 @@ import com.android.settings.accessibility.AccessibilityDetailsSettingsFragment; import com.android.settings.accessibility.AccessibilityHearingAidsFragment; import com.android.settings.accessibility.AccessibilitySettings; import com.android.settings.accessibility.AccessibilitySettingsForSetupWizard; +import com.android.settings.accessibility.AutoBrightnessPreferenceFragmentForSetupWizard; import com.android.settings.accessibility.CaptioningPropertiesFragment; import com.android.settings.accessibility.ColorAndMotionFragment; import com.android.settings.accessibility.HearingDevicePairingFragment; @@ -258,6 +259,7 @@ public class SettingsGateway { EditShortcutsPreferenceFragment.class.getName(), TextReadingPreferenceFragment.class.getName(), TextReadingPreferenceFragmentForSetupWizard.class.getName(), + AutoBrightnessPreferenceFragmentForSetupWizard.class.getName(), CaptioningPropertiesFragment.class.getName(), ToggleDaltonizerPreferenceFragment.class.getName(), ToggleColorInversionPreferenceFragment.class.getName(), diff --git a/src/com/android/settings/display/AutoBrightnessPreferenceController.java b/src/com/android/settings/display/AutoBrightnessPreferenceController.java index 3f9be2fd6cf..3014f62b0a6 100644 --- a/src/com/android/settings/display/AutoBrightnessPreferenceController.java +++ b/src/com/android/settings/display/AutoBrightnessPreferenceController.java @@ -25,6 +25,7 @@ import android.provider.Settings; import androidx.preference.Preference; import com.android.settings.R; +import com.android.settings.accessibility.Flags; import com.android.settings.core.TogglePreferenceController; import com.android.settingslib.PrimarySwitchPreference; @@ -33,10 +34,16 @@ public class AutoBrightnessPreferenceController extends TogglePreferenceControll private final String SYSTEM_KEY = SCREEN_BRIGHTNESS_MODE; private final int DEFAULT_VALUE = SCREEN_BRIGHTNESS_MODE_MANUAL; + private boolean mInSetupWizard; + public AutoBrightnessPreferenceController(Context context, String key) { super(context, key); } + public void setInSetupWizard(boolean inSetupWizard) { + mInSetupWizard = inSetupWizard; + } + @Override public boolean isChecked() { return Settings.System.getInt(mContext.getContentResolver(), @@ -53,10 +60,14 @@ public class AutoBrightnessPreferenceController extends TogglePreferenceControll @Override @AvailabilityStatus public int getAvailabilityStatus() { - return mContext.getResources().getBoolean( - com.android.internal.R.bool.config_automatic_brightness_available) - ? AVAILABLE_UNSEARCHABLE - : UNSUPPORTED_ON_DEVICE; + if (!mContext.getResources().getBoolean( + com.android.internal.R.bool.config_automatic_brightness_available)) { + return UNSUPPORTED_ON_DEVICE; + } + if (mInSetupWizard && !Flags.addBrightnessSettingsInSuw()) { + return CONDITIONALLY_UNAVAILABLE; + } + return AVAILABLE_UNSEARCHABLE; } @Override diff --git a/src/com/android/settings/display/BrightnessLevelPreferenceController.java b/src/com/android/settings/display/BrightnessLevelPreferenceController.java index 49b8da3d2e3..a32c965cb29 100644 --- a/src/com/android/settings/display/BrightnessLevelPreferenceController.java +++ b/src/com/android/settings/display/BrightnessLevelPreferenceController.java @@ -20,6 +20,7 @@ import static com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX; import static com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MIN; import static com.android.settingslib.display.BrightnessUtils.convertLinearToGammaFloat; +import android.annotation.Nullable; import android.app.ActivityOptions; import android.content.ContentResolver; import android.content.Context; @@ -39,10 +40,12 @@ import android.text.TextUtils; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; +import com.android.settings.R; import com.android.settings.Utils; +import com.android.settings.accessibility.Flags; +import com.android.settings.core.BasePreferenceController; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.SettingsBaseActivity; -import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; @@ -51,17 +54,18 @@ import com.android.settingslib.transition.SettingsTransitionHelper; import java.text.NumberFormat; -public class BrightnessLevelPreferenceController extends AbstractPreferenceController implements +public class BrightnessLevelPreferenceController extends BasePreferenceController implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop { private static final String TAG = "BrightnessPrefCtrl"; - private static final String KEY_BRIGHTNESS = "brightness"; + private static final Uri BRIGHTNESS_ADJ_URI; private final ContentResolver mContentResolver; private final Handler mHandler = new Handler(Looper.getMainLooper()); private final DisplayManager mDisplayManager; - + @Nullable private Preference mPreference; + private boolean mInSetupWizard; static { BRIGHTNESS_ADJ_URI = System.getUriFor(System.SCREEN_AUTO_BRIGHTNESS_ADJ); @@ -90,9 +94,12 @@ public class BrightnessLevelPreferenceController extends AbstractPreferenceContr } }; - public BrightnessLevelPreferenceController(Context context, Lifecycle lifecycle) { - super(context); + this(context, context.getString(R.string.preference_key_brightness_level), lifecycle); + } + + private BrightnessLevelPreferenceController(Context context, String key, Lifecycle lifecycle) { + super(context, key); mDisplayManager = context.getSystemService(DisplayManager.class); if (lifecycle != null) { @@ -101,20 +108,22 @@ public class BrightnessLevelPreferenceController extends AbstractPreferenceContr mContentResolver = mContext.getContentResolver(); } - @Override - public boolean isAvailable() { - return true; + public void setInSetupWizard(boolean inSetupWizard) { + mInSetupWizard = inSetupWizard; } @Override - public String getPreferenceKey() { - return KEY_BRIGHTNESS; + public int getAvailabilityStatus() { + if (mInSetupWizard && !Flags.addBrightnessSettingsInSuw()) { + return CONDITIONALLY_UNAVAILABLE; + } + return AVAILABLE; } @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); - mPreference = screen.findPreference(KEY_BRIGHTNESS); + mPreference = screen.findPreference(getPreferenceKey()); } @Override diff --git a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java index 06c3ceeca14..0365f806018 100644 --- a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java +++ b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java @@ -61,9 +61,8 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); mBatteryUsageProgressBarPref = screen.findPreference(getPreferenceKey()); - // Set up loading text first to prevent layout flaky before info loaded. - mBatteryUsageProgressBarPref.setBottomSummary( - mContext.getString(R.string.settings_license_activity_loading)); + // Set up empty space text first to prevent layout flaky before info loaded. + mBatteryUsageProgressBarPref.setBottomSummary(" "); if (com.android.settings.Utils.isBatteryPresent(mContext)) { quickUpdateHeaderPreference(); @@ -86,13 +85,14 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController return mContext.getString( com.android.settingslib.R.string.battery_info_status_charging_on_hold); } + if (info.remainingLabel != null + && mBatterySettingsFeatureProvider.isChargingOptimizationMode(mContext)) { + return info.remainingLabel; + } if (info.remainingLabel == null || info.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING) { return info.statusLabel; } - if (mBatterySettingsFeatureProvider.isChargingOptimizationMode(mContext)) { - return info.remainingLabel; - } if (info.pluggedStatus == BatteryManager.BATTERY_PLUGGED_WIRELESS) { final CharSequence wirelessChargingLabel = mBatterySettingsFeatureProvider.getWirelessChargingLabel(mContext, info); diff --git a/src/com/android/settings/fuelgauge/BatteryInfo.java b/src/com/android/settings/fuelgauge/BatteryInfo.java index c333a33d67f..b54801a677a 100644 --- a/src/com/android/settings/fuelgauge/BatteryInfo.java +++ b/src/com/android/settings/fuelgauge/BatteryInfo.java @@ -390,12 +390,37 @@ public class BatteryInfo { info.remainingLabel = null; int chargingLimitedResId = R.string.power_charging_limited; info.chargeLabel = context.getString(chargingLimitedResId, info.batteryPercentString); - } else if ((chargeTimeMs > 0 + return; + } + final BatterySettingsFeatureProvider featureProvider = + FeatureFactory.getFeatureFactory().getBatterySettingsFeatureProvider(); + if (featureProvider.isChargingOptimizationMode(context)) { + final CharSequence chargeLabel = + featureProvider.getChargingOptimizationChargeLabel( + context, + info.batteryLevel, + info.batteryPercentString, + chargeTimeMs, + currentTimeMs); + if (chargeLabel != null) { + final CharSequence remainingLabel = + featureProvider.getChargingOptimizationRemainingLabel( + context, + info.batteryLevel, + info.pluggedStatus, + chargeTimeMs, + currentTimeMs); + if (remainingLabel != null) { + info.chargeLabel = chargeLabel; + info.remainingLabel = remainingLabel; + return; + } + } + } + if ((chargeTimeMs > 0 && status != BatteryManager.BATTERY_STATUS_FULL && dockDefenderMode == BatteryUtils.DockDefenderMode.DISABLED) || dockDefenderMode == BatteryUtils.DockDefenderMode.TEMPORARILY_BYPASSED) { - final BatterySettingsFeatureProvider featureProvider = - FeatureFactory.getFeatureFactory().getBatterySettingsFeatureProvider(); // Battery is charging to full info.remainingTimeUs = PowerUtil.convertMsToUs(chargeTimeMs); int resId = getChargingDurationResId(info.isFastCharging); @@ -419,8 +444,7 @@ public class BatteryInfo { info.batteryPercentString, chargeTimeMs, info.isFastCharging, - currentTimeMs, - featureProvider); + currentTimeMs); } else if (dockDefenderMode == BatteryUtils.DockDefenderMode.FUTURE_BYPASS) { // Dock defender will be triggered in the future, charging will be optimized. info.chargeLabel = @@ -447,14 +471,6 @@ public class BatteryInfo { int pluggedStatus, long currentTimeMs, BatterySettingsFeatureProvider featureProvider) { - if (featureProvider.isChargingOptimizationMode(context)) { - final CharSequence chargingOptimizationRemainingLabel = - featureProvider.getChargingOptimizationRemainingLabel( - context, chargeRemainingTimeMs, currentTimeMs); - if (chargingOptimizationRemainingLabel != null) { - return chargingOptimizationRemainingLabel; - } - } if (pluggedStatus == BatteryManager.BATTERY_PLUGGED_WIRELESS) { final CharSequence wirelessChargingRemainingLabel = featureProvider.getWirelessChargingRemainingLabel( @@ -488,16 +504,7 @@ public class BatteryInfo { String batteryPercentString, long chargeTimeMs, boolean isFastCharging, - long currentTimeMs, - BatterySettingsFeatureProvider featureProvider) { - if (featureProvider.isChargingOptimizationMode(context)) { - final CharSequence chargingOptimizationChargeLabel = - featureProvider.getChargingOptimizationChargeLabel( - context, batteryPercentString, chargeTimeMs, currentTimeMs); - if (chargingOptimizationChargeLabel != null) { - return chargingOptimizationChargeLabel; - } - } + long currentTimeMs) { if (com.android.settingslib.fuelgauge.BatteryUtils.isChargingStringV2Enabled()) { var timeString = PowerUtil.getTargetTimeShortString(context, chargeTimeMs, currentTimeMs); diff --git a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java index ab6d5acc1eb..454a2956861 100644 --- a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java +++ b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java @@ -60,13 +60,18 @@ public interface BatterySettingsFeatureProvider { /** Return a charging remaining time label for charging optimization mode. */ @Nullable CharSequence getChargingOptimizationRemainingLabel( - @NonNull Context context, long chargeRemainingTimeMs, long currentTimeMs); + @NonNull Context context, + int batteryLevel, + int pluggedStatus, + long chargeRemainingTimeMs, + long currentTimeMs); /** Return a charge label for charging optimization mode. */ @Nullable CharSequence getChargingOptimizationChargeLabel( @NonNull Context context, - @NonNull String batteryPercentageString, + int batteryLevel, + String batteryPercentageString, long chargeRemainingTimeMs, long currentTimeMs); } diff --git a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java index b2db3470fa9..40cfd0cb715 100644 --- a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java +++ b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java @@ -76,7 +76,11 @@ public class BatterySettingsFeatureProviderImpl implements BatterySettingsFeatur @Nullable @Override public CharSequence getChargingOptimizationRemainingLabel( - @NonNull Context context, long chargeRemainingTimeMs, long currentTimeMs) { + @NonNull Context context, + int batteryLevel, + int pluggedStatus, + long chargeRemainingTimeMs, + long currentTimeMs) { return null; } @@ -84,7 +88,8 @@ public class BatterySettingsFeatureProviderImpl implements BatterySettingsFeatur @Override public CharSequence getChargingOptimizationChargeLabel( @NonNull Context context, - @NonNull String batteryPercentageString, + int batteryLevel, + String batteryPercentageString, long chargeRemainingTimeMs, long currentTimeMs) { return null; diff --git a/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java b/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java index 08d49f1a632..fd0d820feb1 100644 --- a/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java +++ b/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java @@ -159,6 +159,11 @@ public class TopLevelBatteryPreferenceController extends BasePreferenceControlle com.android.settingslib.R.string.power_charging_on_hold_settings_home_page, info.batteryPercentString); } + final BatterySettingsFeatureProvider featureProvider = + FeatureFactory.getFeatureFactory().getBatterySettingsFeatureProvider(); + if (info.chargeLabel != null && featureProvider.isChargingOptimizationMode(mContext)) { + return info.chargeLabel; + } if (info.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING) { // Present status only if no remaining time or status anomalous return info.statusLabel; diff --git a/src/com/android/settings/network/telephony/CallsDefaultSubscriptionController.java b/src/com/android/settings/network/telephony/CallsDefaultSubscriptionController.java index eb833b1ae1b..b56c1b93d16 100644 --- a/src/com/android/settings/network/telephony/CallsDefaultSubscriptionController.java +++ b/src/com/android/settings/network/telephony/CallsDefaultSubscriptionController.java @@ -31,6 +31,10 @@ public class CallsDefaultSubscriptionController extends DefaultSubscriptionContr super(context, preferenceKey, lifecycle, lifecycleOwner); } + public CallsDefaultSubscriptionController(Context context, String preferenceKey) { + super(context, preferenceKey); + } + @Override protected int getDefaultSubscriptionId() { int defaultCallSubId = SubscriptionManager.getDefaultVoiceSubscriptionId(); diff --git a/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java b/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java index 1acfaf89859..c08ea99b7ad 100644 --- a/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java +++ b/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java @@ -68,15 +68,19 @@ public class ConvertToEsimPreferenceController extends TelephonyBasePreferenceCo public ConvertToEsimPreferenceController(Context context, String key, Lifecycle lifecycle, LifecycleOwner lifecycleOwner, int subId) { - super(context, key); + this(context, key); mSubId = subId; - mMobileNetworkRepository = MobileNetworkRepository.getInstance(context); mLifecycleOwner = lifecycleOwner; if (lifecycle != null) { lifecycle.addObserver(this); } } + public ConvertToEsimPreferenceController(Context context, String key) { + super(context, key); + mMobileNetworkRepository = MobileNetworkRepository.getInstance(context); + } + public void init(int subId, SubscriptionInfoEntity subInfoEntity) { mSubId = subId; mSubscriptionInfoEntity = subInfoEntity; diff --git a/src/com/android/settings/network/telephony/DefaultSubscriptionController.java b/src/com/android/settings/network/telephony/DefaultSubscriptionController.java index fa8760c2205..b0586949f03 100644 --- a/src/com/android/settings/network/telephony/DefaultSubscriptionController.java +++ b/src/com/android/settings/network/telephony/DefaultSubscriptionController.java @@ -64,16 +64,20 @@ public abstract class DefaultSubscriptionController extends TelephonyBasePrefere public DefaultSubscriptionController(Context context, String preferenceKey, Lifecycle lifecycle, LifecycleOwner lifecycleOwner) { + this(context, preferenceKey); + mLifecycleOwner = lifecycleOwner; + if (lifecycle != null) { + lifecycle.addObserver(this); + } + } + + public DefaultSubscriptionController(Context context, String preferenceKey) { super(context, preferenceKey); mManager = context.getSystemService(SubscriptionManager.class); mIsRtlMode = context.getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; mMobileNetworkRepository = MobileNetworkRepository.getInstance(context); mDataSubscriptionChangedReceiver = new DefaultSubscriptionReceiver(context, this); - mLifecycleOwner = lifecycleOwner; - if (lifecycle != null) { - lifecycle.addObserver(this); - } } /** @return the id of the default subscription for the service, or diff --git a/src/com/android/settings/network/telephony/MobileDataPreferenceController.java b/src/com/android/settings/network/telephony/MobileDataPreferenceController.java index 2c3ab18b223..6adc5052145 100644 --- a/src/com/android/settings/network/telephony/MobileDataPreferenceController.java +++ b/src/com/android/settings/network/telephony/MobileDataPreferenceController.java @@ -73,16 +73,20 @@ public class MobileDataPreferenceController extends TelephonyTogglePreferenceCon public MobileDataPreferenceController(Context context, String key, Lifecycle lifecycle, LifecycleOwner lifecycleOwner, int subId) { - super(context, key); + this(context, key); mSubId = subId; - mSubscriptionManager = context.getSystemService(SubscriptionManager.class); - mMobileNetworkRepository = MobileNetworkRepository.getInstance(context); mLifecycleOwner = lifecycleOwner; if (lifecycle != null) { lifecycle.addObserver(this); } } + public MobileDataPreferenceController(Context context, String key) { + super(context, key); + mSubscriptionManager = context.getSystemService(SubscriptionManager.class); + mMobileNetworkRepository = MobileNetworkRepository.getInstance(context); + } + @Override public int getAvailabilityStatus(int subId) { if (Flags.isDualSimOnboardingEnabled()) { diff --git a/src/com/android/settings/network/telephony/MobileNetworkUtils.java b/src/com/android/settings/network/telephony/MobileNetworkUtils.java index ea549ae7c94..517f66ad60a 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkUtils.java +++ b/src/com/android/settings/network/telephony/MobileNetworkUtils.java @@ -709,12 +709,13 @@ public class MobileNetworkUtils { return tm.getNetworkOperatorName(); } - private static int[] getActiveSubscriptionIdList(Context context) { + @VisibleForTesting + static int[] getActiveSubscriptionIdList(Context context) { final SubscriptionManager subscriptionManager = context.getSystemService( SubscriptionManager.class).createForAllUserProfiles(); final List subInfoList = - subscriptionManager.getActiveSubscriptionInfoList(); - if (subInfoList == null) { + SubscriptionUtil.getActiveSubscriptions(subscriptionManager); + if (subInfoList == null || subInfoList.isEmpty()) { return new int[0]; } int[] activeSubIds = new int[subInfoList.size()]; diff --git a/src/com/android/settings/network/telephony/RoamingPreferenceController.java b/src/com/android/settings/network/telephony/RoamingPreferenceController.java index fb8cd519e27..bf02308be39 100644 --- a/src/com/android/settings/network/telephony/RoamingPreferenceController.java +++ b/src/com/android/settings/network/telephony/RoamingPreferenceController.java @@ -63,16 +63,20 @@ public class RoamingPreferenceController extends TelephonyTogglePreferenceContro public RoamingPreferenceController(Context context, String key, Lifecycle lifecycle, LifecycleOwner lifecycleOwner, int subId) { - super(context, key); + this(context, key); mSubId = subId; - mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class); - mMobileNetworkRepository = MobileNetworkRepository.getInstance(context); mLifecycleOwner = lifecycleOwner; if (lifecycle != null) { lifecycle.addObserver(this); } } + public RoamingPreferenceController(Context context, String key) { + super(context, key); + mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class); + mMobileNetworkRepository = MobileNetworkRepository.getInstance(context); + } + @Override public int getAvailabilityStatus() { final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId); diff --git a/src/com/android/settings/network/telephony/SmsDefaultSubscriptionController.java b/src/com/android/settings/network/telephony/SmsDefaultSubscriptionController.java index c49647dd105..c35a78c550d 100644 --- a/src/com/android/settings/network/telephony/SmsDefaultSubscriptionController.java +++ b/src/com/android/settings/network/telephony/SmsDefaultSubscriptionController.java @@ -35,6 +35,12 @@ public class SmsDefaultSubscriptionController extends DefaultSubscriptionControl .getBoolean(com.android.internal.R.bool.config_sms_ask_every_time_support); } + public SmsDefaultSubscriptionController(Context context, String preferenceKey) { + super(context, preferenceKey); + mIsAskEverytimeSupported = mContext.getResources() + .getBoolean(com.android.internal.R.bool.config_sms_ask_every_time_support); + } + @Override protected int getDefaultSubscriptionId() { int defaultSmsSubId = SubscriptionManager.getDefaultSmsSubscriptionId(); diff --git a/src/com/android/settings/notification/modes/ZenModeFragment.java b/src/com/android/settings/notification/modes/ZenModeFragment.java new file mode 100644 index 00000000000..616332e0d1f --- /dev/null +++ b/src/com/android/settings/notification/modes/ZenModeFragment.java @@ -0,0 +1,78 @@ +/* + * 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.notification.modes; + +import android.app.AutomaticZenRule; +import android.app.settings.SettingsEnums; +import android.content.Context; + +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settingslib.core.AbstractPreferenceController; + +import java.util.ArrayList; +import java.util.List; + +public class ZenModeFragment extends ZenModeFragmentBase { + + @Override + protected int getPreferenceScreenResId() { + return R.xml.modes_rule_settings; + } + + @Override + protected List createPreferenceControllers(Context context) { + // TODO: fill in with all the elements of this page. Each should be an instance of + // {@link AbstractZenModePreferenceController}. + List prefControllers = new ArrayList<>(); + return prefControllers; + } + + @Override + public void onStart() { + super.onStart(); + + // Set title for the entire screen + ZenMode mode = getMode(); + AutomaticZenRule azr = getAZR(); + if (mode == null || azr == null) { + return; + } + getActivity().setTitle(azr.getName()); + + // TODO: b/308819292 - implement the real screen! + final PreferenceScreen screen = getPreferenceScreen(); + if (screen == null) { + return; + } + + Preference tmpPref = screen.findPreference("zen_mode_test"); + if (tmpPref == null) { + return; + } + tmpPref.setTitle(azr.getTriggerDescription()); + tmpPref.setSummary("active?: " + mode.isActive()); + } + + @Override + public int getMetricsCategory() { + // TODO: b/332937635 - make this the correct metrics category + return SettingsEnums.NOTIFICATION_ZEN_MODE_AUTOMATION; + } +} diff --git a/src/com/android/settings/notification/modes/ZenModeListPreference.java b/src/com/android/settings/notification/modes/ZenModeListPreference.java new file mode 100644 index 00000000000..cb0456140bf --- /dev/null +++ b/src/com/android/settings/notification/modes/ZenModeListPreference.java @@ -0,0 +1,65 @@ +/* + * 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.notification.modes; + +import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.os.Bundle; + +import com.android.settings.core.SubSettingLauncher; +import com.android.settings.notification.zen.ZenModeSettings; +import com.android.settingslib.RestrictedPreference; + +/** + * Preference representing a single mode item on the modes aggregator page. Clicking on this + * preference leads to an individual mode's configuration page. + */ +public class ZenModeListPreference extends RestrictedPreference { + final Context mContext; + ZenMode mZenMode; + + ZenModeListPreference(Context context, ZenMode zenMode) { + super(context); + mContext = context; + mZenMode = zenMode; + setTitle(mZenMode.getRule().getName()); + setSummary((mZenMode.isActive() ? "ACTIVE" : "inactive") + ": " + + mZenMode.getRule().getTriggerDescription()); + } + + @Override + public void onClick() { + // TODO: b/322373473 - This implementation is a hack that just leads to the old DND page + // for manual only; remove this in favor of the real implementation. + if (mZenMode.isManualDnd()) { + new SubSettingLauncher(mContext) + .setDestination(ZenModeSettings.class.getName()) + .setSourceMetricsCategory(SettingsEnums.NOTIFICATION_ZEN_MODE) + .launch(); + } else { + Bundle bundle = new Bundle(); + bundle.putString(MODE_ID, mZenMode.getId()); + new SubSettingLauncher(mContext) + .setDestination(ZenModeFragment.class.getName()) + .setArguments(bundle) + .setSourceMetricsCategory(SettingsEnums.NOTIFICATION_ZEN_MODE_AUTOMATION) + .launch(); + } + + } +} diff --git a/src/com/android/settings/notification/modes/ZenModesListFragment.java b/src/com/android/settings/notification/modes/ZenModesListFragment.java new file mode 100644 index 00000000000..040621e6d93 --- /dev/null +++ b/src/com/android/settings/notification/modes/ZenModesListFragment.java @@ -0,0 +1,113 @@ +/* + * 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.notification.modes; + +import android.app.NotificationManager; +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.service.notification.ConditionProviderService; + +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.android.settings.R; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.utils.ManagedServiceSettings; +import com.android.settings.utils.ZenServiceListing; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.search.SearchIndexable; + +import java.util.ArrayList; +import java.util.List; + +@SearchIndexable +public class ZenModesListFragment extends ZenModesFragmentBase { + protected final ManagedServiceSettings.Config CONFIG = getConditionProviderConfig(); + + @Override + protected List createPreferenceControllers(Context context) { + ZenServiceListing serviceListing = new ZenServiceListing(getContext(), CONFIG); + serviceListing.reloadApprovedServices(); + return buildPreferenceControllers(context, this, serviceListing); + } + + private static List buildPreferenceControllers(Context context, + @Nullable Fragment parent, @Nullable ZenServiceListing serviceListing) { + // We need to redefine ZenModesBackend here even though mBackend exists so that this method + // can be static; it must be static to be able to be used in SEARCH_INDEX_DATA_PROVIDER. + ZenModesBackend backend = ZenModesBackend.getInstance(context); + List controllers = new ArrayList<>(); + controllers.add(new ZenModesListPreferenceController( + context, parent, backend)); + + // TODO: b/326442408 - Add controller for "Add Mode" preference/flow, which is what uses + // the ZenServiceListing. + return controllers; + } + + @Override + protected void updateZenModeState() { + // TODO: b/322373473 -- update any overall description of modes state here if necessary. + // Note the preferences linking to individual rules do not need to be updated, as + // updateState() is called on all preference controllers whenever the page is resumed. + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.modes_list_settings; + } + + @Override + public int getMetricsCategory() { + // TODO: b/332937635 - add new & set metrics categories correctly + return SettingsEnums.NOTIFICATION_ZEN_MODE_AUTOMATION; + } + + protected static ManagedServiceSettings.Config getConditionProviderConfig() { + return new ManagedServiceSettings.Config.Builder() + .setTag(TAG) + .setIntentAction(ConditionProviderService.SERVICE_INTERFACE) + .setConfigurationIntentAction(NotificationManager.ACTION_AUTOMATIC_ZEN_RULE) + .setPermission(android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE) + .setNoun("condition provider") + .build(); + } + + // TODO: b/322373473 - Add 3-dot options menu with capability to delete modes. + + /** + * For Search. + */ + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider(R.xml.modes_list_settings) { + + @Override + public List getNonIndexableKeys(Context context) { + final List keys = super.getNonIndexableKeys(context); + // TODO: b/332937523 - determine if this should be removed once the preference + // controller adds dynamic data to index + keys.add(ZenModesListPreferenceController.KEY); + return keys; + } + + @Override + public List createPreferenceControllers( + Context context) { + return buildPreferenceControllers(context, null, null); + } + }; +} diff --git a/src/com/android/settings/notification/modes/ZenModesListPreferenceController.java b/src/com/android/settings/notification/modes/ZenModesListPreferenceController.java new file mode 100644 index 00000000000..53336c84be3 --- /dev/null +++ b/src/com/android/settings/notification/modes/ZenModesListPreferenceController.java @@ -0,0 +1,80 @@ +/* + * 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.notification.modes; + +import android.app.Flags; +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.preference.Preference; +import androidx.preference.PreferenceCategory; + +import com.android.settingslib.core.AbstractPreferenceController; + +/** + * Controller for the PreferenceCategory on the modes aggregator page ({@link ZenModesListFragment}) + * containing links to each individual mode. This is a central controller that populates and updates + * all the preferences that then lead to a mode configuration page. + */ +public class ZenModesListPreferenceController extends AbstractPreferenceController { + protected static final String KEY = "zen_modes_list"; + + @Nullable + protected Fragment mParent; + protected ZenModesBackend mBackend; + + public ZenModesListPreferenceController(Context context, @Nullable Fragment parent, + @NonNull ZenModesBackend backend) { + super(context); + mParent = parent; + mBackend = backend; + } + + @Override + public String getPreferenceKey() { + return KEY; + } + + @Override + public boolean isAvailable() { + return Flags.modesUi(); + } + + @Override + public void updateState(Preference preference) { + if (mBackend == null) { + return; + } + + // The preference given us is a PreferenceCategory; create one preference inside the + // category for each rule that exists. + PreferenceCategory category = (PreferenceCategory) preference; + + // TODO: b/322373473 - This is not the right way to replace these preferences; we should + // follow something similar to what + // ZenModeAutomaticRulesPreferenceController does to change rules + // only as necessary and update them. + category.removeAll(); + + for (ZenMode mode : mBackend.getModes()) { + Preference pref = new ZenModeListPreference(mContext, mode); + category.addPreference(pref); + } + } + +} diff --git a/src/com/android/settings/notification/zen/ZenModePreferenceController.java b/src/com/android/settings/notification/zen/ZenModePreferenceController.java index 24cf158c966..7c36d80f8ad 100644 --- a/src/com/android/settings/notification/zen/ZenModePreferenceController.java +++ b/src/com/android/settings/notification/zen/ZenModePreferenceController.java @@ -16,6 +16,7 @@ package com.android.settings.notification.zen; +import android.app.Flags; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; @@ -27,7 +28,9 @@ import android.provider.Settings; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; +import com.android.settings.R; import com.android.settings.core.BasePreferenceController; +import com.android.settings.notification.modes.ZenModesListFragment; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.core.lifecycle.events.OnResume; @@ -46,7 +49,9 @@ public class ZenModePreferenceController extends BasePreferenceController @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); - mSettingObserver = new SettingObserver(screen.findPreference(getPreferenceKey())); + Preference preference = screen.findPreference(getPreferenceKey()); + mSettingObserver = new SettingObserver(preference); + maybeSetTitleAndDestination(preference); } @Override @@ -71,11 +76,22 @@ public class ZenModePreferenceController extends BasePreferenceController @Override public void updateState(Preference preference) { super.updateState(preference); + maybeSetTitleAndDestination(preference); if (preference.isEnabled()) { preference.setSummary(mSummaryBuilder.getSoundSummary()); } } + // Only when modes_ui is active: change title & target fragment. + private void maybeSetTitleAndDestination(Preference preference) { + if (!Flags.modesUi()) { + return; + } + + preference.setTitle(R.string.zen_modes_list_title); + preference.setFragment(ZenModesListFragment.class.getCanonicalName()); + } + class SettingObserver extends ContentObserver { private final Uri ZEN_MODE_URI = Settings.Global.getUriFor(Settings.Global.ZEN_MODE); private final Uri ZEN_MODE_CONFIG_ETAG_URI = Settings.Global.getUriFor( diff --git a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java index 38f724a4d7b..42e854426f4 100644 --- a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java +++ b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java @@ -19,7 +19,7 @@ package com.android.settings.privatespace; import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE; import static android.provider.Settings.Secure.HIDE_PRIVATESPACE_ENTRY_POINT; import static android.provider.Settings.Secure.PRIVATE_SPACE_AUTO_LOCK; -import static android.provider.Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_DEVICE_RESTART; +import static android.provider.Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK; import static android.provider.Settings.Secure.SKIP_FIRST_USE_HINTS; import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; @@ -69,7 +69,7 @@ public class PrivateSpaceMaintainer { /** Default value for private space auto lock settings. */ @Settings.Secure.PrivateSpaceAutoLockOption public static final int PRIVATE_SPACE_AUTO_LOCK_DEFAULT_VAL = - PRIVATE_SPACE_AUTO_LOCK_AFTER_DEVICE_RESTART; + PRIVATE_SPACE_AUTO_LOCK_ON_DEVICE_LOCK; /** Default value for the hide private space sensitive notifications on lockscreen. */ public static final int HIDE_PRIVATE_SPACE_SENSITIVE_NOTIFICATIONS_DISABLED_VAL = 0; diff --git a/src/com/android/settings/system/DeviceDiagnosticsPreferenceController.kt b/src/com/android/settings/system/DeviceDiagnosticsPreferenceController.kt new file mode 100644 index 00000000000..695bb5bdea9 --- /dev/null +++ b/src/com/android/settings/system/DeviceDiagnosticsPreferenceController.kt @@ -0,0 +1,65 @@ +/* + * 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.system + +import android.content.Context +import android.content.Intent +import android.content.pm.ResolveInfo + +import androidx.preference.Preference + +import com.android.settings.R +import com.android.settings.core.BasePreferenceController +import com.android.settings.flags.Flags + +class DeviceDiagnosticsPreferenceController(context: Context, preferenceKey: String) : + BasePreferenceController(context, preferenceKey) { + + override fun getAvailabilityStatus(): Int { + if (!Flags.enableDeviceDiagnosticsInSettings()) { + return UNSUPPORTED_ON_DEVICE + } + if (getIntent() == null) { + return UNSUPPORTED_ON_DEVICE + } + return AVAILABLE + } + + override fun handlePreferenceTreeClick(preference: Preference): Boolean { + if (preferenceKey != preference.key) { + return false + } + + val intent = getIntent() + if (intent == null) { + return false + } + + preference.getContext().startActivity(intent) + return true + } + + private fun getIntent(): Intent? { + val intent = Intent(Intent.ACTION_MAIN) + + val packageName = mContext.getResources().getString( + R.string.config_device_diagnostics_package_name) + intent.setPackage(packageName) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + return intent + } +} diff --git a/src/com/android/settings/users/MultiUserSwitchBarController.java b/src/com/android/settings/users/MultiUserSwitchBarController.java index 33651c31fc4..f57b7959c37 100644 --- a/src/com/android/settings/users/MultiUserSwitchBarController.java +++ b/src/com/android/settings/users/MultiUserSwitchBarController.java @@ -63,8 +63,7 @@ public class MultiUserSwitchBarController implements SwitchWidgetController.OnSw .checkIfRestrictionEnforced(mContext, UserManager.DISALLOW_ADD_USER, UserHandle.myUserId())); } else { - mSwitchBar.setEnabled(!mUserCapabilities.mDisallowSwitchUser - && !mUserCapabilities.mIsGuest && mUserCapabilities.isAdmin()); + mSwitchBar.setEnabled(mUserCapabilities.mIsMain); } mSwitchBar.setListener(this); } diff --git a/src/com/android/settings/users/UserCapabilities.java b/src/com/android/settings/users/UserCapabilities.java index cf0e10b127d..590cb0cf11a 100644 --- a/src/com/android/settings/users/UserCapabilities.java +++ b/src/com/android/settings/users/UserCapabilities.java @@ -32,6 +32,7 @@ public class UserCapabilities { boolean mEnabled = true; boolean mCanAddUser = true; boolean mCanAddRestrictedProfile; + boolean mIsMain; boolean mIsAdmin; boolean mIsGuest; boolean mIsEphemeral; @@ -57,6 +58,7 @@ public class UserCapabilities { final UserInfo myUserInfo = userManager.getUserInfo(UserHandle.myUserId()); caps.mIsGuest = myUserInfo.isGuest(); caps.mIsAdmin = myUserInfo.isAdmin(); + caps.mIsMain = myUserInfo.isMain(); caps.mIsEphemeral = myUserInfo.isEphemeral(); DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( Context.DEVICE_POLICY_SERVICE); diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java index 083608dde3e..bf21c9b913a 100644 --- a/src/com/android/settings/users/UserSettings.java +++ b/src/com/android/settings/users/UserSettings.java @@ -287,7 +287,7 @@ public class UserSettings extends SettingsPreferenceFragment final SettingsActivity activity = (SettingsActivity) getActivity(); final SettingsMainSwitchBar switchBar = activity.getSwitchBar(); switchBar.setTitle(getContext().getString(R.string.multiple_users_main_switch_title)); - if (isCurrentUserAdmin()) { + if (!mUserCaps.mIsGuest) { switchBar.show(); } else { switchBar.hide(); diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java index 78c88803abc..2ae598418ce 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsForSetupWizardTest.java @@ -23,6 +23,7 @@ import static com.android.settings.accessibility.AccessibilitySettingsForSetupWi import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -45,7 +46,10 @@ import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; +import com.android.settings.display.AutoBrightnessPreferenceController; +import com.android.settings.display.BrightnessLevelPreferenceController; import com.android.settingslib.RestrictedPreference; +import com.android.settingslib.core.AbstractPreferenceController; import com.google.android.setupcompat.template.FooterBarMixin; import com.google.android.setupdesign.GlifPreferenceLayout; @@ -88,7 +92,7 @@ public class AccessibilitySettingsForSetupWizardTest { private GlifPreferenceLayout mGlifLayoutView; @Mock private FooterBarMixin mFooterBarMixin; - private AccessibilitySettingsForSetupWizard mFragment; + private TestAccessibilitySettingsForSetupWizard mFragment; @Before public void setUp() { @@ -141,6 +145,19 @@ public class AccessibilitySettingsForSetupWizardTest { assertThat(mFragment.mSelectToSpeakPreference.getKey()).isNull(); } + @Test + public void createPreferenceControllers_brightnessPreferencesControllersAreCreated() { + mFragment.onAttach(mContext); + + List controllers = + mFragment.createPreferenceControllers(mContext); + + assertTrue(controllers.stream().anyMatch( + controller -> controller instanceof BrightnessLevelPreferenceController)); + assertTrue(controllers.stream().anyMatch( + controller -> controller instanceof AutoBrightnessPreferenceController)); + } + private void addEnabledServiceInfo(ComponentName componentName, boolean isAccessibilityTool) { final AccessibilityServiceInfo a11yServiceInfo = mock(AccessibilityServiceInfo.class); when(a11yServiceInfo.getComponentName()).thenReturn(componentName); @@ -170,12 +187,14 @@ public class AccessibilitySettingsForSetupWizardTest { private final Context mContext; private final PreferenceManager mPreferenceManager; + final PreferenceScreen mPreferenceScreen; TestAccessibilitySettingsForSetupWizard(Context context) { super(); mContext = context; mPreferenceManager = new PreferenceManager(context); - mPreferenceManager.setPreferences(mPreferenceManager.createPreferenceScreen(context)); + mPreferenceScreen = spy(mPreferenceManager.createPreferenceScreen(context)); + mPreferenceManager.setPreferences(mPreferenceScreen); mDisplayMagnificationPreference = new Preference(context); mScreenReaderPreference = new RestrictedPreference(context); mSelectToSpeakPreference = new RestrictedPreference(context); diff --git a/tests/robotests/src/com/android/settings/accessibility/AutoBrightnessPreferenceFragmentForSetupWizardTest.java b/tests/robotests/src/com/android/settings/accessibility/AutoBrightnessPreferenceFragmentForSetupWizardTest.java new file mode 100644 index 00000000000..1e6e068cae6 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/AutoBrightnessPreferenceFragmentForSetupWizardTest.java @@ -0,0 +1,95 @@ +/* + * 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.accessibility; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.settings.SettingsEnums; +import android.content.Context; + +import androidx.lifecycle.LifecycleOwner; +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; + +import com.google.android.setupcompat.template.FooterBarMixin; +import com.google.android.setupdesign.GlifPreferenceLayout; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +/** Tests for {@link AutoBrightnessPreferenceFragmentForSetupWizard}. */ +@RunWith(RobolectricTestRunner.class) +public class AutoBrightnessPreferenceFragmentForSetupWizardTest { + + @Rule + public final MockitoRule mMockito = MockitoJUnit.rule(); + + @Spy + private final Context mContext = ApplicationProvider.getApplicationContext(); + @Mock + private GlifPreferenceLayout mGlifLayoutView; + @Mock + private FooterBarMixin mFooterBarMixin; + private AutoBrightnessPreferenceFragmentForSetupWizard mFragment; + + @Before + public void setUp() { + mFragment = spy(new AutoBrightnessPreferenceFragmentForSetupWizard()); + doReturn(mock(LifecycleOwner.class)).when(mFragment).getViewLifecycleOwner(); + doReturn(mContext).when(mFragment).getContext(); + when(mGlifLayoutView.getMixin(eq(FooterBarMixin.class))).thenReturn(mFooterBarMixin); + } + + @Test + public void setHeaderText_onViewCreated_verifyAction() { + final String title = "title"; + doReturn(title).when(mContext).getString(R.string.auto_brightness_title); + + mFragment.onViewCreated(mGlifLayoutView, null); + + verify(mGlifLayoutView).setHeaderText(title); + } + + @Test + public void getMetricsCategory_returnsCorrectCategory() { + assertThat(mFragment.getMetricsCategory()).isEqualTo( + SettingsEnums.SUW_ACCESSIBILITY_AUTO_BRIGHTNESS); + } + + @Test + public void onViewCreated_verifyAction() { + mFragment.onViewCreated(mGlifLayoutView, null); + + verify(mFooterBarMixin).setPrimaryButton(any()); + } +} diff --git a/tests/robotests/src/com/android/settings/display/AutoBrightnessPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/AutoBrightnessPreferenceControllerTest.java index 02291731634..13cd86d5a2a 100644 --- a/tests/robotests/src/com/android/settings/display/AutoBrightnessPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/AutoBrightnessPreferenceControllerTest.java @@ -21,18 +21,24 @@ import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC; import static android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL; import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE; +import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE; import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; import static com.google.common.truth.Truth.assertThat; import android.content.ContentResolver; import android.content.Context; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import com.android.settings.R; +import com.android.settings.accessibility.Flags; import com.android.settings.testutils.shadow.SettingsShadowResources; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; @@ -44,6 +50,9 @@ import org.robolectric.annotation.Config; @Config(shadows = {SettingsShadowResources.class}) public class AutoBrightnessPreferenceControllerTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private static final String PREFERENCE_KEY = "auto_brightness"; private Context mContext; @@ -121,13 +130,34 @@ public class AutoBrightnessPreferenceControllerTest { } @Test - public void getAvailabilityStatus_configTrueSet_shouldReturnAvailableUnsearchable() { + public void getAvailabilityStatusNotInSUW_configTrueSet_shouldReturnAvailableUnsearchable() { SettingsShadowResources.overrideResource( com.android.internal.R.bool.config_automatic_brightness_available, true); assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE); } + @Test + @EnableFlags(Flags.FLAG_ADD_BRIGHTNESS_SETTINGS_IN_SUW) + public void getAvailabilityStatusInSUW_configTrueAndFlagOn_shouldReturnAvailableUnsearchable() { + SettingsShadowResources.overrideResource( + com.android.internal.R.bool.config_automatic_brightness_available, true); + mController.setInSetupWizard(true); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE); + } + + @Test + @DisableFlags(Flags.FLAG_ADD_BRIGHTNESS_SETTINGS_IN_SUW) + public void + getAvailabilityStatusInSUW_configTrueAndFlagOff_shouldReturnConditionallyUnavailable() { + SettingsShadowResources.overrideResource( + com.android.internal.R.bool.config_automatic_brightness_available, true); + mController.setInSetupWizard(true); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); + } + @Test public void getAvailabilityStatus_configFalseSet_shouldReturnUnsupportedOnDevice() { SettingsShadowResources.overrideResource( diff --git a/tests/robotests/src/com/android/settings/display/BrightnessLevelPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/BrightnessLevelPreferenceControllerTest.java index bbc9cf32b03..009ca95e4d3 100644 --- a/tests/robotests/src/com/android/settings/display/BrightnessLevelPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/BrightnessLevelPreferenceControllerTest.java @@ -33,16 +33,22 @@ import android.content.Context; import android.content.Intent; import android.hardware.display.BrightnessInfo; import android.os.PowerManager; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings.System; import android.view.Display; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; +import com.android.settings.R; +import com.android.settings.accessibility.Flags; import com.android.settings.core.SettingsBaseActivity; import com.android.settingslib.transition.SettingsTransitionHelper; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -58,6 +64,9 @@ import org.robolectric.shadows.ShadowContentResolver; @RunWith(RobolectricTestRunner.class) public class BrightnessLevelPreferenceControllerTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Mock private PowerManager mPowerManager; @Mock @@ -90,10 +99,24 @@ public class BrightnessLevelPreferenceControllerTest { } @Test - public void isAvailable_shouldAlwaysReturnTrue() { + public void isAvailable_shouldAlwaysReturnTrueWhenNotInSetupWizard() { assertThat(mController.isAvailable()).isTrue(); } + @Test + @EnableFlags(Flags.FLAG_ADD_BRIGHTNESS_SETTINGS_IN_SUW) + public void isAvailable_inSetupWizardAndFlagOn_shouldReturnTrue() { + mController.setInSetupWizard(true); + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + @DisableFlags(Flags.FLAG_ADD_BRIGHTNESS_SETTINGS_IN_SUW) + public void isAvailable_inSetupWizardAndFlagOff_shouldReturnFalse() { + mController.setInSetupWizard(true); + assertThat(mController.isAvailable()).isFalse(); + } + @Test public void onStart_shouldRegisterObserver() { BrightnessLevelPreferenceController controller = @@ -169,13 +192,14 @@ public class BrightnessLevelPreferenceControllerTest { final BrightnessLevelPreferenceController controller = new BrightnessLevelPreferenceController(activity, null); final ShadowActivity shadowActivity = shadowOf(activity); - when(mPreference.getKey()).thenReturn("brightness"); + + String preferenceKey = mContext.getString(R.string.preference_key_brightness_level); + when(mPreference.getKey()).thenReturn(preferenceKey); controller.handlePreferenceTreeClick(mPreference); final Intent intent = shadowActivity.getNextStartedActivity(); assertThat(intent.getIntExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE, 0)) .isEqualTo(SettingsTransitionHelper.TransitionType.TRANSITION_NONE); - } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java index bc6dadc72ac..c987bcd898c 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java @@ -35,7 +35,6 @@ import android.os.SystemProperties; import androidx.preference.PreferenceScreen; -import com.android.settings.R; import com.android.settings.core.BasePreferenceController; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip; @@ -286,8 +285,9 @@ public class BatteryHeaderPreferenceControllerTest { /* isFastCharging= */ true, /* isChargingStringV2= */ true); batteryInfo.pluggedStatus = BatteryManager.BATTERY_PLUGGED_WIRELESS; - when(mFactory.batterySettingsFeatureProvider.getWirelessChargingLabel(eq(mContext), - any(BatteryInfo.class))).thenReturn(label); + when(mFactory.batterySettingsFeatureProvider.getWirelessChargingLabel( + eq(mContext), any(BatteryInfo.class))) + .thenReturn(label); mController.updateBatteryStatus(/* label= */ null, batteryInfo); @@ -326,14 +326,64 @@ public class BatteryHeaderPreferenceControllerTest { verify(mBatteryUsageProgressBarPref).setBottomSummary(expectedChargingString); } + @Test + public void updateBatteryStatus_chargingOptimizationMode_remainingLabel() { + var batteryInfo = + arrangeUpdateBatteryStatusTestWithRemainingLabel( + /* remainingLabel= */ "Expected remaining label", + /* statusLabel= */ "Fast Charging", + /* isFastCharging= */ true, + /* isChargingStringV2= */ true); + var expectedChargingString = batteryInfo.remainingLabel; + when(mFactory.batterySettingsFeatureProvider.isChargingOptimizationMode(mContext)) + .thenReturn(true); + + mController.updateBatteryStatus(/* label= */ null, batteryInfo); + + verify(mBatteryUsageProgressBarPref).setBottomSummary(expectedChargingString); + } + + @Test + public void updateBatteryStatus_chargingOptimizationModeNoRemainingLabel_statusLabel() { + var batteryInfo = + arrangeUpdateBatteryStatusTestWithRemainingLabel( + /* remainingLabel= */ null, + /* statusLabel= */ "Fast Charging", + /* isFastCharging= */ true, + /* isChargingStringV2= */ true); + var expectedChargingString = batteryInfo.statusLabel; + when(mFactory.batterySettingsFeatureProvider.isChargingOptimizationMode(mContext)) + .thenReturn(true); + + mController.updateBatteryStatus(/* label= */ null, batteryInfo); + + verify(mBatteryUsageProgressBarPref).setBottomSummary(expectedChargingString); + } + + @Test + public void updateBatteryStatus_notChargingOptimizationMode_statusWithRemainingLabel() { + var batteryInfo = + arrangeUpdateBatteryStatusTestWithRemainingLabel( + /* remainingLabel= */ "Full by 1:30 PM", + /* statusLabel= */ "Fast Charging", + /* isFastCharging= */ true, + /* isChargingStringV2= */ true); + var expectedChargingString = batteryInfo.statusLabel + " • " + batteryInfo.remainingLabel; + when(mFactory.batterySettingsFeatureProvider.isChargingOptimizationMode(mContext)) + .thenReturn(false); + + mController.updateBatteryStatus(/* label= */ null, batteryInfo); + + verify(mBatteryUsageProgressBarPref).setBottomSummary(expectedChargingString); + } + private BatteryInfo arrangeUpdateBatteryStatusTestWithRemainingLabel( String remainingLabel, String statusLabel, boolean isFastCharging, boolean isChargingStringV2) { SystemProperties.set( - BatteryUtils.PROPERTY_CHARGING_STRING_V2_KEY, - String.valueOf(isChargingStringV2)); + BatteryUtils.PROPERTY_CHARGING_STRING_V2_KEY, String.valueOf(isChargingStringV2)); mBatteryInfo.isBatteryDefender = false; mBatteryInfo.remainingLabel = remainingLabel; mBatteryInfo.statusLabel = statusLabel; @@ -389,8 +439,11 @@ public class BatteryHeaderPreferenceControllerTest { mController.updateHeaderPreference(mBatteryInfo); - verify(mBatteryUsageProgressBarPref).setBottomSummary(mContext.getString( - com.android.settingslib.R.string.battery_info_status_charging_on_hold)); + verify(mBatteryUsageProgressBarPref) + .setBottomSummary( + mContext.getString( + com.android.settingslib.R.string + .battery_info_status_charging_on_hold)); } @Test @@ -429,11 +482,10 @@ public class BatteryHeaderPreferenceControllerTest { } @Test - public void displayPreference_init_showLoading() { + public void displayPreference_init_showEmptySpace() { mController.displayPreference(mPreferenceScreen); - verify(mBatteryUsageProgressBarPref) - .setBottomSummary(mContext.getString(R.string.settings_license_activity_loading)); + verify(mBatteryUsageProgressBarPref).setBottomSummary(" "); } private CharSequence formatBatteryPercentageText() { diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java index a1081f4519e..7bafc6d5198 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java @@ -724,15 +724,15 @@ public class BatteryInfoTest { Intent batteryIntent = createIntentForGetBatteryInfoTest( ChargingType.WIRED, ChargingSpeed.REGULAR, /* batteryLevel= */ 65); - var expectedRemainingLabel = "Done charging by"; + var expectedRemainingLabel = "Expected remaining label"; var expectedChargeLabel = "65% - " + expectedRemainingLabel; when(mFeatureFactory.batterySettingsFeatureProvider.isChargingOptimizationMode(mContext)) .thenReturn(true); when(mFeatureFactory.batterySettingsFeatureProvider.getChargingOptimizationRemainingLabel( - eq(mContext), anyLong(), anyLong())) + eq(mContext), anyInt(), anyInt(), anyLong(), anyLong())) .thenReturn(expectedRemainingLabel); when(mFeatureFactory.batterySettingsFeatureProvider.getChargingOptimizationChargeLabel( - eq(mContext), anyString(), anyLong(), anyLong())) + eq(mContext), anyInt(), anyString(), anyLong(), anyLong())) .thenReturn(expectedChargeLabel); var expectedStatusLabel = "Charging"; diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java index c55a121785e..9f04892a3e5 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java @@ -19,6 +19,7 @@ package com.android.settings.fuelgauge; import static com.google.common.truth.Truth.assertThat; import android.content.Context; +import android.os.BatteryManager; import androidx.test.core.app.ApplicationProvider; @@ -87,12 +88,15 @@ public class BatterySettingsFeatureProviderImplTest { @Test public void getChargingOptimizationRemainingLabel_default_returnNull() { - assertThat(mImpl.getChargingOptimizationRemainingLabel(mContext, 1000L, 1000L)).isNull(); + assertThat( + mImpl.getChargingOptimizationRemainingLabel( + mContext, 75, BatteryManager.BATTERY_PLUGGED_AC, 1000L, 1000L)) + .isNull(); } @Test public void getChargingOptimizationChargeLabel_default_returnNull() { - assertThat(mImpl.getChargingOptimizationChargeLabel(mContext, "70%", 1000L, 1000L)) + assertThat(mImpl.getChargingOptimizationChargeLabel(mContext, 70, "70%", 1000L, 1000L)) .isNull(); } } diff --git a/tests/robotests/src/com/android/settings/notification/zen/ZenModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/zen/ZenModePreferenceControllerTest.java index c2abbcdfb40..f611c9b1950 100644 --- a/tests/robotests/src/com/android/settings/notification/zen/ZenModePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/zen/ZenModePreferenceControllerTest.java @@ -16,10 +16,13 @@ package com.android.settings.notification.zen; +import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; + import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; @@ -27,13 +30,20 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.Flags; import android.app.NotificationManager; import android.app.NotificationManager.Policy; import android.content.Context; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.preference.Preference; +import com.android.settings.notification.modes.ZenModesListFragment; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -46,6 +56,9 @@ import org.robolectric.util.ReflectionHelpers; @RunWith(RobolectricTestRunner.class) public class ZenModePreferenceControllerTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); + @Mock private Preference mPreference; @Mock @@ -96,4 +109,20 @@ public class ZenModePreferenceControllerTest { verify(mPreference, never()).setSummary(anyString()); } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI) + public void updateState_modesUi_resetsTitleAndFragment() { + mController.updateState(mPreference); + verify(mPreference).setTitle(anyInt()); // Resource IDs are ints + verify(mPreference).setFragment(ZenModesListFragment.class.getCanonicalName()); + } + + @Test + @DisableFlags(Flags.FLAG_MODES_UI) + public void updateState_noModesUi_doesNotSetTitleAndFragment() { + mController.updateState(mPreference); + verify(mPreference, never()).setTitle(anyInt()); + verify(mPreference, never()).setFragment(anyString()); + } } diff --git a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java index 4bddcd1b847..915231f2573 100644 --- a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java +++ b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java @@ -228,6 +228,8 @@ public class BatteryTestUtils { /** Create a power anomaly event proto of adaptive brightness. */ public static PowerAnomalyEvent createAdaptiveBrightnessAnomalyEvent(boolean changeSettings) { + // TODO: migrate "auto_brightness_entry" to use R.string.preference_key_auto_brightness + // if we can access the Context here. (b/338314718) WarningBannerInfo.Builder warningBannerInfoBuilder = WarningBannerInfo.newBuilder() .setMainButtonDestination(DisplaySettings.class.getName()) diff --git a/tests/robotests/src/com/android/settings/users/MultiUserSwitchBarControllerTest.java b/tests/robotests/src/com/android/settings/users/MultiUserSwitchBarControllerTest.java index 1cfb3f614b9..bfab257e44a 100644 --- a/tests/robotests/src/com/android/settings/users/MultiUserSwitchBarControllerTest.java +++ b/tests/robotests/src/com/android/settings/users/MultiUserSwitchBarControllerTest.java @@ -22,6 +22,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.content.Context; +import android.content.pm.UserInfo; import android.os.UserHandle; import android.os.UserManager; @@ -79,4 +80,26 @@ public class MultiUserSwitchBarControllerTest { verify(mSwitchWidgetController, never()).setDisabledByAdmin(any()); } + + @Test + public void onStart_userIsNotMain_shouldNotBeEnabled() { + mUserManager.setUserRestriction(UserHandle.of(UserHandle.myUserId()), + UserManager.DISALLOW_USER_SWITCH, false); + mUserManager.addUser(10, "Test", UserInfo.FLAG_ADMIN); + mUserManager.switchUser(10); + new MultiUserSwitchBarController(mContext, mSwitchWidgetController, null); + + verify(mSwitchWidgetController, never()).setDisabledByAdmin(any()); + verify(mSwitchWidgetController).setEnabled(false); + } + + @Test + public void onStart_userIsMain_shouldBeEnabled() { + mUserManager.setUserRestriction(UserHandle.of(UserHandle.myUserId()), + UserManager.DISALLOW_USER_SWITCH, false); + new MultiUserSwitchBarController(mContext, mSwitchWidgetController, null); + + verify(mSwitchWidgetController, never()).setDisabledByAdmin(any()); + verify(mSwitchWidgetController).setEnabled(true); + } } diff --git a/tests/unit/src/com/android/settings/LinkifyUtilsTest.java b/tests/unit/src/com/android/settings/LinkifyUtilsTest.java index 34586d56186..5fc98d6e0c1 100644 --- a/tests/unit/src/com/android/settings/LinkifyUtilsTest.java +++ b/tests/unit/src/com/android/settings/LinkifyUtilsTest.java @@ -25,10 +25,12 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) +@Ignore("b/340657656") public class LinkifyUtilsTest { private static final String TEST_STRING = "to LINK_BEGINscanning settingsLINK_END."; private static final String WRONG_STRING = "to scanning settingsLINK_END."; diff --git a/tests/unit/src/com/android/settings/datausage/DataUsageInfoControllerTest.java b/tests/unit/src/com/android/settings/datausage/DataUsageInfoControllerTest.java index cb5860a1806..4193b0ab002 100644 --- a/tests/unit/src/com/android/settings/datausage/DataUsageInfoControllerTest.java +++ b/tests/unit/src/com/android/settings/datausage/DataUsageInfoControllerTest.java @@ -23,10 +23,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.settingslib.net.DataUsageController.DataUsageInfo; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) +@Ignore("b/340657656") public class DataUsageInfoControllerTest { private static final int ZERO = 0; diff --git a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressFeminineControllerTest.java b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressFeminineControllerTest.java index 246fad62af9..215e58f6dc8 100644 --- a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressFeminineControllerTest.java +++ b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressFeminineControllerTest.java @@ -18,8 +18,6 @@ package com.android.settings.localepicker; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.spy; - import android.app.GrammaticalInflectionManager; import android.content.Context; import android.content.res.Configuration; @@ -34,7 +32,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.settings.widget.TickButtonPreference; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; @@ -62,7 +59,7 @@ public class TermsOfAddressFeminineControllerTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mContext = spy(ApplicationProvider.getApplicationContext()); + mContext = ApplicationProvider.getApplicationContext(); if (Looper.myLooper() == null) { Looper.prepare(); @@ -93,7 +90,6 @@ public class TermsOfAddressFeminineControllerTest { } @Test - @Ignore("b/339543490") public void displayPreference_setGrammaticalGenderIsFeminine_FeminineIsSelected() { TickButtonPreference selectedPreference = (TickButtonPreference) mPreferenceScreen.getPreference(2); diff --git a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressMasculineControllerTest.java b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressMasculineControllerTest.java index f5ed3959b76..b4c88937d8e 100644 --- a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressMasculineControllerTest.java +++ b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressMasculineControllerTest.java @@ -18,8 +18,6 @@ package com.android.settings.localepicker; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.spy; - import android.app.GrammaticalInflectionManager; import android.content.Context; import android.content.res.Configuration; @@ -34,7 +32,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.settings.widget.TickButtonPreference; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; @@ -62,7 +59,7 @@ public class TermsOfAddressMasculineControllerTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mContext = spy(ApplicationProvider.getApplicationContext()); + mContext = ApplicationProvider.getApplicationContext(); if (Looper.myLooper() == null) { Looper.prepare(); @@ -93,7 +90,6 @@ public class TermsOfAddressMasculineControllerTest { } @Test - @Ignore("b/339543490") public void displayPreference_setGrammaticalGenderIsMasculine_MasculineIsSelected() { TickButtonPreference selectedPreference = (TickButtonPreference) mPreferenceScreen.getPreference(3); diff --git a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressNeutralControllerTest.java b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressNeutralControllerTest.java index 0e53198188e..76aed0ac9ac 100644 --- a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressNeutralControllerTest.java +++ b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressNeutralControllerTest.java @@ -18,8 +18,6 @@ package com.android.settings.localepicker; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.spy; - import android.app.GrammaticalInflectionManager; import android.content.Context; import android.content.res.Configuration; @@ -34,7 +32,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.settings.widget.TickButtonPreference; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; @@ -62,7 +59,7 @@ public class TermsOfAddressNeutralControllerTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mContext = spy(ApplicationProvider.getApplicationContext()); + mContext = ApplicationProvider.getApplicationContext(); if (Looper.myLooper() == null) { Looper.prepare(); @@ -93,7 +90,6 @@ public class TermsOfAddressNeutralControllerTest { } @Test - @Ignore("b/339543490") public void displayPreference_setGrammaticalGenderIsNotSpecified_NeutralIsSelected() { TickButtonPreference selectedPreference = (TickButtonPreference) mPreferenceScreen.getPreference(4); diff --git a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressNotSpecifiedControllerTest.java b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressNotSpecifiedControllerTest.java index 96bac08dde1..0f51b7dfff9 100644 --- a/tests/unit/src/com/android/settings/localepicker/TermsOfAddressNotSpecifiedControllerTest.java +++ b/tests/unit/src/com/android/settings/localepicker/TermsOfAddressNotSpecifiedControllerTest.java @@ -18,8 +18,6 @@ package com.android.settings.localepicker; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.spy; - import android.app.GrammaticalInflectionManager; import android.content.Context; import android.content.res.Configuration; @@ -34,7 +32,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.settings.widget.TickButtonPreference; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; @@ -62,7 +59,7 @@ public class TermsOfAddressNotSpecifiedControllerTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mContext = spy(ApplicationProvider.getApplicationContext()); + mContext = ApplicationProvider.getApplicationContext(); if (Looper.myLooper() == null) { Looper.prepare(); @@ -93,7 +90,6 @@ public class TermsOfAddressNotSpecifiedControllerTest { } @Test - @Ignore("b/339543490") public void displayPreference_setGrammaticalGenderIsNotSpecified_NotSpecifiedIsSelected() { TickButtonPreference selectedPreference = (TickButtonPreference) mPreferenceScreen.getPreference(1); diff --git a/tests/unit/src/com/android/settings/network/telephony/MobileNetworkUtilsTest.java b/tests/unit/src/com/android/settings/network/telephony/MobileNetworkUtilsTest.java index a6f8f2d5670..5f887de53af 100644 --- a/tests/unit/src/com/android/settings/network/telephony/MobileNetworkUtilsTest.java +++ b/tests/unit/src/com/android/settings/network/telephony/MobileNetworkUtilsTest.java @@ -243,6 +243,33 @@ public class MobileNetworkUtilsTest { .isEqualTo(SubscriptionManager.INVALID_SUBSCRIPTION_ID); } + @Test + public void getActiveSubscriptionIdList_nonActive_returnEmptyArray() { + int[] expectedList = new int[0]; + when(mSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(new ArrayList<>()); + + assertThat(MobileNetworkUtils.getActiveSubscriptionIdList(mContext)) + .isEqualTo(expectedList); + } + + @Test + public void getActiveSubscriptionIdList_normalCaseTwoActiveSims_returnValidSubId() { + int[] expectedList = {SUB_ID_1, SUB_ID_2}; + + assertThat(MobileNetworkUtils.getActiveSubscriptionIdList(mContext)) + .isEqualTo(expectedList); + } + + @Test + public void getActiveSubscriptionIdList_TwoActiveSimsAndOneIsNtn_returnOneSubId() { + int[] expectedList = {SUB_ID_2}; + when(mSubscriptionInfo1.isEmbedded()).thenReturn(true); + when(mSubscriptionInfo1.isOnlyNonTerrestrialNetwork()).thenReturn(true); + + assertThat(MobileNetworkUtils.getActiveSubscriptionIdList(mContext)) + .isEqualTo(expectedList); + } + @Test public void shouldDisplayNetworkSelectOptions_HideCarrierNetwork_returnFalse() { mCarrierConfig.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, diff --git a/tests/unit/src/com/android/settings/privatespace/autolock/AutoLockPreferenceControllerTest.java b/tests/unit/src/com/android/settings/privatespace/autolock/AutoLockPreferenceControllerTest.java index 3a605ea1727..fb2cdddd349 100644 --- a/tests/unit/src/com/android/settings/privatespace/autolock/AutoLockPreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/privatespace/autolock/AutoLockPreferenceControllerTest.java @@ -127,7 +127,7 @@ public class AutoLockPreferenceControllerTest { Settings.Secure.PRIVATE_SPACE_AUTO_LOCK, Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY); assertThat(mAutoLockPreferenceController.getSummary().toString()) - .isEqualTo("After 5 minutes of inactivity"); + .isEqualTo("5 minutes after screen timeout"); } /**