Merge "Create tooltip for notifying auto-adding the font scaling tile" into udc-dev am: 6800d9f6c2
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/22135679 Change-Id: If3839dbc44403db3a86659090556d25b538284e9 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -4595,6 +4595,8 @@
|
||||
<string name="accessibility_one_handed_mode_auto_added_qs_tooltip_content">One-handed mode added to Quick Settings. Swipe down to turn it on or off anytime.</string>
|
||||
<!-- Used in the One-hand mode settings to show quick settings tooltip. [CHAR LIMIT=NONE] -->
|
||||
<string name="accessibility_one_handed_mode_qs_tooltip_content">You can also add one-handed mode to Quick Settings from the top of your screen</string>
|
||||
<!-- Used in the font size settings to show quick settings tooltip for auto-added feature. [CHAR LIMIT=NONE] -->
|
||||
<string name="accessibility_font_scaling_auto_added_qs_tooltip_content">Font size added to Quick Settings. Swipe down to change the font size anytime.</string>
|
||||
<!-- Used in the accessibility action for accessibility quick settings tooltip to dismiss. [CHAR LIMIT=NONE] -->
|
||||
<string name="accessibility_quick_settings_tooltip_dismiss">Dismiss</string>
|
||||
<!-- Used in the Color correction settings screen to control turning on/off the feature entirely [CHAR LIMIT=60] -->
|
||||
|
@@ -16,14 +16,22 @@
|
||||
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.widget.SeekBar;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.widget.LabeledSeekBarPreference;
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
import com.android.settingslib.core.lifecycle.events.OnCreate;
|
||||
import com.android.settingslib.core.lifecycle.events.OnDestroy;
|
||||
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -31,12 +39,19 @@ import java.util.Optional;
|
||||
* The controller of {@link LabeledSeekBarPreference} that listens to display size and font size
|
||||
* settings changes and updates preview size threshold smoothly.
|
||||
*/
|
||||
class PreviewSizeSeekBarController extends BasePreferenceController implements
|
||||
TextReadingResetController.ResetStateListener {
|
||||
abstract class PreviewSizeSeekBarController extends BasePreferenceController implements
|
||||
TextReadingResetController.ResetStateListener, LifecycleObserver, OnCreate,
|
||||
OnDestroy, OnSaveInstanceState {
|
||||
private final PreviewSizeData<? extends Number> mSizeData;
|
||||
private static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
|
||||
private boolean mSeekByTouch;
|
||||
private Optional<ProgressInteractionListener> mInteractionListener = Optional.empty();
|
||||
private LabeledSeekBarPreference mSeekBarPreference;
|
||||
private int mLastProgress;
|
||||
private boolean mNeedsQSTooltipReshow = false;
|
||||
private AccessibilityQuickSettingsTooltipWindow mTooltipWindow;
|
||||
private final Handler mHandler;
|
||||
|
||||
|
||||
private final SeekBar.OnSeekBarChangeListener mSeekBarChangeListener =
|
||||
new SeekBar.OnSeekBarChangeListener() {
|
||||
@@ -54,6 +69,7 @@ class PreviewSizeSeekBarController extends BasePreferenceController implements
|
||||
|
||||
if (!mSeekByTouch) {
|
||||
interactionListener.onProgressChanged();
|
||||
onProgressFinalized();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +83,7 @@ class PreviewSizeSeekBarController extends BasePreferenceController implements
|
||||
mSeekByTouch = false;
|
||||
|
||||
mInteractionListener.ifPresent(ProgressInteractionListener::onEndTrackingTouch);
|
||||
onProgressFinalized();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -74,6 +91,30 @@ class PreviewSizeSeekBarController extends BasePreferenceController implements
|
||||
@NonNull PreviewSizeData<? extends Number> sizeData) {
|
||||
super(context, preferenceKey);
|
||||
mSizeData = sizeData;
|
||||
mHandler = new Handler(context.getMainLooper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
// Restore the tooltip.
|
||||
if (savedInstanceState != null
|
||||
&& savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_RESHOW)) {
|
||||
mNeedsQSTooltipReshow = savedInstanceState.getBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
// remove runnables in the queue.
|
||||
mHandler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
|
||||
if (mNeedsQSTooltipReshow || isTooltipWindowShowing) {
|
||||
outState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, /* value= */ true);
|
||||
}
|
||||
}
|
||||
|
||||
void setInteractionListener(ProgressInteractionListener interactionListener) {
|
||||
@@ -91,11 +132,15 @@ class PreviewSizeSeekBarController extends BasePreferenceController implements
|
||||
|
||||
final int dataSize = mSizeData.getValues().size();
|
||||
final int initialIndex = mSizeData.getInitialIndex();
|
||||
mLastProgress = initialIndex;
|
||||
mSeekBarPreference = screen.findPreference(getPreferenceKey());
|
||||
mSeekBarPreference.setMax(dataSize - 1);
|
||||
mSeekBarPreference.setProgress(initialIndex);
|
||||
mSeekBarPreference.setContinuousUpdates(true);
|
||||
mSeekBarPreference.setOnSeekBarChangeListener(mSeekBarChangeListener);
|
||||
if (mNeedsQSTooltipReshow) {
|
||||
mHandler.post(this::showQuickSettingsTooltipIfNeeded);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,6 +153,44 @@ class PreviewSizeSeekBarController extends BasePreferenceController implements
|
||||
mInteractionListener.ifPresent(ProgressInteractionListener::onProgressChanged);
|
||||
}
|
||||
|
||||
private void onProgressFinalized() {
|
||||
// Using progress in SeekBarPreference since the progresses in
|
||||
// SeekBarPreference and seekbar are not always the same.
|
||||
// See {@link androidx.preference.Preference#callChangeListener(Object)}
|
||||
int seekBarPreferenceProgress = mSeekBarPreference.getProgress();
|
||||
if (seekBarPreferenceProgress != mLastProgress) {
|
||||
showQuickSettingsTooltipIfNeeded();
|
||||
mLastProgress = seekBarPreferenceProgress;
|
||||
}
|
||||
}
|
||||
|
||||
private void showQuickSettingsTooltipIfNeeded() {
|
||||
final ComponentName tileComponentName = getTileComponentName();
|
||||
if (tileComponentName == null) {
|
||||
// Returns if no tile service assigned.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mNeedsQSTooltipReshow && AccessibilityQuickSettingUtils.hasValueInSharedPreferences(
|
||||
mContext, tileComponentName)) {
|
||||
// Returns if quick settings tooltip only show once.
|
||||
return;
|
||||
}
|
||||
|
||||
mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(mContext);
|
||||
mTooltipWindow.setup(getTileTooltipContent(),
|
||||
R.drawable.accessibility_auto_added_qs_tooltip_illustration);
|
||||
mTooltipWindow.showAtTopCenter(mSeekBarPreference.getSeekbar());
|
||||
AccessibilityQuickSettingUtils.optInValueToSharedPreferences(mContext, tileComponentName);
|
||||
mNeedsQSTooltipReshow = false;
|
||||
}
|
||||
|
||||
/** Returns the accessibility Quick Settings tile component name. */
|
||||
abstract ComponentName getTileComponentName();
|
||||
|
||||
/** Returns accessibility Quick Settings tile tooltip content. */
|
||||
abstract CharSequence getTileTooltipContent();
|
||||
|
||||
|
||||
/**
|
||||
* Interface for callbacks when users interact with the seek bar.
|
||||
|
@@ -16,11 +16,13 @@
|
||||
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import static com.android.internal.accessibility.AccessibilityShortcutController.FONT_SIZE_COMPONENT_NAME;
|
||||
import static com.android.settings.accessibility.TextReadingResetController.ResetStateListener;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
@@ -156,12 +158,34 @@ public class TextReadingPreferenceFragment extends DashboardFragment {
|
||||
controllers.add(mPreviewController);
|
||||
|
||||
final PreviewSizeSeekBarController fontSizeController = new PreviewSizeSeekBarController(
|
||||
context, FONT_SIZE_KEY, fontSizeData);
|
||||
context, FONT_SIZE_KEY, fontSizeData) {
|
||||
@Override
|
||||
ComponentName getTileComponentName() {
|
||||
return FONT_SIZE_COMPONENT_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
CharSequence getTileTooltipContent() {
|
||||
return context.getText(
|
||||
R.string.accessibility_font_scaling_auto_added_qs_tooltip_content);
|
||||
}
|
||||
};
|
||||
fontSizeController.setInteractionListener(mPreviewController);
|
||||
getSettingsLifecycle().addObserver(fontSizeController);
|
||||
controllers.add(fontSizeController);
|
||||
|
||||
final PreviewSizeSeekBarController displaySizeController = new PreviewSizeSeekBarController(
|
||||
context, DISPLAY_SIZE_KEY, displaySizeData);
|
||||
context, DISPLAY_SIZE_KEY, displaySizeData) {
|
||||
@Override
|
||||
ComponentName getTileComponentName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
CharSequence getTileTooltipContent() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
displaySizeController.setInteractionListener(mPreviewController);
|
||||
controllers.add(displaySizeController);
|
||||
|
||||
|
@@ -63,6 +63,8 @@ public class LabeledSeekBarPreference extends SeekBarPreference {
|
||||
private OnPreferenceChangeListener mStopListener;
|
||||
private SeekBar.OnSeekBarChangeListener mSeekBarChangeListener;
|
||||
|
||||
private SeekBar mSeekBar;
|
||||
|
||||
public LabeledSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr,
|
||||
int defStyleRes) {
|
||||
|
||||
@@ -104,6 +106,10 @@ public class LabeledSeekBarPreference extends SeekBarPreference {
|
||||
com.android.internal.R.attr.seekBarPreferenceStyle), 0);
|
||||
}
|
||||
|
||||
public SeekBar getSeekbar() {
|
||||
return mSeekBar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||
super.onBindViewHolder(holder);
|
||||
@@ -133,19 +139,19 @@ public class LabeledSeekBarPreference extends SeekBarPreference {
|
||||
final boolean isValidTextResIdExist = mTextStartId > 0 || mTextEndId > 0;
|
||||
labelFrame.setVisibility(isValidTextResIdExist ? View.VISIBLE : View.GONE);
|
||||
|
||||
final SeekBar seekBar = (SeekBar) holder.findViewById(com.android.internal.R.id.seekbar);
|
||||
mSeekBar = (SeekBar) holder.findViewById(com.android.internal.R.id.seekbar);
|
||||
if (mTickMarkId != 0) {
|
||||
final Drawable tickMark = getContext().getDrawable(mTickMarkId);
|
||||
seekBar.setTickMark(tickMark);
|
||||
mSeekBar.setTickMark(tickMark);
|
||||
}
|
||||
|
||||
final ViewGroup iconStartFrame = (ViewGroup) holder.findViewById(R.id.icon_start_frame);
|
||||
final ImageView iconStartView = (ImageView) holder.findViewById(R.id.icon_start);
|
||||
updateIconStartIfNeeded(iconStartFrame, iconStartView, seekBar);
|
||||
updateIconStartIfNeeded(iconStartFrame, iconStartView, mSeekBar);
|
||||
|
||||
final ViewGroup iconEndFrame = (ViewGroup) holder.findViewById(R.id.icon_end_frame);
|
||||
final ImageView iconEndView = (ImageView) holder.findViewById(R.id.icon_end);
|
||||
updateIconEndIfNeeded(iconEndFrame, iconEndView, seekBar);
|
||||
updateIconEndIfNeeded(iconEndFrame, iconEndView, mSeekBar);
|
||||
}
|
||||
|
||||
public void setOnPreferenceChangeStopListener(OnPreferenceChangeListener listener) {
|
||||
|
@@ -16,29 +16,45 @@
|
||||
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import static com.android.internal.accessibility.AccessibilityShortcutController.FONT_SIZE_COMPONENT_NAME;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
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.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.PopupWindow;
|
||||
import android.widget.SeekBar;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.testutils.shadow.ShadowFragment;
|
||||
import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor;
|
||||
import com.android.settings.widget.LabeledSeekBarPreference;
|
||||
|
||||
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.mockito.Spy;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadow.api.Shadow;
|
||||
import org.robolectric.shadows.ShadowApplication;
|
||||
|
||||
/**
|
||||
* Tests for {@link PreviewSizeSeekBarController}.
|
||||
@@ -47,30 +63,67 @@ import org.robolectric.annotation.Config;
|
||||
@Config(shadows = {ShadowInteractionJankMonitor.class})
|
||||
public class PreviewSizeSeekBarControllerTest {
|
||||
private static final String FONT_SIZE_KEY = "font_size";
|
||||
private static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
|
||||
@Spy
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
private PreviewSizeSeekBarController mSeekBarController;
|
||||
private FontSizeData mFontSizeData;
|
||||
private LabeledSeekBarPreference mSeekBarPreference;
|
||||
|
||||
@Mock
|
||||
private PreferenceScreen mPreferenceScreen;
|
||||
private TestFragment mFragment;
|
||||
private PreferenceViewHolder mHolder;
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private PreferenceManager mPreferenceManager;
|
||||
|
||||
@Mock
|
||||
private PreviewSizeSeekBarController.ProgressInteractionListener mInteractionListener;
|
||||
|
||||
private static PopupWindow getLatestPopupWindow() {
|
||||
final ShadowApplication shadowApplication =
|
||||
Shadow.extract(ApplicationProvider.getApplicationContext());
|
||||
return shadowApplication.getLatestPopupWindow();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mFontSizeData = new FontSizeData(mContext);
|
||||
|
||||
mSeekBarController =
|
||||
new PreviewSizeSeekBarController(mContext, FONT_SIZE_KEY, mFontSizeData);
|
||||
|
||||
mContext.setTheme(R.style.Theme_AppCompat);
|
||||
mFragment = spy(new TestFragment());
|
||||
when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
|
||||
when(mFragment.getPreferenceManager().getContext()).thenReturn(mContext);
|
||||
when(mFragment.getContext()).thenReturn(mContext);
|
||||
mPreferenceScreen = spy(new PreferenceScreen(mContext, /* attrs= */ null));
|
||||
when(mPreferenceScreen.getPreferenceManager()).thenReturn(mPreferenceManager);
|
||||
doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen();
|
||||
mSeekBarPreference = spy(new LabeledSeekBarPreference(mContext, /* attrs= */ null));
|
||||
mSeekBarPreference.setKey(FONT_SIZE_KEY);
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
mHolder = PreferenceViewHolder.createInstanceForTests(inflater.inflate(
|
||||
R.layout.preference_labeled_slider, null));
|
||||
mSeekBarPreference.onBindViewHolder(mHolder);
|
||||
|
||||
when(mPreferenceScreen.findPreference(anyString())).thenReturn(mSeekBarPreference);
|
||||
|
||||
mFontSizeData = new FontSizeData(mContext);
|
||||
mSeekBarController =
|
||||
new PreviewSizeSeekBarController(mContext, FONT_SIZE_KEY, mFontSizeData) {
|
||||
@Override
|
||||
ComponentName getTileComponentName() {
|
||||
return FONT_SIZE_COMPONENT_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
CharSequence getTileTooltipContent() {
|
||||
return mContext.getText(
|
||||
R.string.accessibility_font_scaling_auto_added_qs_tooltip_content);
|
||||
}
|
||||
};
|
||||
mSeekBarController.setInteractionListener(mInteractionListener);
|
||||
when(mPreferenceScreen.findPreference(mSeekBarController.getPreferenceKey())).thenReturn(
|
||||
mSeekBarPreference);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -123,4 +176,64 @@ public class PreviewSizeSeekBarControllerTest {
|
||||
|
||||
verify(mInteractionListener).notifyPreferenceChanged();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProgressChanged_showTooltipView() {
|
||||
mSeekBarController.displayPreference(mPreferenceScreen);
|
||||
|
||||
// Simulate changing the progress for the first time
|
||||
int newProgress = (mSeekBarPreference.getProgress() != 0) ? 0 : mSeekBarPreference.getMax();
|
||||
mSeekBarPreference.setProgress(newProgress);
|
||||
mSeekBarPreference.onProgressChanged(new SeekBar(mContext),
|
||||
newProgress,
|
||||
/* fromUser= */ false);
|
||||
|
||||
assertThat(getLatestPopupWindow().isShowing()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProgressChanged_tooltipViewHasBeenShown_notShowTooltipView() {
|
||||
mSeekBarController.displayPreference(mPreferenceScreen);
|
||||
// Simulate changing the progress for the first time
|
||||
int newProgress = (mSeekBarPreference.getProgress() != 0) ? 0 : mSeekBarPreference.getMax();
|
||||
mSeekBarPreference.setProgress(newProgress);
|
||||
mSeekBarPreference.onProgressChanged(new SeekBar(mContext),
|
||||
newProgress,
|
||||
/* fromUser= */ false);
|
||||
getLatestPopupWindow().dismiss();
|
||||
|
||||
// Simulate progress changing for the second time
|
||||
newProgress = (mSeekBarPreference.getProgress() != 0) ? 0 : mSeekBarPreference.getMax();
|
||||
mSeekBarPreference.setProgress(newProgress);
|
||||
mSeekBarPreference.onProgressChanged(new SeekBar(mContext),
|
||||
newProgress,
|
||||
/* fromUser= */ false);
|
||||
|
||||
assertThat(getLatestPopupWindow().isShowing()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(shadows = ShadowFragment.class)
|
||||
public void restoreValueFromSavedInstanceState_showTooltipView() {
|
||||
final Bundle savedInstanceState = new Bundle();
|
||||
savedInstanceState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, /* value= */ true);
|
||||
mSeekBarController.onCreate(savedInstanceState);
|
||||
|
||||
mSeekBarController.displayPreference(mPreferenceScreen);
|
||||
|
||||
assertThat(getLatestPopupWindow().isShowing()).isTrue();
|
||||
}
|
||||
|
||||
private static class TestFragment extends SettingsPreferenceFragment {
|
||||
|
||||
@Override
|
||||
protected boolean shouldSkipForInitialSUW() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user