Root cause: Portrait mode does not always respect the sw600dp qualifier and instead uses the phone (default) resources.
Solution: Using the product to customize the tablet and phone cases is better way than using the configuration qualifier.
Bug: 269130587
Test: Manual testing
Change-Id: I90cc44f1e9bccf8950a4d89f70119ba5e3c0bfdc
(cherry-pick of commit bea25c7513)
326 lines
13 KiB
Java
326 lines
13 KiB
Java
/*
|
|
* 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.accessibility;
|
|
|
|
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
|
|
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
|
|
|
|
import android.app.Dialog;
|
|
import android.app.settings.SettingsEnums;
|
|
import android.content.Context;
|
|
import android.content.DialogInterface;
|
|
import android.os.Bundle;
|
|
import android.provider.Settings;
|
|
import android.text.method.LinkMovementMethod;
|
|
import android.util.Log;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.widget.AdapterView;
|
|
import android.widget.ListView;
|
|
import android.widget.TextView;
|
|
|
|
import androidx.annotation.DrawableRes;
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.annotation.VisibleForTesting;
|
|
import androidx.preference.Preference;
|
|
import androidx.preference.PreferenceScreen;
|
|
|
|
import com.android.settings.DialogCreatable;
|
|
import com.android.settings.R;
|
|
import com.android.settings.accessibility.MagnificationCapabilities.MagnificationMode;
|
|
import com.android.settings.core.BasePreferenceController;
|
|
import com.android.settings.utils.AnnotationSpan;
|
|
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
|
import com.android.settingslib.core.lifecycle.events.OnCreate;
|
|
import com.android.settingslib.core.lifecycle.events.OnResume;
|
|
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
/** Controller that shows the magnification area mode summary and the preference click behavior. */
|
|
public class MagnificationModePreferenceController extends BasePreferenceController implements
|
|
DialogCreatable, LifecycleObserver, OnCreate, OnResume, OnSaveInstanceState {
|
|
|
|
static final String PREF_KEY = "screen_magnification_mode";
|
|
private static final int DIALOG_ID_BASE = 10;
|
|
@VisibleForTesting
|
|
static final int DIALOG_MAGNIFICATION_MODE = DIALOG_ID_BASE + 1;
|
|
@VisibleForTesting
|
|
static final int DIALOG_MAGNIFICATION_TRIPLE_TAP_WARNING = DIALOG_ID_BASE + 2;
|
|
@VisibleForTesting
|
|
static final String EXTRA_MODE = "mode";
|
|
|
|
private static final String TAG = "MagnificationModePreferenceController";
|
|
|
|
private DialogHelper mDialogHelper;
|
|
// The magnification mode in the dialog.
|
|
@MagnificationMode
|
|
private int mModeCache = MagnificationMode.NONE;
|
|
private Preference mModePreference;
|
|
private ShortcutPreference mLinkPreference;
|
|
|
|
@VisibleForTesting
|
|
ListView mMagnificationModesListView;
|
|
|
|
private final List<MagnificationModeInfo> mModeInfos = new ArrayList<>();
|
|
|
|
public MagnificationModePreferenceController(Context context, String preferenceKey) {
|
|
super(context, preferenceKey);
|
|
initModeInfos();
|
|
}
|
|
|
|
private void initModeInfos() {
|
|
mModeInfos.add(new MagnificationModeInfo(mContext.getText(
|
|
R.string.accessibility_magnification_mode_dialog_option_full_screen), null,
|
|
R.drawable.a11y_magnification_mode_fullscreen, MagnificationMode.FULLSCREEN));
|
|
mModeInfos.add(new MagnificationModeInfo(
|
|
mContext.getText(R.string.accessibility_magnification_mode_dialog_option_window),
|
|
null, R.drawable.a11y_magnification_mode_window, MagnificationMode.WINDOW));
|
|
mModeInfos.add(new MagnificationModeInfo(
|
|
mContext.getText(R.string.accessibility_magnification_mode_dialog_option_switch),
|
|
mContext.getText(
|
|
R.string.accessibility_magnification_area_settings_mode_switch_summary),
|
|
R.drawable.a11y_magnification_mode_switch, MagnificationMode.ALL));
|
|
}
|
|
|
|
@Override
|
|
public int getAvailabilityStatus() {
|
|
return AVAILABLE;
|
|
}
|
|
|
|
@Override
|
|
public CharSequence getSummary() {
|
|
final int capabilities = MagnificationCapabilities.getCapabilities(mContext);
|
|
return MagnificationCapabilities.getSummary(mContext, capabilities);
|
|
}
|
|
|
|
@Override
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
if (savedInstanceState != null) {
|
|
mModeCache = savedInstanceState.getInt(EXTRA_MODE, MagnificationMode.NONE);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void displayPreference(PreferenceScreen screen) {
|
|
super.displayPreference(screen);
|
|
mModePreference = screen.findPreference(getPreferenceKey());
|
|
mLinkPreference = screen.findPreference(
|
|
ToggleFeaturePreferenceFragment.KEY_SHORTCUT_PREFERENCE);
|
|
mModePreference.setOnPreferenceClickListener(preference -> {
|
|
mModeCache = MagnificationCapabilities.getCapabilities(mContext);
|
|
mDialogHelper.showDialog(DIALOG_MAGNIFICATION_MODE);
|
|
return true;
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onSaveInstanceState(Bundle outState) {
|
|
outState.putInt(EXTRA_MODE, mModeCache);
|
|
}
|
|
|
|
/**
|
|
* Sets {@link DialogHelper} used to show the dialog.
|
|
*/
|
|
public void setDialogHelper(DialogHelper dialogHelper) {
|
|
mDialogHelper = dialogHelper;
|
|
mDialogHelper.setDialogDelegate(this);
|
|
}
|
|
|
|
@Override
|
|
public Dialog onCreateDialog(int dialogId) {
|
|
switch (dialogId) {
|
|
case DIALOG_MAGNIFICATION_MODE:
|
|
return createMagnificationModeDialog();
|
|
|
|
case DIALOG_MAGNIFICATION_TRIPLE_TAP_WARNING:
|
|
return createMagnificationTripleTapWarningDialog();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public int getDialogMetricsCategory(int dialogId) {
|
|
switch (dialogId) {
|
|
case DIALOG_MAGNIFICATION_MODE:
|
|
return SettingsEnums.DIALOG_MAGNIFICATION_CAPABILITY;
|
|
case DIALOG_MAGNIFICATION_TRIPLE_TAP_WARNING:
|
|
return SettingsEnums.DIALOG_MAGNIFICATION_TRIPLE_TAP_WARNING;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
private Dialog createMagnificationModeDialog() {
|
|
mMagnificationModesListView = AccessibilityDialogUtils.createSingleChoiceListView(
|
|
mContext, mModeInfos, this::onMagnificationModeSelected);
|
|
|
|
final View headerView = LayoutInflater.from(mContext).inflate(
|
|
R.layout.accessibility_magnification_mode_header, mMagnificationModesListView,
|
|
false);
|
|
mMagnificationModesListView.addHeaderView(headerView, /* data= */ null, /* isSelectable= */
|
|
false);
|
|
|
|
mMagnificationModesListView.setItemChecked(computeSelectionIndex(), true);
|
|
final CharSequence title = mContext.getString(
|
|
R.string.accessibility_magnification_mode_dialog_title);
|
|
final CharSequence positiveBtnText = mContext.getString(R.string.save);
|
|
final CharSequence negativeBtnText = mContext.getString(R.string.cancel);
|
|
|
|
return AccessibilityDialogUtils.createCustomDialog(mContext, title,
|
|
mMagnificationModesListView,
|
|
positiveBtnText, this::onMagnificationModeDialogPositiveButtonClicked,
|
|
negativeBtnText, /* negativeListener= */ null);
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void onMagnificationModeDialogPositiveButtonClicked(DialogInterface dialogInterface,
|
|
int which) {
|
|
final int selectedIndex = mMagnificationModesListView.getCheckedItemPosition();
|
|
if (selectedIndex == AdapterView.INVALID_POSITION) {
|
|
Log.w(TAG, "invalid index");
|
|
return;
|
|
}
|
|
|
|
mModeCache = ((MagnificationModeInfo) mMagnificationModesListView.getItemAtPosition(
|
|
selectedIndex)).mMagnificationMode;
|
|
|
|
// Do not save mode until user clicks positive button in triple tap warning dialog.
|
|
if (isTripleTapEnabled(mContext) && mModeCache != MagnificationMode.FULLSCREEN) {
|
|
mDialogHelper.showDialog(DIALOG_MAGNIFICATION_TRIPLE_TAP_WARNING);
|
|
} else { // Save mode (capabilities) value, don't need to show dialog to confirm.
|
|
updateCapabilitiesAndSummary(mModeCache);
|
|
}
|
|
}
|
|
|
|
private void updateCapabilitiesAndSummary(@MagnificationMode int mode) {
|
|
mModeCache = mode;
|
|
MagnificationCapabilities.setCapabilities(mContext, mModeCache);
|
|
mModePreference.setSummary(
|
|
MagnificationCapabilities.getSummary(mContext, mModeCache));
|
|
}
|
|
|
|
private void onMagnificationModeSelected(AdapterView<?> parent, View view, int position,
|
|
long id) {
|
|
final MagnificationModeInfo modeInfo =
|
|
(MagnificationModeInfo) mMagnificationModesListView.getItemAtPosition(
|
|
position);
|
|
if (modeInfo.mMagnificationMode == mModeCache) {
|
|
return;
|
|
}
|
|
mModeCache = modeInfo.mMagnificationMode;
|
|
}
|
|
|
|
private int computeSelectionIndex() {
|
|
final int modesSize = mModeInfos.size();
|
|
for (int i = 0; i < modesSize; i++) {
|
|
if (mModeInfos.get(i).mMagnificationMode == mModeCache) {
|
|
return i + mMagnificationModesListView.getHeaderViewsCount();
|
|
}
|
|
}
|
|
Log.w(TAG, "computeSelectionIndex failed");
|
|
return 0;
|
|
}
|
|
|
|
@VisibleForTesting
|
|
static boolean isTripleTapEnabled(Context context) {
|
|
return Settings.Secure.getInt(context.getContentResolver(),
|
|
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, OFF) == ON;
|
|
}
|
|
|
|
private Dialog createMagnificationTripleTapWarningDialog() {
|
|
final View contentView = LayoutInflater.from(mContext).inflate(
|
|
R.layout.magnification_triple_tap_warning_dialog, /* root= */ null);
|
|
final CharSequence title = mContext.getString(
|
|
R.string.accessibility_magnification_triple_tap_warning_title);
|
|
final CharSequence positiveBtnText = mContext.getString(
|
|
R.string.accessibility_magnification_triple_tap_warning_positive_button);
|
|
final CharSequence negativeBtnText = mContext.getString(
|
|
R.string.accessibility_magnification_triple_tap_warning_negative_button);
|
|
|
|
final Dialog dialog = AccessibilityDialogUtils.createCustomDialog(mContext, title,
|
|
contentView,
|
|
positiveBtnText, this::onMagnificationTripleTapWarningDialogPositiveButtonClicked,
|
|
negativeBtnText, this::onMagnificationTripleTapWarningDialogNegativeButtonClicked);
|
|
|
|
updateLinkInTripleTapWarningDialog(dialog, contentView);
|
|
|
|
return dialog;
|
|
}
|
|
|
|
private void updateLinkInTripleTapWarningDialog(Dialog dialog, View contentView) {
|
|
final TextView messageView = contentView.findViewById(R.id.message);
|
|
// TODO(b/225682559): Need to remove performClick() after refactoring accessibility dialog.
|
|
final View.OnClickListener linkListener = view -> {
|
|
updateCapabilitiesAndSummary(mModeCache);
|
|
mLinkPreference.performClick();
|
|
dialog.dismiss();
|
|
};
|
|
final AnnotationSpan.LinkInfo linkInfo = new AnnotationSpan.LinkInfo(
|
|
AnnotationSpan.LinkInfo.DEFAULT_ANNOTATION, linkListener);
|
|
final CharSequence textWithLink = AnnotationSpan.linkify(mContext.getText(
|
|
R.string.accessibility_magnification_triple_tap_warning_message), linkInfo);
|
|
|
|
if (messageView != null) {
|
|
messageView.setText(textWithLink);
|
|
messageView.setMovementMethod(LinkMovementMethod.getInstance());
|
|
}
|
|
dialog.setContentView(contentView);
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void onMagnificationTripleTapWarningDialogNegativeButtonClicked(
|
|
DialogInterface dialogInterface, int which) {
|
|
mModeCache = MagnificationCapabilities.getCapabilities(mContext);
|
|
mDialogHelper.showDialog(DIALOG_MAGNIFICATION_MODE);
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void onMagnificationTripleTapWarningDialogPositiveButtonClicked(
|
|
DialogInterface dialogInterface, int which) {
|
|
updateCapabilitiesAndSummary(mModeCache);
|
|
}
|
|
|
|
// TODO(b/186731461): Remove it when this controller is used in DashBoardFragment only.
|
|
@Override
|
|
public void onResume() {
|
|
updateState(mModePreference);
|
|
}
|
|
|
|
/**
|
|
* An interface to help the delegate to show the dialog. It will be injected to the delegate.
|
|
*/
|
|
interface DialogHelper extends DialogCreatable {
|
|
void showDialog(int dialogId);
|
|
void setDialogDelegate(DialogCreatable delegate);
|
|
}
|
|
|
|
@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;
|
|
}
|
|
}
|
|
}
|