blob: 711065602bee18c5a85b04c12b6a561d335aea63 [file] [log] [blame]
/*
* 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.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import static com.android.settings.accessibility.AccessibilityDialogUtils.CustomButton;
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.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
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.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;
import java.util.StringJoiner;
/** 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_SWITCH_SHORTCUT = DIALOG_ID_BASE + 2;
@VisibleForTesting
static final String EXTRA_MODE = "mode";
private static final String TAG = "MagnificationModePreferenceController";
private static final char COMPONENT_NAME_SEPARATOR = ':';
private DialogHelper mDialogHelper;
// The magnification mode in the dialog.
private int mMode = MagnificationMode.NONE;
private Preference mModePreference;
@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.ic_illustration_fullscreen, MagnificationMode.FULLSCREEN));
mModeInfos.add(new MagnificationModeInfo(
mContext.getText(R.string.accessibility_magnification_mode_dialog_option_window),
null, R.drawable.ic_illustration_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.ic_illustration_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) {
mMode = savedInstanceState.getInt(EXTRA_MODE, MagnificationMode.NONE);
}
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mModePreference = screen.findPreference(getPreferenceKey());
mModePreference.setOnPreferenceClickListener(preference -> {
mMode = MagnificationCapabilities.getCapabilities(mContext);
mDialogHelper.showDialog(DIALOG_MAGNIFICATION_MODE);
return true;
});
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(EXTRA_MODE, mMode);
}
/**
* 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_SWITCH_SHORTCUT:
return createMagnificationShortCutConfirmDialog();
}
return null;
}
@Override
public int getDialogMetricsCategory(int dialogId) {
switch (dialogId) {
case DIALOG_MAGNIFICATION_MODE:
return SettingsEnums.DIALOG_MAGNIFICATION_CAPABILITY;
case DIALOG_MAGNIFICATION_SWITCH_SHORTCUT:
return SettingsEnums.DIALOG_MAGNIFICATION_SWITCH_SHORTCUT;
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);
return AccessibilityDialogUtils.createCustomDialog(mContext, title,
mMagnificationModesListView, this::onMagnificationModeDialogPositiveButtonClicked);
}
private void onMagnificationModeDialogPositiveButtonClicked(DialogInterface dialogInterface,
int which) {
final int selectedIndex = mMagnificationModesListView.getCheckedItemPosition();
if (selectedIndex != AdapterView.INVALID_POSITION) {
final MagnificationModeInfo modeInfo =
(MagnificationModeInfo) mMagnificationModesListView.getItemAtPosition(
selectedIndex);
setMode(modeInfo.mMagnificationMode);
} else {
Log.w(TAG, "invalid index");
}
}
private void setMode(int mode) {
mMode = mode;
MagnificationCapabilities.setCapabilities(mContext, mMode);
mModePreference.setSummary(
MagnificationCapabilities.getSummary(mContext, mMode));
}
private void onMagnificationModeSelected(AdapterView<?> parent, View view, int position,
long id) {
final MagnificationModeInfo modeInfo =
(MagnificationModeInfo) mMagnificationModesListView.getItemAtPosition(
position);
if (modeInfo.mMagnificationMode == mMode) {
return;
}
mMode = modeInfo.mMagnificationMode;
if (isTripleTapEnabled(mContext) && mMode != MagnificationMode.FULLSCREEN) {
mDialogHelper.showDialog(DIALOG_MAGNIFICATION_SWITCH_SHORTCUT);
}
}
private int computeSelectionIndex() {
final int modesSize = mModeInfos.size();
for (int i = 0; i < modesSize; i++) {
if (mModeInfos.get(i).mMagnificationMode == mMode) {
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 createMagnificationShortCutConfirmDialog() {
return AccessibilityDialogUtils.createMagnificationSwitchShortcutDialog(mContext,
this::onSwitchShortcutDialogButtonClicked);
}
@VisibleForTesting
void onSwitchShortcutDialogButtonClicked(@CustomButton int which) {
optOutMagnificationFromTripleTap();
//TODO(b/147990389): Merge this function into AccessibilityUtils after the format of
// magnification target is changed to ComponentName.
optInMagnificationToAccessibilityButton();
}
private void optOutMagnificationFromTripleTap() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, OFF);
}
private void optInMagnificationToAccessibilityButton() {
final String targetKey = Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
final String targetString = Settings.Secure.getString(mContext.getContentResolver(),
targetKey);
if (targetString != null && targetString.contains(MAGNIFICATION_CONTROLLER_NAME)) {
return;
}
final StringJoiner joiner = new StringJoiner(String.valueOf(COMPONENT_NAME_SEPARATOR));
if (!TextUtils.isEmpty(targetString)) {
joiner.add(targetString);
}
joiner.add(MAGNIFICATION_CONTROLLER_NAME);
Settings.Secure.putString(mContext.getContentResolver(), targetKey,
joiner.toString());
}
// 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;
}
}
}