diff --git a/res/drawable/accessibility_button_navigation.xml b/res/drawable/accessibility_button_navigation.xml new file mode 100644 index 00000000000..82e3c70174f --- /dev/null +++ b/res/drawable/accessibility_button_navigation.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/res/drawable/accessibility_button_preview_base.xml b/res/drawable/accessibility_button_preview_base.xml new file mode 100644 index 00000000000..9e3ec598586 --- /dev/null +++ b/res/drawable/accessibility_button_preview_base.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + diff --git a/res/drawable/accessibility_button_preview_large_floating_menu.xml b/res/drawable/accessibility_button_preview_large_floating_menu.xml new file mode 100644 index 00000000000..e003dc7322c --- /dev/null +++ b/res/drawable/accessibility_button_preview_large_floating_menu.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/res/drawable/accessibility_button_preview_small_floating_menu.xml b/res/drawable/accessibility_button_preview_small_floating_menu.xml new file mode 100644 index 00000000000..3ff8e4b25f4 --- /dev/null +++ b/res/drawable/accessibility_button_preview_small_floating_menu.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/res/drawable/accessibility_shortcut_type_software_floating.xml b/res/drawable/accessibility_shortcut_type_software_floating.xml new file mode 100644 index 00000000000..958201515f1 --- /dev/null +++ b/res/drawable/accessibility_shortcut_type_software_floating.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/layout/accessibility_button_preview.xml b/res/layout/accessibility_button_preview.xml new file mode 100644 index 00000000000..07cb0ffb8ab --- /dev/null +++ b/res/layout/accessibility_button_preview.xml @@ -0,0 +1,33 @@ + + + + + + + \ No newline at end of file diff --git a/res/layout/preference_labeled_continuous_slider.xml b/res/layout/preference_labeled_continuous_slider.xml deleted file mode 100644 index 00e87964e0d..00000000000 --- a/res/layout/preference_labeled_continuous_slider.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/res/values/arrays.xml b/res/values/arrays.xml index 183dd1e579d..2057c50626f 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -966,6 +966,35 @@ -1 + + + Floating over other apps + Navigation bar + + + + + + + 1 + + 0 + + + + + Small + Large + + + + + + 0 + + 1 + + diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 4fef726d599..dbf21fc8626 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -57,6 +57,8 @@ 320dp + 200dp + 4dp diff --git a/res/values/strings.xml b/res/values/strings.xml index 89665de59b9..78fb0a9224f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2416,6 +2416,10 @@ Turn off hotspot automatically When no devices are connected + + Maximize compatibility + + This may reduce speed for devices connected to this hotspot and use more power Turning hotspot on\u2026 @@ -5106,6 +5110,8 @@ Use new accessibility gesture To use this feature, tap the accessibility button %s on the bottom of your screen.\n\nTo switch between features, touch & hold the accessibility button. + + To use this feature, tap the accessibility button on your screen. To use this feature, press & hold both volume keys. @@ -5136,6 +5142,8 @@ Swipe up from the bottom of the screen with 2 fingers.\n\nTo switch between features, swipe up with 2 fingers and hold. Swipe up from the bottom of the screen with 3 fingers.\n\nTo switch between features, swipe up with 3 fingers and hold. + + Customize accessibility button Hold volume keys @@ -5164,6 +5172,26 @@ Shortcut from lock screen Allow feature shortcut to turn on from the lock screen. Hold both volume keys for a few seconds. + + Accessibility button + + Quickly access accessibility features + + Quickly access accessibility features from any screen. \n\nTo get started, go to accessibility settings and select a feature. Tap on the shortcut and select the accessibility button. + + Location + + Size + + Fade when not in use + + Fades after a few seconds so it\u2019s easier to see your screen + + Transparency when not in use + + Transparent + + Non-transparent High contrast text @@ -5338,15 +5366,17 @@ Blue-yellow - - Reduce brightness + + Extra dim + + Make screen extra dim - Make screen darker than your phone\u2019s minimum brightness + Dim screen beyond your phone\u2019s minimum brightness - Make screen darker than your tablet\u2019s minimum brightness + Dim screen beyond your tablet\u2019s minimum brightness -
+
This can be helpful when:
  1. \u00a0Your phone\u2019s default minimum brightness is still too bright
  2. @@ -5366,10 +5396,6 @@ Intensity - - Slightly darker - - Darkest Keep on after device restarts @@ -8193,7 +8219,7 @@ Do Not Disturb is on for %s with custom settings. - View custom settings + View custom settings Priority only @@ -8251,6 +8277,9 @@ Duration for Quick Settings + + General When Do Not Disturb is on, sound and vibration will be muted, except for the items you allow above. @@ -12827,7 +12856,9 @@ Use battery saver - Use Do Not Disturb + Turn off now + + Turn on now Use Night Light diff --git a/res/xml/accessibility_button_settings.xml b/res/xml/accessibility_button_settings.xml new file mode 100644 index 00000000000..5e81616bffc --- /dev/null +++ b/res/xml/accessibility_button_settings.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/xml/accessibility_shortcuts_settings.xml b/res/xml/accessibility_shortcuts_settings.xml index 35314e708bb..465f96d74df 100644 --- a/res/xml/accessibility_shortcuts_settings.xml +++ b/res/xml/accessibility_shortcuts_settings.xml @@ -21,6 +21,13 @@ android:persistent="false" android:title="@string/accessibility_shortcuts_settings_title"> + + + android:title="@string/network_dashboard_title"> + android:title="@string/network_dashboard_title"> - + android:title="@string/reduce_bright_colors_intensity_preference_title"/> - - + android:title="@string/apps_dashboard_title"/> - - + - - + android:title="@string/power_usage_summary_title" + settings:controller="com.android.settings.fuelgauge.TopLevelBatteryPreferenceController"/> - + - + - + - + - - + - - + android:title="@string/privacy_dashboard_title"/> - + - + - - - - - - + android:fragment="com.android.settings.emergency.EmergencyDashboardFragment"/> - + + - + android:title="@string/header_category_system"/> - + - - + diff --git a/res/xml/wifi_tether_settings.xml b/res/xml/wifi_tether_settings.xml index 34d8032cbb9..8648cff0f43 100644 --- a/res/xml/wifi_tether_settings.xml +++ b/res/xml/wifi_tether_settings.xml @@ -37,12 +37,13 @@ android:persistent="false" android:title="@string/wifi_hotspot_password_title"/> - - + + diff --git a/res/xml/zen_mode_settings.xml b/res/xml/zen_mode_settings.xml index 78dee028666..10b3e415b4d 100644 --- a/res/xml/zen_mode_settings.xml +++ b/res/xml/zen_mode_settings.xml @@ -23,7 +23,6 @@ - + + + + android:title="@string/zen_settings_general" + android:key="zen_mode_settings_advanced"> mValueTitleMap = new ArrayMap<>(); + private int mDefaultLocation; + + public AccessibilityButtonLocationPreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); + initValueTitleMap(); + } + + @Override + public int getAvailabilityStatus() { + return AccessibilityUtil.isGestureNavigateEnabled(mContext) + ? DISABLED_DEPENDENT_SETTING : AVAILABLE; + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final ListPreference listPreference = (ListPreference) preference; + final Integer value = Ints.tryParse((String) newValue); + if (value != null) { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_MODE, value); + updateState(listPreference); + } + return true; + } + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + final ListPreference listPreference = (ListPreference) preference; + + listPreference.setValue(getCurrentAccessibilityButtonMode()); + } + + private String getCurrentAccessibilityButtonMode() { + final int mode = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_MODE, mDefaultLocation); + return String.valueOf(mode); + } + + private void initValueTitleMap() { + if (mValueTitleMap.size() == 0) { + final String[] values = mContext.getResources().getStringArray( + R.array.accessibility_button_location_selector_values); + final String[] titles = mContext.getResources().getStringArray( + R.array.accessibility_button_location_selector_titles); + final int mapSize = values.length; + + mDefaultLocation = Integer.parseInt(values[0]); + for (int i = 0; i < mapSize; i++) { + mValueTitleMap.put(values[i], titles[i]); + } + } + } +} diff --git a/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceController.java new file mode 100644 index 00000000000..69a7a46f0c3 --- /dev/null +++ b/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceController.java @@ -0,0 +1,126 @@ +/* + * 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.accessibility; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Looper; +import android.provider.Settings; +import android.widget.ImageView; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnPause; +import com.android.settingslib.core.lifecycle.events.OnResume; +import com.android.settingslib.widget.LayoutPreference; + +/** Preference controller that controls the preview effect in accessibility button page. */ +public class AccessibilityButtonPreviewPreferenceController extends BasePreferenceController + implements LifecycleObserver, OnResume, OnPause { + + private static final int SMALL_SIZE = 0; + private static final float DEFAULT_OPACITY = 0.55f; + private static final int DEFAULT_SIZE = 0; + + private final ContentResolver mContentResolver; + @VisibleForTesting + final ContentObserver mContentObserver; + private FloatingMenuLayerDrawable mFloatingMenuPreviewDrawable; + + @VisibleForTesting + ImageView mPreview; + + public AccessibilityButtonPreviewPreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); + mContentResolver = context.getContentResolver(); + mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange) { + updatePreviewPreference(); + } + }; + } + + @Override + public int getAvailabilityStatus() { + return AVAILABLE; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + final LayoutPreference preference = screen.findPreference(getPreferenceKey()); + mPreview = preference.findViewById(R.id.preview_image); + + updatePreviewPreference(); + } + + @Override + public void onResume() { + mContentResolver.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE), + /* notifyForDescendants= */ false, mContentObserver); + mContentResolver.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE), + /* notifyForDescendants= */ false, mContentObserver); + mContentResolver.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY), + /* notifyForDescendants= */ false, mContentObserver); + } + + @Override + public void onPause() { + mContentResolver.unregisterContentObserver(mContentObserver); + } + + private void updatePreviewPreference() { + if (AccessibilityUtil.isFloatingMenuEnabled(mContext)) { + final int size = Settings.Secure.getInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, DEFAULT_SIZE); + final int opacity = (int) (Settings.Secure.getFloat(mContentResolver, + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, DEFAULT_OPACITY) * 100); + final int floatingMenuIconId = (size == SMALL_SIZE) + ? R.drawable.accessibility_button_preview_small_floating_menu + : R.drawable.accessibility_button_preview_large_floating_menu; + + mPreview.setImageDrawable(getFloatingMenuPreviewDrawable(floatingMenuIconId, opacity)); + // Only change opacity(alpha) would not invoke redraw view, need to invalidate manually. + mPreview.invalidate(); + } else { + mPreview.setImageDrawable( + mContext.getDrawable(R.drawable.accessibility_button_navigation)); + } + } + + private Drawable getFloatingMenuPreviewDrawable(int resId, int opacity) { + if (mFloatingMenuPreviewDrawable == null) { + mFloatingMenuPreviewDrawable = FloatingMenuLayerDrawable.createLayerDrawable( + mContext, resId, opacity); + } else { + mFloatingMenuPreviewDrawable.updateLayerDrawable(mContext, resId, opacity); + } + + return mFloatingMenuPreviewDrawable; + } +} diff --git a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java b/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java index f349a125085..6b31988ecff 100644 --- a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java +++ b/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java @@ -17,6 +17,7 @@ package com.android.settings.accessibility; import android.app.Dialog; +import android.app.settings.SettingsEnums; import android.content.Context; import android.content.DialogInterface; import android.content.res.TypedArray; @@ -24,6 +25,7 @@ import android.graphics.drawable.Drawable; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; +import android.text.method.LinkMovementMethod; import android.text.style.ImageSpan; import android.view.LayoutInflater; import android.view.View; @@ -40,6 +42,8 @@ import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; import com.android.settings.R; +import com.android.settings.core.SubSettingLauncher; +import com.android.settings.utils.AnnotationSpan; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -253,6 +257,8 @@ public class AccessibilityEditDialogUtils { summary.setVisibility(View.GONE); } else { summary.setText(summaryText); + summary.setMovementMethod(LinkMovementMethod.getInstance()); + summary.setFocusable(false); } final ImageView image = view.findViewById(R.id.image); image.setImageResource(imageResId); @@ -260,10 +266,13 @@ public class AccessibilityEditDialogUtils { private static void initSoftwareShortcut(Context context, View view) { final View dialogView = view.findViewById(R.id.software_shortcut); + final CharSequence title = context.getText( + R.string.accessibility_shortcut_edit_dialog_title_software); final TextView summary = dialogView.findViewById(R.id.summary); final int lineHeight = summary.getLineHeight(); - setupShortcutWidget(dialogView, retrieveTitle(context), - retrieveSummary(context, lineHeight), retrieveImageResId(context)); + + setupShortcutWidget(dialogView, title, retrieveSummary(context, lineHeight), + retrieveImageResId(context)); } private static void initHardwareShortcut(Context context, View view) { @@ -297,35 +306,28 @@ public class AccessibilityEditDialogUtils { }); } - private static CharSequence retrieveTitle(Context context) { - int resId = R.string.accessibility_shortcut_edit_dialog_title_software; - if (AccessibilityUtil.isGestureNavigateEnabled(context)) { - resId = AccessibilityUtil.isTouchExploreEnabled(context) - ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback - : R.string.accessibility_shortcut_edit_dialog_title_software_gesture; - } - return context.getText(resId); - } - private static CharSequence retrieveSummary(Context context, int lineHeight) { - if (AccessibilityUtil.isGestureNavigateEnabled(context)) { - final int resId = AccessibilityUtil.isTouchExploreEnabled(context) - ? R.string.accessibility_shortcut_edit_dialog_summary_software_gesture_talkback - : R.string.accessibility_shortcut_edit_dialog_summary_software_gesture; - return context.getText(resId); - } - return getSummaryStringWithIcon(context, lineHeight); + return AccessibilityUtil.isFloatingMenuEnabled(context) + ? getSummaryStringWithLink(context) : getSummaryStringWithIcon(context, lineHeight); } private static int retrieveImageResId(Context context) { - // TODO(b/142531156): Use vector drawable instead of temporal png file to avoid distorted. - int resId = R.drawable.accessibility_shortcut_type_software; - if (AccessibilityUtil.isGestureNavigateEnabled(context)) { - resId = AccessibilityUtil.isTouchExploreEnabled(context) - ? R.drawable.accessibility_shortcut_type_software_gesture_talkback - : R.drawable.accessibility_shortcut_type_software_gesture; - } - return resId; + return AccessibilityUtil.isFloatingMenuEnabled(context) + ? R.drawable.accessibility_shortcut_type_software_floating + : R.drawable.accessibility_shortcut_type_software; + } + + private static CharSequence getSummaryStringWithLink(Context context) { + final View.OnClickListener linkListener = v -> new SubSettingLauncher(context) + .setDestination(AccessibilityButtonFragment.class.getName()) + .setSourceMetricsCategory( + SettingsEnums.SWITCH_SHORTCUT_DIALOG_ACCESSIBILITY_BUTTON_SETTINGS) + .launch(); + final AnnotationSpan.LinkInfo linkInfo = new AnnotationSpan.LinkInfo( + AnnotationSpan.LinkInfo.DEFAULT_ANNOTATION, linkListener); + + return AnnotationSpan.linkify(context.getText( + R.string.accessibility_shortcut_edit_dialog_summary_software_floating), linkInfo); } private static SpannableString getSummaryStringWithIcon(Context context, int lineHeight) { diff --git a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java index 482822e4f66..5ea5462c0c8 100644 --- a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java +++ b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java @@ -333,7 +333,8 @@ public final class AccessibilityGestureNavigationTutorial { } private static TutorialPage createSoftwareTutorialPage(@NonNull Context context) { - final CharSequence title = getSoftwareTitle(context); + final CharSequence title = context.getText( + R.string.accessibility_tutorial_dialog_title_button); final ImageView image = createSoftwareImage(context); final CharSequence instruction = getSoftwareInstruction(context); final ImageView indicatorIcon = @@ -390,44 +391,19 @@ public final class AccessibilityGestureNavigationTutorial { return tutorialPages; } - private static CharSequence getSoftwareTitle(Context context) { - final boolean isGestureNavigationEnabled = - AccessibilityUtil.isGestureNavigateEnabled(context); - final int resId = isGestureNavigationEnabled - ? R.string.accessibility_tutorial_dialog_title_gesture - : R.string.accessibility_tutorial_dialog_title_button; - - return context.getText(resId); - } - private static ImageView createSoftwareImage(Context context) { - int resId = R.drawable.accessibility_shortcut_type_software; - if (AccessibilityUtil.isGestureNavigateEnabled(context)) { - resId = AccessibilityUtil.isTouchExploreEnabled(context) - ? R.drawable.accessibility_shortcut_type_software_gesture_talkback - : R.drawable.accessibility_shortcut_type_software_gesture; - } + final int resId = AccessibilityUtil.isFloatingMenuEnabled(context) + ? R.drawable.accessibility_shortcut_type_software_floating + : R.drawable.accessibility_shortcut_type_software; return createImageView(context, resId); } private static CharSequence getSoftwareInstruction(Context context) { - final boolean isGestureNavigateEnabled = - AccessibilityUtil.isGestureNavigateEnabled(context); - final boolean isTouchExploreEnabled = AccessibilityUtil.isTouchExploreEnabled(context); - int resId = R.string.accessibility_tutorial_dialog_message_button; - if (isGestureNavigateEnabled) { - resId = isTouchExploreEnabled - ? R.string.accessibility_tutorial_dialog_message_gesture_talkback - : R.string.accessibility_tutorial_dialog_message_gesture; - } - - CharSequence text = context.getText(resId); - if (resId == R.string.accessibility_tutorial_dialog_message_button) { - text = getSoftwareInstructionWithIcon(context, text); - } - - return text; + return AccessibilityUtil.isFloatingMenuEnabled(context) + ? context.getText(R.string.accessibility_tutorial_dialog_message_floating_button) + : getSoftwareInstructionWithIcon(context, + context.getText(R.string.accessibility_tutorial_dialog_message_button)); } private static CharSequence getSoftwareInstructionWithIcon(Context context, CharSequence text) { diff --git a/src/com/android/settings/accessibility/AccessibilityUtil.java b/src/com/android/settings/accessibility/AccessibilityUtil.java index f5472098f90..5c316a40acd 100644 --- a/src/com/android/settings/accessibility/AccessibilityUtil.java +++ b/src/com/android/settings/accessibility/AccessibilityUtil.java @@ -16,6 +16,7 @@ package com.android.settings.accessibility; +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import android.accessibilityservice.AccessibilityServiceInfo; @@ -143,6 +144,13 @@ final class AccessibilityUtil { == NAV_BAR_MODE_GESTURAL; } + /** Determines if a accessibility floating menu is being used. */ + public static boolean isFloatingMenuEnabled(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.ACCESSIBILITY_BUTTON_MODE, /* def= */ -1) + == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; + } + /** Determines if a touch explore is being used. */ public static boolean isTouchExploreEnabled(Context context) { final AccessibilityManager am = context.getSystemService(AccessibilityManager.class); diff --git a/src/com/android/settings/accessibility/FloatingMenuFadePreferenceController.java b/src/com/android/settings/accessibility/FloatingMenuFadePreferenceController.java new file mode 100644 index 00000000000..dd419d0a6e5 --- /dev/null +++ b/src/com/android/settings/accessibility/FloatingMenuFadePreferenceController.java @@ -0,0 +1,116 @@ +/* + * 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.accessibility; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.Looper; +import android.provider.Settings; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; +import androidx.preference.SwitchPreference; + +import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnPause; +import com.android.settingslib.core.lifecycle.events.OnResume; + +/** Preference controller that controls the fade switch button in accessibility button page. */ +public class FloatingMenuFadePreferenceController extends BasePreferenceController implements + Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause { + + private static final int OFF = 0; + private static final int ON = 1; + + private final ContentResolver mContentResolver; + @VisibleForTesting + final ContentObserver mContentObserver; + + @VisibleForTesting + SwitchPreference mPreference; + + public FloatingMenuFadePreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); + mContentResolver = context.getContentResolver(); + mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange) { + updateAvailabilityStatus(); + } + }; + } + + @Override + public int getAvailabilityStatus() { + return AccessibilityUtil.isFloatingMenuEnabled(mContext) + ? AVAILABLE : DISABLED_DEPENDENT_SETTING; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + + mPreference = screen.findPreference(getPreferenceKey()); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final boolean isEnabled = (boolean) newValue; + putFloatingMenuFadeValue(isEnabled); + return true; + } + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + final SwitchPreference switchPreference = (SwitchPreference) preference; + + switchPreference.setChecked(getFloatingMenuFadeValue() == ON); + } + + @Override + public void onResume() { + mContentResolver.registerContentObserver( + Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_BUTTON_MODE), + /* notifyForDescendants= */ false, mContentObserver); + } + + @Override + public void onPause() { + mContentResolver.unregisterContentObserver(mContentObserver); + } + + private void updateAvailabilityStatus() { + mPreference.setEnabled(AccessibilityUtil.isFloatingMenuEnabled(mContext)); + } + + private int getFloatingMenuFadeValue() { + return Settings.Secure.getInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, ON); + } + + private void putFloatingMenuFadeValue(boolean isEnabled) { + Settings.Secure.putInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, + isEnabled ? ON : OFF); + } +} diff --git a/src/com/android/settings/accessibility/FloatingMenuLayerDrawable.java b/src/com/android/settings/accessibility/FloatingMenuLayerDrawable.java new file mode 100644 index 00000000000..bfce114ae13 --- /dev/null +++ b/src/com/android/settings/accessibility/FloatingMenuLayerDrawable.java @@ -0,0 +1,133 @@ +/* + * 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.accessibility; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; + +import com.android.settings.R; + +import java.util.Objects; + +/** LayerDrawable that contains device icon as background and floating menu icon as foreground. */ +public class FloatingMenuLayerDrawable extends LayerDrawable { + + private FloatingMenuLayerDrawableState mState; + + /** + * Creates a new layer drawable with the list of specified layers. + * + * @param layers a list of drawables to use as layers in this new drawable, + * must be non-null + */ + private FloatingMenuLayerDrawable(@NonNull Drawable[] layers) { + super(layers); + } + + /** + * Create the {@link LayerDrawable} that contains device icon as background and floating menu + * icon with given {@code opacity} value as foreground. + * + * @param context the valid context used to get the icon + * @param resId the resource ID of the floating menu icon + * @param opacity the opacity to apply to the given icon + * @return the drawable that combines the device icon and the floating menu icon + */ + public static FloatingMenuLayerDrawable createLayerDrawable(Context context, int resId, + int opacity) { + final Drawable bg = context.getDrawable(R.drawable.accessibility_button_preview_base); + final FloatingMenuLayerDrawable basicDrawable = new FloatingMenuLayerDrawable( + new Drawable[]{bg, null}); + + basicDrawable.updateLayerDrawable(context, resId, opacity); + return basicDrawable; + } + + /** + * Update the drawable with given {@code resId} drawable and {@code opacity}(alpha) + * value at index 1 layer. + * + * @param context the valid context used to get the icon + * @param resId the resource ID of the floating menu icon + * @param opacity the opacity to apply to the given icon + */ + public void updateLayerDrawable(Context context, int resId, int opacity) { + final Drawable icon = context.getDrawable(resId); + icon.setAlpha(opacity); + this.setDrawable(/* index= */ 1, icon); + this.setConstantState(context, resId, opacity); + } + + @Override + public ConstantState getConstantState() { + return mState; + } + + /** Stores the constant state and data to the given drawable. */ + private void setConstantState(Context context, int resId, int opacity) { + mState = new FloatingMenuLayerDrawableState(context, resId, opacity); + } + + /** {@link ConstantState} to store the data of {@link FloatingMenuLayerDrawable}. */ + @VisibleForTesting + static class FloatingMenuLayerDrawableState extends ConstantState { + + private final Context mContext; + private final int mResId; + private final int mOpacity; + + FloatingMenuLayerDrawableState(Context context, int resId, int opacity) { + mContext = context; + mResId = resId; + mOpacity = opacity; + } + + @NonNull + @Override + public Drawable newDrawable() { + return createLayerDrawable(mContext, mResId, mOpacity); + } + + @Override + public int getChangingConfigurations() { + return 0; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final FloatingMenuLayerDrawableState that = (FloatingMenuLayerDrawableState) o; + return mResId == that.mResId + && mOpacity == that.mOpacity + && Objects.equals(mContext, that.mContext); + } + + @Override + public int hashCode() { + return Objects.hash(mContext, mResId, mOpacity); + } + } +} diff --git a/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceController.java b/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceController.java new file mode 100644 index 00000000000..fea6fb66c72 --- /dev/null +++ b/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceController.java @@ -0,0 +1,150 @@ +/* + * 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.accessibility; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.Looper; +import android.provider.Settings; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.PreferenceScreen; + +import com.android.settings.core.SliderPreferenceController; +import com.android.settings.widget.SeekBarPreference; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnPause; +import com.android.settingslib.core.lifecycle.events.OnResume; + +/** Preference controller that controls the opacity seekbar in accessibility button page. */ +public class FloatingMenuOpacityPreferenceController extends SliderPreferenceController + implements LifecycleObserver, OnResume, OnPause { + + @VisibleForTesting + static final float DEFAULT_OPACITY = 0.55f; + private static final int FADE_ENABLED = 1; + private static final float MIN_PROGRESS = 10f; + private static final float MAX_PROGRESS = 100f; + @VisibleForTesting + static final float PRECISION = 100f; + + private final ContentResolver mContentResolver; + @VisibleForTesting + final ContentObserver mContentObserver; + + @VisibleForTesting + SeekBarPreference mPreference; + + public FloatingMenuOpacityPreferenceController(Context context, + String preferenceKey) { + super(context, preferenceKey); + mContentResolver = context.getContentResolver(); + mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange) { + updateAvailabilityStatus(); + } + }; + } + + @Override + public int getAvailabilityStatus() { + return AccessibilityUtil.isFloatingMenuEnabled(mContext) + ? AVAILABLE : DISABLED_DEPENDENT_SETTING; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + + mPreference = screen.findPreference(getPreferenceKey()); + mPreference.setContinuousUpdates(true); + mPreference.setMax(getMax()); + mPreference.setMin(getMin()); + mPreference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_ENDS); + + updateState(mPreference); + } + + @Override + public void onResume() { + mContentResolver.registerContentObserver( + Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_BUTTON_MODE), /* notifyForDescendants= */ + false, mContentObserver); + mContentResolver.registerContentObserver( + Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED), + /* notifyForDescendants= */ false, mContentObserver); + } + + @Override + public void onPause() { + mContentResolver.unregisterContentObserver(mContentObserver); + } + + @Override + public int getSliderPosition() { + return convertOpacityFloatToInt(getOpacity()); + } + + @Override + public boolean setSliderPosition(int position) { + final float value = convertOpacityIntToFloat(position); + + return Settings.Secure.putFloat(mContentResolver, + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, value); + } + + @Override + public int getMax() { + return (int) MAX_PROGRESS; + } + + @Override + public int getMin() { + return (int) MIN_PROGRESS; + } + + private void updateAvailabilityStatus() { + final boolean fadeEnabled = Settings.Secure.getInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, FADE_ENABLED) + == FADE_ENABLED; + + mPreference.setEnabled(AccessibilityUtil.isFloatingMenuEnabled(mContext) && fadeEnabled); + } + + private int convertOpacityFloatToInt(float value) { + return Math.round(value * PRECISION); + } + + private float convertOpacityIntToFloat(int value) { + return (float) value / PRECISION; + } + + private float getOpacity() { + float value = Settings.Secure.getFloat(mContentResolver, + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, DEFAULT_OPACITY); + final float minValue = MIN_PROGRESS / PRECISION; + final float maxValue = MAX_PROGRESS / PRECISION; + + return (value < minValue || value > maxValue) ? DEFAULT_OPACITY : value; + } +} + diff --git a/src/com/android/settings/accessibility/FloatingMenuSizePreferenceController.java b/src/com/android/settings/accessibility/FloatingMenuSizePreferenceController.java new file mode 100644 index 00000000000..2f0f833772e --- /dev/null +++ b/src/com/android/settings/accessibility/FloatingMenuSizePreferenceController.java @@ -0,0 +1,157 @@ +/* + * 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.accessibility; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.Looper; +import android.provider.Settings; +import android.util.ArrayMap; + +import androidx.annotation.IntDef; +import androidx.annotation.VisibleForTesting; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnPause; +import com.android.settingslib.core.lifecycle.events.OnResume; + +import com.google.common.primitives.Ints; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** Preference controller that controls the preferred size in accessibility button page. */ +public class FloatingMenuSizePreferenceController extends BasePreferenceController + implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause { + + private final ContentResolver mContentResolver; + @VisibleForTesting + final ContentObserver mContentObserver; + + @VisibleForTesting + ListPreference mPreference; + + private final ArrayMap mValueTitleMap = new ArrayMap<>(); + private int mDefaultSize; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + Size.SMALL, + Size.LARGE, + }) + @VisibleForTesting + @interface Size { + int SMALL = 0; + int LARGE = 1; + } + + public FloatingMenuSizePreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); + mContentResolver = context.getContentResolver(); + mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean selfChange) { + updateAvailabilityStatus(); + } + }; + + initValueTitleMap(); + } + + @Override + public int getAvailabilityStatus() { + return AccessibilityUtil.isFloatingMenuEnabled(mContext) + ? AVAILABLE : DISABLED_DEPENDENT_SETTING; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + + mPreference = screen.findPreference(getPreferenceKey()); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final ListPreference listPreference = (ListPreference) preference; + final Integer value = Ints.tryParse((String) newValue); + if (value != null) { + putAccessibilityFloatingMenuSize(value); + updateState(listPreference); + } + return true; + } + + @Override + public void updateState(Preference preference) { + super.updateState(preference); + final ListPreference listPreference = (ListPreference) preference; + + listPreference.setValue(String.valueOf(getAccessibilityFloatingMenuSize(mDefaultSize))); + } + + @Override + public void onResume() { + mContentResolver.registerContentObserver( + Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_BUTTON_MODE), /* notifyForDescendants= */ + false, mContentObserver); + + } + + @Override + public void onPause() { + mContentResolver.unregisterContentObserver(mContentObserver); + } + + private void updateAvailabilityStatus() { + mPreference.setEnabled(AccessibilityUtil.isFloatingMenuEnabled(mContext)); + } + + private void initValueTitleMap() { + if (mValueTitleMap.size() == 0) { + final String[] values = mContext.getResources().getStringArray( + R.array.accessibility_button_size_selector_values); + final String[] titles = mContext.getResources().getStringArray( + R.array.accessibility_button_size_selector_titles); + final int mapSize = values.length; + + mDefaultSize = Integer.parseInt(values[0]); + for (int i = 0; i < mapSize; i++) { + mValueTitleMap.put(values[i], titles[i]); + } + } + } + + @Size + private int getAccessibilityFloatingMenuSize(@Size int defaultValue) { + return Settings.Secure.getInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, defaultValue); + } + + private void putAccessibilityFloatingMenuSize(@Size int value) { + Settings.Secure.putInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, value); + } +} diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java index ec22a28aeef..cf9c08b73fd 100644 --- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java @@ -608,19 +608,15 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(context, mComponentName.flattenToString(), UserShortcutType.SOFTWARE); - int resId = R.string.accessibility_shortcut_edit_summary_software; - if (AccessibilityUtil.isGestureNavigateEnabled(context)) { - resId = AccessibilityUtil.isTouchExploreEnabled(context) - ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback - : R.string.accessibility_shortcut_edit_dialog_title_software_gesture; - } - final CharSequence softwareTitle = context.getText(resId); - List list = new ArrayList<>(); - if ((shortcutTypes & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) { + final List list = new ArrayList<>(); + final CharSequence softwareTitle = context.getText( + R.string.accessibility_shortcut_edit_summary_software); + + if (hasShortcutType(shortcutTypes, UserShortcutType.SOFTWARE)) { list.add(softwareTitle); } - if ((shortcutTypes & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE) { + if (hasShortcutType(shortcutTypes, UserShortcutType.HARDWARE)) { final CharSequence hardwareTitle = context.getText( R.string.accessibility_shortcut_hardware_keyword); list.add(hardwareTitle); diff --git a/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java index f65bd62d786..61459c494f5 100644 --- a/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java @@ -80,6 +80,8 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre }; final View view = super.onCreateView(inflater, container, savedInstanceState); + // Parent sets the title when creating the view, so set it after calling super + mToggleServiceSwitchPreference.setTitle(R.string.reduce_bright_colors_switch_title); updateGeneralCategoryOrder(); return view; } diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java index dde5be17d74..738d284bda0 100644 --- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java @@ -226,27 +226,23 @@ public class ToggleScreenMagnificationPreferenceFragment extends return context.getText(R.string.switch_off_text); } - final int shortcutType = PreferredShortcuts.retrieveUserShortcutType(context, + final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(context, MAGNIFICATION_CONTROLLER_NAME, UserShortcutType.SOFTWARE); - int resId = R.string.accessibility_shortcut_edit_summary_software; - if (AccessibilityUtil.isGestureNavigateEnabled(context)) { - resId = AccessibilityUtil.isTouchExploreEnabled(context) - ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback - : R.string.accessibility_shortcut_edit_dialog_title_software_gesture; - } - final CharSequence softwareTitle = context.getText(resId); - List list = new ArrayList<>(); - if ((shortcutType & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) { + final List list = new ArrayList<>(); + final CharSequence softwareTitle = context.getText( + R.string.accessibility_shortcut_edit_summary_software); + + if (hasShortcutType(shortcutTypes, UserShortcutType.SOFTWARE)) { list.add(softwareTitle); } - if ((shortcutType & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE) { + if (hasShortcutType(shortcutTypes, UserShortcutType.HARDWARE)) { final CharSequence hardwareTitle = context.getText( R.string.accessibility_shortcut_hardware_keyword); list.add(hardwareTitle); } - if ((shortcutType & UserShortcutType.TRIPLETAP) == UserShortcutType.TRIPLETAP) { + if (hasShortcutType(shortcutTypes, UserShortcutType.TRIPLETAP)) { final CharSequence tripleTapTitle = context.getText( R.string.accessibility_shortcut_triple_tap_keyword); list.add(tripleTapTitle); diff --git a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java index 5f1d9d2a52d..6d515a31363 100644 --- a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java @@ -30,14 +30,11 @@ import androidx.loader.content.Loader; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; -import com.android.internal.os.BatterySipper; -import com.android.internal.os.BatteryStatsHelper; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.core.BasePreferenceController; import com.android.settings.fuelgauge.AdvancedPowerUsageDetail; import com.android.settings.fuelgauge.BatteryEntry; -import com.android.settings.fuelgauge.BatteryStatsHelperLoader; import com.android.settings.fuelgauge.BatteryUsageStatsLoader; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settingslib.core.lifecycle.Lifecycle; @@ -52,22 +49,11 @@ public class AppBatteryPreferenceController extends BasePreferenceController private static final String KEY_BATTERY = "battery"; - // TODO(b/180630447): switch to BatteryUsageStatsLoader and remove all references to - // BatteryStatsHelper and BatterySipper - @VisibleForTesting - final BatteryStatsHelperLoaderCallbacks mBatteryStatsHelperLoaderCallbacks = - new BatteryStatsHelperLoaderCallbacks(); @VisibleForTesting final BatteryUsageStatsLoaderCallbacks mBatteryUsageStatsLoaderCallbacks = new BatteryUsageStatsLoaderCallbacks(); - - @VisibleForTesting - BatterySipper mSipper; - @VisibleForTesting - BatteryStatsHelper mBatteryHelper; @VisibleForTesting BatteryUtils mBatteryUtils; - @VisibleForTesting BatteryUsageStats mBatteryUsageStats; @VisibleForTesting @@ -124,9 +110,6 @@ public class AppBatteryPreferenceController extends BasePreferenceController @Override public void onResume() { - mParent.getLoaderManager().restartLoader( - AppInfoDashboardFragment.LOADER_BATTERY, Bundle.EMPTY, - mBatteryStatsHelperLoaderCallbacks); mParent.getLoaderManager().restartLoader( AppInfoDashboardFragment.LOADER_BATTERY_USAGE_STATS, Bundle.EMPTY, mBatteryUsageStatsLoaderCallbacks); @@ -134,20 +117,17 @@ public class AppBatteryPreferenceController extends BasePreferenceController @Override public void onPause() { - mParent.getLoaderManager().destroyLoader(AppInfoDashboardFragment.LOADER_BATTERY); mParent.getLoaderManager().destroyLoader( AppInfoDashboardFragment.LOADER_BATTERY_USAGE_STATS); } private void onLoadFinished() { - // Wait for both loaders to finish before proceeding. - if (mBatteryHelper == null || mBatteryUsageStats == null) { + if (mBatteryUsageStats == null) { return; } final PackageInfo packageInfo = mParent.getPackageInfo(); if (packageInfo != null) { - mSipper = findTargetSipper(mBatteryHelper, packageInfo.applicationInfo.uid); mUidBatteryConsumer = findTargetUidBatteryConsumer(mBatteryUsageStats, packageInfo.applicationInfo.uid); if (mParent.getActivity() != null) { @@ -172,19 +152,7 @@ public class AppBatteryPreferenceController extends BasePreferenceController @VisibleForTesting boolean isBatteryStatsAvailable() { - return mBatteryHelper != null && mSipper != null && mUidBatteryConsumer != null; - } - - @VisibleForTesting - BatterySipper findTargetSipper(BatteryStatsHelper batteryHelper, int uid) { - final List usageList = batteryHelper.getUsageList(); - for (int i = 0, size = usageList.size(); i < size; i++) { - final BatterySipper sipper = usageList.get(i); - if (sipper.getUid() == uid) { - return sipper; - } - } - return null; + return mUidBatteryConsumer != null; } @VisibleForTesting @@ -199,25 +167,6 @@ public class AppBatteryPreferenceController extends BasePreferenceController return null; } - private class BatteryStatsHelperLoaderCallbacks - implements LoaderManager.LoaderCallbacks { - @Override - public Loader onCreateLoader(int id, Bundle args) { - return new BatteryStatsHelperLoader(mContext); - } - - @Override - public void onLoadFinished(Loader loader, - BatteryStatsHelper batteryHelper) { - mBatteryHelper = batteryHelper; - AppBatteryPreferenceController.this.onLoadFinished(); - } - - @Override - public void onLoaderReset(Loader loader) { - } - } - private class BatteryUsageStatsLoaderCallbacks implements LoaderManager.LoaderCallbacks { @Override diff --git a/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java b/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java index 205b6d275ee..23dd9602f77 100644 --- a/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java @@ -17,6 +17,7 @@ package com.android.settings.applications.appinfo; import android.content.Context; +import android.content.pm.PackageInfo; import android.text.BidiFormatter; import com.android.settings.R; @@ -29,7 +30,13 @@ public class AppVersionPreferenceController extends AppInfoPreferenceControllerB @Override public CharSequence getSummary() { + // TODO(b/168333280): Review the null case in detail since this is just a quick + // workaround to fix NPE. + final PackageInfo packageInfo = mParent.getPackageInfo(); + if (packageInfo == null) { + return null; + } return mContext.getString(R.string.version_text, - BidiFormatter.getInstance().unicodeWrap(mParent.getPackageInfo().versionName)); + BidiFormatter.getInstance().unicodeWrap(packageInfo.versionName)); } } diff --git a/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java b/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java index 1a45640c1df..0b0fa27f62c 100644 --- a/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java +++ b/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java @@ -15,10 +15,10 @@ */ package com.android.settings.datetime; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED; +import static android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED; +import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE; +import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED; +import static android.app.time.Capabilities.CAPABILITY_POSSESSED; import android.app.time.TimeManager; import android.app.time.TimeZoneCapabilities; diff --git a/src/com/android/settings/display/TopLevelWallpaperPreferenceController.java b/src/com/android/settings/display/TopLevelWallpaperPreferenceController.java index 5118b277b59..7b3d788d5ef 100644 --- a/src/com/android/settings/display/TopLevelWallpaperPreferenceController.java +++ b/src/com/android/settings/display/TopLevelWallpaperPreferenceController.java @@ -105,7 +105,7 @@ public class TopLevelWallpaperPreferenceController extends BasePreferenceControl final Intent intent = new Intent().setComponent( getComponentName()).putExtra(mWallpaperLaunchExtra, LAUNCHED_SETTINGS); if (areStylesAvailable()) { - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); } preference.getContext().startActivity(intent); return true; diff --git a/src/com/android/settings/fuelgauge/BatteryStatsHelperLoader.java b/src/com/android/settings/fuelgauge/BatteryStatsHelperLoader.java deleted file mode 100644 index 5de83d3f429..00000000000 --- a/src/com/android/settings/fuelgauge/BatteryStatsHelperLoader.java +++ /dev/null @@ -1,57 +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.fuelgauge; - -import android.content.Context; -import android.os.UserManager; - -import androidx.annotation.VisibleForTesting; - -import com.android.internal.os.BatteryStatsHelper; -import com.android.settingslib.utils.AsyncLoaderCompat; - -/** - * Loader to get new {@link BatteryStatsHelper} in the background - */ -public class BatteryStatsHelperLoader extends AsyncLoaderCompat { - @VisibleForTesting - UserManager mUserManager; - @VisibleForTesting - BatteryUtils mBatteryUtils; - - public BatteryStatsHelperLoader(Context context) { - super(context); - mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); - mBatteryUtils = BatteryUtils.getInstance(context); - } - - @Override - public BatteryStatsHelper loadInBackground() { - Context context = getContext(); - final BatteryStatsHelper statsHelper = new BatteryStatsHelper(context, - true /* collectBatteryBroadcast */); - mBatteryUtils.initBatteryStatsHelper(statsHelper, null /* bundle */, mUserManager); - - return statsHelper; - } - - @Override - protected void onDiscardResult(BatteryStatsHelper result) { - - } - -} diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java index d3633b1b92e..661c1ea3c34 100644 --- a/src/com/android/settings/fuelgauge/BatteryUtils.java +++ b/src/com/android/settings/fuelgauge/BatteryUtils.java @@ -42,7 +42,6 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; -import com.android.internal.os.BatterySipper; import com.android.internal.os.BatteryStatsHelper; import com.android.internal.util.ArrayUtils; import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper; @@ -173,22 +172,12 @@ public class BatteryUtils { } /** - * Check whether we should hide the battery sipper. + * Returns true if the specified battery consumer should be excluded from the summary + * battery consumption list. */ - public boolean shouldHideSipper(BatterySipper sipper) { - final BatterySipper.DrainType drainType = sipper.drainType; - - return drainType == BatterySipper.DrainType.IDLE - || drainType == BatterySipper.DrainType.CELL - || drainType == BatterySipper.DrainType.SCREEN - || drainType == BatterySipper.DrainType.UNACCOUNTED - || drainType == BatterySipper.DrainType.OVERCOUNTED - || drainType == BatterySipper.DrainType.BLUETOOTH - || drainType == BatterySipper.DrainType.WIFI - || (sipper.totalPowerMah * SECONDS_IN_HOUR) < MIN_POWER_THRESHOLD_MILLI_AMP - || mPowerUsageFeatureProvider.isTypeService(sipper) - || mPowerUsageFeatureProvider.isTypeSystem(sipper) - || isHiddenSystemModule(sipper); + public boolean shouldHideUidBatteryConsumer(UidBatteryConsumer consumer) { + return shouldHideUidBatteryConsumer(consumer, + mPackageManager.getPackagesForUid(consumer.getUid())); } /** @@ -227,17 +216,6 @@ public class BatteryUtils { } } - /** - * Return {@code true} if one of packages in {@code sipper} is hidden system modules - */ - public boolean isHiddenSystemModule(BatterySipper sipper) { - if (sipper.uidObj == null) { - return false; - } - sipper.mPackages = mPackageManager.getPackagesForUid(sipper.getUid()); - return isHiddenSystemModule(sipper.mPackages); - } - /** * Returns true if one the specified packages belongs to a hidden system module. */ @@ -270,23 +248,6 @@ public class BatteryUtils { return (powerUsageMah / totalPowerMah) * dischargeAmount; } - /** - * Calculate the whole running time in the state {@code statsType} - * - * @param batteryStatsHelper utility class that contains the data - * @param statsType state that we want to calculate the time for - * @return the running time in millis - */ - public long calculateRunningTimeBasedOnStatsType(BatteryStatsHelper batteryStatsHelper, - int statsType) { - final long elapsedRealtimeUs = PowerUtil.convertMsToUs( - SystemClock.elapsedRealtime()); - // Return the battery time (millisecond) on status mStatsType - return PowerUtil.convertUsToMs( - batteryStatsHelper.getStats().computeBatteryRealtime(elapsedRealtimeUs, statsType)); - - } - /** * Find the package name for a {@link android.os.BatteryStats.Uid} * @@ -336,14 +297,13 @@ public class BatteryUtils { /** * Calculate the time since last full charge, including the device off time * - * @param batteryStatsHelper utility class that contains the data + * @param batteryUsageStats class that contains the data * @param currentTimeMs current wall time * @return time in millis */ - public long calculateLastFullChargeTime(BatteryStatsHelper batteryStatsHelper, + public long calculateLastFullChargeTime(BatteryUsageStats batteryUsageStats, long currentTimeMs) { - return currentTimeMs - batteryStatsHelper.getStats().getStartClockTime(); - + return currentTimeMs - batteryUsageStats.getStatsStartRealtime(); } public static void logRuntime(String tag, String message, long startTime) { diff --git a/src/com/android/settings/fuelgauge/PowerUsageBase.java b/src/com/android/settings/fuelgauge/PowerUsageBase.java index 29ecedc2f10..28d77159938 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageBase.java +++ b/src/com/android/settings/fuelgauge/PowerUsageBase.java @@ -29,7 +29,6 @@ import androidx.annotation.VisibleForTesting; import androidx.loader.app.LoaderManager; import androidx.loader.content.Loader; -import com.android.internal.os.BatteryStatsHelper; import com.android.settings.dashboard.DashboardFragment; /** @@ -44,10 +43,8 @@ public abstract class PowerUsageBase extends DashboardFragment { private static final String KEY_REFRESH_TYPE = "refresh_type"; private static final String KEY_INCLUDE_HISTORY = "include_history"; - private static final int LOADER_BATTERY_STATS_HELPER = 0; private static final int LOADER_BATTERY_USAGE_STATS = 1; - protected BatteryStatsHelper mStatsHelper; @VisibleForTesting BatteryUsageStats mBatteryUsageStats; @@ -55,12 +52,6 @@ public abstract class PowerUsageBase extends DashboardFragment { private BatteryBroadcastReceiver mBatteryBroadcastReceiver; protected boolean mIsBatteryPresent = true; - // TODO(b/180630447): switch to BatteryUsageStatsLoader and remove all references to - // BatteryStatsHelper and BatterySipper - @VisibleForTesting - final BatteryStatsHelperLoaderCallbacks mBatteryStatsHelperLoaderCallbacks = - new BatteryStatsHelperLoaderCallbacks(); - @VisibleForTesting final BatteryUsageStatsLoaderCallbacks mBatteryUsageStatsLoaderCallbacks = new BatteryUsageStatsLoaderCallbacks(); @@ -69,13 +60,11 @@ public abstract class PowerUsageBase extends DashboardFragment { public void onAttach(Activity activity) { super.onAttach(activity); mUm = (UserManager) activity.getSystemService(Context.USER_SERVICE); - mStatsHelper = new BatteryStatsHelper(activity, true); } @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - mStatsHelper.create(icicle); setHasOptionsMenu(true); mBatteryBroadcastReceiver = new BatteryBroadcastReceiver(getContext()); @@ -103,18 +92,11 @@ public abstract class PowerUsageBase extends DashboardFragment { final Bundle bundle = new Bundle(); bundle.putInt(KEY_REFRESH_TYPE, refreshType); bundle.putBoolean(KEY_INCLUDE_HISTORY, isBatteryHistoryNeeded()); - getLoaderManager().restartLoader(LOADER_BATTERY_STATS_HELPER, bundle, - mBatteryStatsHelperLoaderCallbacks); getLoaderManager().restartLoader(LOADER_BATTERY_USAGE_STATS, bundle, mBatteryUsageStatsLoaderCallbacks); } private void onLoadFinished(@BatteryUpdateType int refreshType) { - // Wait for both loaders to finish before proceeding. - if (mStatsHelper == null || mBatteryUsageStats == null) { - return; - } - refreshUi(refreshType); } @@ -127,28 +109,6 @@ public abstract class PowerUsageBase extends DashboardFragment { BatteryUtils.logRuntime(TAG, "updatePreference", startTime); } - private class BatteryStatsHelperLoaderCallbacks - implements LoaderManager.LoaderCallbacks { - private int mRefreshType; - - @Override - public Loader onCreateLoader(int id, Bundle args) { - mRefreshType = args.getInt(KEY_REFRESH_TYPE); - return new BatteryStatsHelperLoader(getContext()); - } - - @Override - public void onLoadFinished(Loader loader, - BatteryStatsHelper batteryHelper) { - mStatsHelper = batteryHelper; - PowerUsageBase.this.onLoadFinished(mRefreshType); - } - - @Override - public void onLoaderReset(Loader loader) { - } - } - private class BatteryUsageStatsLoaderCallbacks implements LoaderManager.LoaderCallbacks { private int mRefreshType; diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java index 4f8ac628092..9e619973122 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java +++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java @@ -108,7 +108,7 @@ public class PowerUsageSummary extends PowerUsageBase implements @Override public Loader> onCreateLoader(int id, Bundle args) { - return new BatteryTipLoader(getContext(), mStatsHelper); + return new BatteryTipLoader(getContext(), mBatteryUsageStats); } @Override diff --git a/src/com/android/settings/fuelgauge/RequestIgnoreBatteryOptimizations.java b/src/com/android/settings/fuelgauge/RequestIgnoreBatteryOptimizations.java index c06a4ffba1f..f75fccc0efb 100644 --- a/src/com/android/settings/fuelgauge/RequestIgnoreBatteryOptimizations.java +++ b/src/com/android/settings/fuelgauge/RequestIgnoreBatteryOptimizations.java @@ -93,6 +93,13 @@ public class RequestIgnoreBatteryOptimizations extends AlertActivity implements setupAlert(); } + @Override + protected void onStart() { + super.onStart(); + getWindow().addSystemFlags(android.view.WindowManager.LayoutParams + .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); + } + @Override public void onClick(DialogInterface dialog, int which) { switch (which) { diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java index 9b80a1f4029..433c06d0220 100644 --- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java +++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java @@ -17,10 +17,10 @@ package com.android.settings.fuelgauge.batterytip; import android.content.Context; +import android.os.BatteryUsageStats; import androidx.annotation.VisibleForTesting; -import com.android.internal.os.BatteryStatsHelper; import com.android.settings.fuelgauge.BatteryInfo; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.batterytip.detectors.BatteryDefenderDetector; @@ -48,13 +48,13 @@ public class BatteryTipLoader extends AsyncLoaderCompat> { private static final boolean USE_FAKE_DATA = false; - private BatteryStatsHelper mBatteryStatsHelper; + private BatteryUsageStats mBatteryUsageStats; @VisibleForTesting BatteryUtils mBatteryUtils; - public BatteryTipLoader(Context context, BatteryStatsHelper batteryStatsHelper) { + public BatteryTipLoader(Context context, BatteryUsageStats batteryUsageStats) { super(context); - mBatteryStatsHelper = batteryStatsHelper; + mBatteryUsageStats = batteryUsageStats; mBatteryUtils = BatteryUtils.getInstance(context); } @@ -69,7 +69,7 @@ public class BatteryTipLoader extends AsyncLoaderCompat> { final Context context = getContext(); tips.add(new LowBatteryDetector(context, policy, batteryInfo).detect()); - tips.add(new HighUsageDetector(context, policy, mBatteryStatsHelper, batteryInfo).detect()); + tips.add(new HighUsageDetector(context, policy, mBatteryUsageStats, batteryInfo).detect()); tips.add(new SmartBatteryDetector(policy, context.getContentResolver()).detect()); tips.add(new EarlyWarningDetector(policy, context).detect()); tips.add(new BatteryDefenderDetector(batteryInfo).detect()); diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java index 6c102fb5d4d..4b3f2df3a7f 100644 --- a/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java +++ b/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java @@ -19,12 +19,11 @@ package com.android.settings.fuelgauge.batterytip.detectors; import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME; import android.content.Context; -import android.os.BatteryStats; +import android.os.BatteryUsageStats; +import android.os.UidBatteryConsumer; import androidx.annotation.VisibleForTesting; -import com.android.internal.os.BatterySipper; -import com.android.internal.os.BatteryStatsHelper; import com.android.settings.fuelgauge.BatteryInfo; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.batterytip.AppInfo; @@ -34,7 +33,6 @@ import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -44,7 +42,7 @@ import java.util.concurrent.TimeUnit; */ public class HighUsageDetector implements BatteryTipDetector { private BatteryTipPolicy mPolicy; - private BatteryStatsHelper mBatteryStatsHelper; + private BatteryUsageStats mBatteryUsageStats; private final BatteryInfo mBatteryInfo; private List mHighUsageAppList; @VisibleForTesting @@ -55,9 +53,9 @@ public class HighUsageDetector implements BatteryTipDetector { boolean mDischarging; public HighUsageDetector(Context context, BatteryTipPolicy policy, - BatteryStatsHelper batteryStatsHelper, BatteryInfo batteryInfo) { + BatteryUsageStats batteryUsageStats, BatteryInfo batteryInfo) { mPolicy = policy; - mBatteryStatsHelper = batteryStatsHelper; + mBatteryUsageStats = batteryUsageStats; mBatteryInfo = batteryInfo; mHighUsageAppList = new ArrayList<>(); mBatteryUtils = BatteryUtils.getInstance(context); @@ -69,37 +67,35 @@ public class HighUsageDetector implements BatteryTipDetector { @Override public BatteryTip detect() { final long lastFullChargeTimeMs = mBatteryUtils.calculateLastFullChargeTime( - mBatteryStatsHelper, System.currentTimeMillis()); + mBatteryUsageStats, System.currentTimeMillis()); if (mPolicy.highUsageEnabled && mDischarging) { parseBatteryData(); if (mDataParser.isDeviceHeavilyUsed() || mPolicy.testHighUsageTip) { - final BatteryStats batteryStats = mBatteryStatsHelper.getStats(); - final List batterySippers - = new ArrayList<>(mBatteryStatsHelper.getUsageList()); - final double totalPower = mBatteryStatsHelper.getTotalPower(); - final int dischargeAmount = batteryStats != null - ? batteryStats.getDischargeAmount(BatteryStats.STATS_SINCE_CHARGED) - : 0; - - Collections.sort(batterySippers, - (sipper1, sipper2) -> Double.compare(sipper2.totalSmearedPowerMah, - sipper1.totalSmearedPowerMah)); - for (BatterySipper batterySipper : batterySippers) { + final double totalPower = mBatteryUsageStats.getConsumedPower(); + final int dischargeAmount = mBatteryUsageStats.getDischargePercentage(); + final List uidBatteryConsumers = + mBatteryUsageStats.getUidBatteryConsumers(); + // Sort by descending power + uidBatteryConsumers.sort( + (consumer1, consumer2) -> Double.compare(consumer2.getConsumedPower(), + consumer1.getConsumedPower())); + for (UidBatteryConsumer consumer : uidBatteryConsumers) { final double percent = mBatteryUtils.calculateBatteryPercent( - batterySipper.totalSmearedPowerMah, totalPower, dischargeAmount); - if ((percent + 0.5f < 1f) || mBatteryUtils.shouldHideSipper(batterySipper)) { + consumer.getConsumedPower(), totalPower, dischargeAmount); + if ((percent + 0.5f < 1f) + || mBatteryUtils.shouldHideUidBatteryConsumer(consumer)) { // Don't show it if we should hide or usage percentage is lower than 1% continue; } + mHighUsageAppList.add(new AppInfo.Builder() - .setUid(batterySipper.getUid()) + .setUid(consumer.getUid()) .setPackageName( - mBatteryUtils.getPackageName(batterySipper.getUid())) + mBatteryUtils.getPackageName(consumer.getUid())) .build()); if (mHighUsageAppList.size() >= mPolicy.highUsageAppCount) { break; } - } // When in test mode, add an app if necessary diff --git a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java index 14fe6a664be..388d87ae9a9 100644 --- a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java +++ b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java @@ -30,15 +30,12 @@ import android.content.om.OverlayInfo; import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; -import android.text.TextUtils; import android.util.FeatureFlagUtils; -import android.view.accessibility.AccessibilityManager; import androidx.annotation.VisibleForTesting; import androidx.preference.PreferenceScreen; import com.android.settings.R; -import com.android.settings.SettingsTutorialDialogWrapperActivity; import com.android.settings.core.FeatureFlags; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; import com.android.settings.overlay.FeatureFactory; @@ -188,12 +185,7 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment i protected boolean setDefaultKey(String key) { setCurrentSystemNavigationMode(mOverlayManager, key); setIllustrationVideo(mVideoPreference, key); - if (TextUtils.equals(KEY_SYSTEM_NAV_GESTURAL, key) && ( - isAnyServiceSupportAccessibilityButton() || isNavBarMagnificationEnabled())) { - Intent intent = new Intent(getActivity(), SettingsTutorialDialogWrapperActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - } + return true; } @@ -267,18 +259,6 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment i } } - private boolean isAnyServiceSupportAccessibilityButton() { - final AccessibilityManager ams = getContext().getSystemService(AccessibilityManager.class); - final List targets = ams.getAccessibilityShortcutTargets( - AccessibilityManager.ACCESSIBILITY_BUTTON); - return !targets.isEmpty(); - } - - private boolean isNavBarMagnificationEnabled() { - return Settings.Secure.getInt(getContext().getContentResolver(), - Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1; - } - public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider(R.xml.system_navigation_gesture_settings) { diff --git a/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java b/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java index 6a5b3000bc8..94d3f69ed2f 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java +++ b/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java @@ -29,6 +29,7 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.BatteryUsageStats; import android.util.ArrayMap; import android.view.View; @@ -40,11 +41,10 @@ import androidx.slice.builders.ListBuilder; import androidx.slice.builders.ListBuilder.RowBuilder; import androidx.slice.builders.SliceAction; -import com.android.internal.os.BatteryStatsHelper; import com.android.settings.R; import com.android.settings.SubSettings; import com.android.settings.Utils; -import com.android.settings.fuelgauge.BatteryStatsHelperLoader; +import com.android.settings.fuelgauge.BatteryUsageStatsLoader; import com.android.settings.fuelgauge.PowerUsageSummary; import com.android.settings.fuelgauge.batterytip.BatteryTipLoader; import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController; @@ -206,9 +206,10 @@ public class BatteryFixSlice implements CustomSliceable { @WorkerThread @VisibleForTesting static List refreshBatteryTips(Context context) { - final BatteryStatsHelperLoader statsLoader = new BatteryStatsHelperLoader(context); - final BatteryStatsHelper statsHelper = statsLoader.loadInBackground(); - final BatteryTipLoader loader = new BatteryTipLoader(context, statsHelper); + final BatteryUsageStatsLoader statsLoader = new BatteryUsageStatsLoader(context, + /* includeBatteryHistory */ false); + final BatteryUsageStats batteryUsageStats = statsLoader.loadInBackground(); + final BatteryTipLoader loader = new BatteryTipLoader(context, batteryUsageStats); final List batteryTips = loader.loadInBackground(); for (BatteryTip batteryTip : batteryTips) { if (batteryTip.getState() != BatteryTip.StateType.INVISIBLE) { diff --git a/src/com/android/settings/network/ProviderModelSlice.java b/src/com/android/settings/network/ProviderModelSlice.java index a1fdb1cebf4..18765a8e2fe 100644 --- a/src/com/android/settings/network/ProviderModelSlice.java +++ b/src/com/android/settings/network/ProviderModelSlice.java @@ -147,31 +147,6 @@ public class ProviderModelSlice extends WifiSlice { listBuilder.addRow(getWifiSliceItemRow(item)); } } - - // Fifth section: If device has connection problem, this row show the message for user. - // 1) show non_carrier_network_unavailable: - // - while no wifi item - // 2) show all_network_unavailable: - // - while no wifi item + no carrier - // - while no wifi item + no data capability - if (worker == null || wifiList == null || wifiList.size() == 0) { - log("no wifi item"); - int resId = R.string.non_carrier_network_unavailable; - if (!hasCarrier || !mHelper.isDataSimActive()) { - log("No carrier item or no carrier data."); - resId = R.string.all_network_unavailable; - } - - if (!hasCarrier && !hasEthernet) { - // If there is no item in ProviderModelItem, slice needs a header. - listBuilder.setHeader(mHelper.createHeader( - NetworkProviderSettings.ACTION_NETWORK_PROVIDER_SETTINGS)); - } - listBuilder.addGridRow( - mHelper.createMessageGridRow(resId, - NetworkProviderSettings.ACTION_NETWORK_PROVIDER_SETTINGS)); - } - return listBuilder.build(); } diff --git a/src/com/android/settings/network/ProviderModelSliceHelper.java b/src/com/android/settings/network/ProviderModelSliceHelper.java index 8ae4197c9fa..440d425fe26 100644 --- a/src/com/android/settings/network/ProviderModelSliceHelper.java +++ b/src/com/android/settings/network/ProviderModelSliceHelper.java @@ -36,7 +36,6 @@ import android.util.Log; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.graphics.drawable.IconCompat; -import androidx.slice.builders.GridRowBuilder; import androidx.slice.builders.ListBuilder; import androidx.slice.builders.SliceAction; @@ -79,12 +78,6 @@ public class ProviderModelSliceHelper { Log.d(TAG, s); } - protected ListBuilder.HeaderBuilder createHeader(String intentAction) { - return new ListBuilder.HeaderBuilder() - .setTitle(mContext.getText(R.string.summary_placeholder)) - .setPrimaryAction(getPrimarySliceAction(intentAction)); - } - protected ListBuilder createListBuilder(Uri uri) { final ListBuilder builder = new ListBuilder(mContext, uri, ListBuilder.INFINITY) .setAccentColor(-1) @@ -92,14 +85,6 @@ public class ProviderModelSliceHelper { return builder; } - protected GridRowBuilder createMessageGridRow(int messageResId, String intentAction) { - final CharSequence title = mContext.getText(messageResId); - return new GridRowBuilder() - // Add cells to the grid row. - .addCell(new GridRowBuilder.CellBuilder().addTitleText(title)) - .setPrimaryAction(getPrimarySliceAction(intentAction)); - } - @Nullable protected WifiSliceItem getConnectedWifiItem(List wifiList) { if (wifiList == null) { @@ -111,7 +96,10 @@ public class ProviderModelSliceHelper { return item.isPresent() ? item.get() : null; } - protected boolean hasCarrier() { + /** + * @return whether there is the carrier item in the slice. + */ + public boolean hasCarrier() { if (isAirplaneModeEnabled() || mSubscriptionManager == null || mTelephonyManager == null || mSubscriptionManager.getDefaultDataSubscriptionId() @@ -175,7 +163,12 @@ public class ProviderModelSliceHelper { return mTelephonyManager.isDataEnabled(); } - protected boolean isDataSimActive() { + /** + * To check the carrier data status. + * + * @return whether the carrier data is active. + */ + public boolean isDataSimActive() { return isNoCarrierData() ? false : MobileNetworkUtils.activeNetworkIsCellular(mContext); } @@ -193,11 +186,6 @@ public class ProviderModelSliceHelper { return mobileDataOnAndNoData || mobileDataOffAndOutOfService; } - private boolean isAirplaneSafeNetworksModeEnabled() { - // TODO: isAirplaneSafeNetworksModeEnabled is not READY - return false; - } - @VisibleForTesting Drawable getMobileDrawable(Drawable drawable) throws Throwable { // set color and drawable diff --git a/src/com/android/settings/notification/zen/ZenModeButtonPreferenceController.java b/src/com/android/settings/notification/zen/ZenModeButtonPreferenceController.java index a4318835786..8bfcd13ed11 100644 --- a/src/com/android/settings/notification/zen/ZenModeButtonPreferenceController.java +++ b/src/com/android/settings/notification/zen/ZenModeButtonPreferenceController.java @@ -25,6 +25,7 @@ import androidx.fragment.app.FragmentManager; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; +import com.android.settings.R; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.notification.SettingsEnableZenModeDialog; @@ -90,9 +91,11 @@ public class ZenModeButtonPreferenceController extends AbstractZenModePreference case Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS: mPreference.updateStatus(true); + mPreference.setTitle(R.string.do_not_disturb_main_switch_title_on); break; case Settings.Global.ZEN_MODE_OFF: default: + mPreference.setTitle(R.string.do_not_disturb_main_switch_title_off); mPreference.updateStatus(false); } } diff --git a/src/com/android/settings/panel/InternetConnectivityPanel.java b/src/com/android/settings/panel/InternetConnectivityPanel.java index 4fda0a4f0c3..238cbb4ed97 100644 --- a/src/com/android/settings/panel/InternetConnectivityPanel.java +++ b/src/com/android/settings/panel/InternetConnectivityPanel.java @@ -22,10 +22,23 @@ import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; import static com.android.settings.network.NetworkProviderSettings.ACTION_NETWORK_PROVIDER_SETTINGS; import android.app.settings.SettingsEnums; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.net.Uri; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiManager; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.Looper; import android.provider.Settings; +import android.telephony.ServiceState; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyCallback; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; import androidx.annotation.VisibleForTesting; import androidx.lifecycle.LifecycleObserver; @@ -35,6 +48,9 @@ import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.network.AirplaneModePreferenceController; import com.android.settings.network.InternetUpdater; +import com.android.settings.network.ProviderModelSliceHelper; +import com.android.settings.network.SubscriptionsChangeListener; +import com.android.settings.network.telephony.DataConnectivityListener; import com.android.settings.slices.CustomSliceRegistry; import java.util.ArrayList; @@ -44,23 +60,69 @@ import java.util.List; * Represents the Internet Connectivity Panel. */ public class InternetConnectivityPanel implements PanelContent, LifecycleObserver, - InternetUpdater.InternetChangeListener { + InternetUpdater.InternetChangeListener, DataConnectivityListener.Client, + SubscriptionsChangeListener.SubscriptionsChangeListenerClient { + private static final String TAG = "InternetConnectivityPanel"; + private static final int SUBTITLE_TEXT_NONE = -1; + private static final int SUBTITLE_TEXT_WIFI_IS_TURNED_ON = R.string.wifi_is_turned_on_subtitle; + private static final int SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE = + R.string.non_carrier_network_unavailable; + private static final int SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE = + R.string.all_network_unavailable; private final Context mContext; + private final WifiManager mWifiManager; + private final IntentFilter mWifiStateFilter; + private final NetworkProviderTelephonyCallback mTelephonyCallback; + private final BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent == null) { + return; + } + if (TextUtils.equals(intent.getAction(), WifiManager.NETWORK_STATE_CHANGED_ACTION) + || TextUtils.equals(intent.getAction(), + WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { + updatePanelTitle(); + } + } + }; + @VisibleForTesting boolean mIsProviderModelEnabled; - private PanelContentCallback mCallback; @VisibleForTesting InternetUpdater mInternetUpdater; + @VisibleForTesting + ProviderModelSliceHelper mProviderModelSliceHelper; - public static InternetConnectivityPanel create(Context context) { - return new InternetConnectivityPanel(context); - } + private int mSubtitle = SUBTITLE_TEXT_NONE; + private PanelContentCallback mCallback; + private TelephonyManager mTelephonyManager; + private SubscriptionsChangeListener mSubscriptionsListener; + private DataConnectivityListener mConnectivityListener; + private int mDefaultDataSubid = SubscriptionManager.INVALID_SUBSCRIPTION_ID; private InternetConnectivityPanel(Context context) { mContext = context.getApplicationContext(); mIsProviderModelEnabled = Utils.isProviderModelEnabled(mContext); mInternetUpdater = new InternetUpdater(context, null /* Lifecycle */, this); + + mSubscriptionsListener = new SubscriptionsChangeListener(context, this); + mConnectivityListener = new DataConnectivityListener(context, this); + mTelephonyCallback = new NetworkProviderTelephonyCallback(); + mDefaultDataSubid = getDefaultDataSubscriptionId(); + mTelephonyManager = mContext.getSystemService(TelephonyManager.class); + + mWifiManager = mContext.getSystemService(WifiManager.class); + mWifiStateFilter = new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION); + mWifiStateFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); + + mProviderModelSliceHelper = new ProviderModelSliceHelper(mContext, null); + } + + /** create the panel */ + public static InternetConnectivityPanel create(Context context) { + return new InternetConnectivityPanel(context); } /** @OnLifecycleEvent(ON_RESUME) */ @@ -70,6 +132,12 @@ public class InternetConnectivityPanel implements PanelContent, LifecycleObserve return; } mInternetUpdater.onResume(); + mSubscriptionsListener.start(); + mConnectivityListener.start(); + mTelephonyManager.registerTelephonyCallback( + new HandlerExecutor(new Handler(Looper.getMainLooper())), mTelephonyCallback); + mContext.registerReceiver(mWifiStateReceiver, mWifiStateFilter); + updatePanelTitle(); } /** @OnLifecycleEvent(ON_PAUSE) */ @@ -79,6 +147,10 @@ public class InternetConnectivityPanel implements PanelContent, LifecycleObserve return; } mInternetUpdater.onPause(); + mSubscriptionsListener.stop(); + mConnectivityListener.stop(); + mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback); + mContext.unregisterReceiver(mWifiStateReceiver); } /** @@ -98,9 +170,8 @@ public class InternetConnectivityPanel implements PanelContent, LifecycleObserve */ @Override public CharSequence getSubTitle() { - if (mIsProviderModelEnabled && mInternetUpdater.isAirplaneModeOn() - && mInternetUpdater.isWifiEnabled()) { - return mContext.getText(R.string.wifi_is_turned_on_subtitle); + if (mIsProviderModelEnabled && mSubtitle != SUBTITLE_TEXT_NONE) { + return mContext.getText(mSubtitle); } return null; } @@ -170,15 +241,36 @@ public class InternetConnectivityPanel implements PanelContent, LifecycleObserve updatePanelTitle(); } - private void updatePanelTitle() { + @Override + public void onSubscriptionsChanged() { + final int defaultDataSubId = getDefaultDataSubscriptionId(); + log("onSubscriptionsChanged: defaultDataSubId:" + defaultDataSubId); + if (mDefaultDataSubid == defaultDataSubId) { + return; + } + if (SubscriptionManager.isUsableSubscriptionId(defaultDataSubId)) { + mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback); + mTelephonyManager.registerTelephonyCallback( + new HandlerExecutor(new Handler(Looper.getMainLooper())), mTelephonyCallback); + } + updatePanelTitle(); + } + + @Override + public void onDataConnectivityChange() { + log("onDataConnectivityChange"); + updatePanelTitle(); + } + + @VisibleForTesting + void updatePanelTitle() { if (mCallback == null) { return; } + updateSubtitleText(); - if (mInternetUpdater.isAirplaneModeOn() && mInternetUpdater.isWifiEnabled()) { - // When the airplane mode is on and Wi-Fi is enabled. - // Title: Airplane mode - // Sub-Title: Wi-Fi is turned on + log("Subtitle:" + mSubtitle); + if (mSubtitle != SUBTITLE_TEXT_NONE) { mCallback.onHeaderChanged(); } else { // Other situations. @@ -187,4 +279,63 @@ public class InternetConnectivityPanel implements PanelContent, LifecycleObserve } mCallback.onCustomizedButtonStateChanged(); } + + @VisibleForTesting + int getDefaultDataSubscriptionId() { + return SubscriptionManager.getDefaultDataSubscriptionId(); + } + + private void updateSubtitleText() { + mSubtitle = SUBTITLE_TEXT_NONE; + if (!mInternetUpdater.isWifiEnabled()) { + return; + } + + if (mInternetUpdater.isAirplaneModeOn()) { + // When the airplane mode is on and Wi-Fi is enabled. + // Title: Airplane mode + // Sub-Title: Wi-Fi is turned on + log("Airplane mode is on + Wi-Fi on."); + mSubtitle = SUBTITLE_TEXT_WIFI_IS_TURNED_ON; + return; + } + + final List wifiList = mWifiManager.getScanResults(); + if (wifiList != null && wifiList.size() == 0) { + // Sub-Title: + // show non_carrier_network_unavailable + // - while Wi-Fi on + no Wi-Fi item + // show all_network_unavailable: + // - while Wi-Fi on + no Wi-Fi item + no carrier + // - while Wi-Fi on + no Wi-Fi item + no data capability + log("No Wi-Fi item."); + mSubtitle = SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE; + if (!mProviderModelSliceHelper.hasCarrier() + || !mProviderModelSliceHelper.isDataSimActive()) { + log("No carrier item or no carrier data."); + mSubtitle = SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE; + } + } + } + + private class NetworkProviderTelephonyCallback extends TelephonyCallback implements + TelephonyCallback.DataConnectionStateListener, + TelephonyCallback.ServiceStateListener { + @Override + public void onServiceStateChanged(ServiceState state) { + log("onServiceStateChanged voiceState=" + state.getState() + + " dataState=" + state.getDataRegistrationState()); + updatePanelTitle(); + } + + @Override + public void onDataConnectionStateChanged(int state, int networkType) { + log("onDataConnectionStateChanged: networkType=" + networkType + " state=" + state); + updatePanelTitle(); + } + } + + private static void log(String s) { + Log.d(TAG, s); + } } diff --git a/src/com/android/settings/vpn2/ConfigDialog.java b/src/com/android/settings/vpn2/ConfigDialog.java index 42bc67dc15a..03e3613b9eb 100644 --- a/src/com/android/settings/vpn2/ConfigDialog.java +++ b/src/com/android/settings/vpn2/ConfigDialog.java @@ -592,7 +592,7 @@ class ConfigDialog extends AlertDialog implements TextWatcher, // 0 is a last resort default, but the interface validates that the proxy port is // present and non-zero. int port = proxyPort.isEmpty() ? 0 : Integer.parseInt(proxyPort); - profile.proxy = new ProxyInfo(proxyHost, port, null); + profile.proxy = ProxyInfo.buildDirectProxy(proxyHost, port); } else { profile.proxy = null; } diff --git a/src/com/android/settings/widget/LabeledContinuousSeekBarPreference.java b/src/com/android/settings/widget/LabeledContinuousSeekBarPreference.java deleted file mode 100644 index e269818d9f5..00000000000 --- a/src/com/android/settings/widget/LabeledContinuousSeekBarPreference.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.widget; - -import android.content.Context; -import android.util.AttributeSet; - -import com.android.settings.R; - -/** A continuous labeled slider preference */ -public class LabeledContinuousSeekBarPreference extends LabeledSeekBarPreference { - public LabeledContinuousSeekBarPreference(Context context) { - this(context, null); - } - - public LabeledContinuousSeekBarPreference(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public LabeledContinuousSeekBarPreference(Context context, AttributeSet attrs, - int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public LabeledContinuousSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - setLayoutResource(R.layout.preference_labeled_continuous_slider); - } -} diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java index e3ca5755fea..db355f47c8e 100644 --- a/src/com/android/settings/wifi/WifiConfigController.java +++ b/src/com/android/settings/wifi/WifiConfigController.java @@ -79,6 +79,7 @@ import com.android.settingslib.wifi.AccessPoint; import java.net.Inet4Address; import java.net.InetAddress; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; @@ -319,9 +320,9 @@ public class WifiConfigController implements TextWatcher, // Display IP address. StaticIpConfiguration staticConfig = config.getIpConfiguration() .getStaticIpConfiguration(); - if (staticConfig != null && staticConfig.ipAddress != null) { + if (staticConfig != null && staticConfig.getIpAddress() != null) { addRow(group, R.string.wifi_ip_address, - staticConfig.ipAddress.getAddress().getHostAddress()); + staticConfig.getIpAddress().getAddress().getHostAddress()); } } else { mIpSettingsSpinner.setSelection(DHCP); @@ -860,7 +861,8 @@ public class WifiConfigController implements TextWatcher, result = R.string.proxy_error_invalid_port; } if (result == 0) { - mHttpProxy = new ProxyInfo(host, port, exclusionList); + mHttpProxy = ProxyInfo.buildDirectProxy( + host, port, Arrays.asList(exclusionList.split(","))); } else { return false; } @@ -874,7 +876,7 @@ public class WifiConfigController implements TextWatcher, if (uri == null) { return false; } - mHttpProxy = new ProxyInfo(uri); + mHttpProxy = ProxyInfo.buildPacProxy(uri); } return true; } @@ -897,67 +899,81 @@ public class WifiConfigController implements TextWatcher, if (inetAddr == null || inetAddr.equals(Inet4Address.ANY)) { return R.string.wifi_ip_settings_invalid_ip_address; } - - int networkPrefixLength = -1; + // Copy all fields into the builder first and set desired value later with builder. + final StaticIpConfiguration.Builder staticIPBuilder = new StaticIpConfiguration.Builder() + .setDnsServers(staticIpConfiguration.getDnsServers()) + .setDomains(staticIpConfiguration.getDomains()) + .setGateway(staticIpConfiguration.getGateway()) + .setIpAddress(staticIpConfiguration.getIpAddress()); try { - networkPrefixLength = Integer.parseInt(mNetworkPrefixLengthView.getText().toString()); - if (networkPrefixLength < 0 || networkPrefixLength > 32) { - return R.string.wifi_ip_settings_invalid_network_prefix_length; - } - staticIpConfiguration.ipAddress = new LinkAddress(inetAddr, networkPrefixLength); - } catch (NumberFormatException e) { - // Set the hint as default after user types in ip address - mNetworkPrefixLengthView.setText(mConfigUi.getContext().getString( - R.string.wifi_network_prefix_length_hint)); - } catch (IllegalArgumentException e) { - return R.string.wifi_ip_settings_invalid_ip_address; - } - - String gateway = mGatewayView.getText().toString(); - if (TextUtils.isEmpty(gateway)) { + int networkPrefixLength = -1; try { - //Extract a default gateway from IP address - InetAddress netPart = NetUtils.getNetworkPart(inetAddr, networkPrefixLength); - byte[] addr = netPart.getAddress(); - addr[addr.length - 1] = 1; - mGatewayView.setText(InetAddress.getByAddress(addr).getHostAddress()); - } catch (RuntimeException ee) { - } catch (java.net.UnknownHostException u) { + networkPrefixLength = Integer.parseInt( + mNetworkPrefixLengthView.getText().toString()); + if (networkPrefixLength < 0 || networkPrefixLength > 32) { + return R.string.wifi_ip_settings_invalid_network_prefix_length; + } + staticIPBuilder.setIpAddress(new LinkAddress(inetAddr, networkPrefixLength)); + } catch (NumberFormatException e) { + // Set the hint as default after user types in ip address + mNetworkPrefixLengthView.setText(mConfigUi.getContext().getString( + R.string.wifi_network_prefix_length_hint)); + } catch (IllegalArgumentException e) { + return R.string.wifi_ip_settings_invalid_ip_address; } - } else { - InetAddress gatewayAddr = getIPv4Address(gateway); - if (gatewayAddr == null) { - return R.string.wifi_ip_settings_invalid_gateway; - } - if (gatewayAddr.isMulticastAddress()) { - return R.string.wifi_ip_settings_invalid_gateway; - } - staticIpConfiguration.gateway = gatewayAddr; - } - String dns = mDns1View.getText().toString(); - InetAddress dnsAddr = null; - - if (TextUtils.isEmpty(dns)) { - //If everything else is valid, provide hint as a default option - mDns1View.setText(mConfigUi.getContext().getString(R.string.wifi_dns1_hint)); - } else { - dnsAddr = getIPv4Address(dns); - if (dnsAddr == null) { - return R.string.wifi_ip_settings_invalid_dns; + String gateway = mGatewayView.getText().toString(); + if (TextUtils.isEmpty(gateway)) { + try { + //Extract a default gateway from IP address + InetAddress netPart = NetUtils.getNetworkPart(inetAddr, networkPrefixLength); + byte[] addr = netPart.getAddress(); + addr[addr.length - 1] = 1; + mGatewayView.setText(InetAddress.getByAddress(addr).getHostAddress()); + } catch (RuntimeException ee) { + } catch (java.net.UnknownHostException u) { + } + } else { + InetAddress gatewayAddr = getIPv4Address(gateway); + if (gatewayAddr == null) { + return R.string.wifi_ip_settings_invalid_gateway; + } + if (gatewayAddr.isMulticastAddress()) { + return R.string.wifi_ip_settings_invalid_gateway; + } + staticIPBuilder.setGateway(gatewayAddr); } - staticIpConfiguration.dnsServers.add(dnsAddr); - } - if (mDns2View.length() > 0) { - dns = mDns2View.getText().toString(); - dnsAddr = getIPv4Address(dns); - if (dnsAddr == null) { - return R.string.wifi_ip_settings_invalid_dns; + String dns = mDns1View.getText().toString(); + InetAddress dnsAddr = null; + final ArrayList dnsServers = new ArrayList<>(); + + if (TextUtils.isEmpty(dns)) { + //If everything else is valid, provide hint as a default option + mDns1View.setText(mConfigUi.getContext().getString(R.string.wifi_dns1_hint)); + } else { + dnsAddr = getIPv4Address(dns); + if (dnsAddr == null) { + return R.string.wifi_ip_settings_invalid_dns; + } + dnsServers.add(dnsAddr); } - staticIpConfiguration.dnsServers.add(dnsAddr); + + if (mDns2View.length() > 0) { + dns = mDns2View.getText().toString(); + dnsAddr = getIPv4Address(dns); + if (dnsAddr == null) { + return R.string.wifi_ip_settings_invalid_dns; + } + dnsServers.add(dnsAddr); + } + staticIPBuilder.setDnsServers(dnsServers); + return 0; + } finally { + // Caller of this method may rely on staticIpConfiguration, so build the final result + // at the end of the method. + staticIpConfiguration = staticIPBuilder.build(); } - return 0; } private void showSecurityFields(boolean refreshEapMethods, boolean refreshCertificates) { diff --git a/src/com/android/settings/wifi/WifiConfigController2.java b/src/com/android/settings/wifi/WifiConfigController2.java index e42c538cd4b..5cc04991497 100644 --- a/src/com/android/settings/wifi/WifiConfigController2.java +++ b/src/com/android/settings/wifi/WifiConfigController2.java @@ -79,6 +79,7 @@ import com.android.wifitrackerlib.WifiEntry.ConnectedInfo; import java.net.Inet4Address; import java.net.InetAddress; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; @@ -305,9 +306,9 @@ public class WifiConfigController2 implements TextWatcher, // Display IP address. StaticIpConfiguration staticConfig = config.getIpConfiguration() .getStaticIpConfiguration(); - if (staticConfig != null && staticConfig.ipAddress != null) { + if (staticConfig != null && staticConfig.getIpAddress() != null) { addRow(group, R.string.wifi_ip_address, - staticConfig.ipAddress.getAddress().getHostAddress()); + staticConfig.getIpAddress().getAddress().getHostAddress()); } } else { mIpSettingsSpinner.setSelection(DHCP); @@ -822,7 +823,8 @@ public class WifiConfigController2 implements TextWatcher, result = R.string.proxy_error_invalid_port; } if (result == 0) { - mHttpProxy = new ProxyInfo(host, port, exclusionList); + mHttpProxy = ProxyInfo.buildDirectProxy( + host, port, Arrays.asList(exclusionList.split(","))); } else { return false; } @@ -836,7 +838,7 @@ public class WifiConfigController2 implements TextWatcher, if (uri == null) { return false; } - mHttpProxy = new ProxyInfo(uri); + mHttpProxy = ProxyInfo.buildPacProxy(uri); } return true; } @@ -860,66 +862,83 @@ public class WifiConfigController2 implements TextWatcher, return R.string.wifi_ip_settings_invalid_ip_address; } - int networkPrefixLength = -1; + // Copy all fields into the builder first and set desired value later with builder. + final StaticIpConfiguration.Builder staticIPBuilder = new StaticIpConfiguration.Builder() + .setDnsServers(staticIpConfiguration.getDnsServers()) + .setDomains(staticIpConfiguration.getDomains()) + .setGateway(staticIpConfiguration.getGateway()) + .setIpAddress(staticIpConfiguration.getIpAddress()); try { - networkPrefixLength = Integer.parseInt(mNetworkPrefixLengthView.getText().toString()); - if (networkPrefixLength < 0 || networkPrefixLength > 32) { - return R.string.wifi_ip_settings_invalid_network_prefix_length; - } - staticIpConfiguration.ipAddress = new LinkAddress(inetAddr, networkPrefixLength); - } catch (NumberFormatException e) { - // Set the hint as default after user types in ip address - mNetworkPrefixLengthView.setText(mConfigUi.getContext().getString( - R.string.wifi_network_prefix_length_hint)); - } catch (IllegalArgumentException e) { - return R.string.wifi_ip_settings_invalid_ip_address; - } - - String gateway = mGatewayView.getText().toString(); - if (TextUtils.isEmpty(gateway)) { + int networkPrefixLength = -1; try { - //Extract a default gateway from IP address - InetAddress netPart = NetUtils.getNetworkPart(inetAddr, networkPrefixLength); - byte[] addr = netPart.getAddress(); - addr[addr.length - 1] = 1; - mGatewayView.setText(InetAddress.getByAddress(addr).getHostAddress()); - } catch (RuntimeException ee) { - } catch (java.net.UnknownHostException u) { + networkPrefixLength = + Integer.parseInt(mNetworkPrefixLengthView.getText().toString()); + if (networkPrefixLength < 0 || networkPrefixLength > 32) { + return R.string.wifi_ip_settings_invalid_network_prefix_length; + } + staticIPBuilder.setIpAddress(new LinkAddress(inetAddr, networkPrefixLength)); + } catch (NumberFormatException e) { + // Set the hint as default after user types in ip address + mNetworkPrefixLengthView.setText(mConfigUi.getContext().getString( + R.string.wifi_network_prefix_length_hint)); + } catch (IllegalArgumentException e) { + return R.string.wifi_ip_settings_invalid_ip_address; } - } else { - InetAddress gatewayAddr = getIPv4Address(gateway); - if (gatewayAddr == null) { - return R.string.wifi_ip_settings_invalid_gateway; - } - if (gatewayAddr.isMulticastAddress()) { - return R.string.wifi_ip_settings_invalid_gateway; - } - staticIpConfiguration.gateway = gatewayAddr; - } - String dns = mDns1View.getText().toString(); - InetAddress dnsAddr = null; - - if (TextUtils.isEmpty(dns)) { - //If everything else is valid, provide hint as a default option - mDns1View.setText(mConfigUi.getContext().getString(R.string.wifi_dns1_hint)); - } else { - dnsAddr = getIPv4Address(dns); - if (dnsAddr == null) { - return R.string.wifi_ip_settings_invalid_dns; + String gateway = mGatewayView.getText().toString(); + if (TextUtils.isEmpty(gateway)) { + try { + //Extract a default gateway from IP address + InetAddress netPart = NetUtils.getNetworkPart(inetAddr, networkPrefixLength); + byte[] addr = netPart.getAddress(); + addr[addr.length - 1] = 1; + mGatewayView.setText(InetAddress.getByAddress(addr).getHostAddress()); + } catch (RuntimeException ee) { + } catch (java.net.UnknownHostException u) { + } + } else { + InetAddress gatewayAddr = getIPv4Address(gateway); + if (gatewayAddr == null) { + return R.string.wifi_ip_settings_invalid_gateway; + } + if (gatewayAddr.isMulticastAddress()) { + return R.string.wifi_ip_settings_invalid_gateway; + } + staticIPBuilder.setGateway(gatewayAddr); } - staticIpConfiguration.dnsServers.add(dnsAddr); - } - if (mDns2View.length() > 0) { - dns = mDns2View.getText().toString(); - dnsAddr = getIPv4Address(dns); - if (dnsAddr == null) { - return R.string.wifi_ip_settings_invalid_dns; + String dns = mDns1View.getText().toString(); + InetAddress dnsAddr = null; + final ArrayList dnsServers = new ArrayList<>(); + + if (TextUtils.isEmpty(dns)) { + //If everything else is valid, provide hint as a default option + mDns1View.setText(mConfigUi.getContext().getString(R.string.wifi_dns1_hint)); + } else { + dnsAddr = getIPv4Address(dns); + if (dnsAddr == null) { + return R.string.wifi_ip_settings_invalid_dns; + } + dnsServers.add(dnsAddr); + staticIpConfiguration.dnsServers.add(dnsAddr); } - staticIpConfiguration.dnsServers.add(dnsAddr); + + if (mDns2View.length() > 0) { + dns = mDns2View.getText().toString(); + dnsAddr = getIPv4Address(dns); + if (dnsAddr == null) { + return R.string.wifi_ip_settings_invalid_dns; + } + dnsServers.add(dnsAddr); + staticIpConfiguration.dnsServers.add(dnsAddr); + } + staticIPBuilder.setDnsServers(dnsServers); + return 0; + } finally { + // Caller of this method may rely on staticIpConfiguration, so build the final result + // at the end of the method. + staticIpConfiguration = staticIPBuilder.build(); } - return 0; } private void showSecurityFields(boolean refreshEapMethods, boolean refreshCertificates) { @@ -1330,18 +1349,18 @@ public class WifiConfigController2 implements TextWatcher, StaticIpConfiguration staticConfig = config.getIpConfiguration() .getStaticIpConfiguration(); if (staticConfig != null) { - if (staticConfig.ipAddress != null) { + if (staticConfig.getIpAddress() != null) { mIpAddressView.setText( - staticConfig.ipAddress.getAddress().getHostAddress()); - mNetworkPrefixLengthView.setText(Integer.toString(staticConfig.ipAddress - .getPrefixLength())); + staticConfig.getIpAddress().getAddress().getHostAddress()); + mNetworkPrefixLengthView.setText(Integer.toString( + staticConfig.getIpAddress().getPrefixLength())); } if (staticConfig.gateway != null) { - mGatewayView.setText(staticConfig.gateway.getHostAddress()); + mGatewayView.setText(staticConfig.getGateway().getHostAddress()); } - Iterator dnsIterator = staticConfig.dnsServers.iterator(); + Iterator dnsIterator = staticConfig.getDnsServers().iterator(); if (dnsIterator.hasNext()) { mDns1View.setText(dnsIterator.next().getHostAddress()); } diff --git a/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java new file mode 100644 index 00000000000..bc87d5cc5d0 --- /dev/null +++ b/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java @@ -0,0 +1,124 @@ +/* + * 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.wifi.tether; + +import android.content.Context; +import android.net.wifi.SoftApConfiguration; +import android.util.Log; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; +import androidx.preference.SwitchPreference; + +/** + * This controller helps to manage the state of maximize compatibility switch preference. + */ +public class WifiTetherMaximizeCompatibilityPreferenceController extends + WifiTetherBasePreferenceController { + + private static final String TAG = "WifiTetherMaximizeCompatibilityPref"; + public static final String PREF_KEY = "wifi_tether_maximize_compatibility"; + + private boolean mIsChecked; + + public WifiTetherMaximizeCompatibilityPreferenceController(Context context, + WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener) { + super(context, listener); + mIsChecked = isMaximizeCompatibilityEnabled(); + } + + @Override + public String getPreferenceKey() { + return PREF_KEY; + } + + @Override + public void updateDisplay() { + if (mPreference == null) { + return; + } + mPreference.setEnabled(is5GhzBandSupported()); + ((SwitchPreference) mPreference).setChecked(mIsChecked); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + mIsChecked = (Boolean) newValue; + if (mListener != null) { + mListener.onTetherConfigUpdated(this); + } + return true; + } + + private boolean is5GhzBandSupported() { + if (mWifiManager == null) { + return false; + } + if (!mWifiManager.is5GHzBandSupported() || mWifiManager.getCountryCode() == null) { + return false; + } + return true; + } + + @VisibleForTesting + boolean isMaximizeCompatibilityEnabled() { + if (mWifiManager == null) { + return false; + } + final SoftApConfiguration config = mWifiManager.getSoftApConfiguration(); + if (config == null) { + return false; + } + if (mWifiManager.isBridgedApConcurrencySupported()) { + final boolean isEnabled = config.isBridgedModeOpportunisticShutdownEnabled(); + Log.d(TAG, "isBridgedModeOpportunisticShutdownEnabled:" + isEnabled); + return isEnabled; + } + + // If the BridgedAp Concurrency is not supported in early Pixel devices (e.g. Pixel 2~5), + // show toggle on if the band includes SoftApConfiguration.BAND_5GHZ. + final int band = config.getBand(); + Log.d(TAG, "getBand:" + band); + return (band & SoftApConfiguration.BAND_5GHZ) > 0; + } + + /** + * Setup the Maximize Compatibility setting to the SoftAp Configuration + * + * @param builder The builder to build the SoftApConfiguration. + */ + public void setupMaximizeCompatibility(SoftApConfiguration.Builder builder) { + if (builder == null) { + return; + } + final boolean enabled = mIsChecked; + if (mWifiManager.isBridgedApConcurrencySupported()) { + int[] bands = { + SoftApConfiguration.BAND_2GHZ, + SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ}; + builder.setBands(bands); + Log.d(TAG, "setBridgedModeOpportunisticShutdownEnabled:" + enabled); + builder.setBridgedModeOpportunisticShutdownEnabled(enabled); + } else { + int band = enabled + ? SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ + : SoftApConfiguration.BAND_2GHZ; + Log.d(TAG, "setBand:" + band); + builder.setBand(band); + } + } +} diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java index 939f0778943..e34255035e5 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java +++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java @@ -54,8 +54,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment private static final String TAG = "WifiTetherSettings"; private static final IntentFilter TETHER_STATE_CHANGE_FILTER; private static final String KEY_WIFI_TETHER_SCREEN = "wifi_tether_settings_screen"; - private static final int EXPANDED_CHILD_COUNT_WITH_SECURITY_NON = 3; - private static final int EXPANDED_CHILD_COUNT_DEFAULT = 4; + private static final int EXPANDED_CHILD_COUNT_DEFAULT = 3; @VisibleForTesting static final String KEY_WIFI_TETHER_NETWORK_NAME = "wifi_tether_network_name"; @@ -64,13 +63,14 @@ public class WifiTetherSettings extends RestrictedDashboardFragment @VisibleForTesting static final String KEY_WIFI_TETHER_AUTO_OFF = "wifi_tether_auto_turn_off"; @VisibleForTesting - static final String KEY_WIFI_TETHER_NETWORK_AP_BAND = "wifi_tether_network_ap_band"; + static final String KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY = + WifiTetherMaximizeCompatibilityPreferenceController.PREF_KEY; private WifiTetherSwitchBarController mSwitchBarController; private WifiTetherSSIDPreferenceController mSSIDPreferenceController; private WifiTetherPasswordPreferenceController mPasswordPreferenceController; - private WifiTetherApBandPreferenceController mApBandPreferenceController; private WifiTetherSecurityPreferenceController mSecurityPreferenceController; + private WifiTetherMaximizeCompatibilityPreferenceController mMaxCompatibilityPrefController; private WifiManager mWifiManager; private boolean mRestartWifiApAfterConfigChange; @@ -116,7 +116,8 @@ public class WifiTetherSettings extends RestrictedDashboardFragment mSSIDPreferenceController = use(WifiTetherSSIDPreferenceController.class); mSecurityPreferenceController = use(WifiTetherSecurityPreferenceController.class); mPasswordPreferenceController = use(WifiTetherPasswordPreferenceController.class); - mApBandPreferenceController = use(WifiTetherApBandPreferenceController.class); + mMaxCompatibilityPrefController = + use(WifiTetherMaximizeCompatibilityPreferenceController.class); } @Override @@ -180,10 +181,9 @@ public class WifiTetherSettings extends RestrictedDashboardFragment controllers.add(new WifiTetherSSIDPreferenceController(context, listener)); controllers.add(new WifiTetherSecurityPreferenceController(context, listener)); controllers.add(new WifiTetherPasswordPreferenceController(context, listener)); - controllers.add(new WifiTetherApBandPreferenceController(context, listener)); controllers.add( new WifiTetherAutoOffPreferenceController(context, KEY_WIFI_TETHER_AUTO_OFF)); - + controllers.add(new WifiTetherMaximizeCompatibilityPreferenceController(context, listener)); return controllers; } @@ -219,7 +219,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment mPasswordPreferenceController.getPasswordValidated(securityType), securityType); } - configBuilder.setBand(mApBandPreferenceController.getBandIndex()); + mMaxCompatibilityPrefController.setupMaximizeCompatibility(configBuilder); return configBuilder.build(); } @@ -229,14 +229,10 @@ public class WifiTetherSettings extends RestrictedDashboardFragment } private void updateDisplayWithNewConfig() { - use(WifiTetherSSIDPreferenceController.class) - .updateDisplay(); - use(WifiTetherSecurityPreferenceController.class) - .updateDisplay(); - use(WifiTetherPasswordPreferenceController.class) - .updateDisplay(); - use(WifiTetherApBandPreferenceController.class) - .updateDisplay(); + use(WifiTetherSSIDPreferenceController.class).updateDisplay(); + use(WifiTetherSecurityPreferenceController.class).updateDisplay(); + use(WifiTetherPasswordPreferenceController.class).updateDisplay(); + use(WifiTetherMaximizeCompatibilityPreferenceController.class).updateDisplay(); } public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = @@ -250,7 +246,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment keys.add(KEY_WIFI_TETHER_NETWORK_NAME); keys.add(KEY_WIFI_TETHER_NETWORK_PASSWORD); keys.add(KEY_WIFI_TETHER_AUTO_OFF); - keys.add(KEY_WIFI_TETHER_NETWORK_AP_BAND); + keys.add(KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); } // Remove duplicate @@ -294,22 +290,17 @@ public class WifiTetherSettings extends RestrictedDashboardFragment private void reConfigInitialExpandedChildCount() { final PreferenceGroup screen = getPreferenceScreen(); - if (mSecurityPreferenceController.getSecurityType() - == SoftApConfiguration.SECURITY_TYPE_OPEN) { - screen.setInitialExpandedChildrenCount(EXPANDED_CHILD_COUNT_WITH_SECURITY_NON); - return; + if (screen != null) { + screen.setInitialExpandedChildrenCount(getInitialExpandedChildCount()); } - screen.setInitialExpandedChildrenCount(EXPANDED_CHILD_COUNT_DEFAULT); } @Override public int getInitialExpandedChildCount() { - if (mSecurityPreferenceController == null) { - return EXPANDED_CHILD_COUNT_DEFAULT; + if (mSecurityPreferenceController != null && mSecurityPreferenceController.getSecurityType() + == SoftApConfiguration.SECURITY_TYPE_OPEN) { + return (EXPANDED_CHILD_COUNT_DEFAULT - 1); } - - return (mSecurityPreferenceController.getSecurityType() - == SoftApConfiguration.SECURITY_TYPE_OPEN) - ? EXPANDED_CHILD_COUNT_WITH_SECURITY_NON : EXPANDED_CHILD_COUNT_DEFAULT; + return EXPANDED_CHILD_COUNT_DEFAULT; } } diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFragmentTest.java new file mode 100644 index 00000000000..473b56687bf --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFragmentTest.java @@ -0,0 +1,50 @@ +/* + * 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.accessibility; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; +import com.android.settings.testutils.XmlTestUtils; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import java.util.List; + +/** Tests for {@link AccessibilityButtonFragment}. */ +@RunWith(RobolectricTestRunner.class) +public class AccessibilityButtonFragmentTest { + + private Context mContext = ApplicationProvider.getApplicationContext(); + + @Test + public void getNonIndexableKeys_existInXmlLayout() { + final List niks = AccessibilityButtonFragment.SEARCH_INDEX_DATA_PROVIDER + .getNonIndexableKeys(mContext); + final List keys = + XmlTestUtils.getKeysFromPreferenceXml(mContext, + R.xml.accessibility_button_settings); + + assertThat(keys).containsAtLeastElementsIn(niks); + } +} diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceControllerTest.java new file mode 100644 index 00000000000..a67038a6b2d --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceControllerTest.java @@ -0,0 +1,106 @@ +/* + * 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.accessibility; + +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; + +import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.Resources; +import android.provider.Settings; + +import androidx.preference.ListPreference; +import androidx.test.core.app.ApplicationProvider; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +/** Tests for {@link AccessibilityButtonLocationPreferenceController}. */ +@RunWith(RobolectricTestRunner.class) +public class AccessibilityButtonLocationPreferenceControllerTest { + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Spy + private final Context mContext = ApplicationProvider.getApplicationContext(); + @Spy + private final Resources mResources = mContext.getResources(); + private final ContentResolver mContentResolver = mContext.getContentResolver(); + private final ListPreference mListPreference = new ListPreference(mContext); + private AccessibilityButtonLocationPreferenceController mController; + + + @Before + public void setUp() { + mController = new AccessibilityButtonLocationPreferenceController(mContext, + "test_key"); + when(mContext.getResources()).thenReturn(mResources); + } + + @Test + public void getAvailabilityStatus_navigationGestureEnabled_returnDisabledDependentSetting() { + when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode)) + .thenReturn(NAV_BAR_MODE_GESTURAL); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING); + } + + @Test + public void getAvailabilityStatus_navigationGestureDisabled_returnAvailable() { + when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode)) + .thenReturn(NAV_BAR_MODE_2BUTTON); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); + } + + @Test + public void updateState_a11yBtnModeNavigationBar_navigationBarValue() { + Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); + + mController.updateState(mListPreference); + + final String navigationBarValue = String.valueOf(ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); + assertThat(mListPreference.getValue()).isEqualTo(navigationBarValue); + } + + @Test + public void onPreferenceChange_a11yBtnModeFloatingMenu_floatingMenuValue() { + final String floatingMenuValue = String.valueOf(ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU); + + mController.onPreferenceChange(mListPreference, floatingMenuValue); + + assertThat(mListPreference.getValue()).isEqualTo(floatingMenuValue); + } +} diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceControllerTest.java new file mode 100644 index 00000000000..eb881757b28 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceControllerTest.java @@ -0,0 +1,120 @@ +/* + * 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.accessibility; + +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.provider.Settings; +import android.widget.ImageView; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; + +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 AccessibilityButtonPreviewPreferenceController}. */ +@RunWith(RobolectricTestRunner.class) +public class AccessibilityButtonPreviewPreferenceControllerTest { + + @Rule + public MockitoRule mocks = MockitoJUnit.rule(); + + @Spy + private final Context mContext = ApplicationProvider.getApplicationContext(); + @Mock + private ContentResolver mContentResolver; + private AccessibilityButtonPreviewPreferenceController mController; + + @Before + public void setUp() { + when(mContext.getContentResolver()).thenReturn(mContentResolver); + mController = new AccessibilityButtonPreviewPreferenceController(mContext, "test_key"); + mController.mPreview = new ImageView(mContext); + } + + @Test + public void onChange_a11yBtnModeNavigationBar_getNavigationBarDrawable() { + Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); + + mController.mContentObserver.onChange(false); + + final Drawable navigationBarDrawable = mContext.getDrawable( + R.drawable.accessibility_button_navigation); + assertThat(mController.mPreview.getDrawable().getConstantState()).isEqualTo( + navigationBarDrawable.getConstantState()); + } + + @Test + public void onChange_updatePreviewPreferenceWithConfig_expectedPreviewDrawable() { + Settings.Secure.putInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU); + Settings.Secure.putInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, /* small size */ 0); + Settings.Secure.putFloat(mContentResolver, + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, 0.1f); + + mController.mContentObserver.onChange(false); + + final Drawable smallFloatingMenuWithTenOpacityDrawable = + FloatingMenuLayerDrawable.createLayerDrawable(mContext, + R.drawable.accessibility_button_preview_small_floating_menu, 10); + assertThat(mController.mPreview.getDrawable().getConstantState()).isEqualTo( + smallFloatingMenuWithTenOpacityDrawable.getConstantState()); + } + + @Test + public void onResume_registerSpecificContentObserver() { + mController.onResume(); + + verify(mContentResolver).registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE), false, + mController.mContentObserver); + verify(mContentResolver).registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE), false, + mController.mContentObserver); + verify(mContentResolver).registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY), + false, + mController.mContentObserver); + } + + @Test + public void onPause_unregisterContentObserver() { + mController.onPause(); + + verify(mContentResolver).unregisterContentObserver(mController.mContentObserver); + } +} diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuFadePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuFadePreferenceControllerTest.java new file mode 100644 index 00000000000..5cf87ee61a1 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuFadePreferenceControllerTest.java @@ -0,0 +1,131 @@ +/* + * 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.accessibility; + +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR; + +import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.Context; +import android.provider.Settings; + +import androidx.preference.SwitchPreference; +import androidx.test.core.app.ApplicationProvider; + +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 FloatingMenuFadePreferenceController}. */ +@RunWith(RobolectricTestRunner.class) +public class FloatingMenuFadePreferenceControllerTest { + + @Rule + public MockitoRule mocks = MockitoJUnit.rule(); + + private static final int OFF = 0; + private static final int ON = 1; + + @Spy + private final Context mContext = ApplicationProvider.getApplicationContext(); + @Mock + private ContentResolver mContentResolver; + private final SwitchPreference mSwitchPreference = new SwitchPreference(mContext); + private FloatingMenuFadePreferenceController mController; + + @Before + public void setUp() { + when(mContext.getContentResolver()).thenReturn(mContentResolver); + mController = new FloatingMenuFadePreferenceController(mContext, "test_key"); + } + + @Test + public void getAvailabilityStatus_a11yBtnModeFloatingMenu_returnAvailable() { + Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); + } + + @Test + public void getAvailabilityStatus_a11yBtnModeNavigationBar_returnDisabledDependentSetting() { + Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING); + } + + @Test + public void updateState_keyFloatingMenuFadeDisabled_fadeIsDisabled() { + Settings.Secure.putInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, OFF); + + mController.updateState(mSwitchPreference); + + assertThat(mSwitchPreference.isChecked()).isFalse(); + } + + @Test + public void onPreferenceChange_floatingMenuFadeEnabled_keyFloatingMenuFadeIsOn() { + mController.onPreferenceChange(mSwitchPreference, Boolean.TRUE); + + final int actualValue = Settings.Secure.getInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, OFF); + assertThat(actualValue).isEqualTo(ON); + } + + @Test + public void onChange_floatingMenuFadeChangeToDisabled_preferenceDisabled() { + mController.mPreference = mSwitchPreference; + Settings.Secure.putInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, OFF); + + mController.mContentObserver.onChange(false); + + assertThat(mController.mPreference.isEnabled()).isFalse(); + } + + @Test + public void onResume_registerSpecificContentObserver() { + mController.onResume(); + + verify(mContentResolver).registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE), false, + mController.mContentObserver); + } + + @Test + public void onPause_unregisterContentObserver() { + mController.onPause(); + + verify(mContentResolver).unregisterContentObserver(mController.mContentObserver); + } +} diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuLayerDrawableTest.java b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuLayerDrawableTest.java new file mode 100644 index 00000000000..ec449d249b5 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuLayerDrawableTest.java @@ -0,0 +1,73 @@ +/* + * 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.accessibility; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.graphics.drawable.Drawable; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** Tests for {@link FloatingMenuLayerDrawable}. */ +@RunWith(RobolectricTestRunner.class) +public class FloatingMenuLayerDrawableTest { + + private static final int TEST_RES_ID = + com.android.internal.R.drawable.ic_accessibility_magnification; + private static final int TEST_RES_ID_2 = + com.android.internal.R.drawable.ic_accessibility_color_inversion; + private final Context mContext = ApplicationProvider.getApplicationContext(); + + @Test + public void createLayerDrawable_configCorrect() { + final Drawable expected1stDrawable = mContext.getDrawable( + R.drawable.accessibility_button_preview_base); + final Drawable expected2ndDrawable = mContext.getDrawable(TEST_RES_ID); + + final FloatingMenuLayerDrawable actualDrawable = + FloatingMenuLayerDrawable.createLayerDrawable(mContext, TEST_RES_ID, + /* opacity= */ 27); + + final Drawable actual1stDrawable = actualDrawable.getDrawable(0); + final Drawable actual2ndDrawable = actualDrawable.getDrawable(1); + // These are VectorDrawables, so it can use getConstantState() to compare. + assertThat(actual1stDrawable.getConstantState()).isEqualTo( + expected1stDrawable.getConstantState()); + assertThat(actual2ndDrawable.getConstantState()).isEqualTo( + expected2ndDrawable.getConstantState()); + } + + @Test + public void updateLayerDrawable_expectedFloatingMenuLayerDrawableState() { + final FloatingMenuLayerDrawable originalDrawable = + FloatingMenuLayerDrawable.createLayerDrawable(mContext, TEST_RES_ID, /* opacity= */ + 72); + + originalDrawable.updateLayerDrawable(mContext, TEST_RES_ID_2, /* opacity= */ 27); + + assertThat(originalDrawable.getConstantState()).isEqualTo( + new FloatingMenuLayerDrawable.FloatingMenuLayerDrawableState(mContext, + TEST_RES_ID_2, /* opacity= */ 27)); + } +} diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceControllerTest.java new file mode 100644 index 00000000000..1638f907407 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceControllerTest.java @@ -0,0 +1,141 @@ +/* + * 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.accessibility; + +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR; + +import static com.android.settings.accessibility.FloatingMenuOpacityPreferenceController.DEFAULT_OPACITY; +import static com.android.settings.accessibility.FloatingMenuOpacityPreferenceController.PRECISION; +import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.Context; +import android.provider.Settings; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.widget.SeekBarPreference; + +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 FloatingMenuOpacityPreferenceController}. */ +@RunWith(RobolectricTestRunner.class) +public class FloatingMenuOpacityPreferenceControllerTest { + + @Rule + public MockitoRule mocks = MockitoJUnit.rule(); + + @Spy + private final Context mContext = ApplicationProvider.getApplicationContext(); + @Mock + private ContentResolver mContentResolver; + private FloatingMenuOpacityPreferenceController mController; + + @Before + public void setUp() { + when(mContext.getContentResolver()).thenReturn(mContentResolver); + mController = new FloatingMenuOpacityPreferenceController(mContext, "test_key"); + } + + @Test + public void getAvailabilityStatus_a11yBtnModeFloatingMenu_returnAvailable() { + Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); + } + + @Test + public void getAvailabilityStatus_a11yBtnModeNavigationBar_returnDisabledDependentSetting() { + Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING); + } + + @Test + public void onChange_a11yBtnModeChangeToNavigationBar_preferenceDisabled() { + mController.mPreference = new SeekBarPreference(mContext); + Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); + + mController.mContentObserver.onChange(false); + + assertThat(mController.mPreference.isEnabled()).isFalse(); + } + + @Test + public void getSliderPosition_putNormalOpacityValue_expectedValue() { + Settings.Secure.putFloat(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, 0.35f); + + assertThat(mController.getSliderPosition()).isEqualTo(35); + } + + @Test + public void getSliderPosition_putOutOfBoundOpacityValue_defaultValue() { + Settings.Secure.putFloat(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, 0.01f); + + final int defaultValue = Math.round(DEFAULT_OPACITY * PRECISION); + assertThat(mController.getSliderPosition()).isEqualTo(defaultValue); + } + + @Test + public void setSliderPosition_expectedValue() { + mController.setSliderPosition(27); + + final float value = Settings.Secure.getFloat(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, -1); + assertThat(value).isEqualTo(0.27f); + } + + @Test + public void onResume_registerSpecificContentObserver() { + mController.onResume(); + + verify(mContentResolver).registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE), false, + mController.mContentObserver); + verify(mContentResolver).registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED), + false, + mController.mContentObserver); + } + + @Test + public void onPause_unregisterContentObserver() { + mController.onPause(); + + verify(mContentResolver).unregisterContentObserver(mController.mContentObserver); + } +} diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuSizePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuSizePreferenceControllerTest.java new file mode 100644 index 00000000000..4d7d98d7aff --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuSizePreferenceControllerTest.java @@ -0,0 +1,120 @@ +/* + * 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.accessibility; + +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; +import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR; + +import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.Context; +import android.provider.Settings; + +import androidx.preference.ListPreference; +import androidx.test.core.app.ApplicationProvider; + +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 FloatingMenuSizePreferenceController}. */ +@RunWith(RobolectricTestRunner.class) +public class FloatingMenuSizePreferenceControllerTest { + + @Rule + public MockitoRule mocks = MockitoJUnit.rule(); + + @Spy + private final Context mContext = ApplicationProvider.getApplicationContext(); + @Mock + private ContentResolver mContentResolver; + private final ListPreference mListPreference = new ListPreference(mContext); + private FloatingMenuSizePreferenceController mController; + + @Before + public void setUp() { + when(mContext.getContentResolver()).thenReturn(mContentResolver); + mController = new FloatingMenuSizePreferenceController(mContext, "test_key"); + } + + @Test + public void getAvailabilityStatus_a11yBtnModeFloatingMenu_returnAvailable() { + Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); + } + + @Test + public void getAvailabilityStatus_a11yBtnModeNavigationBar_returnDisabledDependentSetting() { + Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING); + } + + @Test + public void updateState_floatingMenuLargeSizeAndFullCircle_largeSizeValue() { + Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, + FloatingMenuSizePreferenceController.Size.LARGE); + + mController.updateState(mListPreference); + + final String largeSize = String.valueOf(FloatingMenuSizePreferenceController.Size.LARGE); + assertThat(mListPreference.getValue()).isEqualTo(largeSize); + } + + @Test + public void onChange_a11yBtnModeChangeToNavigationBar_preferenceDisabled() { + mController.mPreference = mListPreference; + Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); + + mController.mContentObserver.onChange(false); + + assertThat(mController.mPreference.isEnabled()).isFalse(); + } + + @Test + public void onResume_registerSpecificContentObserver() { + mController.onResume(); + + verify(mContentResolver).registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE), false, + mController.mContentObserver); + } + + @Test + public void onPause_unregisterContentObserver() { + mController.onPause(); + + verify(mContentResolver).unregisterContentObserver(mController.mContentObserver); + } +} diff --git a/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java b/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java index 02b318fb3bb..534d3c656ef 100644 --- a/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java +++ b/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java @@ -120,7 +120,6 @@ public class AvatarViewMixinTest { @Test @Config(qualifiers = "mcc999", shadows = { - BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class, BatteryFixSliceTest.ShadowBatteryTipLoader.class }) public void onStart_useMockAvatarViewMixin_shouldBeExecuted() { diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java index 440ad044087..71ab3344b29 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java @@ -30,7 +30,6 @@ import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.os.BatteryStats; import android.os.BatteryUsageStats; import android.os.Bundle; import android.os.UidBatteryConsumer; @@ -39,15 +38,12 @@ import androidx.loader.app.LoaderManager; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; -import com.android.internal.os.BatterySipper; -import com.android.internal.os.BatteryStatsHelper; import com.android.settings.SettingsActivity; import com.android.settings.fuelgauge.BatteryUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Answers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; @@ -73,13 +69,7 @@ public class AppBatteryPreferenceControllerTest { @Mock private UidBatteryConsumer mUidBatteryConsumer; @Mock - private BatterySipper mBatterySipper; - @Mock - private BatterySipper mOtherBatterySipper; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private BatteryStatsHelper mBatteryStatsHelper; - @Mock - private BatteryStats.Uid mUid; + private UidBatteryConsumer mOtherUidBatteryConsumer; @Mock private PreferenceScreen mScreen; @Mock @@ -102,10 +92,8 @@ public class AppBatteryPreferenceControllerTest { mBatteryPreference = spy(new Preference(RuntimeEnvironment.application)); - mBatterySipper.drainType = BatterySipper.DrainType.IDLE; - mBatterySipper.uidObj = mUid; - doReturn(TARGET_UID).when(mBatterySipper).getUid(); - doReturn(OTHER_UID).when(mOtherBatterySipper).getUid(); + when(mUidBatteryConsumer.getUid()).thenReturn(TARGET_UID); + when(mOtherUidBatteryConsumer.getUid()).thenReturn(OTHER_UID); mController = spy(new AppBatteryPreferenceController( RuntimeEnvironment.application, mFragment, "package1", null /* lifecycle */)); @@ -125,14 +113,14 @@ public class AppBatteryPreferenceControllerTest { } @Test - public void findTargetSipper_findCorrectSipper() { - final List usageList = new ArrayList<>(); - usageList.add(mBatterySipper); - usageList.add(mOtherBatterySipper); - when(mBatteryStatsHelper.getUsageList()).thenReturn(usageList); + public void findTargetBatteryConsumer_findCorrectBatteryConsumer() { + final List uidBatteryConsumers = new ArrayList<>(); + uidBatteryConsumers.add(mUidBatteryConsumer); + uidBatteryConsumers.add(mOtherUidBatteryConsumer); + when(mBatteryUsageStats.getUidBatteryConsumers()).thenReturn(uidBatteryConsumers); - assertThat(mController.findTargetSipper(mBatteryStatsHelper, TARGET_UID)) - .isEqualTo(mBatterySipper); + assertThat(mController.findTargetUidBatteryConsumer(mBatteryUsageStats, TARGET_UID)) + .isEqualTo(mUidBatteryConsumer); } @Test @@ -147,13 +135,10 @@ public class AppBatteryPreferenceControllerTest { @Test public void updateBattery_hasBatteryStats_summaryPercent() { - mController.mBatteryHelper = mBatteryStatsHelper; - mController.mSipper = mBatterySipper; mController.mBatteryUsageStats = mBatteryUsageStats; mController.mUidBatteryConsumer = mUidBatteryConsumer; doReturn(BATTERY_LEVEL).when(mBatteryUtils).calculateBatteryPercent(anyDouble(), anyDouble(), anyInt()); - doReturn(new ArrayList<>()).when(mBatteryStatsHelper).getUsageList(); mController.displayPreference(mScreen); mController.updateBattery(); @@ -163,8 +148,6 @@ public class AppBatteryPreferenceControllerTest { @Test public void isBatteryStatsAvailable_hasBatteryStatsHelperAndSipper_returnTrue() { - mController.mBatteryHelper = mBatteryStatsHelper; - mController.mSipper = mBatterySipper; mController.mBatteryUsageStats = mBatteryUsageStats; mController.mUidBatteryConsumer = mUidBatteryConsumer; @@ -183,8 +166,6 @@ public class AppBatteryPreferenceControllerTest { when(mFragment.getActivity()).thenReturn(mActivity); final String key = mController.getPreferenceKey(); when(mBatteryPreference.getKey()).thenReturn(key); - mController.mSipper = mBatterySipper; - mController.mBatteryHelper = mBatteryStatsHelper; mController.mBatteryUsageStats = mBatteryUsageStats; mController.mUidBatteryConsumer = mUidBatteryConsumer; @@ -199,8 +180,8 @@ public class AppBatteryPreferenceControllerTest { mController.onResume(); verify(mLoaderManager) - .restartLoader(AppInfoDashboardFragment.LOADER_BATTERY, Bundle.EMPTY, - mController.mBatteryStatsHelperLoaderCallbacks); + .restartLoader(AppInfoDashboardFragment.LOADER_BATTERY_USAGE_STATS, Bundle.EMPTY, + mController.mBatteryUsageStatsLoaderCallbacks); } @Test @@ -209,6 +190,6 @@ public class AppBatteryPreferenceControllerTest { mController.onPause(); - verify(mLoaderManager).destroyLoader(AppInfoDashboardFragment.LOADER_BATTERY); + verify(mLoaderManager).destroyLoader(AppInfoDashboardFragment.LOADER_BATTERY_USAGE_STATS); } } diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppVersionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppVersionPreferenceControllerTest.java index 1f513a36b41..d5e5080ac70 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppVersionPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppVersionPreferenceControllerTest.java @@ -16,6 +16,8 @@ package com.android.settings.applications.appinfo; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -62,4 +64,13 @@ public class AppVersionPreferenceControllerTest { verify(mPreference).setSummary("version test1234"); } + + @Test + public void updateState_packageInfoNull_shouldNotCrash() { + when(mFragment.getPackageInfo()).thenReturn(null); + + mController.updateState(mPreference); + + assertThat(mController.getSummary()).isNull(); + } } diff --git a/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java index 8a68f38fcfe..eb29b7ca76e 100644 --- a/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java @@ -18,17 +18,16 @@ package com.android.settings.datetime; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; -import static org.robolectric.shadow.api.Shadow.extract; +import static org.mockito.Mockito.when; import android.content.Context; -import android.net.ConnectivityManager; import android.provider.Settings; +import android.telephony.TelephonyManager; import androidx.preference.Preference; -import com.android.settings.testutils.shadow.ShadowConnectivityManager; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -36,27 +35,28 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) -@Config(shadows = ShadowConnectivityManager.class) public class AutoTimeZonePreferenceControllerTest { @Mock private UpdateTimeAndDateCallback mCallback; - + @Mock private Context mContext; private AutoTimeZonePreferenceController mController; private Preference mPreference; - private ShadowConnectivityManager connectivityManager; + @Mock + private TelephonyManager mTelephonyManager; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; + mContext = spy(RuntimeEnvironment.application); + mPreference = new Preference(mContext); - connectivityManager = extract(mContext.getSystemService(ConnectivityManager.class)); - connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, true); + + when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); + when(mTelephonyManager.isDataCapable()).thenReturn(true); } @Test @@ -77,8 +77,7 @@ public class AutoTimeZonePreferenceControllerTest { @Test public void isWifiOnly_notAvailable() { - connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, false); - + when(mTelephonyManager.isDataCapable()).thenReturn(false); mController = new AutoTimeZonePreferenceController( mContext, null /* callback */, false /* fromSUW */); @@ -95,8 +94,7 @@ public class AutoTimeZonePreferenceControllerTest { @Test public void isWifiOnly_notEnable() { - connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, false); - + when(mTelephonyManager.isDataCapable()).thenReturn(false); mController = new AutoTimeZonePreferenceController( mContext, null /* callback */, false /* fromSUW */); diff --git a/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java index ae200b9a709..68b2990a03b 100644 --- a/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java @@ -16,9 +16,9 @@ package com.android.settings.datetime; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED; +import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE; +import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED; +import static android.app.time.Capabilities.CAPABILITY_POSSESSED; import static com.google.common.truth.Truth.assertThat; @@ -26,6 +26,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.time.Capabilities; import android.app.time.TimeManager; import android.app.time.TimeZoneCapabilities; import android.app.time.TimeZoneCapabilitiesAndConfig; @@ -142,7 +143,7 @@ public class LocationTimeZoneDetectionPreferenceControllerTest { } private static TimeZoneCapabilities createTimeZoneCapabilities( - @TimeZoneCapabilities.CapabilityState int geoDetectionCapability) { + @Capabilities.CapabilityState int geoDetectionCapability) { UserHandle arbitraryUserHandle = UserHandle.of(123); return new TimeZoneCapabilities.Builder(arbitraryUserHandle) .setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED) diff --git a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceControllerTest.java index 79df2215545..87fdb222c42 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceControllerTest.java @@ -21,44 +21,42 @@ import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_ import static com.google.common.truth.Truth.assertThat; -import static org.robolectric.shadow.api.Shadow.extract; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import android.content.Context; -import android.net.ConnectivityManager; import android.sysprop.TelephonyProperties; - -import com.android.settings.testutils.shadow.ShadowConnectivityManager; +import android.telephony.TelephonyManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; import java.util.Arrays; @RunWith(RobolectricTestRunner.class) -@Config(shadows = ShadowConnectivityManager.class) public class BasebandVersionPreferenceControllerTest { - + @Mock private Context mContext; private BasebandVersionPreferenceController mController; + @Mock + private TelephonyManager mTelephonyManager; @Before public void setup() { MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; + mContext = spy(RuntimeEnvironment.application); mController = new BasebandVersionPreferenceController(mContext, "key"); + when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); } @Test public void getAvailability_wifiOnly_unavailable() { - final ShadowConnectivityManager connectivityManager = - extract(mContext.getSystemService(ConnectivityManager.class)); - connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, false); - + when(mTelephonyManager.isDataCapable()).thenReturn(false); assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); } @@ -66,10 +64,7 @@ public class BasebandVersionPreferenceControllerTest { public void getAvailability_hasMobile_available() { final String text = "test"; TelephonyProperties.baseband_version(Arrays.asList(new String[]{text})); - ShadowConnectivityManager connectivityManager = - extract(mContext.getSystemService(ConnectivityManager.class)); - connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, true); - + when(mTelephonyManager.isDataCapable()).thenReturn(true); assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); } } diff --git a/tests/robotests/src/com/android/settings/display/TopLevelWallpaperPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/TopLevelWallpaperPreferenceControllerTest.java index 7b3ae659c33..6ad99745748 100644 --- a/tests/robotests/src/com/android/settings/display/TopLevelWallpaperPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/TopLevelWallpaperPreferenceControllerTest.java @@ -202,4 +202,20 @@ public class TopLevelWallpaperPreferenceControllerTest { assertThat(Shadows.shadowOf(mContext).getNextStartedActivityForResult() .intent.hasExtra("com.android.wallpaper.LAUNCH_SOURCE")).isTrue(); } + + @Test + public void handlePreferenceTreeClick_launchClearTask() { + mShadowPackageManager.setResolveInfosForIntent( + mWallpaperIntent, Lists.newArrayList()); + mShadowPackageManager.setResolveInfosForIntent( + mStylesAndWallpaperIntent, Lists.newArrayList(mock(ResolveInfo.class))); + + Preference preference = new Preference(mContext); + preference.setKey(TEST_KEY); + + mController.handlePreferenceTreeClick(preference); + + assertThat((Shadows.shadowOf(mContext).getNextStartedActivityForResult() + .intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_TASK) != 0).isTrue(); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java index a072988df4c..5f08698b4bd 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java @@ -78,7 +78,6 @@ public class BatteryBroadcastReceiverTest { @Test @Config(shadows = { - BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class, BatteryFixSliceTest.ShadowBatteryTipLoader.class }) public void testOnReceive_batteryLevelChanged_dataUpdated() { @@ -93,7 +92,6 @@ public class BatteryBroadcastReceiverTest { @Test @Config(shadows = { - BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class, BatteryFixSliceTest.ShadowBatteryTipLoader.class }) public void testOnReceive_batteryHealthChanged_dataUpdated() { @@ -108,7 +106,6 @@ public class BatteryBroadcastReceiverTest { @Test @Config(shadows = { - BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class, BatteryFixSliceTest.ShadowBatteryTipLoader.class }) public void onReceive_batteryNotPresent_shouldShowHelpMessage() { @@ -121,7 +118,6 @@ public class BatteryBroadcastReceiverTest { @Test @Config(shadows = { - BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class, BatteryFixSliceTest.ShadowBatteryTipLoader.class }) public void testOnReceive_powerSaveModeChanged_listenerInvoked() { @@ -133,7 +129,6 @@ public class BatteryBroadcastReceiverTest { @Test @Config(shadows = { - BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class, BatteryFixSliceTest.ShadowBatteryTipLoader.class }) public void testOnReceive_batteryDataNotChanged_listenerNotInvoked() { @@ -154,7 +149,6 @@ public class BatteryBroadcastReceiverTest { @Test @Config(shadows = { - BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class, BatteryFixSliceTest.ShadowBatteryTipLoader.class }) public void testRegister_updateBatteryStatus() { diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryStatsHelperLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryStatsHelperLoaderTest.java deleted file mode 100644 index 92a3dc0110c..00000000000 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryStatsHelperLoaderTest.java +++ /dev/null @@ -1,64 +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.fuelgauge; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.net.ConnectivityManager; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -@RunWith(RobolectricTestRunner.class) -public class BatteryStatsHelperLoaderTest { - @Mock - private BatteryUtils mBatteryUtils; - @Mock - private ConnectivityManager mConnectivityManager; - - private Context mContext; - private BatteryStatsHelperLoader mBatteryStatsHelperLoader; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mContext = spy(RuntimeEnvironment.application); - doReturn(mConnectivityManager).when(mContext).getSystemService( - Context.CONNECTIVITY_SERVICE); - - mBatteryStatsHelperLoader = spy(new BatteryStatsHelperLoader(mContext)); - mBatteryStatsHelperLoader.mBatteryUtils = mBatteryUtils; - } - - @Test - public void testLoadInBackground_loadWithoutBundle() { - when(mBatteryStatsHelperLoader.getContext()).thenReturn(mContext); - mBatteryStatsHelperLoader.loadInBackground(); - - verify(mBatteryUtils).initBatteryStatsHelper(any(), eq(null), any()); - } -} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUsageStatsLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUsageStatsLoaderTest.java new file mode 100644 index 00000000000..8c47ff680bd --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUsageStatsLoaderTest.java @@ -0,0 +1,86 @@ +/* + * 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.fuelgauge; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.os.BatteryStatsManager; +import android.os.BatteryUsageStats; +import android.os.BatteryUsageStatsQuery; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class BatteryUsageStatsLoaderTest { + private Context mContext; + @Mock + private BatteryStatsManager mBatteryStatsManager; + @Mock + private BatteryUsageStats mBatteryUsageStats; + @Captor + private ArgumentCaptor mUsageStatsQueryCaptor; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + doReturn(mBatteryStatsManager).when(mContext).getSystemService( + Context.BATTERY_STATS_SERVICE); + } + + @Test + public void testLoadInBackground_loadWithoutHistory() { + BatteryUsageStatsLoader loader = new BatteryUsageStatsLoader( + mContext, /* includeBatteryHistory */ false); + + when(mBatteryStatsManager.getBatteryUsageStats(mUsageStatsQueryCaptor.capture())) + .thenReturn(mBatteryUsageStats); + + loader.loadInBackground(); + + final int queryFlags = mUsageStatsQueryCaptor.getValue().getFlags(); + assertThat(queryFlags + & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY).isEqualTo(0); + } + + @Test + public void testLoadInBackground_loadWithHistory() { + BatteryUsageStatsLoader loader = new BatteryUsageStatsLoader( + mContext, /* includeBatteryHistory */ true); + + when(mBatteryStatsManager.getBatteryUsageStats(mUsageStatsQueryCaptor.capture())) + .thenReturn(mBatteryUsageStats); + + loader.loadInBackground(); + + final int queryFlags = mUsageStatsQueryCaptor.getValue().getFlags(); + assertThat(queryFlags + & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY).isNotEqualTo(0); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java index 2d22a12a7e8..4c2d5edc43e 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java @@ -46,6 +46,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.BatteryStats; import android.os.BatteryStatsManager; +import android.os.BatteryUsageStats; import android.os.Build; import android.os.Bundle; import android.os.Process; @@ -120,6 +121,8 @@ public class BatteryUtilsTest { @Mock private BatteryStats.Timer mTimer; @Mock + private BatteryUsageStats mBatteryUsageStats; + @Mock private SystemBatteryConsumer mSystemBatteryConsumer; @Mock private BatterySipper mNormalBatterySipper; @@ -336,81 +339,6 @@ public class BatteryUtilsTest { assertThat(mBatteryUtils.shouldHideSystemBatteryConsumer(mSystemBatteryConsumer)).isFalse(); } - @Test - public void testShouldHideSipper_TypeUnAccounted_ReturnTrue() { - mNormalBatterySipper.drainType = BatterySipper.DrainType.UNACCOUNTED; - assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); - } - - @Test - public void testShouldHideSipper_TypeOverAccounted_ReturnTrue() { - mNormalBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED; - assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); - } - - @Test - public void testShouldHideSipper_TypeIdle_ReturnTrue() { - mNormalBatterySipper.drainType = BatterySipper.DrainType.IDLE; - assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); - } - - @Test - public void testShouldHideSipper_TypeCell_ReturnTrue() { - mNormalBatterySipper.drainType = BatterySipper.DrainType.CELL; - assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); - } - - @Test - public void testShouldHideSipper_TypeScreen_ReturnTrue() { - mNormalBatterySipper.drainType = BatterySipper.DrainType.SCREEN; - assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); - } - - @Test - public void testShouldHideSipper_TypeWifi_ReturnTrue() { - mNormalBatterySipper.drainType = BatterySipper.DrainType.WIFI; - assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); - } - - @Test - public void testShouldHideSipper_TypeBluetooth_ReturnTrue() { - mNormalBatterySipper.drainType = BatterySipper.DrainType.BLUETOOTH; - assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); - } - - @Test - public void testShouldHideSipper_TypeSystem_ReturnTrue() { - mNormalBatterySipper.drainType = BatterySipper.DrainType.APP; - when(mNormalBatterySipper.getUid()).thenReturn(Process.ROOT_UID); - when(mProvider.isTypeSystem(any())).thenReturn(true); - assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); - } - - @Test - public void testShouldHideSipper_UidNormal_ReturnFalse() { - mNormalBatterySipper.drainType = BatterySipper.DrainType.APP; - when(mNormalBatterySipper.getUid()).thenReturn(UID); - assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isFalse(); - } - - @Test - public void testShouldHideSipper_TypeService_ReturnTrue() { - mNormalBatterySipper.drainType = BatterySipper.DrainType.APP; - when(mNormalBatterySipper.getUid()).thenReturn(UID); - when(mProvider.isTypeService(any())).thenReturn(true); - - assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); - } - - @Test - public void testShouldHideSipper_hiddenSystemModule_ReturnTrue() { - mNormalBatterySipper.drainType = BatterySipper.DrainType.APP; - when(mNormalBatterySipper.getUid()).thenReturn(UID); - when(mBatteryUtils.isHiddenSystemModule(mNormalBatterySipper)).thenReturn(true); - - assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue(); - } - @Test public void testCalculateBatteryPercent() { assertThat(mBatteryUtils.calculateBatteryPercent(BATTERY_SYSTEM_USAGE, TOTAL_BATTERY_USAGE, @@ -418,20 +346,14 @@ public class BatteryUtilsTest { .isWithin(PRECISION).of(PERCENT_SYSTEM_USAGE); } - @Test - public void testCalculateRunningTimeBasedOnStatsType() { - assertThat(mBatteryUtils.calculateRunningTimeBasedOnStatsType(mBatteryStatsHelper, - BatteryStats.STATS_SINCE_CHARGED)).isEqualTo(TIME_SINCE_LAST_FULL_CHARGE_MS); - } - @Test public void testCalculateLastFullChargeTime() { final long currentTimeMs = System.currentTimeMillis(); - when(mBatteryStatsHelper.getStats().getStartClockTime()).thenReturn( + when(mBatteryUsageStats.getStatsStartRealtime()).thenReturn( currentTimeMs - TIME_SINCE_LAST_FULL_CHARGE_MS); - assertThat(mBatteryUtils.calculateLastFullChargeTime( - mBatteryStatsHelper, currentTimeMs)).isEqualTo(TIME_SINCE_LAST_FULL_CHARGE_MS); + assertThat(mBatteryUtils.calculateLastFullChargeTime(mBatteryUsageStats, currentTimeMs)) + .isEqualTo(TIME_SINCE_LAST_FULL_CHARGE_MS); } @Test diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageBaseTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageBaseTest.java index 87547008d2f..451e605985b 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageBaseTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageBaseTest.java @@ -27,7 +27,6 @@ import android.os.Bundle; import androidx.loader.app.LoaderManager; -import com.android.internal.os.BatteryStatsHelper; import com.android.settings.testutils.shadow.ShadowDashboardFragment; import com.android.settingslib.core.AbstractPreferenceController; @@ -45,8 +44,6 @@ import java.util.List; @Config(shadows = ShadowDashboardFragment.class) public class PowerUsageBaseTest { - @Mock - private BatteryStatsHelper mBatteryStatsHelper; @Mock private LoaderManager mLoaderManager; private TestFragment mFragment; @@ -56,7 +53,6 @@ public class PowerUsageBaseTest { MockitoAnnotations.initMocks(this); mFragment = spy(new TestFragment()); - mFragment.setBatteryStatsHelper(mBatteryStatsHelper); doReturn(mLoaderManager).when(mFragment).getLoaderManager(); } @@ -98,9 +94,5 @@ public class PowerUsageBaseTest { protected List createPreferenceControllers(Context context) { return null; } - - private void setBatteryStatsHelper(BatteryStatsHelper batteryStatsHelper) { - mStatsHelper = batteryStatsHelper; - } } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java index e345ab23a27..11b6ad27f94 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java @@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; @@ -40,8 +39,6 @@ import android.provider.Settings; import androidx.loader.app.LoaderManager; import androidx.preference.PreferenceScreen; -import com.android.internal.os.BatterySipper; -import com.android.internal.os.BatteryStatsHelper; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController; @@ -54,7 +51,6 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Answers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; @@ -62,35 +58,19 @@ import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.util.ReflectionHelpers; -import java.util.ArrayList; import java.util.List; // TODO: Improve this test class so that it starts up the real activity and fragment. @RunWith(RobolectricTestRunner.class) public class PowerUsageSummaryTest { - private static final int UID = 123; - private static final int POWER_MAH = 100; private static final long TIME_SINCE_LAST_FULL_CHARGE_MS = 120 * 60 * 1000; - private static final long TIME_SINCE_LAST_FULL_CHARGE_US = - TIME_SINCE_LAST_FULL_CHARGE_MS * 1000; - private static final long USAGE_TIME_MS = 65 * 60 * 1000; - private static final double TOTAL_POWER = 200; private static Intent sAdditionalBatteryInfoIntent; @BeforeClass public static void beforeClass() { sAdditionalBatteryInfoIntent = new Intent("com.example.app.ADDITIONAL_BATTERY_INFO"); } - - @Mock - private BatterySipper mNormalBatterySipper; - @Mock - private BatterySipper mScreenBatterySipper; - @Mock - private BatterySipper mCellBatterySipper; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private BatteryStatsHelper mBatteryHelper; @Mock private SettingsActivity mSettingsActivity; @Mock @@ -104,7 +84,6 @@ public class PowerUsageSummaryTest { @Mock private PreferenceScreen mPreferenceScreen; - private List mUsageList; private Context mRealContext; private TestFragment mFragment; private FakeFeatureFactory mFeatureFactory; @@ -123,27 +102,6 @@ public class PowerUsageSummaryTest { when(mFragment.getActivity()).thenReturn(mSettingsActivity); when(mFeatureFactory.powerUsageFeatureProvider.getAdditionalBatteryInfoIntent()) .thenReturn(sAdditionalBatteryInfoIntent); - when(mBatteryHelper.getTotalPower()).thenReturn(TOTAL_POWER); - when(mBatteryHelper.getStats().computeBatteryRealtime(anyLong(), anyInt())) - .thenReturn(TIME_SINCE_LAST_FULL_CHARGE_US); - - when(mNormalBatterySipper.getUid()).thenReturn(UID); - mNormalBatterySipper.totalPowerMah = POWER_MAH; - mNormalBatterySipper.drainType = BatterySipper.DrainType.APP; - - mCellBatterySipper.drainType = BatterySipper.DrainType.CELL; - mCellBatterySipper.totalPowerMah = POWER_MAH; - - mScreenBatterySipper.drainType = BatterySipper.DrainType.SCREEN; - mScreenBatterySipper.usageTimeMs = USAGE_TIME_MS; - - mUsageList = new ArrayList<>(); - mUsageList.add(mNormalBatterySipper); - mUsageList.add(mScreenBatterySipper); - mUsageList.add(mCellBatterySipper); - - mFragment.mStatsHelper = mBatteryHelper; - when(mBatteryHelper.getUsageList()).thenReturn(mUsageList); mFragment.mBatteryUtils = spy(new BatteryUtils(mRealContext)); ReflectionHelpers.setField(mFragment, "mVisibilityLoggerMixin", mVisibilityLoggerMixin); ReflectionHelpers.setField(mFragment, "mBatteryBroadcastReceiver", diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java index c97d79ff2a1..82448d138d9 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java @@ -24,9 +24,9 @@ import static org.mockito.Mockito.spy; import android.content.Context; import android.content.Intent; +import android.os.BatteryUsageStats; import android.os.PowerManager; -import com.android.internal.os.BatteryStatsHelper; import com.android.settings.fuelgauge.BatteryInfo; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.batterytip.tips.AppLabelPredicate; @@ -57,7 +57,7 @@ public class BatteryTipLoaderTest { BatteryTip.TipType.SUMMARY, BatteryTip.TipType.SMART_BATTERY_MANAGER}; @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private BatteryStatsHelper mBatteryStatsHelper; + private BatteryUsageStats mBatteryUsageStats; @Mock private PowerManager mPowerManager; @Mock @@ -78,7 +78,7 @@ public class BatteryTipLoaderTest { doReturn(mPowerManager).when(mContext).getSystemService(Context.POWER_SERVICE); doReturn(mIntent).when(mContext).registerReceiver(any(), any()); doReturn(mBatteryInfo).when(mBatteryUtils).getBatteryInfo(any()); - mBatteryTipLoader = new BatteryTipLoader(mContext, mBatteryStatsHelper); + mBatteryTipLoader = new BatteryTipLoader(mContext, mBatteryUsageStats); mBatteryTipLoader.mBatteryUtils = mBatteryUtils; } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java index 93005d51093..c12587622c6 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java @@ -19,23 +19,20 @@ package com.android.settings.fuelgauge.batterytip.detectors; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.content.Context; import android.content.Intent; -import android.os.BatteryStats; +import android.os.BatteryManager; import android.os.BatteryStatsManager; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; +import android.os.UidBatteryConsumer; -import com.android.internal.os.BatterySipper; -import com.android.internal.os.BatteryStatsHelper; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.batterytip.AppInfo; import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy; @@ -46,7 +43,6 @@ import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Answers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; @@ -64,15 +60,14 @@ public class HighUsageDetectorTest { private static final int UID_LOW = 345; private static final double POWER_HIGH = 20000; private static final double POWER_LOW = 10000; + private Context mContext; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private BatteryStatsHelper mBatteryStatsHelper; @Mock - private BatterySipper mHighBatterySipper; + private UidBatteryConsumer mHighBatteryConsumer; @Mock - private BatterySipper mLowBatterySipper; + private UidBatteryConsumer mLowBatteryConsumer; @Mock - private BatterySipper mSystemBatterySipper; + private UidBatteryConsumer mSystemBatteryConsumer; @Mock private HighUsageDataParser mDataParser; @Mock @@ -85,7 +80,6 @@ public class HighUsageDetectorTest { private BatteryTipPolicy mPolicy; private BatteryUtils mBatteryUtils; private HighUsageDetector mHighUsageDetector; - private List mUsageList; @Before public void setUp() { @@ -100,27 +94,22 @@ public class HighUsageDetectorTest { when(mBatteryStatsManager.getBatteryUsageStats(any(BatteryUsageStatsQuery.class))) .thenReturn(mBatteryUsageStats); - mContext.sendStickyBroadcast(new Intent(Intent.ACTION_BATTERY_CHANGED)); + mContext.sendStickyBroadcast(new Intent(Intent.ACTION_BATTERY_CHANGED) + .putExtra(BatteryManager.EXTRA_PLUGGED, 0)); - mHighUsageDetector = spy(new HighUsageDetector(mContext, mPolicy, mBatteryStatsHelper, + mHighUsageDetector = spy(new HighUsageDetector(mContext, mPolicy, mBatteryUsageStats, mBatteryUtils.getBatteryInfo(TAG))); mHighUsageDetector.mBatteryUtils = mBatteryUtils; mHighUsageDetector.mDataParser = mDataParser; doNothing().when(mHighUsageDetector).parseBatteryData(); - doReturn(UID_HIGH).when(mHighBatterySipper).getUid(); - doReturn(UID_LOW).when(mLowBatterySipper).getUid(); - mHighBatterySipper.uidObj = mock(BatteryStats.Uid.class); - mHighBatterySipper.drainType = BatterySipper.DrainType.APP; - mHighBatterySipper.totalSmearedPowerMah = POWER_HIGH; - mLowBatterySipper.uidObj = mock(BatteryStats.Uid.class); - mLowBatterySipper.drainType = BatterySipper.DrainType.APP; - mLowBatterySipper.totalSmearedPowerMah = POWER_LOW; - when(mBatteryUtils.shouldHideSipper(mSystemBatterySipper)).thenReturn(true); - when(mBatteryUtils.shouldHideSipper(mHighBatterySipper)).thenReturn(false); - when(mBatteryUtils.shouldHideSipper(mLowBatterySipper)).thenReturn(false); - when(mBatteryStatsHelper.getStats().getDischargeAmount(anyInt())).thenReturn(100); - when(mBatteryStatsHelper.getTotalPower()).thenReturn(POWER_HIGH + POWER_LOW); - + doReturn(UID_HIGH).when(mHighBatteryConsumer).getUid(); + doReturn(UID_LOW).when(mLowBatteryConsumer).getUid(); + doReturn(POWER_HIGH).when(mHighBatteryConsumer).getConsumedPower(); + doReturn(POWER_LOW).when(mLowBatteryConsumer).getConsumedPower(); + doReturn(false).when(mBatteryUtils).shouldHideUidBatteryConsumer(mHighBatteryConsumer); + doReturn(false).when(mBatteryUtils).shouldHideUidBatteryConsumer(mLowBatteryConsumer); + when(mBatteryUsageStats.getDischargePercentage()).thenReturn(100); + when(mBatteryUsageStats.getConsumedPower()).thenReturn(POWER_HIGH + POWER_LOW); mHighAppInfo = new AppInfo.Builder() .setUid(UID_HIGH) @@ -129,11 +118,11 @@ public class HighUsageDetectorTest { .setUid(UID_LOW) .build(); - mUsageList = new ArrayList<>(); - mUsageList.add(mSystemBatterySipper); - mUsageList.add(mLowBatterySipper); - mUsageList.add(mHighBatterySipper); - when(mBatteryStatsHelper.getUsageList()).thenReturn(mUsageList); + ArrayList consumers = new ArrayList<>(); + consumers.add(mSystemBatteryConsumer); + consumers.add(mLowBatteryConsumer); + consumers.add(mHighBatteryConsumer); + when(mBatteryUsageStats.getUidBatteryConsumers()).thenReturn(consumers); } @Test diff --git a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java index 40315d267fb..173f62558c7 100644 --- a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java +++ b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java @@ -68,7 +68,6 @@ public class SettingsHomepageActivityTest { @Test @Config(shadows = { - BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class, BatteryFixSliceTest.ShadowBatteryTipLoader.class }) public void onStart_isNotDebuggable_shouldHideSystemOverlay() { @@ -88,7 +87,6 @@ public class SettingsHomepageActivityTest { @Test @Config(shadows = { - BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class, BatteryFixSliceTest.ShadowBatteryTipLoader.class, }) public void onStop_isNotDebuggable_shouldRemoveHideSystemOverlay() { diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java index 68c97cfd0d4..cffc4d1f55b 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java @@ -23,15 +23,15 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.net.Uri; +import android.os.BatteryUsageStats; import androidx.slice.Slice; import androidx.slice.SliceMetadata; import androidx.slice.SliceProvider; import androidx.slice.widget.SliceLiveData; -import com.android.internal.os.BatteryStatsHelper; import com.android.settings.R; -import com.android.settings.fuelgauge.BatteryStatsHelperLoader; +import com.android.settings.fuelgauge.BatteryUsageStatsLoader; import com.android.settings.fuelgauge.batterytip.AppInfo; import com.android.settings.fuelgauge.batterytip.BatteryTipLoader; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; @@ -57,7 +57,7 @@ import java.util.List; @RunWith(RobolectricTestRunner.class) @Config(shadows = { - BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class, + BatteryFixSliceTest.ShadowBatteryUsageStatsLoader.class, BatteryFixSliceTest.ShadowBatteryTipLoader.class }) public class BatteryFixSliceTest { @@ -135,11 +135,11 @@ public class BatteryFixSliceTest { assertThat(ShadowEarlyWarningTip.isIconTintColorIdCalled()).isTrue(); } - @Implements(BatteryStatsHelperLoader.class) - public static class ShadowBatteryStatsHelperLoader { + @Implements(BatteryUsageStatsLoader.class) + public static class ShadowBatteryUsageStatsLoader { @Implementation - protected BatteryStatsHelper loadInBackground() { + protected BatteryUsageStats loadInBackground() { return null; } } diff --git a/tests/robotests/src/com/android/settings/location/LocationServicesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/location/LocationServicesPreferenceControllerTest.java index d7d76bef7c7..f5868c2f8ec 100644 --- a/tests/robotests/src/com/android/settings/location/LocationServicesPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/location/LocationServicesPreferenceControllerTest.java @@ -19,13 +19,8 @@ package com.android.settings.location; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; import android.content.Context; -import android.net.wifi.WifiManager; -import android.provider.Settings; - -import com.android.settings.R; import org.junit.Before; import org.junit.Test; @@ -39,7 +34,6 @@ import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) public class LocationServicesPreferenceControllerTest { @Mock - private WifiManager mWifiManager; private Context mContext; private LocationServicesPreferenceController mController; @@ -47,7 +41,6 @@ public class LocationServicesPreferenceControllerTest { public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); - when(mContext.getSystemService(WifiManager.class)).thenReturn(mWifiManager); mController = new LocationServicesPreferenceController(mContext, "key"); } @@ -56,42 +49,6 @@ public class LocationServicesPreferenceControllerTest { assertThat(mController.isAvailable()).isTrue(); } - @Test - public void testLocationScanning_WifiOnBleOn() { - when(mWifiManager.isScanAlwaysAvailable()).thenReturn(true); - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 1); - assertThat(mController.getSummary()).isEqualTo( - mContext.getString(R.string.scanning_status_text_wifi_on_ble_on)); - } - - @Test - public void testLocationScanning_WifiOnBleOff() { - when(mWifiManager.isScanAlwaysAvailable()).thenReturn(true); - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0); - assertThat(mController.getSummary()).isEqualTo( - mContext.getString(R.string.scanning_status_text_wifi_on_ble_off)); - } - - @Test - public void testLocationScanning_WifiOffBleOn() { - when(mWifiManager.isScanAlwaysAvailable()).thenReturn(false); - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 1); - assertThat(mController.getSummary()).isEqualTo( - mContext.getString(R.string.scanning_status_text_wifi_off_ble_on)); - } - - @Test - public void testLocationScanning_WifiOffBleOff() { - when(mWifiManager.isScanAlwaysAvailable()).thenReturn(false); - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0); - assertThat(mController.getSummary()).isEqualTo( - mContext.getString(R.string.scanning_status_text_wifi_off_ble_off)); - } - @Test @Config(qualifiers = "mcc999") public void testLocationScanning_ifDisabled_shouldNotBeShown() { diff --git a/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSettingsTest.java b/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSettingsTest.java index ea908602e24..44611cec616 100644 --- a/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSettingsTest.java +++ b/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSettingsTest.java @@ -74,7 +74,7 @@ public class MobileNetworkSettingsTest { public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); - when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager); + when(mActivity.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager); when(mContext.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager); ShadowEntityHeaderController.setUseMock(mock(EntityHeaderController.class)); diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java index a2412961919..2ecc7d26d54 100644 --- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java +++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java @@ -101,7 +101,7 @@ public class WifiTetherSettingsTest { assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); - assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_AP_BAND); + assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); } @Test @@ -115,7 +115,7 @@ public class WifiTetherSettingsTest { assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME); assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD); assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF); - assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_AP_BAND); + assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY); } @Test diff --git a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java index d205607db23..ac2e24d8061 100644 --- a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java +++ b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java @@ -34,7 +34,6 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.Uri; import android.os.PersistableBundle; -import android.provider.Settings; import android.telephony.CarrierConfigManager; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; @@ -43,8 +42,6 @@ import android.telephony.TelephonyManager; import android.text.Html; import androidx.slice.Slice; -import androidx.slice.builders.GridRowBuilder; -import androidx.slice.builders.GridRowBuilder.CellBuilder; import androidx.slice.builders.ListBuilder; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -114,20 +111,6 @@ public class ProviderModelSliceHelperTest { mProviderModelSliceHelper = new MockProviderModelSliceHelper(mContext, testCustomSliceable); } - @Test - public void createMessageGridRow_inputTheResourceId_verifyTitle() { - int messageResId = ResourcesUtils.getResourcesId(mContext, "string", - "non_carrier_network_unavailable"); - CharSequence title = ResourcesUtils.getResourcesString(mContext, - "non_carrier_network_unavailable"); - - GridRowBuilder testGridRow = mProviderModelSliceHelper.createMessageGridRow(messageResId, - Settings.ACTION_AIRPLANE_MODE_SETTINGS); - List cellItem = testGridRow.getCells(); - - assertThat(cellItem.get(0).getTitle()).isEqualTo(title); - } - @Test public void getConnectedWifiItem_inputListInvolveOneConnectedWifiItem_verifyReturnItem() { when(mWifiSliceItem1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED); diff --git a/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java b/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java index 705f60ec972..4760daacef8 100644 --- a/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java +++ b/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java @@ -22,7 +22,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -42,7 +41,6 @@ import android.telephony.TelephonyManager; import androidx.slice.Slice; import androidx.slice.SliceProvider; -import androidx.slice.builders.GridRowBuilder; import androidx.slice.builders.ListBuilder; import androidx.slice.builders.SliceAction; import androidx.slice.widget.SliceLiveData; @@ -97,12 +95,6 @@ public class ProviderModelSliceTest { private WifiSliceItem mMockWifiSliceItem3; @Mock ListBuilder.RowBuilder mMockCarrierRowBuild; - @Mock - ListBuilder.HeaderBuilder mMockHeader; - @Mock - GridRowBuilder mMockGridRowBuilderNonCarrierNetworkUnavailable; - @Mock - GridRowBuilder mMockGridRowBuilderAllNetworkUnavailable; private FakeFeatureFactory mFeatureFactory; @Mock @@ -147,35 +139,7 @@ public class ProviderModelSliceTest { @Test @UiThreadTest - public void getSlice_noWorkerAndNoCarrier_getOneHeaderOneGridRowWithAllNetworkUnavailable() { - mWifiList.clear(); - mMockProviderModelSlice = new MockProviderModelSlice(mContext, null); - mockHelperCondition(false, false, false, null); - - final Slice slice = mMockProviderModelSlice.getSlice(); - - assertThat(slice).isNotNull(); - verify(mListBuilder, times(1)).setHeader(mMockHeader); - verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderAllNetworkUnavailable); - } - - @Test - @UiThreadTest - public void getSlice_noWifiAndNoCarrier_getOneHeaderOneGridRowWithAllNetworkUnavailable() { - mWifiList.clear(); - mMockNetworkProviderWorker.updateSelfResults(null); - mockHelperCondition(false, false, false, null); - - final Slice slice = mMockProviderModelSlice.getSlice(); - - assertThat(slice).isNotNull(); - verify(mListBuilder, times(1)).setHeader(mMockHeader); - verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderAllNetworkUnavailable); - } - - @Test - @UiThreadTest - public void getSlice_noWifiAndHasCarrierNoData_oneCarrierOneGridRowWithAllNetworkUnavailable() { + public void getSlice_noWifiAndHasCarrierNoData_oneCarrier() { mWifiList.clear(); mMockNetworkProviderWorker.updateSelfResults(null); mockHelperCondition(false, true, false, null); @@ -184,12 +148,11 @@ public class ProviderModelSliceTest { assertThat(slice).isNotNull(); verify(mListBuilder, times(1)).addRow(mMockCarrierRowBuild); - verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderAllNetworkUnavailable); } @Test @UiThreadTest - public void getSlice_noWifiAndNoCarrier_oneCarrierOneGridRowWithNonCarrierNetworkUnavailable() { + public void getSlice_noWifiAndNoCarrier_oneCarrier() { mWifiList.clear(); mMockProviderModelSlice = new MockProviderModelSlice(mContext, null); mockHelperCondition(false, true, true, null); @@ -198,7 +161,6 @@ public class ProviderModelSliceTest { assertThat(slice).isNotNull(); verify(mListBuilder, times(1)).addRow(mMockCarrierRowBuild); - verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderNonCarrierNetworkUnavailable); } @Test @@ -331,19 +293,6 @@ public class ProviderModelSliceTest { private void mockBuilder() { SliceAction mockSliceAction = getPrimarySliceAction(); - when(mMockHeader.getTitle()).thenReturn("mockHeader"); - when(mMockHeader.getPrimaryAction()).thenReturn(mockSliceAction); - when(mProviderModelSliceHelper.createHeader(anyString())).thenReturn(mMockHeader); - - int resId = ResourcesUtils.getResourcesId(mContext, "string", - "non_carrier_network_unavailable"); - when(mProviderModelSliceHelper.createMessageGridRow(eq(resId), anyString())).thenReturn( - mMockGridRowBuilderNonCarrierNetworkUnavailable); - resId = ResourcesUtils.getResourcesId(mContext, "string", - "all_network_unavailable"); - when(mProviderModelSliceHelper.createMessageGridRow(eq(resId), anyString())).thenReturn( - mMockGridRowBuilderAllNetworkUnavailable); - when(mMockCarrierRowBuild.getTitle()).thenReturn("mockRow"); when(mMockCarrierRowBuild.getPrimaryAction()).thenReturn(mockSliceAction); when(mProviderModelSliceHelper.createCarrierRow(anyString())).thenReturn( diff --git a/tests/unit/src/com/android/settings/panel/InternetConnectivityPanelTest.java b/tests/unit/src/com/android/settings/panel/InternetConnectivityPanelTest.java index ba5ee8e3263..8f0cfb31538 100644 --- a/tests/unit/src/com/android/settings/panel/InternetConnectivityPanelTest.java +++ b/tests/unit/src/com/android/settings/panel/InternetConnectivityPanelTest.java @@ -22,15 +22,19 @@ import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.content.Context; import android.net.Uri; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiManager; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.settings.network.AirplaneModePreferenceController; import com.android.settings.network.InternetUpdater; +import com.android.settings.network.ProviderModelSliceHelper; import com.android.settings.slices.CustomSliceRegistry; import com.android.settings.testutils.ResourcesUtils; @@ -42,6 +46,7 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import java.util.ArrayList; import java.util.List; @RunWith(AndroidJUnit4.class) @@ -55,6 +60,12 @@ public class InternetConnectivityPanelTest { ApplicationProvider.getApplicationContext(), "wifi_is_turned_on_subtitle"); public static final String BUTTON_SETTINGS = ResourcesUtils.getResourcesString( ApplicationProvider.getApplicationContext(), "settings_button"); + public static final String SUBTITLE_NON_CARRIER_NETWORK_UNAVAILABLE = + ResourcesUtils.getResourcesString(ApplicationProvider.getApplicationContext(), + "non_carrier_network_unavailable"); + public static final String SUBTITLE_ALL_NETWORK_UNAVAILABLE = + ResourcesUtils.getResourcesString(ApplicationProvider.getApplicationContext(), + "all_network_unavailable"); @Rule public final MockitoRule mMocks = MockitoJUnit.rule(); @@ -62,6 +73,10 @@ public class InternetConnectivityPanelTest { PanelContentCallback mPanelContentCallback; @Mock InternetUpdater mInternetUpdater; + @Mock + private WifiManager mWifiManager; + @Mock + private ProviderModelSliceHelper mProviderModelSliceHelper; private Context mContext; private InternetConnectivityPanel mPanel; @@ -69,11 +84,14 @@ public class InternetConnectivityPanelTest { @Before public void setUp() { mContext = spy(ApplicationProvider.getApplicationContext()); + when(mContext.getApplicationContext()).thenReturn(mContext); + when(mContext.getSystemService(WifiManager.class)).thenReturn(mWifiManager); mPanel = InternetConnectivityPanel.create(mContext); mPanel.registerCallback(mPanelContentCallback); mPanel.mIsProviderModelEnabled = true; mPanel.mInternetUpdater = mInternetUpdater; + mPanel.mProviderModelSliceHelper = mProviderModelSliceHelper; } @Test @@ -90,13 +108,6 @@ public class InternetConnectivityPanelTest { assertThat(mPanel.getTitle()).isEqualTo(TITLE_APM); } - @Test - public void getSubTitle_apmOff_shouldBeNull() { - doReturn(false).when(mInternetUpdater).isAirplaneModeOn(); - - assertThat(mPanel.getSubTitle()).isNull(); - } - @Test public void getSubTitle_apmOnWifiOff_shouldBeNull() { doReturn(true).when(mInternetUpdater).isAirplaneModeOn(); @@ -110,9 +121,43 @@ public class InternetConnectivityPanelTest { doReturn(true).when(mInternetUpdater).isAirplaneModeOn(); doReturn(true).when(mInternetUpdater).isWifiEnabled(); + mPanel.updatePanelTitle(); + assertThat(mPanel.getSubTitle()).isEqualTo(SUBTITLE_WIFI_IS_TURNED_ON); } + @Test + public void getSubTitle_apmOffWifiOnNoWifiListHasCarrierData_NonCarrierNetworkUnavailable() { + List wifiList = new ArrayList(); + mockCondition(false, true, true, true, wifiList); + + mPanel.updatePanelTitle(); + + assertThat(mPanel.getSubTitle()).isEqualTo(SUBTITLE_NON_CARRIER_NETWORK_UNAVAILABLE); + } + + @Test + public void getSubTitle_apmOffWifiOnNoWifiListNoCarrierData_AllNetworkUnavailable() { + List wifiList = new ArrayList(); + mockCondition(false, true, false, true, wifiList); + + mPanel.updatePanelTitle(); + + assertThat(mPanel.getSubTitle()).isEqualTo(SUBTITLE_ALL_NETWORK_UNAVAILABLE); + } + + @Test + public void getSubTitle_apmOffWifiOnTwoWifiItemsNoCarrierData_shouldBeNull() { + List wifiList = new ArrayList(); + wifiList.add(new ScanResult()); + wifiList.add(new ScanResult()); + mockCondition(false, true, false, true, wifiList); + + mPanel.updatePanelTitle(); + + assertThat(mPanel.getSubTitle()).isNull(); + } + @Test public void getCustomizedButtonTitle_apmOff_shouldBeSettings() { doReturn(false).when(mInternetUpdater).isAirplaneModeOn(); @@ -244,4 +289,13 @@ public class InternetConnectivityPanelTest { verify(mPanelContentCallback).onCustomizedButtonStateChanged(); } + + private void mockCondition(boolean airplaneMode, boolean hasCarrier, + boolean isDataSimActive, boolean isWifiEnabled, List wifiItems) { + doReturn(airplaneMode).when(mInternetUpdater).isAirplaneModeOn(); + when(mProviderModelSliceHelper.hasCarrier()).thenReturn(hasCarrier); + when(mProviderModelSliceHelper.isDataSimActive()).thenReturn(isDataSimActive); + doReturn(isWifiEnabled).when(mInternetUpdater).isWifiEnabled(); + doReturn(wifiItems).when(mWifiManager).getScanResults(); + } } diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java new file mode 100644 index 00000000000..a2b99bf614a --- /dev/null +++ b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java @@ -0,0 +1,217 @@ +/* + * 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.wifi.tether; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.net.wifi.SoftApConfiguration; +import android.net.wifi.WifiManager; +import android.os.Looper; + +import androidx.preference.PreferenceManager; +import androidx.preference.PreferenceScreen; +import androidx.preference.SwitchPreference; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +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; + +@RunWith(AndroidJUnit4.class) +public class WifiTetherMaximizeCompatibilityPreferenceControllerTest { + + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Mock + private WifiManager mWifiManager; + @Mock + private WifiTetherBasePreferenceController.OnTetherConfigUpdateListener mListener; + + private WifiTetherMaximizeCompatibilityPreferenceController mController; + private SwitchPreference mPreference; + private SoftApConfiguration mConfig; + + @Before + public void setUp() { + final Context context = spy(ApplicationProvider.getApplicationContext()); + mConfig = new SoftApConfiguration.Builder() + .setSsid("test_Ssid") + .setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_OPEN) + .setBridgedModeOpportunisticShutdownEnabled(true) + .build(); + doReturn(mWifiManager).when(context).getSystemService(Context.WIFI_SERVICE); + doReturn(true).when(mWifiManager).isBridgedApConcurrencySupported(); + doReturn(mConfig).when(mWifiManager).getSoftApConfiguration(); + + mController = new WifiTetherMaximizeCompatibilityPreferenceController(context, mListener); + if (Looper.myLooper() == null) { + Looper.prepare(); + } + final PreferenceManager preferenceManager = new PreferenceManager(context); + final PreferenceScreen screen = preferenceManager.createPreferenceScreen(context); + mPreference = new SwitchPreference(context); + mPreference.setKey(WifiTetherMaximizeCompatibilityPreferenceController.PREF_KEY); + screen.addPreference(mPreference); + mController.displayPreference(screen); + } + + @Test + public void getPreferenceKey_shouldBeCorrect() { + assertThat(mController.getPreferenceKey()) + .isEqualTo(WifiTetherMaximizeCompatibilityPreferenceController.PREF_KEY); + } + + @Test + public void updateDisplay_notSupport5GHzBand_setPreferenceDisabled() { + doReturn(false).when(mWifiManager).is5GHzBandSupported(); + + mController.updateDisplay(); + + assertThat(mPreference.isEnabled()).isEqualTo(false); + } + + @Test + public void updateDisplay_getNullCountryCode_setPreferenceDisabled() { + doReturn(null).when(mWifiManager).getCountryCode(); + + mController.updateDisplay(); + + assertThat(mPreference.isEnabled()).isEqualTo(false); + } + + @Test + public void updateDisplay_supported5GHzBandAndCountryCodeIsNotNull_setPreferenceEnabled() { + doReturn(true).when(mWifiManager).is5GHzBandSupported(); + doReturn("US").when(mWifiManager).getCountryCode(); + + mController.updateDisplay(); + + assertThat(mPreference.isEnabled()).isEqualTo(true); + } + + @Test + public void onPreferenceChange_callbackOnTetherConfigUpdated() { + mController.onPreferenceChange(mPreference, true); + verify(mListener).onTetherConfigUpdated(any()); + } + + @Test + public void isMaximizeCompatibilityEnabled_concurrencySupportedAndEnabled_returnTure() { + // The preconditions are ready in setup(). + + assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(true); + } + + @Test + public void isMaximizeCompatibilityEnabled_concurrencySupportedAndDisabled_returnFalse() { + SoftApConfiguration config = new SoftApConfiguration.Builder() + .setBridgedModeOpportunisticShutdownEnabled(false) + .build(); + doReturn(config).when(mWifiManager).getSoftApConfiguration(); + + assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(false); + } + + @Test + public void isMaximizeCompatibilityEnabled_noConcurrencyAndGetBand2gOnly_returnFalse() { + doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported(); + SoftApConfiguration config = new SoftApConfiguration.Builder() + .setBand(SoftApConfiguration.BAND_2GHZ) + .build(); + doReturn(config).when(mWifiManager).getSoftApConfiguration(); + + assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(false); + } + + @Test + public void isMaximizeCompatibilityEnabled_noConcurrencyAndGetBand5gOnly_returnTrue() { + doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported(); + SoftApConfiguration config = new SoftApConfiguration.Builder() + .setBand(SoftApConfiguration.BAND_5GHZ) + .build(); + doReturn(config).when(mWifiManager).getSoftApConfiguration(); + + assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(true); + } + + @Test + public void isMaximizeCompatibilityEnabled_noConcurrencyAndGetBand2gAnd5g_returnTrue() { + doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported(); + SoftApConfiguration config = new SoftApConfiguration.Builder() + .setBand(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ) + .build(); + doReturn(config).when(mWifiManager).getSoftApConfiguration(); + + assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(true); + } + + @Test + public void setupMaximizeCompatibility_concurrencySupportedAndDisabled_setDisabled() { + // The precondition of the concurrency supported is ready in setup(). + mController.onPreferenceChange(mPreference, false); + + SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder(); + mController.setupMaximizeCompatibility(builder); + + assertThat(builder.build().isBridgedModeOpportunisticShutdownEnabled()).isEqualTo(false); + } + + @Test + public void setupMaximizeCompatibility_concurrencySupportedAndEnabled_setEnabled() { + // The precondition of the concurrency supported is ready in setup(). + mController.onPreferenceChange(mPreference, true); + + SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder(); + mController.setupMaximizeCompatibility(builder); + + assertThat(builder.build().isBridgedModeOpportunisticShutdownEnabled()).isEqualTo(true); + } + + @Test + public void setupMaximizeCompatibility_noConcurrencyAndSetDisabled_setBand2gOnly() { + doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported(); + mController.onPreferenceChange(mPreference, false); + + SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder(); + mController.setupMaximizeCompatibility(builder); + + assertThat(builder.build().getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ); + } + + @Test + public void setupMaximizeCompatibility_noConcurrencyAndSetEnabled_setBand2gAnd5g() { + doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported(); + mController.onPreferenceChange(mPreference, true); + + SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder(); + mController.setupMaximizeCompatibility(builder); + + assertThat(builder.build().getBand()) + .isEqualTo(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ); + } +}