diff --git a/res/drawable/baseline_near_me.xml b/res/drawable/baseline_near_me.xml deleted file mode 100644 index 343f97041ed..00000000000 --- a/res/drawable/baseline_near_me.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - diff --git a/res/layout/apps_filter_spinner.xml b/res/layout/apps_filter_spinner.xml index 13d80786db2..1de570528be 100644 --- a/res/layout/apps_filter_spinner.xml +++ b/res/layout/apps_filter_spinner.xml @@ -36,7 +36,7 @@ android:layout_width="56dp" android:layout_height="56dp" android:layout_marginTop="12dp" - android:layout_toRightOf="@id/filter_spinner" + android:layout_toEndOf="@id/filter_spinner" android:contentDescription="@string/configure" android:scaleType="center" android:src="@drawable/ic_apps_filter_settings_24dp" diff --git a/res/layout/settings_collapsing_base_layout.xml b/res/layout/settings_collapsing_base_layout.xml deleted file mode 100644 index 97d249c00ac..00000000000 --- a/res/layout/settings_collapsing_base_layout.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 6047092f8d3..304feb82021 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -446,12 +446,4 @@ 2dp 1dp 2dp - - - 226dp - 270dp - 314dp - 174dp - 24dp - 24dp diff --git a/res/values/strings.xml b/res/values/strings.xml index 6134100c59f..c2ba35f3d6e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4121,7 +4121,7 @@ Allow apps and services to scan for nearby devices at any time, even when Bluetooth is off. This can be used, for example, to improve location-based features and services. - Manage location services + Location services Location Services @@ -6255,8 +6255,39 @@ Since full charge Manage battery usage + ^1 total • ^2 background for past 24 hr + + ^1 total • ^2 background for ^3 + + + Total less than a minute for past 24 hr + + Total less than a minute for ^1 + + + Background less than a minute for past 24 hr + + Background less than a minute for ^1 + + + ^1 total for past 24 hr + + ^1 total for ^2 + + + ^1 background for past 24 hr + + ^1 background for ^2 + + + ^1 total • background less than a minute for past 24 hr + + ^1 total • background less than a minute for ^2 + + + No usage for past 24 hr Battery left estimate is based on your device usage @@ -13187,4 +13218,13 @@ Don\u2019t allow + + + Ultra-WideBand (UWB) + + + Helps identify the relative position of nearby devices that have UWB + + + Turn off Airplane mode to use UWB. diff --git a/res/values/styles.xml b/res/values/styles.xml index e91ddcbccf1..b27cc5de530 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -787,7 +787,7 @@ @*android:string/config_headlineFontFamily - - - diff --git a/res/xml/app_notification_settings.xml b/res/xml/app_notification_settings.xml index a801ae17980..edf8cb9ecc1 100644 --- a/res/xml/app_notification_settings.xml +++ b/res/xml/app_notification_settings.xml @@ -26,7 +26,7 @@ - @@ -96,11 +96,11 @@ - - diff --git a/res/xml/channel_notification_settings.xml b/res/xml/channel_notification_settings.xml index 95f794458a9..97dcc269220 100644 --- a/res/xml/channel_notification_settings.xml +++ b/res/xml/channel_notification_settings.xml @@ -116,11 +116,11 @@ android:title="@string/app_settings_link" settings:allowDividerAbove="true"/> - - diff --git a/res/xml/connected_devices_advanced.xml b/res/xml/connected_devices_advanced.xml index 3ff7d9975f8..85e4a762429 100644 --- a/res/xml/connected_devices_advanced.xml +++ b/res/xml/connected_devices_advanced.xml @@ -65,6 +65,13 @@ android:icon="@drawable/ic_folder_vd_theme_24" android:title="@string/bluetooth_show_files_received_via_bluetooth"/> + + diff --git a/res/xml/conversation_notification_settings.xml b/res/xml/conversation_notification_settings.xml index ea555c293f7..7e475be7ebe 100644 --- a/res/xml/conversation_notification_settings.xml +++ b/res/xml/conversation_notification_settings.xml @@ -58,7 +58,7 @@ android:summary="@string/demote_conversation_summary" settings:allowDividerAbove="true"/> - diff --git a/res/xml/location_settings.xml b/res/xml/location_settings.xml index cae128ba645..dc176d9592c 100644 --- a/res/xml/location_settings.xml +++ b/res/xml/location_settings.xml @@ -65,7 +65,7 @@ diff --git a/res/xml/notification_group_settings.xml b/res/xml/notification_group_settings.xml index cbe23c655f4..e714759aed3 100644 --- a/res/xml/notification_group_settings.xml +++ b/res/xml/notification_group_settings.xml @@ -31,9 +31,9 @@ android:title="@string/app_settings_link" settings:allowDividerAbove="true"/> - - diff --git a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java b/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java index 5402a9bc9e9..26c23137729 100644 --- a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java +++ b/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java @@ -26,6 +26,7 @@ import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.text.Spannable; import android.text.SpannableString; +import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.method.LinkMovementMethod; import android.text.style.ImageSpan; @@ -320,8 +321,13 @@ public class AccessibilityEditDialogUtils { } private static CharSequence retrieveSummary(Context context, int lineHeight) { - return AccessibilityUtil.isFloatingMenuEnabled(context) - ? getSummaryStringWithLink(context) : getSummaryStringWithIcon(context, lineHeight); + final SpannableStringBuilder sb = new SpannableStringBuilder(); + if (!AccessibilityUtil.isFloatingMenuEnabled(context)) { + sb.append(getSummaryStringWithIcon(context, lineHeight)); + sb.append("\n\n"); + } + sb.append(getCustomizeAccessibilityButtonLink(context)); + return sb; } private static int retrieveSoftwareShortcutImageResId(Context context) { @@ -330,7 +336,7 @@ public class AccessibilityEditDialogUtils { : R.drawable.accessibility_shortcut_type_software; } - private static CharSequence getSummaryStringWithLink(Context context) { + private static CharSequence getCustomizeAccessibilityButtonLink(Context context) { final View.OnClickListener linkListener = v -> new SubSettingLauncher(context) .setDestination(AccessibilityButtonFragment.class.getName()) .setSourceMetricsCategory( diff --git a/src/com/android/settings/biometrics/BiometricEnrollActivity.java b/src/com/android/settings/biometrics/BiometricEnrollActivity.java index cede6a02351..0a2e5fe804d 100644 --- a/src/com/android/settings/biometrics/BiometricEnrollActivity.java +++ b/src/com/android/settings/biometrics/BiometricEnrollActivity.java @@ -16,6 +16,9 @@ package com.android.settings.biometrics; +import static android.provider.Settings.ACTION_BIOMETRIC_ENROLL; +import static android.provider.Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED; + import android.annotation.NonNull; import android.app.admin.DevicePolicyManager; import android.app.settings.SettingsEnums; @@ -24,6 +27,7 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricManager.Authenticators; +import android.hardware.biometrics.BiometricManager.BiometricError; import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.fingerprint.FingerprintManager; @@ -31,11 +35,11 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; -import android.provider.Settings; import android.util.Log; import androidx.annotation.Nullable; +import com.android.internal.util.FrameworkStatsLog; import com.android.internal.widget.LockPatternUtils; import com.android.settings.R; import com.android.settings.SetupWizardUtils; @@ -68,12 +72,14 @@ public class BiometricEnrollActivity extends InstrumentedActivity { public static final String EXTRA_SKIP_INTRO = "skip_intro"; private static final String SAVED_STATE_CONFIRMING_CREDENTIALS = "confirming_credentials"; + private static final String SAVED_STATE_ENROLL_ACTION_LOGGED = "enroll_action_logged"; private static final String SAVED_STATE_GK_PW_HANDLE = "gk_pw_handle"; public static final class InternalActivity extends BiometricEnrollActivity {} private int mUserId = UserHandle.myUserId(); private boolean mConfirmingCredentials; + private boolean mIsEnrollActionLogged; private boolean mIsFaceEnrollable; private boolean mIsFingerprintEnrollable; @Nullable private Long mGkPwHandle; @@ -91,15 +97,44 @@ public class BiometricEnrollActivity extends InstrumentedActivity { } if (savedInstanceState != null) { - mConfirmingCredentials = savedInstanceState - .getBoolean(SAVED_STATE_CONFIRMING_CREDENTIALS, false); + mConfirmingCredentials = savedInstanceState.getBoolean( + SAVED_STATE_CONFIRMING_CREDENTIALS, false); + mIsEnrollActionLogged = savedInstanceState.getBoolean( + SAVED_STATE_ENROLL_ACTION_LOGGED, false); if (savedInstanceState.containsKey(SAVED_STATE_GK_PW_HANDLE)) { mGkPwHandle = savedInstanceState.getLong(SAVED_STATE_GK_PW_HANDLE); } } - // Put the theme in the intent so it gets propagated to other activities in the flow + // Log a framework stats event if this activity was launched via intent action. final Intent intent = getIntent(); + if (!mIsEnrollActionLogged && ACTION_BIOMETRIC_ENROLL.equals(intent.getAction())) { + mIsEnrollActionLogged = true; + + // Get the current status for each authenticator type. + @BiometricError final int strongBiometricStatus; + @BiometricError final int weakBiometricStatus; + @BiometricError final int deviceCredentialStatus; + final BiometricManager bm = getSystemService(BiometricManager.class); + if (bm != null) { + strongBiometricStatus = bm.canAuthenticate(Authenticators.BIOMETRIC_STRONG); + weakBiometricStatus = bm.canAuthenticate(Authenticators.BIOMETRIC_WEAK); + deviceCredentialStatus = bm.canAuthenticate(Authenticators.DEVICE_CREDENTIAL); + } else { + strongBiometricStatus = BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE; + weakBiometricStatus = BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE; + deviceCredentialStatus = BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE; + } + + FrameworkStatsLog.write(FrameworkStatsLog.AUTH_ENROLL_ACTION_INVOKED, + strongBiometricStatus == BiometricManager.BIOMETRIC_SUCCESS, + weakBiometricStatus == BiometricManager.BIOMETRIC_SUCCESS, + deviceCredentialStatus == BiometricManager.BIOMETRIC_SUCCESS, + intent.hasExtra(EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED), + intent.getIntExtra(EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, 0)); + } + + // Put the theme in the intent so it gets propagated to other activities in the flow if (intent.getStringExtra(WizardManagerHelper.EXTRA_THEME) == null) { intent.putExtra( WizardManagerHelper.EXTRA_THEME, @@ -108,7 +143,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity { // Default behavior is to enroll BIOMETRIC_WEAK or above. See ACTION_BIOMETRIC_ENROLL. final int authenticators = intent.getIntExtra( - Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, Authenticators.BIOMETRIC_WEAK); + EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, Authenticators.BIOMETRIC_WEAK); Log.d(TAG, "Authenticators: " + authenticators); @@ -132,7 +167,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity { } else { // If the caller is not setup wizard, and the user has something enrolled, finish. final BiometricManager bm = getSystemService(BiometricManager.class); - final @BiometricManager.BiometricError int result = bm.canAuthenticate(authenticators); + final @BiometricError int result = bm.canAuthenticate(authenticators); if (result != BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED) { Log.e(TAG, "Unexpected result: " + result); finish(); @@ -159,6 +194,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity { protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(SAVED_STATE_CONFIRMING_CREDENTIALS, mConfirmingCredentials); + outState.putBoolean(SAVED_STATE_ENROLL_ACTION_LOGGED, mIsEnrollActionLogged); if (mGkPwHandle != null) { outState.putLong(SAVED_STATE_GK_PW_HANDLE, mGkPwHandle); } diff --git a/src/com/android/settings/core/SettingsBaseActivity.java b/src/com/android/settings/core/SettingsBaseActivity.java index 569e4805b8d..e0e41dee2d2 100644 --- a/src/com/android/settings/core/SettingsBaseActivity.java +++ b/src/com/android/settings/core/SettingsBaseActivity.java @@ -49,8 +49,6 @@ import com.android.settingslib.drawer.Tile; import com.google.android.material.appbar.CollapsingToolbarLayout; import com.google.android.setupcompat.util.WizardManagerHelper; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -61,7 +59,6 @@ public class SettingsBaseActivity extends FragmentActivity { protected static final boolean DEBUG_TIMING = false; private static final String TAG = "SettingsBaseActivity"; private static final String DATA_SCHEME_PKG = "package"; - private static final int TOOLBAR_MAX_LINE_NUMBER = 2; // Serves as a temporary list of tiles to ignore until we heard back from the PM that they // are disabled. @@ -95,7 +92,7 @@ public class SettingsBaseActivity extends FragmentActivity { if (FeatureFlagUtils.isEnabled(this, FeatureFlags.SILKY_HOME) && isToolbarEnabled() && !isAnySetupWizard) { - super.setContentView(R.layout.settings_collapsing_base_layout); + super.setContentView(R.layout.collapsing_toolbar_base_layout); mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar); } else { super.setContentView(R.layout.settings_base_layout); @@ -108,7 +105,6 @@ public class SettingsBaseActivity extends FragmentActivity { return; } setActionBar(toolbar); - initCollapsingToolbar(); if (DEBUG_TIMING) { Log.d(TAG, "onCreate took " + (System.currentTimeMillis() - startTime) + " ms"); @@ -207,50 +203,6 @@ public class SettingsBaseActivity extends FragmentActivity { return true; } - private void initCollapsingToolbar() { - if (mCollapsingToolbarLayout == null) { - return; - } - mCollapsingToolbarLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, - int oldLeft, int oldTop, int oldRight, int oldBottom) { - v.removeOnLayoutChangeListener(this); - final int count = getLineCount(); - if (count > TOOLBAR_MAX_LINE_NUMBER) { - ViewGroup.LayoutParams lp = mCollapsingToolbarLayout.getLayoutParams(); - lp.height = getResources() - .getDimensionPixelSize(R.dimen.toolbar_three_lines_height); - mCollapsingToolbarLayout.setLayoutParams(lp); - } else if (count == TOOLBAR_MAX_LINE_NUMBER) { - ViewGroup.LayoutParams lp = mCollapsingToolbarLayout.getLayoutParams(); - lp.height = - getResources().getDimensionPixelSize(R.dimen.toolbar_two_lines_height); - mCollapsingToolbarLayout.setLayoutParams(lp); - } - } - }); - } - - private int getLineCount() { - try { - final Class toolbarClazz = mCollapsingToolbarLayout.getClass(); - final Field textHelperField = toolbarClazz.getDeclaredField("collapsingTextHelper"); - textHelperField.setAccessible(true); - final Object textHelperObj = textHelperField.get(mCollapsingToolbarLayout); - - final Field layoutField = textHelperObj.getClass().getDeclaredField("textLayout"); - layoutField.setAccessible(true); - final Object layoutObj = layoutField.get(textHelperObj); - - final Method method = layoutObj.getClass().getDeclaredMethod("getLineCount"); - return (int) method.invoke(layoutObj); - } catch (Exception e) { - return 0; - } - } - - private void onCategoriesChanged(Set categories) { final int N = mCategoryListeners.size(); for (int i = 0; i < N; i++) { diff --git a/src/com/android/settings/development/qstile/DevelopmentTiles.java b/src/com/android/settings/development/qstile/DevelopmentTiles.java index 16084c080ce..c55d0cb354d 100644 --- a/src/com/android/settings/development/qstile/DevelopmentTiles.java +++ b/src/com/android/settings/development/qstile/DevelopmentTiles.java @@ -371,7 +371,7 @@ public abstract class DevelopmentTiles extends TileService { mContext = getApplicationContext(); mSensorPrivacyManager = (SensorPrivacyManager) mContext.getSystemService( Context.SENSOR_PRIVACY_SERVICE); - mIsEnabled = mSensorPrivacyManager.isSensorPrivacyEnabled(); + mIsEnabled = mSensorPrivacyManager.isAllSensorPrivacyEnabled(); mMetricsFeatureProvider = FeatureFactory.getFactory( mContext).getMetricsFeatureProvider(); mKeyguardManager = (KeyguardManager) mContext.getSystemService( @@ -392,7 +392,7 @@ public abstract class DevelopmentTiles extends TileService { mMetricsFeatureProvider.action(getApplicationContext(), SettingsEnums.QS_SENSOR_PRIVACY, isEnabled); mIsEnabled = isEnabled; - mSensorPrivacyManager.setSensorPrivacy(isEnabled); + mSensorPrivacyManager.setAllSensorPrivacy(isEnabled); } } diff --git a/src/com/android/settings/display/AdaptiveSleepCameraStatePreferenceController.java b/src/com/android/settings/display/AdaptiveSleepCameraStatePreferenceController.java index 72a1c66a431..ba7a3aba752 100644 --- a/src/com/android/settings/display/AdaptiveSleepCameraStatePreferenceController.java +++ b/src/com/android/settings/display/AdaptiveSleepCameraStatePreferenceController.java @@ -43,7 +43,7 @@ public class AdaptiveSleepCameraStatePreferenceController { mPreference.setPositiveButtonText(R.string.allow); mPrivacyManager = SensorPrivacyManager.getInstance(context); mPrivacyManager.addSensorPrivacyListener(CAMERA, - enabled -> updateVisibility()); + (sensor, enabled) -> updateVisibility()); mPreference.setPositiveButtonOnClickListener(p -> { mPrivacyManager.setSensorPrivacy(CAMERA, false); }); diff --git a/src/com/android/settings/display/ScreenTimeoutSettings.java b/src/com/android/settings/display/ScreenTimeoutSettings.java index 444f8ef8de2..27e1e1bfc45 100644 --- a/src/com/android/settings/display/ScreenTimeoutSettings.java +++ b/src/com/android/settings/display/ScreenTimeoutSettings.java @@ -111,9 +111,7 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements mPrivacyPreference.setLayoutResource(R.layout.preference_footer); mPrivacyManager = SensorPrivacyManager.getInstance(context); mPrivacyManager.addSensorPrivacyListener(CAMERA, - enabled -> { - mAdaptiveSleepController.updatePreference(); - }); + (sensor, enabled) -> mAdaptiveSleepController.updatePreference()); } @Override diff --git a/src/com/android/settings/display/SmartAutoRotateCameraStateController.java b/src/com/android/settings/display/SmartAutoRotateCameraStateController.java index 14807855521..9e2a7848d81 100644 --- a/src/com/android/settings/display/SmartAutoRotateCameraStateController.java +++ b/src/com/android/settings/display/SmartAutoRotateCameraStateController.java @@ -43,7 +43,7 @@ public class SmartAutoRotateCameraStateController extends BasePreferenceControll public SmartAutoRotateCameraStateController(Context context, String key) { super(context, key); mPrivacyManager = SensorPrivacyManager.getInstance(context); - mPrivacyManager.addSensorPrivacyListener(CAMERA, enabled -> { + mPrivacyManager.addSensorPrivacyListener(CAMERA, (sensor, enabled) -> { mPreference.setVisible(enabled); updateState(mPreference); }); diff --git a/src/com/android/settings/display/SmartAutoRotateController.java b/src/com/android/settings/display/SmartAutoRotateController.java index e3b2665195b..cf2a9a60567 100644 --- a/src/com/android/settings/display/SmartAutoRotateController.java +++ b/src/com/android/settings/display/SmartAutoRotateController.java @@ -52,7 +52,8 @@ public class SmartAutoRotateController extends TogglePreferenceController implem super(context, preferenceKey); mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); mPrivacyManager = SensorPrivacyManager.getInstance(context); - mPrivacyManager.addSensorPrivacyListener(CAMERA, enabled -> updateState(mPreference)); + mPrivacyManager + .addSensorPrivacyListener(CAMERA, (sensor, enabled) -> updateState(mPreference)); } @Override diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java index 432224d537a..3ef7c455302 100644 --- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java @@ -27,6 +27,7 @@ import android.os.Bundle; import android.os.UserHandle; import android.text.Html; import android.text.TextUtils; +import android.text.format.DateUtils; import android.util.Log; import android.view.View; @@ -282,20 +283,8 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements final long foregroundTimeMs = bundle.getLong(EXTRA_FOREGROUND_TIME); final long backgroundTimeMs = bundle.getLong(EXTRA_BACKGROUND_TIME); - final long totalTimeMs = foregroundTimeMs + backgroundTimeMs; - //TODO(b/178197718) Refine the layout - controller.setSummary(TextUtils.expandTemplate( - getText(R.string.battery_total_and_background_usage), - StringUtil.formatElapsedTime( - getContext(), - totalTimeMs, - /* withSeconds */ false, - /* collapseTimeUnit */ false), - StringUtil.formatElapsedTime( - getContext(), - backgroundTimeMs, - /* withSeconds */ false, - /* collapseTimeUnit */ false))); + //TODO(b/178197718) Update layout to support multiple lines + controller.setSummary(getAppActiveTime(foregroundTimeMs, backgroundTimeMs)); controller.done(context, true /* rebindActions */); } @@ -388,4 +377,53 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements private void updatePreferenceState(RadioButtonPreference preference, String selectedKey) { preference.setChecked(selectedKey.equals(preference.getKey())); } + + //TODO(b/178197718) Update method to support time period + private CharSequence getAppActiveTime(long foregroundTimeMs, long backgroundTimeMs) { + final long totalTimeMs = foregroundTimeMs + backgroundTimeMs; + final CharSequence usageTimeSummary; + + if (totalTimeMs == 0) { + usageTimeSummary = getText(R.string.battery_not_usage); + // Shows background summary only if we don't have foreground usage time. + } else if (foregroundTimeMs == 0 && backgroundTimeMs != 0) { + usageTimeSummary = backgroundTimeMs < DateUtils.MINUTE_IN_MILLIS ? + getText(R.string.battery_background_usage_less_minute) : + TextUtils.expandTemplate(getText(R.string.battery_background_usage), + StringUtil.formatElapsedTime( + getContext(), + backgroundTimeMs, + /* withSeconds */ false, + /* collapseTimeUnit */ false)); + // Shows total usage summary only if total usage time is small. + } else if (totalTimeMs < DateUtils.MINUTE_IN_MILLIS) { + usageTimeSummary = getText(R.string.battery_total_usage_less_minute); + // Shows different total usage summary when background usage time is small. + } else if (backgroundTimeMs < DateUtils.MINUTE_IN_MILLIS) { + usageTimeSummary = TextUtils.expandTemplate( + getText(backgroundTimeMs == 0 ? + R.string.battery_total_usage : + R.string.battery_total_usage_and_background_less_minute_usage), + StringUtil.formatElapsedTime( + getContext(), + totalTimeMs, + /* withSeconds */ false, + /* collapseTimeUnit */ false)); + // Shows default summary. + } else { + usageTimeSummary = TextUtils.expandTemplate( + getText(R.string.battery_total_and_background_usage), + StringUtil.formatElapsedTime( + getContext(), + totalTimeMs, + /* withSeconds */ false, + /* collapseTimeUnit */ false), + StringUtil.formatElapsedTime( + getContext(), + backgroundTimeMs, + /* withSeconds */ false, + /* collapseTimeUnit */ false)); + } + return usageTimeSummary; + } } diff --git a/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java index f12de01df4a..4cafbc7b693 100644 --- a/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java +++ b/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. * - * */ package com.android.settings.fuelgauge; @@ -51,7 +50,7 @@ import java.util.Map; /** Controls the update for chart graph and the list items. */ public class BatteryChartPreferenceController extends AbstractPreferenceController implements PreferenceControllerMixin, LifecycleObserver, OnPause, OnDestroy, - BatteryChartView.OnSelectListener { + BatteryChartView.OnSelectListener, ExpandDividerPreference.OnExpandListener { private static final String TAG = "BatteryChartPreferenceController"; private static final int CHART_KEY_ARRAY_SIZE = 25; private static final int CHART_LEVEL_ARRAY_SIZE = 13; @@ -65,6 +64,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll @VisibleForTesting BatteryUtils mBatteryUtils; @VisibleForTesting PreferenceGroup mAppListPrefGroup; @VisibleForTesting BatteryChartView mBatteryChartView; + @VisibleForTesting ExpandDividerPreference mExpandDividerPreference; @VisibleForTesting int[] mBatteryHistoryLevels; @VisibleForTesting long[] mBatteryHistoryKeys; @@ -76,9 +76,13 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll private final Handler mHandler = new Handler(Looper.getMainLooper()); private final CharSequence[] mNotAllowShowSummaryPackages; + private boolean mIsExpanded = false; + // Preference cache to avoid create new instance each time. @VisibleForTesting final Map mPreferenceCache = new HashMap<>(); + @VisibleForTesting + final List mSystemEntries = new ArrayList<>(); public BatteryChartPreferenceController( Context context, String preferenceKey, @@ -163,6 +167,12 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll refreshUi(trapezoidIndex, /*isForce=*/ false); } + @Override + public void onExpand(boolean isExpanded) { + mIsExpanded = isExpanded; + refreshExpandUi(); + } + void setBatteryHistoryMap( final Map> batteryHistoryMap) { mHandler.post(() -> setBatteryHistoryMapInner(batteryHistoryMap)); @@ -266,10 +276,10 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll } // Separates data into two groups and sort them individually. final List appEntries = new ArrayList<>(); - final List systemEntries = new ArrayList<>(); + mSystemEntries.clear(); entries.forEach(entry -> { if (entry.isSystemEntry()) { - systemEntries.add(entry); + mSystemEntries.add(entry); } else { appEntries.add(entry); } @@ -279,11 +289,25 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll } }); Collections.sort(appEntries, BatteryDiffEntry.COMPARATOR); - Collections.sort(systemEntries, BatteryDiffEntry.COMPARATOR); + Collections.sort(mSystemEntries, BatteryDiffEntry.COMPARATOR); Log.d(TAG, String.format("addAllPreferences() app=%d system=%d", - appEntries.size(), systemEntries.size())); - addPreferenceToScreen(appEntries); - addPreferenceToScreen(systemEntries); + appEntries.size(), mSystemEntries.size())); + + // Adds app entries to the list if it is not empty. + if (!appEntries.isEmpty()) { + addPreferenceToScreen(appEntries); + } + // Adds the expabable divider if we have two sections data. + if (!appEntries.isEmpty() && !mSystemEntries.isEmpty()) { + if (mExpandDividerPreference == null) { + mExpandDividerPreference = new ExpandDividerPreference(mPrefContext); + mExpandDividerPreference.setOnExpandListener(this); + } + mExpandDividerPreference.setOrder( + mAppListPrefGroup.getPreferenceCount()); + mAppListPrefGroup.addPreference(mExpandDividerPreference); + } + refreshExpandUi(); } @VisibleForTesting @@ -337,6 +361,22 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll mAppListPrefGroup.removeAll(); } + private void refreshExpandUi() { + if (mIsExpanded) { + addPreferenceToScreen(mSystemEntries); + } else { + // Removes and recycles all system entries to hide all of them. + for (BatteryDiffEntry entry : mSystemEntries) { + final String prefKey = entry.mBatteryHistEntry.getKey(); + final Preference pref = mAppListPrefGroup.findPreference(prefKey); + if (pref != null) { + mAppListPrefGroup.removePreference(pref); + mPreferenceCache.put(pref.getKey(), pref); + } + } + } + } + @VisibleForTesting void setPreferenceSummary( PowerGaugePreference preference, BatteryDiffEntry entry) { diff --git a/src/com/android/settings/network/telephony/Enable2gPreferenceController.java b/src/com/android/settings/network/telephony/Enable2gPreferenceController.java index cf08d107528..d336cb47e47 100644 --- a/src/com/android/settings/network/telephony/Enable2gPreferenceController.java +++ b/src/com/android/settings/network/telephony/Enable2gPreferenceController.java @@ -72,7 +72,7 @@ public class Enable2gPreferenceController extends TelephonyTogglePreferenceContr public int getAvailabilityStatus(int subId) { final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId); boolean visible = - subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID + SubscriptionManager.isUsableSubscriptionId(subId) && carrierConfig != null && !carrierConfig.getBoolean(CarrierConfigManager.KEY_HIDE_ENABLE_2G) && mTelephonyManager.isRadioInterfaceCapabilitySupported( @@ -89,6 +89,9 @@ public class Enable2gPreferenceController extends TelephonyTogglePreferenceContr @Override public boolean setChecked(boolean isChecked) { + if (!SubscriptionManager.isUsableSubscriptionId(mSubId)) { + return false; + } long currentlyAllowedNetworkTypes = mTelephonyManager.getAllowedNetworkTypesForReason( mTelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G); boolean enabled = (currentlyAllowedNetworkTypes & BITMASK_2G) != 0; diff --git a/src/com/android/settings/notification/NotificationAssistantPreferenceController.java b/src/com/android/settings/notification/NotificationAssistantPreferenceController.java index 6237326f4e4..a46f1646113 100644 --- a/src/com/android/settings/notification/NotificationAssistantPreferenceController.java +++ b/src/com/android/settings/notification/NotificationAssistantPreferenceController.java @@ -77,11 +77,7 @@ public class NotificationAssistantPreferenceController extends TogglePreferenceC protected void setNotificationAssistantGranted(ComponentName cn) { if (Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.NAS_SETTINGS_UPDATED, 0, mUserId) == 0) { - for (int profileId : mUserManager.getProfileIds(mUserId, false)) { - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.NAS_SETTINGS_UPDATED, 1, profileId); - } - mNotificationBackend.resetDefaultNotificationAssistant(cn != null); + mNotificationBackend.setNASMigrationDoneAndResetDefault(mUserId, cn != null); } mNotificationBackend.setNotificationAssistantGranted(cn); } diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java index b08d02cef26..e448dda20aa 100644 --- a/src/com/android/settings/notification/NotificationBackend.java +++ b/src/com/android/settings/notification/NotificationBackend.java @@ -570,9 +570,9 @@ public class NotificationBackend { } } - public void resetDefaultNotificationAssistant(boolean loadFromConfig) { + public void setNASMigrationDoneAndResetDefault(int userId, boolean loadFromConfig) { try { - sINM.resetDefaultNotificationAssistant(loadFromConfig); + sINM.setNASMigrationDoneAndResetDefault(userId, loadFromConfig); } catch (Exception e) { Log.w(TAG, "Error calling NoMan", e); } diff --git a/src/com/android/settings/notification/app/NotificationFooterPreference.java b/src/com/android/settings/notification/app/NotificationFooterPreference.java deleted file mode 100644 index 6f34cdd5c90..00000000000 --- a/src/com/android/settings/notification/app/NotificationFooterPreference.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2017 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.app; - -import android.content.Context; -import android.text.method.LinkMovementMethod; -import android.util.AttributeSet; -import android.widget.TextView; - -import androidx.core.content.res.TypedArrayUtils; -import androidx.preference.Preference; -import androidx.preference.PreferenceViewHolder; - -import com.android.settingslib.R; - -/** - * FooterPreference that can have any key or ordering. - */ -public class NotificationFooterPreference extends Preference { - - public NotificationFooterPreference(Context context, AttributeSet attrs) { - super(context, attrs, TypedArrayUtils.getAttr( - context, R.attr.footerPreferenceStyle, android.R.attr.preferenceStyle)); - init(); - } - - public NotificationFooterPreference(Context context) { - this(context, null); - } - - @Override - public void onBindViewHolder(PreferenceViewHolder holder) { - super.onBindViewHolder(holder); - TextView title = holder.itemView.findViewById(android.R.id.title); - title.setMovementMethod(new LinkMovementMethod()); - title.setClickable(false); - title.setLongClickable(false); - } - - private void init() { - setIcon(R.drawable.ic_info_outline_24dp); - setSelectable(false); - } -} diff --git a/src/com/android/settings/uwb/UwbPreferenceController.java b/src/com/android/settings/uwb/UwbPreferenceController.java new file mode 100644 index 00000000000..daadb63b8dd --- /dev/null +++ b/src/com/android/settings/uwb/UwbPreferenceController.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.uwb; + +import static androidx.lifecycle.Lifecycle.Event.ON_START; +import static androidx.lifecycle.Lifecycle.Event.ON_STOP; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.provider.Settings; +import android.uwb.UwbManager; +import android.uwb.UwbManager.AdapterStateCallback; + +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.OnLifecycleEvent; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.settings.R; +import com.android.settings.core.TogglePreferenceController; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +/** Controller for "UWB" toggle. */ +public class UwbPreferenceController extends TogglePreferenceController implements + AdapterStateCallback, LifecycleObserver { + @VisibleForTesting + static final String KEY_UWB_SETTINGS = "uwb_settings"; + @VisibleForTesting + UwbManager mUwbManager; + @VisibleForTesting + boolean mAirplaneModeOn; + @VisibleForTesting + private final BroadcastReceiver mAirplaneModeChangedReceiver; + private final Executor mExecutor; + private boolean mIsChecked = true; + boolean mRegisteredAdapterStateCallback = false; + private Preference mPreference; + + public UwbPreferenceController(Context context, String key) { + super(context, key); + mExecutor = Executors.newSingleThreadExecutor(); + mUwbManager = context.getSystemService(UwbManager.class); + mAirplaneModeOn = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) == 1; + mAirplaneModeChangedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + updateState(mPreference); + } + }; + } + + @VisibleForTesting + boolean isUwbSupportedOnDevice() { + return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB); + } + + @Override + public int getAvailabilityStatus() { + if (!isUwbSupportedOnDevice()) { + return UNSUPPORTED_ON_DEVICE; + } else if (mAirplaneModeOn) { + return DISABLED_DEPENDENT_SETTING; + } else { + return AVAILABLE; + } + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference = screen.findPreference(getPreferenceKey()); + } + + @Override + public boolean isChecked() { + //TODO(b/186075119): Update toggle state by assigning to the real value by default. + return mIsChecked; + } + + @Override + public boolean setChecked(boolean isChecked) { + mIsChecked = isChecked; + mUwbManager.setUwbEnabled(isChecked); + return true; + } + + @Override + public void onStateChanged(int state, int reason) { + // Only update toggle state from service the first time. Otherwise toggle state is + // changed from controller. For example, UWB is disabled if airplane mode is on but we do + // not want to change the preference for the user in this case. + if (!mRegisteredAdapterStateCallback) { + mIsChecked = state == STATE_ENABLED_ACTIVE || state == STATE_ENABLED_INACTIVE; + mRegisteredAdapterStateCallback = true; + } + } + + /** Called when activity starts being displayed to user. */ + @OnLifecycleEvent(ON_START) + public void onStart() { + if (isUwbSupportedOnDevice()) { + mUwbManager.registerAdapterStateCallback(mExecutor, this); + } + if (mAirplaneModeChangedReceiver != null) { + mContext.registerReceiver(mAirplaneModeChangedReceiver, + new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); + } + refreshSummary(mPreference); + } + + /** Called when activity stops being displayed to user. */ + @OnLifecycleEvent(ON_STOP) + public void onStop() { + if (isUwbSupportedOnDevice()) { + mUwbManager.unregisterAdapterStateCallback(this); + } + if (mAirplaneModeChangedReceiver != null) { + mContext.unregisterReceiver(mAirplaneModeChangedReceiver); + } + } + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + mAirplaneModeOn = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) == 1; + preference.setEnabled(!mAirplaneModeOn); + if (isUwbSupportedOnDevice()) { + if (mAirplaneModeOn) { + mUwbManager.setUwbEnabled(false); + } else { + mUwbManager.setUwbEnabled(mIsChecked); + } + } + refreshSummary(preference); + } + + @Override + public CharSequence getSummary() { + if (mAirplaneModeOn) { + return mContext.getResources().getString(R.string.uwb_settings_summary_airplane_mode); + } else { + return mContext.getResources().getString(R.string.uwb_settings_summary); + } + } +} + diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java index a288c3a84c2..dd10a9e1556 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java @@ -249,14 +249,122 @@ public class AdvancedPowerUsageDetailTest { } @Test - public void testInitHeader_hasCorrectSummary() { - mFragment.mAppEntry = null; + public void testInitHeader_noUsageTime_hasCorrectSummary() { + Bundle bundle = new Bundle(2); + bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, /* value */ 0); + bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, /* value */ 0); + when(mFragment.getArguments()).thenReturn(bundle); + mFragment.initHeader(); ArgumentCaptor captor = ArgumentCaptor.forClass(CharSequence.class); verify(mEntityHeaderController).setSummary(captor.capture()); assertThat(captor.getValue().toString()) - .isEqualTo("0 min total • 0 min background for past 24 hr"); + .isEqualTo("No usage for past 24 hr"); + } + + @Test + public void testInitHeader_backgroundTwoMinutesForegroundZero_hasCorrectSummary() { + final long backgroundTimeTwoMinutes = 120000; + final long foregroundTimeZero = 0; + Bundle bundle = new Bundle(2); + bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, backgroundTimeTwoMinutes); + bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, foregroundTimeZero); + when(mFragment.getArguments()).thenReturn(bundle); + + mFragment.initHeader(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(CharSequence.class); + verify(mEntityHeaderController).setSummary(captor.capture()); + assertThat(captor.getValue().toString()) + .isEqualTo("2 min background for past 24 hr"); + } + + @Test + public void testInitHeader_backgroundLessThanAMinutesForegroundZero_hasCorrectSummary() { + final long backgroundTimeLessThanAMinute = 59999; + final long foregroundTimeZero = 0; + Bundle bundle = new Bundle(2); + bundle.putLong( + AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, backgroundTimeLessThanAMinute); + bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, foregroundTimeZero); + when(mFragment.getArguments()).thenReturn(bundle); + + mFragment.initHeader(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(CharSequence.class); + verify(mEntityHeaderController).setSummary(captor.capture()); + assertThat(captor.getValue().toString()) + .isEqualTo("Background less than a minute for past 24 hr"); + } + + @Test + public void testInitHeader_totalUsageLessThanAMinutes_hasCorrectSummary() { + final long backgroundTimeLessThanHalfMinute = 20000; + final long foregroundTimeLessThanHalfMinute = 20000; + Bundle bundle = new Bundle(2); + bundle.putLong( + AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, backgroundTimeLessThanHalfMinute); + bundle.putLong( + AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, foregroundTimeLessThanHalfMinute); + when(mFragment.getArguments()).thenReturn(bundle); + + mFragment.initHeader(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(CharSequence.class); + verify(mEntityHeaderController).setSummary(captor.capture()); + assertThat(captor.getValue().toString()) + .isEqualTo("Total less than a minute for past 24 hr"); + } + + @Test + public void testInitHeader_TotalAMinutesBackgroundLessThanAMinutes_hasCorrectSummary() { + final long backgroundTimeZero = 59999; + final long foregroundTimeTwoMinutes = 1; + Bundle bundle = new Bundle(2); + bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, backgroundTimeZero); + bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, foregroundTimeTwoMinutes); + when(mFragment.getArguments()).thenReturn(bundle); + + mFragment.initHeader(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(CharSequence.class); + verify(mEntityHeaderController).setSummary(captor.capture()); + assertThat(captor.getValue().toString()) + .isEqualTo("1 min total • background less than a minute for past 24 hr"); + } + + @Test + public void testInitHeader_TotalAMinutesBackgroundZero_hasCorrectSummary() { + final long backgroundTimeZero = 0; + final long foregroundTimeAMinutes = 60000; + Bundle bundle = new Bundle(2); + bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, backgroundTimeZero); + bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, foregroundTimeAMinutes); + when(mFragment.getArguments()).thenReturn(bundle); + + mFragment.initHeader(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(CharSequence.class); + verify(mEntityHeaderController).setSummary(captor.capture()); + assertThat(captor.getValue().toString()) + .isEqualTo("1 min total for past 24 hr"); + } + + @Test + public void testInitHeader_foregroundTwoMinutesBackgroundFourMinutes_hasCorrectSummary() { + final long backgroundTimeFourMinute = 240000; + final long foregroundTimeTwoMinutes = 120000; + Bundle bundle = new Bundle(2); + bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, backgroundTimeFourMinute); + bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, foregroundTimeTwoMinutes); + when(mFragment.getArguments()).thenReturn(bundle); + mFragment.initHeader(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(CharSequence.class); + verify(mEntityHeaderController).setSummary(captor.capture()); + assertThat(captor.getValue().toString()) + .isEqualTo("6 min total • 4 min background for past 24 hr"); } @Test diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java index c8bbba3623c..a70a109330c 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java @@ -43,6 +43,7 @@ import com.android.settings.testutils.FakeFeatureFactory; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; @@ -416,23 +417,56 @@ public final class BatteryChartPreferenceControllerTest { @Test public void testValidateSlotTimestamp_returnExpectedResult() { - final List slotTimestampList = + final ArrayList slotTimestampList = new ArrayList( Arrays.asList( Long.valueOf(0), Long.valueOf(DateUtils.HOUR_IN_MILLIS), Long.valueOf(DateUtils.HOUR_IN_MILLIS * 2 + DateUtils.MINUTE_IN_MILLIS), - Long.valueOf(DateUtils.HOUR_IN_MILLIS * 3 + DateUtils.MINUTE_IN_MILLIS * 2)); + Long.valueOf(DateUtils.HOUR_IN_MILLIS * 3 + DateUtils.MINUTE_IN_MILLIS * 2))); // Verifies the testing data is correct before we added invalid data into it. assertThat(BatteryChartPreferenceController.validateSlotTimestamp(slotTimestampList)) .isTrue(); // Insert invalid timestamp into the list. slotTimestampList.add( - Long.valueOf(DateUtils.HOUR_IN_MILLIS * 4 + DateUtils.MINUTE_IN_MILLIS * 3)); + Long.valueOf(DateUtils.HOUR_IN_MILLIS * 4 + DateUtils.MINUTE_IN_MILLIS * 6)); assertThat(BatteryChartPreferenceController.validateSlotTimestamp(slotTimestampList)) .isFalse(); } + @Test + public void testOnExpand_expandedIsTrue_addSystemEntriesToPreferenceGroup() { + final String prefKey = "preference_key"; + doReturn(1).when(mAppListGroup).getPreferenceCount(); + mBatteryChartPreferenceController.mSystemEntries.add(mBatteryDiffEntry); + doReturn("label").when(mBatteryDiffEntry).getAppLabel(); + doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon(); + doReturn(prefKey).when(mBatteryHistEntry).getKey(); + + mBatteryChartPreferenceController.onExpand(/*isExpanded=*/ true); + + final ArgumentCaptor captor = ArgumentCaptor.forClass(Preference.class); + verify(mAppListGroup).addPreference(captor.capture()); + // Verifies the added preference. + assertThat(captor.getValue().getKey()).isEqualTo(prefKey); + } + + @Test + public void testOnExpand_expandedIsFalse_removeSystemEntriesFromPreferenceGroup() { + final String prefKey = "preference_key"; + doReturn(prefKey).when(mBatteryHistEntry).getKey(); + doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(prefKey); + mBatteryChartPreferenceController.mSystemEntries.add(mBatteryDiffEntry); + // Verifies the cache is empty first. + assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty(); + + mBatteryChartPreferenceController.onExpand(/*isExpanded=*/ false); + + verify(mAppListGroup).findPreference(prefKey); + verify(mAppListGroup).removePreference(mPowerGaugePreference); + assertThat(mBatteryChartPreferenceController.mPreferenceCache).hasSize(1); + } + private static Map> createBatteryHistoryMap(int size) { final Map> batteryHistoryMap = new HashMap<>(); for (int index = 0; index < size; index++) { diff --git a/tests/robotests/src/com/android/settings/notification/NotificationAssistantPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/NotificationAssistantPreferenceControllerTest.java index f6b72d963bc..9b45a9808eb 100644 --- a/tests/robotests/src/com/android/settings/notification/NotificationAssistantPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/NotificationAssistantPreferenceControllerTest.java @@ -122,13 +122,13 @@ public class NotificationAssistantPreferenceControllerTest { assertEquals(1, Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.NAS_SETTINGS_UPDATED, 0, 10)); verify(mBackend, times(1)) - .resetDefaultNotificationAssistant(eq(true)); + .setNASMigrationDoneAndResetDefault(eq(0), eq(true)); //Test user enable again, migration should not happen mPreferenceController.setNotificationAssistantGranted(mNASComponent); //Number of invocations should not increase verify(mBackend, times(1)) - .resetDefaultNotificationAssistant(eq(true)); + .setNASMigrationDoneAndResetDefault(eq(0), eq(true)); } @Test @@ -146,13 +146,13 @@ public class NotificationAssistantPreferenceControllerTest { assertEquals(0, Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.NAS_SETTINGS_UPDATED, 0, 20)); verify(mBackend, times(1)) - .resetDefaultNotificationAssistant(eq(true)); + .setNASMigrationDoneAndResetDefault(eq(0), eq(true)); //Test user enable again, migration should not happen mPreferenceController.setNotificationAssistantGranted(mNASComponent); //Number of invocations should not increase verify(mBackend, times(1)) - .resetDefaultNotificationAssistant(eq(true)); + .setNASMigrationDoneAndResetDefault(eq(0), eq(true)); } @Test @@ -170,13 +170,13 @@ public class NotificationAssistantPreferenceControllerTest { assertEquals(1, Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.NAS_SETTINGS_UPDATED, 0, 10)); verify(mBackend, times(1)) - .resetDefaultNotificationAssistant(eq(false)); + .setNASMigrationDoneAndResetDefault(eq(0), eq(false)); //Test user disable again, migration should not happen mPreferenceController.setChecked(false); //Number of invocations should not increase verify(mBackend, times(1)) - .resetDefaultNotificationAssistant(eq(false)); + .setNASMigrationDoneAndResetDefault(eq(0), eq(false)); } } diff --git a/tests/robotests/src/com/android/settings/uwb/UwbPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/uwb/UwbPreferenceControllerTest.java new file mode 100644 index 00000000000..00dc4dff4ae --- /dev/null +++ b/tests/robotests/src/com/android/settings/uwb/UwbPreferenceControllerTest.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.uwb; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.uwb.UwbManager; + +import com.android.settings.core.BasePreferenceController; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +/** Unit tests for UWB preference toggle. */ +@RunWith(RobolectricTestRunner.class) +public class UwbPreferenceControllerTest { + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + private Context mContext; + private PackageManager mPackageManager; + private UwbPreferenceController mController; + + @Mock + private UwbManager mUwbManager; + + @Before + public void setUp() { + mContext = spy(RuntimeEnvironment.application); + mPackageManager = spy(mContext.getPackageManager()); + mController = new UwbPreferenceController(mContext, "uwb_settings"); + mController.mUwbManager = mUwbManager; + } + + @Test + public void getAvailabilityStatus_uwbDisabled_shouldReturnDisabled() { + doReturn(mPackageManager).when(mContext).getPackageManager(); + doReturn(true).when(mPackageManager) + .hasSystemFeature(PackageManager.FEATURE_UWB); + mController.mAirplaneModeOn = true; + + assertThat(mController.getAvailabilityStatus()) + .isEqualTo(BasePreferenceController.DISABLED_DEPENDENT_SETTING); + } + + @Test + public void getAvailabilityStatus_uwbShown_shouldReturnAvailable() { + doReturn(mPackageManager).when(mContext).getPackageManager(); + doReturn(true).when(mPackageManager) + .hasSystemFeature(PackageManager.FEATURE_UWB); + mController.mAirplaneModeOn = false; + + assertThat(mController.getAvailabilityStatus()) + .isEqualTo(BasePreferenceController.AVAILABLE); + } + + @Test + public void getAvailabilityStatus_uwbNotShown_shouldReturnUnsupported() { + doReturn(mPackageManager).when(mContext).getPackageManager(); + doReturn(false).when(mPackageManager) + .hasSystemFeature(PackageManager.FEATURE_UWB); + + assertThat(mController.getAvailabilityStatus()) + .isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE); + } + + @Test + public void onStateChanged_stateNotRegistered_shouldUpdate() { + mController.mRegisteredAdapterStateCallback = false; + mController.onStateChanged(UwbManager.AdapterStateCallback.STATE_DISABLED, + UwbManager.AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY); + + assertThat(mController.isChecked()).isFalse(); + } + + @Test + public void onStateChanged_stateRegistered_shouldNotUpdate() { + mController.mRegisteredAdapterStateCallback = true; + mController.onStateChanged(UwbManager.AdapterStateCallback.STATE_ENABLED_INACTIVE, + UwbManager.AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY); + + assertThat(mController.isChecked()).isTrue(); + } + + @Test + public void isChecked_uwbEnabledInactive_shouldReturnTrue() { + doReturn(mPackageManager).when(mContext).getPackageManager(); + doReturn(true).when(mPackageManager) + .hasSystemFeature(PackageManager.FEATURE_UWB); + mController.mRegisteredAdapterStateCallback = false; + mController.onStateChanged(UwbManager.AdapterStateCallback.STATE_ENABLED_INACTIVE, + UwbManager.AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY); + + assertThat(mController.isChecked()).isTrue(); + } + + @Test + public void isChecked_uwbEnabledActive_shouldReturnTrue() { + doReturn(mPackageManager).when(mContext).getPackageManager(); + doReturn(true).when(mPackageManager) + .hasSystemFeature(PackageManager.FEATURE_UWB); + mController.mRegisteredAdapterStateCallback = false; + mController.onStateChanged(UwbManager.AdapterStateCallback.STATE_ENABLED_ACTIVE, + UwbManager.AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY); + + assertThat(mController.isChecked()).isTrue(); + } + + @Test + public void isChecked_uwbDisabled_shouldReturnFalse() { + doReturn(mPackageManager).when(mContext).getPackageManager(); + doReturn(true).when(mPackageManager) + .hasSystemFeature(PackageManager.FEATURE_UWB); + mController.mRegisteredAdapterStateCallback = false; + mController.onStateChanged(UwbManager.AdapterStateCallback.STATE_DISABLED, + UwbManager.AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY); + + assertThat(mController.isChecked()).isFalse(); + } + + @Test + public void setChecked_uwbDisabled_shouldEnableUwb() { + clearInvocations(mUwbManager); + doReturn(mPackageManager).when(mContext).getPackageManager(); + doReturn(true).when(mPackageManager) + .hasSystemFeature(PackageManager.FEATURE_UWB); + + mController.setChecked(true); + + verify(mUwbManager).setUwbEnabled(true); + verify(mUwbManager, never()).setUwbEnabled(false); + } + + @Test + public void setChecked_uwbEnabled_shouldDisableUwb() { + clearInvocations(mUwbManager); + doReturn(mPackageManager).when(mContext).getPackageManager(); + doReturn(true).when(mPackageManager) + .hasSystemFeature(PackageManager.FEATURE_UWB); + + mController.setChecked(false); + + verify(mUwbManager).setUwbEnabled(false); + verify(mUwbManager, never()).setUwbEnabled(true); + } +} + diff --git a/tests/unit/src/com/android/settings/network/telephony/Enable2gPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/Enable2gPreferenceControllerTest.java index 293a19dad09..5ade97953e0 100644 --- a/tests/unit/src/com/android/settings/network/telephony/Enable2gPreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/network/telephony/Enable2gPreferenceControllerTest.java @@ -48,6 +48,8 @@ public final class Enable2gPreferenceControllerTest { @Mock private TelephonyManager mTelephonyManager; @Mock + private TelephonyManager mInvalidTelephonyManager; + @Mock private CarrierConfigManager mCarrierConfigManager; private PersistableBundle mPersistableBundle; @@ -65,6 +67,8 @@ public final class Enable2gPreferenceControllerTest { .thenReturn(mCarrierConfigManager); doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID); + doReturn(mInvalidTelephonyManager).when(mTelephonyManager).createForSubscriptionId( + SubscriptionManager.INVALID_SUBSCRIPTION_ID); mPersistableBundle = new PersistableBundle(); doReturn(mPersistableBundle).when(mCarrierConfigManager).getConfigForSubId(SUB_ID); @@ -120,6 +124,18 @@ public final class Enable2gPreferenceControllerTest { assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); } + @Test + public void setChecked_invalidSubIdAndIsCheckedTrue_returnFalse() { + mController.init(SubscriptionManager.INVALID_SUBSCRIPTION_ID); + assertThat(mController.setChecked(true)).isFalse(); + } + + @Test + public void setChecked_invalidSubIdAndIsCheckedFalse_returnFalse() { + mController.init(SubscriptionManager.INVALID_SUBSCRIPTION_ID); + assertThat(mController.setChecked(false)).isFalse(); + } + @Test public void onPreferenceChange_update() { // Set "Enable 2G" flag to "on"