mModeInfos = new ArrayList<>();
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -77,6 +84,7 @@ public class MagnificationSettingsFragment extends DashboardFragment {
if (mCapabilities == NONE) {
mCapabilities = MagnificationCapabilities.getCapabilities(getPrefContext());
}
+ initModeInfos();
}
@Override
@@ -121,13 +129,10 @@ public class MagnificationSettingsFragment extends DashboardFragment {
@Override
public Dialog onCreateDialog(int dialogId) {
final CharSequence title;
+
switch (dialogId) {
case DIALOG_MAGNIFICATION_CAPABILITY:
- title = getPrefContext().getString(
- R.string.accessibility_magnification_mode_title);
- mDialog = AccessibilityEditDialogUtils.showMagnificationModeDialog(getPrefContext(),
- title, this::callOnAlertDialogCheckboxClicked);
- initializeDialogCheckBox(mDialog);
+ mDialog = createMagnificationModeDialog();
return mDialog;
case DIALOG_MAGNIFICATION_SWITCH_SHORTCUT:
title = getPrefContext().getString(
@@ -136,10 +141,97 @@ public class MagnificationSettingsFragment extends DashboardFragment {
getPrefContext(), title, this::onSwitchShortcutDialogPositiveButtonClicked);
return mDialog;
}
-
throw new IllegalArgumentException("Unsupported dialogId " + dialogId);
}
+ private Dialog createMagnificationModeDialog() {
+ mMagnificationModesListView = AccessibilityEditDialogUtils.createSingleChoiceListView(
+ getPrefContext(), mModeInfos, this::onMagnificationModeSelected);
+
+ final View headerView = LayoutInflater.from(getPrefContext()).inflate(
+ R.layout.accessibility_magnification_mode_header, mMagnificationModesListView,
+ false);
+ mMagnificationModesListView.addHeaderView(headerView, null, /* isSelectable= */false);
+
+ mMagnificationModesListView.setItemChecked(computeSelectedMagnificationModeIndex(), true);
+ final CharSequence title = getPrefContext().getString(
+ R.string.accessibility_magnification_mode_dialog_title);
+
+ return AccessibilityEditDialogUtils.createCustomDialog(getPrefContext(), title,
+ mMagnificationModesListView, this::onMagnificationModeDialogPositiveButtonClicked);
+ }
+
+ private int computeSelectedMagnificationModeIndex() {
+ final int size = mModeInfos.size();
+ for (int i = 0; i < size; i++) {
+ if (mModeInfos.get(i).mMagnificationMode == mCapabilities) {
+ return i + mMagnificationModesListView.getHeaderViewsCount();
+ }
+ }
+ Log.w(TAG, "chosen mode" + mCapabilities + "is not in the list");
+ return 0;
+ }
+
+ private void onMagnificationModeSelected(AdapterView> parent, View view, int position,
+ long id) {
+ final MagnificationModeInfo modeInfo =
+ (MagnificationModeInfo) mMagnificationModesListView.getItemAtPosition(position);
+ if (modeInfo.mMagnificationMode == mCapabilities) {
+ return;
+ }
+ mCapabilities = modeInfo.mMagnificationMode;
+ if (isTripleTapEnabled() && mCapabilities != MagnificationMode.FULLSCREEN) {
+ showDialog(DIALOG_MAGNIFICATION_SWITCH_SHORTCUT);
+ }
+ }
+
+ private void onMagnificationModeDialogPositiveButtonClicked(DialogInterface dialogInterface,
+ int which) {
+ final int selectedIndex = mMagnificationModesListView.getCheckedItemPosition();
+ if (selectedIndex != AdapterView.INVALID_POSITION) {
+ final MagnificationModeInfo modeInfo =
+ (MagnificationModeInfo) mMagnificationModesListView.getItemAtPosition(
+ selectedIndex);
+ updateCapabilities(modeInfo.mMagnificationMode);
+ } else {
+ Log.w(TAG, "no checked item in the list");
+ }
+ }
+
+ private void updateCapabilities(int mode) {
+ mCapabilities = mode;
+ MagnificationCapabilities.setCapabilities(getPrefContext(), mCapabilities);
+ mModePreference.setSummary(
+ MagnificationCapabilities.getSummary(getPrefContext(), mCapabilities));
+ }
+
+ private void initModeInfos() {
+ mModeInfos.clear();
+ mModeInfos.add(new MagnificationModeInfo(getPrefContext().getText(
+ R.string.accessibility_magnification_mode_dialog_option_full_screen), null,
+ R.drawable.accessibility_magnification_full_screen, MagnificationMode.FULLSCREEN));
+ mModeInfos.add(new MagnificationModeInfo(getPrefContext().getText(
+ R.string.accessibility_magnification_mode_dialog_option_window), null,
+ R.drawable.accessibility_magnification_window_screen, MagnificationMode.WINDOW));
+ mModeInfos.add(new MagnificationModeInfo(getPrefContext().getText(
+ R.string.accessibility_magnification_mode_dialog_option_switch),
+ getPrefContext().getText(
+ R.string.accessibility_magnification_area_settings_mode_switch_summary),
+ R.drawable.accessibility_magnification_switch, MagnificationMode.ALL));
+ }
+
+ @VisibleForTesting
+ static class MagnificationModeInfo extends ItemInfoArrayAdapter.ItemInfo {
+ @MagnificationMode
+ public final int mMagnificationMode;
+
+ MagnificationModeInfo(@NonNull CharSequence title, @Nullable CharSequence summary,
+ @DrawableRes int drawableId, @MagnificationMode int magnificationMode) {
+ super(title, summary, drawableId);
+ mMagnificationMode = magnificationMode;
+ }
+ }
+
private void initModePreference() {
mModePreference = findPreference(PREF_KEY_MODE);
mModePreference.setOnPreferenceClickListener(preference -> {
@@ -149,12 +241,6 @@ public class MagnificationSettingsFragment extends DashboardFragment {
});
}
- private void callOnAlertDialogCheckboxClicked(DialogInterface dialog, int which) {
- updateCapabilities(true);
- mModePreference.setSummary(
- MagnificationCapabilities.getSummary(getPrefContext(), mCapabilities));
- }
-
private void onSwitchShortcutDialogPositiveButtonClicked(View view) {
//TODO(b/147990389): Merge this function into util until magnification change format to
// Component.
@@ -188,95 +274,6 @@ public class MagnificationSettingsFragment extends DashboardFragment {
joiner.toString());
}
- private void initializeDialogCheckBox(Dialog dialog) {
- final View dialogFullScreenView = dialog.findViewById(R.id.magnify_full_screen);
- final View dialogFullScreenTextArea = dialogFullScreenView.findViewById(R.id.container);
- mMagnifyFullScreenCheckBox = dialogFullScreenView.findViewById(R.id.checkbox);
-
- final View dialogWidowView = dialog.findViewById(R.id.magnify_window_screen);
- final View dialogWindowTextArea = dialogWidowView.findViewById(R.id.container);
- mMagnifyWindowCheckBox = dialogWidowView.findViewById(R.id.checkbox);
-
- updateAlertDialogCheckState();
- updateAlertDialogEnableState(dialogFullScreenTextArea, dialogWindowTextArea);
-
- setTextAreasClickListener(dialogFullScreenTextArea, mMagnifyFullScreenCheckBox,
- dialogWindowTextArea, mMagnifyWindowCheckBox);
- }
-
- private void setTextAreasClickListener(View fullScreenTextArea, CheckBox fullScreenCheckBox,
- View windowTextArea, CheckBox windowCheckBox) {
- fullScreenTextArea.setOnClickListener(v -> {
- fullScreenCheckBox.toggle();
- updateCapabilities(false);
- updateAlertDialogEnableState(fullScreenTextArea, windowTextArea);
- });
-
- windowTextArea.setOnClickListener(v -> {
- windowCheckBox.toggle();
- updateCapabilities(false);
- updateAlertDialogEnableState(fullScreenTextArea, windowTextArea);
-
- if (isTripleTapEnabled() && windowCheckBox.isChecked()) {
- showDialog(DIALOG_MAGNIFICATION_SWITCH_SHORTCUT);
- }
- });
- }
-
- private void updateAlertDialogCheckState() {
- updateCheckStatus(mMagnifyWindowCheckBox,
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
- updateCheckStatus(mMagnifyFullScreenCheckBox,
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-
- }
-
- private void updateCheckStatus(CheckBox checkBox, int mode) {
- checkBox.setChecked((mode & mCapabilities) != 0);
- }
-
- private void updateAlertDialogEnableState(View fullScreenTextArea, View windowTextArea) {
- switch (mCapabilities) {
- case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN:
- setViewAndChildrenEnabled(fullScreenTextArea, false);
- break;
- case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW:
- setViewAndChildrenEnabled(windowTextArea, false);
- break;
- case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL:
- setViewAndChildrenEnabled(fullScreenTextArea, true);
- setViewAndChildrenEnabled(windowTextArea, true);
- break;
- default:
- throw new IllegalArgumentException(
- "Unsupported ACCESSIBILITY_MAGNIFICATION_CAPABILITY " + mCapabilities);
- }
- }
-
- private void setViewAndChildrenEnabled(View view, boolean enabled) {
- view.setEnabled(enabled);
- if (view instanceof ViewGroup) {
- final ViewGroup viewGroup = (ViewGroup) view;
- for (int i = 0; i < viewGroup.getChildCount(); i++) {
- View child = viewGroup.getChildAt(i);
- setViewAndChildrenEnabled(child, enabled);
- }
- }
- }
-
- private void updateCapabilities(boolean saveToDB) {
- int capabilities = 0;
- capabilities |=
- mMagnifyFullScreenCheckBox.isChecked()
- ? Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN : 0;
- capabilities |= mMagnifyWindowCheckBox.isChecked()
- ? Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW : 0;
- mCapabilities = capabilities;
- if (saveToDB) {
- MagnificationCapabilities.setCapabilities(getPrefContext(), mCapabilities);
- }
- }
-
private boolean isTripleTapEnabled() {
return Settings.Secure.getInt(getPrefContext().getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, OFF) == ON;
diff --git a/src/com/android/settings/widget/CheckableRelativeLayout.java b/src/com/android/settings/widget/CheckableRelativeLayout.java
new file mode 100644
index 00000000000..d26c042dc29
--- /dev/null
+++ b/src/com/android/settings/widget/CheckableRelativeLayout.java
@@ -0,0 +1,127 @@
+/*
+ * 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 android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.Checkable;
+import android.widget.RelativeLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * A RelativeLayout which implements {@link Checkable}. With this implementation, it could be used
+ * in the list item layout for {@link android.widget.AbsListView} to change UI after item click.
+ * Its checked state would be propagated to the checkable child.
+ *
+ *
+ * To support accessibility, the state description is from the checkable view and is
+ * changed with {@link #setChecked(boolean)}. We make the checkable child unclickable, unfocusable
+ * and non-important for accessibility, so that the announcement wouldn't include
+ * the checkable view.
+ * <
+ */
+public class CheckableRelativeLayout extends RelativeLayout implements Checkable {
+
+ private Checkable mCheckable;
+ private View mCheckableChild;
+ private boolean mChecked;
+
+ public CheckableRelativeLayout(Context context) {
+ super(context);
+ }
+
+ public CheckableRelativeLayout(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mCheckableChild = findFirstCheckableView(this);
+ if (mCheckableChild != null) {
+ mCheckableChild.setClickable(false);
+ mCheckableChild.setFocusable(false);
+ mCheckableChild.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+ mCheckable = (Checkable) mCheckableChild;
+ mCheckable.setChecked(isChecked());
+ setStateDescriptionIfNeeded();
+ }
+ super.onFinishInflate();
+ }
+
+ @Nullable
+ private static View findFirstCheckableView(@NonNull ViewGroup viewGroup) {
+ final int childCount = viewGroup.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = viewGroup.getChildAt(i);
+ if (child instanceof Checkable) {
+ return child;
+ }
+ if (child instanceof ViewGroup) {
+ findFirstCheckableView((ViewGroup) child);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void setChecked(boolean checked) {
+ if (mChecked != checked) {
+ mChecked = checked;
+ if (mCheckable != null) {
+ mCheckable.setChecked(checked);
+ }
+ }
+ setStateDescriptionIfNeeded();
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+ }
+
+ private void setStateDescriptionIfNeeded() {
+ if (mCheckableChild == null) {
+ return;
+ }
+ setStateDescription(mCheckableChild.getStateDescription());
+ }
+
+ @Override
+ public boolean isChecked() {
+ return mChecked;
+ }
+
+ @Override
+ public void toggle() {
+ setChecked(!mChecked);
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setChecked(mChecked);
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setChecked(mChecked);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/MagnificationSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/MagnificationSettingsFragmentTest.java
index d49213c8eb4..11128e421e7 100644
--- a/tests/robotests/src/com/android/settings/accessibility/MagnificationSettingsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/MagnificationSettingsFragmentTest.java
@@ -16,12 +16,15 @@
package com.android.settings.accessibility;
+import static com.android.settings.accessibility.MagnificationCapabilities.MagnificationMode;
import static com.android.settings.accessibility.MagnificationPreferenceFragment.ON;
+import static com.android.settings.accessibility.MagnificationSettingsFragment.MagnificationModeInfo;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -29,7 +32,8 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.Bundle;
import android.provider.Settings;
-import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListView;
import androidx.fragment.app.FragmentManager;
import androidx.preference.PreferenceManager;
@@ -77,7 +81,7 @@ public class MagnificationSettingsFragmentTest {
}
@Test
- public void onCreateDialog_capabilitiesInBundle_matchCheckBoxStatus() {
+ public void onCreateDialog_capabilitiesInBundle_checkedModeInDialogIsExpected() {
final Bundle windowModeSavedInstanceState = new Bundle();
windowModeSavedInstanceState.putInt(EXTRA_CAPABILITY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
@@ -85,23 +89,21 @@ public class MagnificationSettingsFragmentTest {
mFragment.onCreate(windowModeSavedInstanceState);
mFragment.onCreateDialog(MagnificationSettingsFragment.DIALOG_MAGNIFICATION_CAPABILITY);
- assertThat(mFragment.mMagnifyFullScreenCheckBox.isChecked()).isFalse();
- assertThat(mFragment.mMagnifyWindowCheckBox.isChecked()).isTrue();
+ assertThat(getChoseModeFromDialog()).isEqualTo(MagnificationMode.WINDOW);
}
@Test
- public void onCreateDialog_capabilitiesInSettings_matchCheckBoxStatus() {
+ public void onCreateDialog_capabilitiesInSetting_checkedModeInDialogIsExpected() {
MagnificationCapabilities.setCapabilities(mContext,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
mFragment.onCreate(Bundle.EMPTY);
mFragment.onCreateDialog(MagnificationSettingsFragment.DIALOG_MAGNIFICATION_CAPABILITY);
- assertThat(mFragment.mMagnifyFullScreenCheckBox.isChecked()).isTrue();
- assertThat(mFragment.mMagnifyWindowCheckBox.isChecked()).isFalse();
+ assertThat(getChoseModeFromDialog()).isEqualTo(MagnificationMode.FULLSCREEN);
}
@Test
- public void onCreateDialog_capabilitiesInSettingsAndBundle_matchBundleValueCheckBoxStatus() {
+ public void onCreateDialog_choseModeIsDifferentFromInSettings_ShowUsersChoseModeInDialog() {
final Bundle allModeSavedInstanceState = new Bundle();
allModeSavedInstanceState.putInt(EXTRA_CAPABILITY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
@@ -111,37 +113,93 @@ public class MagnificationSettingsFragmentTest {
mFragment.onCreate(allModeSavedInstanceState);
mFragment.onCreateDialog(MagnificationSettingsFragment.DIALOG_MAGNIFICATION_CAPABILITY);
- assertThat(mFragment.mMagnifyFullScreenCheckBox.isChecked()).isTrue();
- assertThat(mFragment.mMagnifyWindowCheckBox.isChecked()).isTrue();
+ assertThat(getChoseModeFromDialog()).isEqualTo(MagnificationMode.ALL);
}
@Test
- public void onCreateDialog_emptySettingsAndBundle_matchDefaultValueCheckBoxStatus() {
+ public void onCreateDialog_emptySettingsAndBundle_checkedModeInDialogIsDefaultValue() {
mFragment.onCreate(Bundle.EMPTY);
mFragment.onCreateDialog(MagnificationSettingsFragment.DIALOG_MAGNIFICATION_CAPABILITY);
- // Compare to default Capabilities
- assertThat(mFragment.mMagnifyFullScreenCheckBox.isChecked()).isTrue();
- assertThat(mFragment.mMagnifyWindowCheckBox.isChecked()).isFalse();
+ assertThat(getChoseModeFromDialog()).isEqualTo(MagnificationMode.FULLSCREEN);
}
@Test
- public void checkWindowModeCheckBox_tripleTapEnabled_showSwitchShortcutDialog() {
+ public void chooseWindowMode_tripleTapEnabled_showSwitchShortcutDialog() {
+ enableTripleTap();
final Bundle fullScreenModeSavedInstanceState = new Bundle();
fullScreenModeSavedInstanceState.putInt(EXTRA_CAPABILITY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
mFragment.onCreate(fullScreenModeSavedInstanceState);
mFragment.onCreateDialog(MagnificationSettingsFragment.DIALOG_MAGNIFICATION_CAPABILITY);
- enableTripleTap();
- final View dialogWidowView = mFragment.mDialog.findViewById(R.id.magnify_window_screen);
- final View dialogWindowTextArea = dialogWidowView.findViewById(R.id.container);
- dialogWindowTextArea.performClick();
+ performItemClickWith(MagnificationMode.WINDOW);
verify(mFragment).showDialog(
MagnificationSettingsFragment.DIALOG_MAGNIFICATION_SWITCH_SHORTCUT);
}
+ @Test
+ public void chooseModeAll_tripleTapEnabled_showSwitchShortcutDialog() {
+ enableTripleTap();
+ final Bundle fullScreenModeSavedInstanceState = new Bundle();
+ fullScreenModeSavedInstanceState.putInt(EXTRA_CAPABILITY,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+ mFragment.onCreate(fullScreenModeSavedInstanceState);
+ mFragment.onCreateDialog(MagnificationSettingsFragment.DIALOG_MAGNIFICATION_CAPABILITY);
+
+ performItemClickWith(MagnificationMode.ALL);
+
+ verify(mFragment).showDialog(
+ MagnificationSettingsFragment.DIALOG_MAGNIFICATION_SWITCH_SHORTCUT);
+ }
+
+ @Test
+ public void chooseWindowMode_WindowModeInSettingsAndTripleTapEnabled_notShowShortCutDialog() {
+ enableTripleTap();
+ MagnificationCapabilities.setCapabilities(mContext,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+ mFragment.onCreate(Bundle.EMPTY);
+ mFragment.onCreateDialog(MagnificationSettingsFragment.DIALOG_MAGNIFICATION_CAPABILITY);
+
+ performItemClickWith(MagnificationMode.WINDOW);
+
+ verify(mFragment, never()).showDialog(
+ MagnificationSettingsFragment.DIALOG_MAGNIFICATION_SWITCH_SHORTCUT);
+ }
+
+ private int getChoseModeFromDialog() {
+ final ListView listView = mFragment.mMagnificationModesListView;
+ assertThat(listView).isNotNull();
+
+ final int checkedPosition = listView.getCheckedItemPosition();
+ final MagnificationModeInfo modeInfo =
+ (MagnificationModeInfo) listView.getAdapter().getItem(
+ checkedPosition);
+ return modeInfo.mMagnificationMode;
+ }
+
+ private void performItemClickWith(@MagnificationMode int mode) {
+ final ListView listView = mFragment.mMagnificationModesListView;
+ assertThat(listView).isNotNull();
+
+ int modeIndex = AdapterView.NO_ID;
+ // Index 0 is header.
+ for (int i = 1; i < listView.getAdapter().getCount(); i++) {
+ final MagnificationModeInfo modeInfo =
+ (MagnificationModeInfo) listView.getAdapter().getItem(i);
+ if (modeInfo.mMagnificationMode == mode) {
+ modeIndex = i;
+ break;
+ }
+ }
+ if (modeIndex == AdapterView.NO_ID) {
+ throw new RuntimeException("The mode is not in the list.");
+ }
+
+ listView.performItemClick(listView.getChildAt(modeIndex), modeIndex, modeIndex);
+ }
+
private void enableTripleTap() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, ON);