diff --git a/res/layout/battery_saver_schedule_percentage_seekbar.xml b/res/layout/battery_saver_schedule_percentage_seekbar.xml new file mode 100644 index 00000000000..78628220cdb --- /dev/null +++ b/res/layout/battery_saver_schedule_percentage_seekbar.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + diff --git a/res/layout/preference_labeled_slider.xml b/res/layout/preference_labeled_slider.xml new file mode 100644 index 00000000000..42ad988d957 --- /dev/null +++ b/res/layout/preference_labeled_slider.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/res/layout/preference_volume_slider.xml b/res/layout/preference_volume_slider.xml index 018506f09ed..7c07d5eeca5 100644 --- a/res/layout/preference_volume_slider.xml +++ b/res/layout/preference_volume_slider.xml @@ -25,13 +25,28 @@ android:clickable="false" android:orientation="horizontal"> - + + + + android:layout_marginTop="8dp" + android:layout_marginBottom="8dp"> + + + + + + + + + + + + diff --git a/res/values/attrs.xml b/res/values/attrs.xml index a5611f034bb..573d906b166 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -157,6 +157,13 @@ + + + + + + + diff --git a/res/values/styles_preference.xml b/res/values/styles_preference.xml index 620eec6ec2a..1c8d0153db9 100644 --- a/res/values/styles_preference.xml +++ b/res/values/styles_preference.xml @@ -23,6 +23,7 @@ @style/ApnPreference @style/CardPreference @style/SlicePreference + @style/SettingsSeekBarPreference @style/TwoStateButtonPreference @style/SettingsPreferenceFragmentStyle @@ -43,6 +44,10 @@ @layout/slice_preference_layout + + diff --git a/res/xml/accessibility_button_settings.xml b/res/xml/accessibility_button_settings.xml index 47ea896cdeb..02b1c7d4693 100644 --- a/res/xml/accessibility_button_settings.xml +++ b/res/xml/accessibility_button_settings.xml @@ -53,7 +53,7 @@ android:persistent="false" settings:controller="com.android.settings.accessibility.FloatingMenuFadePreferenceController"/> - - - - - diff --git a/res/xml/tts_settings.xml b/res/xml/tts_settings.xml index 920193830ca..bcc9c85f39d 100644 --- a/res/xml/tts_settings.xml +++ b/res/xml/tts_settings.xml @@ -34,14 +34,14 @@ - - mRestrictedItems = new ArrayList<>(); private boolean mRequiresActiveUnlockedProfile = false; @@ -67,10 +63,6 @@ public class RestrictedListPreference extends CustomListPreference implements Re mHelper = new RestrictedPreferenceHelper(context, this, attrs); } - public RestrictedListPreference(Context context) { - this(context, /* attrs= */ null); - } - @Override public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); @@ -115,14 +107,14 @@ public class RestrictedListPreference extends CustomListPreference implements Re super.setEnabled(enabled); } - @Override - public RestrictedPreferenceHelper getHelper() { - return mHelper; + public void setDisabledByAdmin(EnforcedAdmin admin) { + if (mHelper.setDisabledByAdmin(admin)) { + notifyChanged(); + } } - @Override - public void notifyPreferenceChanged() { - notifyChanged(); + public boolean isDisabledByAdmin() { + return mHelper.isDisabledByAdmin(); } public void setRequiresActiveUnlockedProfile(boolean reqState) { diff --git a/src/com/android/settings/accessibility/BalanceSeekBarPreference.java b/src/com/android/settings/accessibility/BalanceSeekBarPreference.java index ef779d13bdc..b03c8abd314 100644 --- a/src/com/android/settings/accessibility/BalanceSeekBarPreference.java +++ b/src/com/android/settings/accessibility/BalanceSeekBarPreference.java @@ -17,19 +17,23 @@ package com.android.settings.accessibility; import android.content.Context; +import android.media.AudioSystem; import android.os.UserHandle; import android.provider.Settings; import android.util.AttributeSet; +import android.view.View; import android.widget.ImageView; +import android.widget.SeekBar; import androidx.core.content.res.TypedArrayUtils; +import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; import com.android.settings.R; -import com.android.settingslib.RestrictedSeekBarPreference; +import com.android.settings.widget.SeekBarPreference; /** A slider preference that directly controls audio balance **/ -public class BalanceSeekBarPreference extends RestrictedSeekBarPreference { +public class BalanceSeekBarPreference extends SeekBarPreference { private static final int BALANCE_CENTER_VALUE = 100; private static final int BALANCE_MAX_VALUE = 200; diff --git a/src/com/android/settings/accessibility/FloatingMenuTransparencyPreferenceController.java b/src/com/android/settings/accessibility/FloatingMenuTransparencyPreferenceController.java index e9572d60e42..894d3aea2d4 100644 --- a/src/com/android/settings/accessibility/FloatingMenuTransparencyPreferenceController.java +++ b/src/com/android/settings/accessibility/FloatingMenuTransparencyPreferenceController.java @@ -28,10 +28,10 @@ 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; -import com.android.settingslib.widget.SeekBarPreference; /** Preference controller that controls the transparency seekbar in accessibility button page. */ public class FloatingMenuTransparencyPreferenceController extends SliderPreferenceController diff --git a/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceController.java b/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceController.java index d1dd50a83d0..7e6b42daac3 100644 --- a/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceController.java +++ b/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceController.java @@ -23,7 +23,7 @@ import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import com.android.settings.core.SliderPreferenceController; -import com.android.settingslib.widget.SeekBarPreference; +import com.android.settings.widget.SeekBarPreference; /** PreferenceController for feature intensity. */ public class ReduceBrightColorsIntensityPreferenceController extends SliderPreferenceController { diff --git a/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java index 632d4495f95..b629eaa1023 100644 --- a/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java @@ -34,9 +34,9 @@ import androidx.preference.SwitchPreference; import com.android.internal.accessibility.AccessibilityShortcutController; import com.android.settings.R; import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.widget.SeekBarPreference; import com.android.settings.widget.SettingsMainSwitchPreference; import com.android.settingslib.search.SearchIndexable; -import com.android.settingslib.widget.SeekBarPreference; import java.util.ArrayList; import java.util.List; diff --git a/src/com/android/settings/accounts/AccountRestrictionHelper.java b/src/com/android/settings/accounts/AccountRestrictionHelper.java index 8c4d220d6de..c67f5d1f9f4 100644 --- a/src/com/android/settings/accounts/AccountRestrictionHelper.java +++ b/src/com/android/settings/accounts/AccountRestrictionHelper.java @@ -27,11 +27,9 @@ import android.content.pm.UserInfo; import android.os.UserHandle; import android.os.UserManager; -import androidx.preference.Preference; - import com.android.settings.AccessiblePreferenceCategory; -import com.android.settingslib.Restrictable; import com.android.settingslib.RestrictedLockUtilsInternal; +import com.android.settingslib.RestrictedPreference; import java.util.ArrayList; @@ -43,75 +41,35 @@ public class AccountRestrictionHelper { mContext = context; } - /** - * Checks if the account should be shown based on the required authorities for the account type - * - * @param authorities given authority that is passed as activity extra - * @param auths list of authorities for particular account type - * @return true if the activity has the required authority to show the account - */ - public static boolean showAccount(String[] authorities, ArrayList auths) { - boolean showAccount = true; - if (authorities != null && auths != null) { - showAccount = false; - for (String requestedAuthority : authorities) { - if (auths.contains(requestedAuthority)) { - return true; - } - } - } - return showAccount; - } - /** * Configure the UI of the preference by checking user restriction. - * - * @param preference The preference we are configuring. + * @param preference The preference we are configuring. * @param userRestriction The user restriction related to the preference. - * @param userId The user that we retrieve user restriction of. + * @param userId The user that we retrieve user restriction of. */ - public void enforceRestrictionOnPreference(Preference preference, - String userRestriction, @UserIdInt int userId) { + public void enforceRestrictionOnPreference(RestrictedPreference preference, + String userRestriction, @UserIdInt int userId) { if (preference == null) { return; } - if (!(preference instanceof Restrictable)) { - return; - } - - final Restrictable restrictedPreference = (Restrictable) preference; - if (hasBaseUserRestriction(userRestriction, userId)) { if (userRestriction.equals(DISALLOW_REMOVE_MANAGED_PROFILE) && isOrganizationOwnedDevice()) { - restrictedPreference.setDisabledByAdmin(getEnforcedAdmin(userRestriction, userId)); + preference.setDisabledByAdmin(getEnforcedAdmin(userRestriction, userId)); } else { preference.setEnabled(false); } } else { - restrictedPreference.checkRestrictionAndSetDisabled(userRestriction, userId); + preference.checkRestrictionAndSetDisabled(userRestriction, userId); } } - /** - * Check if the system has set the user restriction. - * @param userRestriction The user restriction. - * @param userId The user that we retrieve user restriction of. - * @return {@code true} if set restriction. - */ public boolean hasBaseUserRestriction(String userRestriction, @UserIdInt int userId) { return RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext, userRestriction, userId); } - /** - * Apps can use this method to find out if the device was provisioned as - * organization-owend device. - * - * @return {@code true} if the device was provisioned as organization-owned device, - * {@code false} otherwise. - */ - public boolean isOrganizationOwnedDevice() { + private boolean isOrganizationOwnedDevice() { final DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService( Context.DEVICE_POLICY_SERVICE); if (dpm == null) { @@ -120,13 +78,7 @@ public class AccountRestrictionHelper { return dpm.isOrganizationOwnedDeviceWithManagedProfile(); } - /** - * Get the restriction enforced by admin - * @param userRestriction The user restriction. - * @param userId The user that we retrieve user restriction of. - * @return {EnforcedAdmin} - */ - public EnforcedAdmin getEnforcedAdmin(String userRestriction, int userId) { + private EnforcedAdmin getEnforcedAdmin(String userRestriction, int userId) { final DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService( Context.DEVICE_POLICY_SERVICE); if (dpm == null) { @@ -155,4 +107,23 @@ public class AccountRestrictionHelper { public AccessiblePreferenceCategory createAccessiblePreferenceCategory(Context context) { return new AccessiblePreferenceCategory(context); } + + /** + * Checks if the account should be shown based on the required authorities for the account type + * @param authorities given authority that is passed as activity extra + * @param auths list of authorities for particular account type + * @return true if the activity has the required authority to show the account + */ + public static boolean showAccount(String[] authorities, ArrayList auths) { + boolean showAccount = true; + if (authorities != null && auths != null) { + showAccount = false; + for (String requestedAuthority : authorities) { + if (auths.contains(requestedAuthority)) { + return true; + } + } + } + return showAccount; + } } diff --git a/src/com/android/settings/core/SliderPreferenceController.java b/src/com/android/settings/core/SliderPreferenceController.java index 010036ab0f5..0a7ece25d4b 100644 --- a/src/com/android/settings/core/SliderPreferenceController.java +++ b/src/com/android/settings/core/SliderPreferenceController.java @@ -19,7 +19,6 @@ import android.content.Context; import androidx.preference.Preference; import com.android.settings.slices.SliceData; -import com.android.settingslib.widget.SeekBarPreference; public abstract class SliderPreferenceController extends BasePreferenceController implements Preference.OnPreferenceChangeListener { @@ -35,10 +34,12 @@ public abstract class SliderPreferenceController extends BasePreferenceControlle @Override public void updateState(Preference preference) { - if (preference instanceof SeekBarPreference) { - ((SeekBarPreference) preference).setProgress(getSliderPosition()); + if (preference instanceof com.android.settings.widget.SeekBarPreference) { + ((com.android.settings.widget.SeekBarPreference) preference) + .setProgress(getSliderPosition()); } else if (preference instanceof androidx.preference.SeekBarPreference) { - ((androidx.preference.SeekBarPreference) preference).setValue(getSliderPosition()); + ((androidx.preference.SeekBarPreference) preference) + .setValue(getSliderPosition()); } } diff --git a/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java b/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java index 74031c464c7..a41b5828b46 100644 --- a/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java +++ b/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java @@ -24,14 +24,14 @@ import androidx.preference.PreferenceViewHolder; import com.android.settings.R; import com.android.settings.applications.appinfo.AppInfoDashboardFragment; import com.android.settings.dashboard.DashboardFragment; -import com.android.settingslib.Restrictable; +import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedPreferenceHelper; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.widget.AppSwitchPreference; public class UnrestrictedDataAccessPreference extends AppSwitchPreference implements - DataSaverBackend.Listener, Restrictable { + DataSaverBackend.Listener { private final ApplicationsState mApplicationsState; private final AppEntry mEntry; @@ -159,14 +159,12 @@ public class UnrestrictedDataAccessPreference extends AppSwitchPreference implem return mEntry; } - @Override - public RestrictedPreferenceHelper getHelper() { - return mHelper; + public boolean isDisabledByAdmin() { + return mHelper.isDisabledByAdmin(); } - @Override - public void notifyPreferenceChanged() { - notifyChanged(); + public void setDisabledByAdmin(EnforcedAdmin admin) { + mHelper.setDisabledByAdmin(admin); } // Sets UI state based on allowlist/denylist status. diff --git a/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java b/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java index 6b1cdbfad41..0f8ecc515e1 100644 --- a/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java +++ b/src/com/android/settings/display/NightDisplayIntensityPreferenceController.java @@ -24,7 +24,7 @@ import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import com.android.settings.core.SliderPreferenceController; -import com.android.settingslib.widget.SeekBarPreference; +import com.android.settings.widget.SeekBarPreference; public class NightDisplayIntensityPreferenceController extends SliderPreferenceController { diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java index 04ddb048e02..3e62ea1450d 100644 --- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java +++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java @@ -28,7 +28,7 @@ import androidx.preference.PreferenceScreen; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.R; import com.android.settings.Utils; -import com.android.settingslib.widget.SeekBarPreference; +import com.android.settings.widget.SeekBarPreference; /** * Responds to user actions in the Settings > Battery > Set a Schedule Screen for the seekbar. @@ -55,6 +55,7 @@ public class BatterySaverScheduleSeekBarController implements public BatterySaverScheduleSeekBarController(Context context) { mContext = context; mSeekBarPreference = new SeekBarPreference(context); + mSeekBarPreference.setLayoutResource(R.layout.battery_saver_schedule_percentage_seekbar); mSeekBarPreference.setOnPreferenceChangeListener(this); mSeekBarPreference.setContinuousUpdates(true); mSeekBarPreference.setMax(MAX_SEEKBAR_VALUE); diff --git a/src/com/android/settings/gestures/GestureNavigationSettingsFragment.java b/src/com/android/settings/gestures/GestureNavigationSettingsFragment.java index 9ec7424f332..546581bd128 100644 --- a/src/com/android/settings/gestures/GestureNavigationSettingsFragment.java +++ b/src/com/android/settings/gestures/GestureNavigationSettingsFragment.java @@ -27,9 +27,9 @@ import android.view.WindowManager; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.widget.LabeledSeekBarPreference; +import com.android.settings.widget.SeekBarPreference; import com.android.settingslib.search.SearchIndexable; -import com.android.settingslib.widget.LabeledSeekBarPreference; -import com.android.settingslib.widget.SeekBarPreference; /** * A fragment to include all the settings related to Gesture Navigation mode. diff --git a/src/com/android/settings/notification/AdjustVolumeRestrictedPreferenceController.java b/src/com/android/settings/notification/AdjustVolumeRestrictedPreferenceController.java index 651df01910a..f75fd4b8d0f 100644 --- a/src/com/android/settings/notification/AdjustVolumeRestrictedPreferenceController.java +++ b/src/com/android/settings/notification/AdjustVolumeRestrictedPreferenceController.java @@ -27,7 +27,7 @@ import androidx.preference.Preference; import com.android.settings.accounts.AccountRestrictionHelper; import com.android.settings.core.SliderPreferenceController; -import com.android.settingslib.Restrictable; +import com.android.settingslib.RestrictedPreference; /** * Base class for preference controller that handles preference that enforce adjust volume @@ -51,10 +51,10 @@ public abstract class AdjustVolumeRestrictedPreferenceController extends @Override public void updateState(Preference preference) { - if (!(preference instanceof Restrictable)) { + if (!(preference instanceof RestrictedPreference)) { return; } - mHelper.enforceRestrictionOnPreference(preference, + mHelper.enforceRestrictionOnPreference((RestrictedPreference) preference, UserManager.DISALLOW_ADJUST_VOLUME, UserHandle.myUserId()); } diff --git a/src/com/android/settings/notification/VolumeSeekBarPreference.java b/src/com/android/settings/notification/VolumeSeekBarPreference.java index 04ea6b087af..36a4d8aed06 100644 --- a/src/com/android/settings/notification/VolumeSeekBarPreference.java +++ b/src/com/android/settings/notification/VolumeSeekBarPreference.java @@ -32,12 +32,12 @@ import androidx.annotation.VisibleForTesting; import androidx.preference.PreferenceViewHolder; import com.android.settings.R; -import com.android.settingslib.RestrictedSeekBarPreference; +import com.android.settings.widget.SeekBarPreference; import java.util.Objects; /** A slider preference that directly controls an audio stream volume (no dialog) **/ -public class VolumeSeekBarPreference extends RestrictedSeekBarPreference { +public class VolumeSeekBarPreference extends SeekBarPreference { private static final String TAG = "VolumeSeekBarPreference"; protected SeekBar mSeekBar; diff --git a/src/com/android/settings/notification/app/BubblePreference.java b/src/com/android/settings/notification/app/BubblePreference.java index 2774a8460bc..f0046d793d0 100644 --- a/src/com/android/settings/notification/app/BubblePreference.java +++ b/src/com/android/settings/notification/app/BubblePreference.java @@ -22,6 +22,7 @@ import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED; import android.content.Context; import android.content.res.ColorStateList; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; import android.widget.ImageView; @@ -32,13 +33,13 @@ import androidx.preference.PreferenceViewHolder; import com.android.settings.Utils; import com.android.settingslib.R; -import com.android.settingslib.Restrictable; +import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedPreferenceHelper; /** * A tri-state preference allowing a user to specify what gets to bubble. */ -public class BubblePreference extends Preference implements View.OnClickListener, Restrictable { +public class BubblePreference extends Preference implements View.OnClickListener { RestrictedPreferenceHelper mHelper; private int mSelectedPreference; @@ -80,6 +81,12 @@ public class BubblePreference extends Preference implements View.OnClickListener return mSelectedPreference; } + public void setDisabledByAdmin(RestrictedLockUtils.EnforcedAdmin admin) { + if (mHelper.setDisabledByAdmin(admin)) { + notifyChanged(); + } + } + public void setSelectedVisibility(boolean visible) { mSelectedVisible = visible; notifyChanged(); @@ -142,16 +149,6 @@ public class BubblePreference extends Preference implements View.OnClickListener mBubbleNoneButton.setSelected(mContext, selected == BUBBLE_PREFERENCE_NONE); } - @Override - public RestrictedPreferenceHelper getHelper() { - return mHelper; - } - - @Override - public void notifyPreferenceChanged() { - notifyChanged(); - } - private class ButtonViewHolder { private View mView; private ImageView mImageView; diff --git a/src/com/android/settings/tts/TextToSpeechSettings.java b/src/com/android/settings/tts/TextToSpeechSettings.java index 30bc633db2a..30e99677301 100644 --- a/src/com/android/settings/tts/TextToSpeechSettings.java +++ b/src/com/android/settings/tts/TextToSpeechSettings.java @@ -49,9 +49,9 @@ import com.android.settings.Utils; import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.widget.GearPreference; +import com.android.settings.widget.SeekBarPreference; import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.widget.ActionButtonsPreference; -import com.android.settingslib.widget.SeekBarPreference; import java.text.Collator; import java.util.ArrayList; diff --git a/src/com/android/settings/widget/DefaultIndicatorSeekBar.java b/src/com/android/settings/widget/DefaultIndicatorSeekBar.java new file mode 100644 index 00000000000..d8edf030fba --- /dev/null +++ b/src/com/android/settings/widget/DefaultIndicatorSeekBar.java @@ -0,0 +1,89 @@ +/* + * 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.widget; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.widget.SeekBar; + +public class DefaultIndicatorSeekBar extends SeekBar { + + private int mDefaultProgress = -1; + + public DefaultIndicatorSeekBar(Context context) { + super(context); + } + + public DefaultIndicatorSeekBar(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public DefaultIndicatorSeekBar(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public DefaultIndicatorSeekBar(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + /** + * N.B. Only draws the default indicator tick mark, NOT equally spaced tick marks. + */ + @Override + protected void drawTickMarks(Canvas canvas) { + if (isEnabled() && mDefaultProgress <= getMax() && mDefaultProgress >= getMin()) { + final Drawable defaultIndicator = getTickMark(); + + // Adjust the drawable's bounds to center it at the point where it's drawn. + final int w = defaultIndicator.getIntrinsicWidth(); + final int h = defaultIndicator.getIntrinsicHeight(); + final int halfW = w >= 0 ? w / 2 : 1; + final int halfH = h >= 0 ? h / 2 : 1; + defaultIndicator.setBounds(-halfW, -halfH, halfW, halfH); + + // This mimics the computation of the thumb position, to get the true "default." + final int availableWidth = getWidth() - mPaddingLeft - mPaddingRight; + final int range = getMax() - getMin(); + final float scale = range > 0f ? mDefaultProgress / (float) range : 0f; + final int offset = (int) ((scale * availableWidth) + 0.5f); + final int indicatorPosition = isLayoutRtl() && getMirrorForRtl() + ? availableWidth - offset + mPaddingRight : offset + mPaddingLeft; + + final int saveCount = canvas.save(); + canvas.translate(indicatorPosition, getHeight() / 2); + defaultIndicator.draw(canvas); + canvas.restoreToCount(saveCount); + } + } + + /** + * N.B. This sets the default *unadjusted* progress, i.e. in the SeekBar's [0 - max] terms. + */ + public void setDefaultProgress(int defaultProgress) { + if (mDefaultProgress != defaultProgress) { + mDefaultProgress = defaultProgress; + invalidate(); + } + } + + public int getDefaultProgress() { + return mDefaultProgress; + } +} diff --git a/src/com/android/settings/widget/LabeledSeekBarPreference.java b/src/com/android/settings/widget/LabeledSeekBarPreference.java new file mode 100644 index 00000000000..3a9ac18beb0 --- /dev/null +++ b/src/com/android/settings/widget/LabeledSeekBarPreference.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2019 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.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.widget.SeekBar; +import android.widget.TextView; + +import androidx.core.content.res.TypedArrayUtils; +import androidx.preference.PreferenceViewHolder; + +import com.android.settings.R; + +/** A slider preference with left and right labels **/ +public class LabeledSeekBarPreference extends SeekBarPreference { + + private final int mTextStartId; + private final int mTextEndId; + private final int mTickMarkId; + private OnPreferenceChangeListener mStopListener; + + public LabeledSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + + super(context, attrs, defStyleAttr, defStyleRes); + setLayoutResource(R.layout.preference_labeled_slider); + + final TypedArray styledAttrs = context.obtainStyledAttributes(attrs, + R.styleable.LabeledSeekBarPreference); + mTextStartId = styledAttrs.getResourceId( + R.styleable.LabeledSeekBarPreference_textStart, + R.string.summary_placeholder); + mTextEndId = styledAttrs.getResourceId( + R.styleable.LabeledSeekBarPreference_textEnd, + R.string.summary_placeholder); + mTickMarkId = styledAttrs.getResourceId( + R.styleable.LabeledSeekBarPreference_tickMark, /* defValue= */ 0); + styledAttrs.recycle(); + } + + public LabeledSeekBarPreference(Context context, AttributeSet attrs) { + this(context, attrs, TypedArrayUtils.getAttr(context, + androidx.preference.R.attr.seekBarPreferenceStyle, + com.android.internal.R.attr.seekBarPreferenceStyle), 0); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + final TextView startText = (TextView) holder.findViewById(android.R.id.text1); + final TextView endText = (TextView) holder.findViewById(android.R.id.text2); + startText.setText(mTextStartId); + endText.setText(mTextEndId); + + if (mTickMarkId != 0) { + final Drawable tickMark = getContext().getDrawable(mTickMarkId); + final SeekBar seekBar = (SeekBar) holder.findViewById( + com.android.internal.R.id.seekbar); + seekBar.setTickMark(tickMark); + } + } + + public void setOnPreferenceChangeStopListener(OnPreferenceChangeListener listener) { + mStopListener = listener; + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + super.onStopTrackingTouch(seekBar); + + if (mStopListener != null) { + mStopListener.onPreferenceChange(this, seekBar.getProgress()); + } + } +} + diff --git a/src/com/android/settings/widget/RestrictedAppPreference.java b/src/com/android/settings/widget/RestrictedAppPreference.java index 5878fd5293f..cd953571ab6 100644 --- a/src/com/android/settings/widget/RestrictedAppPreference.java +++ b/src/com/android/settings/widget/RestrictedAppPreference.java @@ -26,7 +26,7 @@ import androidx.preference.PreferenceManager; import androidx.preference.PreferenceViewHolder; import com.android.settings.R; -import com.android.settingslib.Restrictable; +import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedPreferenceHelper; import com.android.settingslib.widget.AppPreference; @@ -35,7 +35,7 @@ import com.android.settingslib.widget.AppPreference; * {@link com.android.settingslib.RestrictedPreferenceHelper}. * Used to show policy transparency on {@link AppPreference}. */ -public class RestrictedAppPreference extends AppPreference implements Restrictable { +public class RestrictedAppPreference extends AppPreference { private RestrictedPreferenceHelper mHelper; private String userRestriction; @@ -85,14 +85,14 @@ public class RestrictedAppPreference extends AppPreference implements Restrictab super.setEnabled(enabled); } - @Override - public RestrictedPreferenceHelper getHelper() { - return mHelper; + public void setDisabledByAdmin(RestrictedLockUtils.EnforcedAdmin admin) { + if (mHelper.setDisabledByAdmin(admin)) { + notifyChanged(); + } } - @Override - public void notifyPreferenceChanged() { - notifyChanged(); + public boolean isDisabledByAdmin() { + return mHelper.isDisabledByAdmin(); } public void useAdminDisabledSummary(boolean useSummary) { @@ -111,4 +111,12 @@ public class RestrictedAppPreference extends AppPreference implements Restrictab } mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId()); } + + public void checkRestrictionAndSetDisabled(String userRestriction) { + mHelper.checkRestrictionAndSetDisabled(userRestriction, UserHandle.myUserId()); + } + + public void checkRestrictionAndSetDisabled(String userRestriction, int userId) { + mHelper.checkRestrictionAndSetDisabled(userRestriction, userId); + } } diff --git a/src/com/android/settings/widget/SeekBarPreference.java b/src/com/android/settings/widget/SeekBarPreference.java new file mode 100644 index 00000000000..47bb28608d5 --- /dev/null +++ b/src/com/android/settings/widget/SeekBarPreference.java @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2011 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 static android.view.HapticFeedbackConstants.CLOCK_TICK; + +import android.content.Context; +import android.content.res.TypedArray; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; + +import androidx.core.content.res.TypedArrayUtils; +import androidx.preference.PreferenceViewHolder; + +import com.android.settingslib.RestrictedPreference; + +/** + * Based on android.preference.SeekBarPreference, but uses support preference as base. + */ +public class SeekBarPreference extends RestrictedPreference + implements OnSeekBarChangeListener, View.OnKeyListener { + + public static final int HAPTIC_FEEDBACK_MODE_NONE = 0; + public static final int HAPTIC_FEEDBACK_MODE_ON_TICKS = 1; + public static final int HAPTIC_FEEDBACK_MODE_ON_ENDS = 2; + + private int mProgress; + private int mMax; + private int mMin; + private boolean mTrackingTouch; + + private boolean mContinuousUpdates; + private int mHapticFeedbackMode = HAPTIC_FEEDBACK_MODE_NONE; + private int mDefaultProgress = -1; + + private SeekBar mSeekBar; + private boolean mShouldBlink; + private int mAccessibilityRangeInfoType = AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT; + private CharSequence mSeekBarContentDescription; + private CharSequence mSeekBarStateDescription; + + public SeekBarPreference( + Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + + TypedArray a = context.obtainStyledAttributes( + attrs, com.android.internal.R.styleable.ProgressBar, defStyleAttr, defStyleRes); + setMax(a.getInt(com.android.internal.R.styleable.ProgressBar_max, mMax)); + setMin(a.getInt(com.android.internal.R.styleable.ProgressBar_min, mMin)); + a.recycle(); + + a = context.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.SeekBarPreference, defStyleAttr, defStyleRes); + final int layoutResId = a.getResourceId( + com.android.internal.R.styleable.SeekBarPreference_layout, + com.android.internal.R.layout.preference_widget_seekbar); + a.recycle(); + + a = context.obtainStyledAttributes( + attrs, com.android.internal.R.styleable.Preference, defStyleAttr, defStyleRes); + final boolean isSelectable = a.getBoolean( + com.android.settings.R.styleable.Preference_android_selectable, false); + setSelectable(isSelectable); + a.recycle(); + + setLayoutResource(layoutResId); + } + + public SeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public SeekBarPreference(Context context, AttributeSet attrs) { + this(context, attrs, TypedArrayUtils.getAttr(context, + androidx.preference.R.attr.seekBarPreferenceStyle, + com.android.internal.R.attr.seekBarPreferenceStyle)); + } + + public SeekBarPreference(Context context) { + this(context, null); + } + + public void setShouldBlink(boolean shouldBlink) { + mShouldBlink = shouldBlink; + notifyChanged(); + } + + @Override + public boolean isSelectable() { + if(isDisabledByAdmin()) { + return true; + } else { + return super.isSelectable(); + } + } + + @Override + public void onBindViewHolder(PreferenceViewHolder view) { + super.onBindViewHolder(view); + view.itemView.setOnKeyListener(this); + mSeekBar = (SeekBar) view.findViewById( + com.android.internal.R.id.seekbar); + mSeekBar.setOnSeekBarChangeListener(this); + mSeekBar.setMax(mMax); + mSeekBar.setMin(mMin); + mSeekBar.setProgress(mProgress); + mSeekBar.setEnabled(isEnabled()); + final CharSequence title = getTitle(); + if (!TextUtils.isEmpty(mSeekBarContentDescription)) { + mSeekBar.setContentDescription(mSeekBarContentDescription); + } else if (!TextUtils.isEmpty(title)) { + mSeekBar.setContentDescription(title); + } + if (!TextUtils.isEmpty(mSeekBarStateDescription)) { + mSeekBar.setStateDescription(mSeekBarStateDescription); + } + if (mSeekBar instanceof DefaultIndicatorSeekBar) { + ((DefaultIndicatorSeekBar) mSeekBar).setDefaultProgress(mDefaultProgress); + } + if (mShouldBlink) { + View v = view.itemView; + v.post(() -> { + if (v.getBackground() != null) { + final int centerX = v.getWidth() / 2; + final int centerY = v.getHeight() / 2; + v.getBackground().setHotspot(centerX, centerY); + } + v.setPressed(true); + v.setPressed(false); + mShouldBlink = false; + }); + } + mSeekBar.setAccessibilityDelegate(new View.AccessibilityDelegate() { + @Override + public void onInitializeAccessibilityNodeInfo(View view, AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(view, info); + // Update the range info with the correct type + AccessibilityNodeInfo.RangeInfo rangeInfo = info.getRangeInfo(); + if (rangeInfo != null) { + info.setRangeInfo(AccessibilityNodeInfo.RangeInfo.obtain( + mAccessibilityRangeInfoType, rangeInfo.getMin(), + rangeInfo.getMax(), rangeInfo.getCurrent())); + } + } + }); + } + + @Override + public CharSequence getSummary() { + return null; + } + + @Override + protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { + setProgress(restoreValue ? getPersistedInt(mProgress) + : (Integer) defaultValue); + } + + @Override + protected Object onGetDefaultValue(TypedArray a, int index) { + return a.getInt(index, 0); + } + + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (event.getAction() != KeyEvent.ACTION_DOWN) { + return false; + } + + SeekBar seekBar = (SeekBar) v.findViewById(com.android.internal.R.id.seekbar); + if (seekBar == null) { + return false; + } + return seekBar.onKeyDown(keyCode, event); + } + + public void setMax(int max) { + if (max != mMax) { + mMax = max; + notifyChanged(); + } + } + + public void setMin(int min) { + if (min != mMin) { + mMin = min; + notifyChanged(); + } + } + + public int getMax() { + return mMax; + } + + public int getMin() { + return mMin; + } + + public void setProgress(int progress) { + setProgress(progress, true); + } + + /** + * Sets the progress point to draw a single tick mark representing a default value. + */ + public void setDefaultProgress(int defaultProgress) { + if (mDefaultProgress != defaultProgress) { + mDefaultProgress = defaultProgress; + if (mSeekBar instanceof DefaultIndicatorSeekBar) { + ((DefaultIndicatorSeekBar) mSeekBar).setDefaultProgress(mDefaultProgress); + } + } + } + + /** + * When {@code continuousUpdates} is true, update the persisted setting immediately as the thumb + * is dragged along the SeekBar. Otherwise, only update the value of the setting when the thumb + * is dropped. + */ + public void setContinuousUpdates(boolean continuousUpdates) { + mContinuousUpdates = continuousUpdates; + } + + /** + * Sets the haptic feedback mode. HAPTIC_FEEDBACK_MODE_ON_TICKS means to perform haptic feedback + * as the SeekBar's progress is updated; HAPTIC_FEEDBACK_MODE_ON_ENDS means to perform haptic + * feedback as the SeekBar's progress value is equal to the min/max value. + * + * @param hapticFeedbackMode the haptic feedback mode. + */ + public void setHapticFeedbackMode(int hapticFeedbackMode) { + mHapticFeedbackMode = hapticFeedbackMode; + } + + private void setProgress(int progress, boolean notifyChanged) { + if (progress > mMax) { + progress = mMax; + } + if (progress < mMin) { + progress = mMin; + } + if (progress != mProgress) { + mProgress = progress; + persistInt(progress); + if (notifyChanged) { + notifyChanged(); + } + } + } + + public int getProgress() { + return mProgress; + } + + /** + * Persist the seekBar's progress value if callChangeListener + * returns true, otherwise set the seekBar's progress to the stored value + */ + void syncProgress(SeekBar seekBar) { + int progress = seekBar.getProgress(); + if (progress != mProgress) { + if (callChangeListener(progress)) { + setProgress(progress, false); + switch (mHapticFeedbackMode) { + case HAPTIC_FEEDBACK_MODE_ON_TICKS: + seekBar.performHapticFeedback(CLOCK_TICK); + break; + case HAPTIC_FEEDBACK_MODE_ON_ENDS: + if (progress == mMax || progress == mMin) { + seekBar.performHapticFeedback(CLOCK_TICK); + } + break; + } + } else { + seekBar.setProgress(mProgress); + } + } + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (fromUser && (mContinuousUpdates || !mTrackingTouch)) { + syncProgress(seekBar); + } + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + mTrackingTouch = true; + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + mTrackingTouch = false; + if (seekBar.getProgress() != mProgress) { + syncProgress(seekBar); + } + } + + /** + * Specify the type of range this seek bar represents. + * + * @param rangeInfoType The type of range to be shared with accessibility + * + * @see android.view.accessibility.AccessibilityNodeInfo.RangeInfo + */ + public void setAccessibilityRangeInfoType(int rangeInfoType) { + mAccessibilityRangeInfoType = rangeInfoType; + } + + public void setSeekBarContentDescription(CharSequence contentDescription) { + mSeekBarContentDescription = contentDescription; + if (mSeekBar != null) { + mSeekBar.setContentDescription(contentDescription); + } + } + + /** + * Specify the state description for this seek bar represents. + * + * @param stateDescription the state description of seek bar + */ + public void setSeekBarStateDescription(CharSequence stateDescription) { + mSeekBarStateDescription = stateDescription; + if (mSeekBar != null) { + mSeekBar.setStateDescription(stateDescription); + } + } + + @Override + protected Parcelable onSaveInstanceState() { + /* + * Suppose a client uses this preference type without persisting. We + * must save the instance state so it is able to, for example, survive + * orientation changes. + */ + + final Parcelable superState = super.onSaveInstanceState(); + if (isPersistent()) { + // No need to save instance state since it's persistent + return superState; + } + + // Save the instance state + final SavedState myState = new SavedState(superState); + myState.progress = mProgress; + myState.max = mMax; + myState.min = mMin; + return myState; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (!state.getClass().equals(SavedState.class)) { + // Didn't save state for us in onSaveInstanceState + super.onRestoreInstanceState(state); + return; + } + + // Restore the instance state + SavedState myState = (SavedState) state; + super.onRestoreInstanceState(myState.getSuperState()); + mProgress = myState.progress; + mMax = myState.max; + mMin = myState.min; + notifyChanged(); + } + + /** + * SavedState, a subclass of {@link BaseSavedState}, will store the state + * of MyPreference, a subclass of Preference. + *

+ * It is important to always call through to super methods. + */ + private static class SavedState extends BaseSavedState { + int progress; + int max; + int min; + + public SavedState(Parcel source) { + super(source); + + // Restore the click counter + progress = source.readInt(); + max = source.readInt(); + min = source.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + + // Save the click counter + dest.writeInt(progress); + dest.writeInt(max); + dest.writeInt(min); + } + + public SavedState(Parcelable superState) { + super(superState); + } + + @SuppressWarnings("unused") + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } +} diff --git a/tests/robotests/res/xml-mcc998/seekbar_preference.xml b/tests/robotests/res/xml-mcc998/seekbar_preference.xml index 3efd3a1eb55..e474c1e06bb 100644 --- a/tests/robotests/res/xml-mcc998/seekbar_preference.xml +++ b/tests/robotests/res/xml-mcc998/seekbar_preference.xml @@ -17,7 +17,7 @@ - diff --git a/tests/robotests/res/xml-mcc999/seekbar_preference.xml b/tests/robotests/res/xml-mcc999/seekbar_preference.xml index 68cdbdadc1d..77435e8d198 100644 --- a/tests/robotests/res/xml-mcc999/seekbar_preference.xml +++ b/tests/robotests/res/xml-mcc999/seekbar_preference.xml @@ -17,7 +17,7 @@ - diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuTransparencyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuTransparencyPreferenceControllerTest.java index 216368e1a07..55637cd137d 100644 --- a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuTransparencyPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuTransparencyPreferenceControllerTest.java @@ -36,7 +36,7 @@ import android.provider.Settings; import androidx.test.core.app.ApplicationProvider; -import com.android.settingslib.widget.SeekBarPreference; +import com.android.settings.widget.SeekBarPreference; import org.junit.Before; import org.junit.Rule; diff --git a/tests/robotests/src/com/android/settings/gestures/LabeledSeekBarPreferenceTest.java b/tests/robotests/src/com/android/settings/gestures/LabeledSeekBarPreferenceTest.java new file mode 100644 index 00000000000..4fb94f8cad5 --- /dev/null +++ b/tests/robotests/src/com/android/settings/gestures/LabeledSeekBarPreferenceTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 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.gestures; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.SeekBar; + +import androidx.preference.Preference; + +import com.android.settings.widget.LabeledSeekBarPreference; + +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 LabeledSeekBarPreferenceTest { + + private Context mContext; + private SeekBar mSeekBar; + private LabeledSeekBarPreference mSeekBarPreference; + + @Mock + private Preference.OnPreferenceChangeListener mListener; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = RuntimeEnvironment.application; + mSeekBarPreference = new LabeledSeekBarPreference(mContext, null); + LayoutInflater inflater = LayoutInflater.from(mContext); + final View view = + inflater.inflate(mSeekBarPreference.getLayoutResource(), + new LinearLayout(mContext), false); + mSeekBar = view.findViewById(com.android.internal.R.id.seekbar); + } + + @Test + public void seekBarPreferenceOnStopTrackingTouch_callsListener() { + mSeekBar.setProgress(2); + + mSeekBarPreference.setOnPreferenceChangeStopListener(mListener); + mSeekBarPreference.onStopTrackingTouch(mSeekBar); + + verify(mListener, times(1)).onPreferenceChange(mSeekBarPreference, 2); + } +} diff --git a/tests/robotests/src/com/android/settings/notification/AdjustVolumeRestrictedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/AdjustVolumeRestrictedPreferenceControllerTest.java index a43fa013958..de001896ea9 100644 --- a/tests/robotests/src/com/android/settings/notification/AdjustVolumeRestrictedPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/AdjustVolumeRestrictedPreferenceControllerTest.java @@ -20,20 +20,19 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; -import android.os.UserHandle; import android.os.UserManager; import androidx.preference.Preference; import com.android.settings.accounts.AccountRestrictionHelper; -import com.android.settingslib.RestrictedLockUtilsInternal; -import com.android.settingslib.RestrictedSeekBarPreference; +import com.android.settingslib.RestrictedPreference; import org.junit.Before; import org.junit.Test; @@ -47,10 +46,8 @@ import org.robolectric.RuntimeEnvironment; public class AdjustVolumeRestrictedPreferenceControllerTest { private static final String KEY = "key"; - private AccountRestrictionHelper mAccountHelper; - @Mock - UserManager mUserManager; + private AccountRestrictionHelper mAccountHelper; private Context mContext; private AdjustVolumeRestrictedPreferenceControllerTestable mController; @@ -59,17 +56,15 @@ public class AdjustVolumeRestrictedPreferenceControllerTest { public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); - when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); - mAccountHelper = new AccountRestrictionHelper(mContext); mController = new AdjustVolumeRestrictedPreferenceControllerTestable(mContext, mAccountHelper, KEY); } @Test public void updateState_hasBaseRestriction_shouldDisable() { - RestrictedSeekBarPreference preference = mock(RestrictedSeekBarPreference.class); - when(RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext, - UserManager.DISALLOW_ADJUST_VOLUME, UserHandle.myUserId())).thenReturn(true); + RestrictedPreference preference = mock(RestrictedPreference.class); + when(mAccountHelper.hasBaseUserRestriction( + eq(UserManager.DISALLOW_ADJUST_VOLUME), anyInt())).thenReturn(true); mController.updateState(preference); @@ -78,11 +73,13 @@ public class AdjustVolumeRestrictedPreferenceControllerTest { @Test public void updateState_NoBaseRestriction_shouldCheckRestriction() { - RestrictedSeekBarPreference preference = spy(new RestrictedSeekBarPreference(mContext)); + RestrictedPreference preference = spy(new RestrictedPreference(mContext)); when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(null); - when(RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext, - UserManager.DISALLOW_ADJUST_VOLUME, UserHandle.myUserId())).thenReturn(false); + when(mAccountHelper.hasBaseUserRestriction( + eq(UserManager.DISALLOW_ADJUST_VOLUME), anyInt())).thenReturn(false); + doCallRealMethod().when(mAccountHelper).enforceRestrictionOnPreference( + eq(preference), eq(UserManager.DISALLOW_ADJUST_VOLUME), anyInt()); mController.updateState(preference); diff --git a/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java b/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java index d24fe16b4c7..8b46374b684 100644 --- a/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/RemoteVolumeGroupControllerTest.java @@ -43,8 +43,8 @@ import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; +import com.android.settings.widget.SeekBarPreference; import com.android.settingslib.media.LocalMediaManager; -import com.android.settingslib.widget.SeekBarPreference; import org.junit.Before; import org.junit.Test; diff --git a/tests/robotests/src/com/android/settings/security/trustagent/TrustAgentsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/security/trustagent/TrustAgentsPreferenceControllerTest.java index cd192ef5220..d7aa42c5737 100644 --- a/tests/robotests/src/com/android/settings/security/trustagent/TrustAgentsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/security/trustagent/TrustAgentsPreferenceControllerTest.java @@ -28,6 +28,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.service.trust.TrustAgentService; +import android.text.TextUtils; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; diff --git a/tests/robotests/src/com/android/settings/widget/SeekBarPreferenceTest.java b/tests/robotests/src/com/android/settings/widget/SeekBarPreferenceTest.java new file mode 100644 index 00000000000..451b84bd882 --- /dev/null +++ b/tests/robotests/src/com/android/settings/widget/SeekBarPreferenceTest.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.widget; + +import static android.view.HapticFeedbackConstants.CLOCK_TICK; +import static android.view.HapticFeedbackConstants.CONTEXT_CLICK; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; + +import android.content.Context; +import android.os.Bundle; +import android.os.Parcelable; +import android.widget.SeekBar; + +import androidx.preference.PreferenceFragmentCompat; + +import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.androidx.fragment.FragmentController; + +@RunWith(RobolectricTestRunner.class) +@Config(shadows = ShadowRestrictedLockUtilsInternal.class) +public class SeekBarPreferenceTest { + + private static final int MAX = 75; + private static final int MIN = 5; + private static final int PROGRESS = 16; + private static final int NEW_PROGRESS = 17; + + private Context mContext; + private SeekBarPreference mSeekBarPreference; + private SeekBar mSeekBar; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + + mSeekBarPreference = spy(new SeekBarPreference(mContext)); + mSeekBarPreference.setMax(MAX); + mSeekBarPreference.setMin(MIN); + mSeekBarPreference.setProgress(PROGRESS); + mSeekBarPreference.setPersistent(false); + mSeekBarPreference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_NONE); + + mSeekBar = new SeekBar(mContext); + mSeekBar.setMax(MAX); + mSeekBar.setMin(MIN); + } + + @Test + public void testSaveAndRestoreInstanceState() { + final Parcelable parcelable = mSeekBarPreference.onSaveInstanceState(); + + final SeekBarPreference preference = new SeekBarPreference(mContext); + preference.onRestoreInstanceState(parcelable); + + assertThat(preference.getMax()).isEqualTo(MAX); + assertThat(preference.getMin()).isEqualTo(MIN); + assertThat(preference.getProgress()).isEqualTo(PROGRESS); + } + + @Test + public void isSelectable_disabledByAdmin_returnTrue() { + when(mSeekBarPreference.isDisabledByAdmin()).thenReturn(true); + + assertThat(mSeekBarPreference.isSelectable()).isTrue(); + } + + @Test + @Config(qualifiers = "mcc998") + public void isSelectable_default_returnFalse() { + final PreferenceFragmentCompat fragment = FragmentController.of(new TestFragment(), + new Bundle()) + .create() + .start() + .resume() + .get(); + + final SeekBarPreference seekBarPreference = fragment.findPreference("seek_bar"); + + assertThat(seekBarPreference.isSelectable()).isFalse(); + } + + @Test + @Config(qualifiers = "mcc999") + public void isSelectable_selectableInXml_returnTrue() { + final PreferenceFragmentCompat fragment = FragmentController.of(new TestFragment(), + new Bundle()) + .create() + .start() + .resume() + .get(); + + final SeekBarPreference seekBarPreference = fragment.findPreference("seek_bar"); + + assertThat(seekBarPreference.isSelectable()).isTrue(); + } + + @Test + public void testSetSeekBarStateDescription() { + mSeekBarPreference.setSeekBarStateDescription("test"); + + verify(mSeekBarPreference).setSeekBarStateDescription("test"); + } + + @Test + public void onProgressChanged_hapticFeedbackModeNone_clockTickFeedbackNotPerformed() { + mSeekBar.setProgress(NEW_PROGRESS); + when(mSeekBarPreference.callChangeListener(anyInt())).thenReturn(true); + mSeekBar.performHapticFeedback(CONTEXT_CLICK); + + mSeekBarPreference.onProgressChanged(mSeekBar, NEW_PROGRESS, true); + + assertThat(shadowOf(mSeekBar).lastHapticFeedbackPerformed()).isNotEqualTo(CLOCK_TICK); + } + + @Test + public void onProgressChanged_hapticFeedbackModeOnTicks_clockTickFeedbackPerformed() { + mSeekBarPreference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_TICKS); + mSeekBar.setProgress(NEW_PROGRESS); + when(mSeekBarPreference.callChangeListener(anyInt())).thenReturn(true); + mSeekBar.performHapticFeedback(CONTEXT_CLICK); + + mSeekBarPreference.onProgressChanged(mSeekBar, NEW_PROGRESS, true); + + assertThat(shadowOf(mSeekBar).lastHapticFeedbackPerformed()).isEqualTo(CLOCK_TICK); + } + + @Test + public void onProgressChanged_hapticFeedbackModeOnEnds_clockTickFeedbackNotPerformed() { + mSeekBarPreference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_ENDS); + mSeekBar.setProgress(NEW_PROGRESS); + when(mSeekBarPreference.callChangeListener(anyInt())).thenReturn(true); + mSeekBar.performHapticFeedback(CONTEXT_CLICK); + + mSeekBarPreference.onProgressChanged(mSeekBar, NEW_PROGRESS, true); + + assertThat(shadowOf(mSeekBar).lastHapticFeedbackPerformed()).isNotEqualTo(CLOCK_TICK); + } + + @Test + public void onProgressChanged_hapticFeedbackModeOnEndsAndMinValue_clockTickFeedbackPerformed() { + mSeekBarPreference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_ENDS); + mSeekBar.setProgress(MIN); + when(mSeekBarPreference.callChangeListener(anyInt())).thenReturn(true); + mSeekBar.performHapticFeedback(CONTEXT_CLICK); + + mSeekBarPreference.onProgressChanged(mSeekBar, MIN, true); + + assertThat(shadowOf(mSeekBar).lastHapticFeedbackPerformed()).isEqualTo(CLOCK_TICK); + } + + public static class TestFragment extends PreferenceFragmentCompat { + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(com.android.settings.R.xml.seekbar_preference); + } + } +} diff --git a/tests/unit/src/com/android/settings/core/SettingsSliderPreferenceControllerTest.java b/tests/unit/src/com/android/settings/core/SettingsSliderPreferenceControllerTest.java index 982eb3714e0..846ffd753af 100644 --- a/tests/unit/src/com/android/settings/core/SettingsSliderPreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/core/SettingsSliderPreferenceControllerTest.java @@ -23,7 +23,7 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.settings.slices.SliceData; -import com.android.settingslib.widget.SeekBarPreference; +import com.android.settings.widget.SeekBarPreference; import org.junit.Before; import org.junit.Test; diff --git a/tests/unit/src/com/android/settings/widget/DefaultIndicatorSeekBarTest.java b/tests/unit/src/com/android/settings/widget/DefaultIndicatorSeekBarTest.java new file mode 100644 index 00000000000..777d9845cd3 --- /dev/null +++ b/tests/unit/src/com/android/settings/widget/DefaultIndicatorSeekBarTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 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 static org.junit.Assert.assertEquals; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class DefaultIndicatorSeekBarTest { + + private DefaultIndicatorSeekBar mDefaultIndicatorSeekBar; + + @Before + public void setUp() { + mDefaultIndicatorSeekBar = new DefaultIndicatorSeekBar( + ApplicationProvider.getApplicationContext()); + mDefaultIndicatorSeekBar.setMax(100); + } + + @After + public void tearDown() { + mDefaultIndicatorSeekBar = null; + } + + @Test + public void defaultProgress_setSucceeds() { + mDefaultIndicatorSeekBar.setDefaultProgress(40); + assertEquals(40, mDefaultIndicatorSeekBar.getDefaultProgress()); + } +}