Merge tm-dev-plus-aosp-without-vendor@8763363

Bug: 236760014
Merged-In: Ifcb9d4c564839199d998bd503f390f021c6bf3ad
Change-Id: I9d69bcbc6916176beece2616f152ebd3d74fc0f8
This commit is contained in:
Xin Li
2022-06-28 21:23:28 +00:00
1671 changed files with 85400 additions and 30222 deletions

View File

@@ -0,0 +1,65 @@
/*
* 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;
import android.app.Activity;
import android.app.AppOpsManager;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
public class ActionDisabledByAppOpsDialog extends Activity
implements DialogInterface.OnDismissListener {
private static final String TAG = "ActionDisabledByAppOpsDialog";
private ActionDisabledByAppOpsHelper mDialogHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDialogHelper = new ActionDisabledByAppOpsHelper(this);
mDialogHelper.prepareDialogBuilder()
.setOnDismissListener(this)
.show();
updateAppOps();
}
private void updateAppOps() {
final Intent intent = getIntent();
final String packageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
final int uid = intent.getIntExtra(Intent.EXTRA_UID, android.os.Process.INVALID_UID);
getSystemService(AppOpsManager.class)
.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
uid,
packageName,
AppOpsManager.MODE_IGNORED);
}
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
mDialogHelper.updateDialog();
updateAppOps();
}
@Override
public void onDismiss(DialogInterface dialog) {
finish();
}
}

View File

@@ -0,0 +1,88 @@
/*
* 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;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import com.android.settingslib.HelpUtils;
final class ActionDisabledByAppOpsHelper {
private final ViewGroup mDialogView;
private final Activity mActivity;
ActionDisabledByAppOpsHelper(Activity activity) {
mActivity = activity;
mDialogView = (ViewGroup) LayoutInflater.from(mActivity).inflate(
R.layout.support_details_dialog, null);
}
public AlertDialog.Builder prepareDialogBuilder() {
final String helpUrl = mActivity.getString(
R.string.help_url_action_disabled_by_restricted_settings);
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity)
.setPositiveButton(R.string.okay, null)
.setView(mDialogView);
if (!TextUtils.isEmpty(helpUrl)) {
builder.setNeutralButton(R.string.learn_more,
(DialogInterface.OnClickListener) (dialog, which) -> {
final Intent intent = HelpUtils.getHelpIntent(mActivity,
helpUrl, mActivity.getClass().getName());
if (intent != null) {
mActivity.startActivity(intent);
}
});
}
initializeDialogViews(mDialogView);
return builder;
}
public void updateDialog() {
initializeDialogViews(mDialogView);
}
private void initializeDialogViews(View root) {
setSupportTitle(root);
setSupportDetails(root);
}
@VisibleForTesting
void setSupportTitle(View root) {
final TextView titleView = root.findViewById(R.id.admin_support_dialog_title);
if (titleView == null) {
return;
}
titleView.setText(R.string.blocked_by_restricted_settings_title);
}
void setSupportDetails(final View root) {
final TextView textView = root.findViewById(R.id.admin_support_msg);
textView.setText(R.string.blocked_by_restricted_settings_content);
}
}

View File

@@ -59,25 +59,25 @@ import java.util.List;
*/
public class ActivityPicker extends AlertActivity implements
DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
/**
* Adapter of items that are displayed in this dialog.
*/
private PickAdapter mAdapter;
/**
* Base {@link Intent} used when building list.
*/
private Intent mBaseIntent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addPrivateFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
final Intent intent = getIntent();
// Read base intent from extras, otherwise assume default
Parcelable parcel = intent.getParcelableExtra(Intent.EXTRA_INTENT);
if (parcel instanceof Intent) {
@@ -95,14 +95,14 @@ public class ActivityPicker extends AlertActivity implements
AlertController.AlertParams params = mAlertParams;
params.mOnClickListener = this;
params.mOnCancelListener = this;
// Use custom title if provided, otherwise default window title
if (intent.hasExtra(Intent.EXTRA_TITLE)) {
params.mTitle = intent.getStringExtra(Intent.EXTRA_TITLE);
} else {
params.mTitle = getTitle();
}
// Build list adapter of pickable items
List<PickAdapter.Item> items = getItems();
mAdapter = new PickAdapter(this, items);
@@ -110,7 +110,7 @@ public class ActivityPicker extends AlertActivity implements
setupAlert();
}
/**
* Handle clicking of dialog item by passing back
* {@link #getIntentForPosition(int)} in {@link #setResult(int, Intent)}.
@@ -120,7 +120,7 @@ public class ActivityPicker extends AlertActivity implements
setResult(Activity.RESULT_OK, intent);
finish();
}
/**
* Handle canceled dialog by passing back {@link Activity#RESULT_CANCELED}.
*/
@@ -148,19 +148,19 @@ public class ActivityPicker extends AlertActivity implements
protected List<PickAdapter.Item> getItems() {
PackageManager packageManager = getPackageManager();
List<PickAdapter.Item> items = new ArrayList<PickAdapter.Item>();
// Add any injected pick items
final Intent intent = getIntent();
ArrayList<String> labels =
intent.getStringArrayListExtra(Intent.EXTRA_SHORTCUT_NAME);
ArrayList<ShortcutIconResource> icons =
intent.getParcelableArrayListExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
if (labels != null && icons != null && labels.size() == icons.size()) {
for (int i = 0; i < labels.size(); i++) {
String label = labels.get(i);
Drawable icon = null;
try {
// Try loading icon from requested package
ShortcutIconResource iconResource = icons.get(i);
@@ -171,7 +171,7 @@ public class ActivityPicker extends AlertActivity implements
} catch (NameNotFoundException e) {
// Ignore
}
items.add(new PickAdapter.Item(this, label, icon));
}
}
@@ -180,38 +180,38 @@ public class ActivityPicker extends AlertActivity implements
if (mBaseIntent != null) {
putIntentItems(mBaseIntent, items);
}
return items;
}
/**
* Fill the given list with any activities matching the base {@link Intent}.
* Fill the given list with any activities matching the base {@link Intent}.
*/
protected void putIntentItems(Intent baseIntent, List<PickAdapter.Item> items) {
PackageManager packageManager = getPackageManager();
List<ResolveInfo> list = packageManager.queryIntentActivities(baseIntent,
0 /* no flags */);
Collections.sort(list, new ResolveInfo.DisplayNameComparator(packageManager));
final int listSize = list.size();
for (int i = 0; i < listSize; i++) {
ResolveInfo resolveInfo = list.get(i);
items.add(new PickAdapter.Item(this, packageManager, resolveInfo));
}
}
/**
* Adapter which shows the set of activities that can be performed for a
* given {@link Intent}.
*/
protected static class PickAdapter extends BaseAdapter {
/**
* Item that appears in a {@link PickAdapter} list.
*/
public static class Item implements AppWidgetLoader.LabelledItem {
protected static IconResizer sResizer;
protected IconResizer getResizer(Context context) {
if (sResizer == null) {
final Resources resources = context.getResources();
@@ -220,13 +220,13 @@ public class ActivityPicker extends AlertActivity implements
}
return sResizer;
}
CharSequence label;
Drawable icon;
String packageName;
String className;
Bundle extras;
/**
* Create a list item from given label and icon.
*/
@@ -275,10 +275,10 @@ public class ActivityPicker extends AlertActivity implements
return label;
}
}
private final LayoutInflater mInflater;
private final List<Item> mItems;
/**
* Create an adapter for the given items.
*/
@@ -315,16 +315,16 @@ public class ActivityPicker extends AlertActivity implements
if (convertView == null) {
convertView = mInflater.inflate(R.layout.pick_item, parent, false);
}
Item item = (Item) getItem(position);
TextView textView = (TextView) convertView;
textView.setText(item.label);
textView.setCompoundDrawablesWithIntrinsicBounds(item.icon, null, null, null);
return convertView;
}
}
/**
* Utility class to resize icons to match default icon size. Code is mostly
* borrowed from Launcher.
@@ -336,14 +336,14 @@ public class ActivityPicker extends AlertActivity implements
private final DisplayMetrics mMetrics;
private final Rect mOldBounds = new Rect();
private final Canvas mCanvas = new Canvas();
public IconResizer(int width, int height, DisplayMetrics metrics) {
mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
Paint.FILTER_BITMAP_FLAG));
mMetrics = metrics;
mIconWidth = width;
mIconHeight = height;
mIconHeight = height;
}
/**
@@ -356,7 +356,7 @@ public class ActivityPicker extends AlertActivity implements
* @param icon The icon to get a thumbnail of.
*
* @return A thumbnail for the specified icon or the icon itself if the
* thumbnail could not be created.
* thumbnail could not be created.
*/
public Drawable createIconThumbnail(Drawable icon) {
int width = mIconWidth;
@@ -365,7 +365,7 @@ public class ActivityPicker extends AlertActivity implements
if (icon == null) {
return new EmptyDrawable(width, height);
}
try {
if (icon instanceof PaintDrawable) {
PaintDrawable painter = (PaintDrawable) icon;
@@ -381,17 +381,17 @@ public class ActivityPicker extends AlertActivity implements
}
int iconWidth = icon.getIntrinsicWidth();
int iconHeight = icon.getIntrinsicHeight();
if (iconWidth > 0 && iconHeight > 0) {
if (width < iconWidth || height < iconHeight) {
final float ratio = (float) iconWidth / iconHeight;
if (iconWidth > iconHeight) {
height = (int) (width / ratio);
} else if (iconHeight > iconWidth) {
width = (int) (height * ratio);
}
final Bitmap.Config c = icon.getOpacity() != PixelFormat.OPAQUE ?
Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c);
@@ -429,7 +429,7 @@ public class ActivityPicker extends AlertActivity implements
canvas.setBitmap(null);
}
}
} catch (Throwable t) {
icon = new EmptyDrawable(width, height);
}

View File

@@ -22,6 +22,9 @@ import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
public class DefaultRingtonePreference extends RingtonePreference {
private static final String TAG = "DefaultRingtonePreference";
@@ -43,6 +46,29 @@ public class DefaultRingtonePreference extends RingtonePreference {
@Override
protected void onSaveRingtone(Uri ringtoneUri) {
if (ringtoneUri == null) {
setActualDefaultRingtoneUri(ringtoneUri);
return;
}
String mimeType = mUserContext.getContentResolver().getType(ringtoneUri);
if (mimeType == null) {
Log.e(TAG, "onSaveRingtone for URI:" + ringtoneUri
+ " ignored: failure to find mimeType (no access from this context?)");
return;
}
if (!(mimeType.startsWith("audio/") || mimeType.equals("application/ogg"))) {
Log.e(TAG, "onSaveRingtone for URI:" + ringtoneUri
+ " ignored: associated mimeType:" + mimeType + " is not an audio type");
return;
}
setActualDefaultRingtoneUri(ringtoneUri);
}
@VisibleForTesting
void setActualDefaultRingtoneUri(Uri ringtoneUri) {
RingtoneManager.setActualDefaultRingtoneUri(mUserContext, getRingtoneType(), ringtoneUri);
}

View File

@@ -42,7 +42,7 @@ import java.util.Objects;
public class FallbackHome extends Activity {
private static final String TAG = "FallbackHome";
private static final int PROGRESS_TIMEOUT = 2000;
private int mProgressTimeout;
private boolean mProvisioned;
private WallpaperManager mWallManager;
@@ -76,6 +76,12 @@ public class FallbackHome extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mProgressTimeout = getResources().getInteger(
com.android.internal.R.integer.config_progressTimeoutFallbackHome);
if (mProgressTimeout <= 0) {
mProgressTimeout = 0;
}
// Set ourselves totally black before the device is provisioned so that
// we don't flash the wallpaper before SUW
@@ -107,7 +113,7 @@ public class FallbackHome extends Activity {
protected void onResume() {
super.onResume();
if (mProvisioned) {
mHandler.postDelayed(mProgressTimeoutRunnable, PROGRESS_TIMEOUT);
mHandler.postDelayed(mProgressTimeoutRunnable, mProgressTimeout);
}
}

View File

@@ -16,6 +16,9 @@
package com.android.settings;
import static android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER;
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import android.accounts.Account;
@@ -23,6 +26,7 @@ import android.accounts.AccountManager;
import android.accounts.AuthenticatorDescription;
import android.app.ActionBar;
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -490,9 +494,20 @@ public class MainClear extends InstrumentedFragment implements OnGlobalLayoutLis
if (profilesSize > 1) {
View titleView = Utils.inflateCategoryHeader(inflater, contents);
titleView.setPadding(0 /* left */, titleView.getPaddingTop(),
0 /* right */, titleView.getPaddingBottom());
final TextView titleText = (TextView) titleView.findViewById(android.R.id.title);
titleText.setText(userInfo.isManagedProfile() ? R.string.category_work
: R.string.category_personal);
DevicePolicyManager devicePolicyManager =
context.getSystemService(DevicePolicyManager.class);
if (userInfo.isManagedProfile()) {
titleText.setText(devicePolicyManager.getResources().getString(
WORK_CATEGORY_HEADER, () -> getString(R.string.category_work)));
} else {
titleText.setText(devicePolicyManager.getResources().getString(
PERSONAL_CATEGORY_HEADER, () -> getString(R.string.category_personal)));
}
contents.addView(titleView);
}

View File

@@ -31,6 +31,7 @@ import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.oemlock.OemLockManager;
@@ -67,6 +68,8 @@ import com.google.android.setupdesign.GlifLayout;
public class MainClearConfirm extends InstrumentedFragment {
private static final String TAG = "MainClearConfirm";
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
@VisibleForTesting View mContentView;
private boolean mEraseSdCard;
@VisibleForTesting boolean mEraseEsims;
@@ -83,6 +86,11 @@ public class MainClearConfirm extends InstrumentedFragment {
return;
}
// pre-flight check hardware support PersistentDataBlockManager
if (SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals("")) {
return;
}
final PersistentDataBlockManager pdbManager = (PersistentDataBlockManager)
getActivity().getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);

View File

@@ -16,12 +16,16 @@
package com.android.settings;
import static android.app.admin.DevicePolicyResources.Strings.Settings.DEVICE_OWNER_INSTALLED_CERTIFICATE_AUTHORITY_WARNING;
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_INSTALLED_CERTIFICATE_AUTHORITY_WARNING;
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.icu.text.MessageFormat;
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
@@ -30,6 +34,10 @@ import androidx.appcompat.app.AlertDialog;
import com.android.settingslib.RestrictedLockUtils;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* Activity that shows a dialog explaining that a CA cert is allowing someone to monitor network
* traffic. This activity should be launched for the user into which the CA cert is installed
@@ -71,12 +79,30 @@ public class MonitoringCertInfoActivity extends Activity implements OnClickListe
builder.setOnDismissListener(this);
if (dpm.getProfileOwnerAsUser(mUserId) != null) {
builder.setMessage(getResources().getQuantityString(R.plurals.ssl_ca_cert_info_message,
numberOfCertificates, dpm.getProfileOwnerNameAsUser(mUserId)));
MessageFormat msgFormat = new MessageFormat(
dpm.getResources().getString(
WORK_PROFILE_INSTALLED_CERTIFICATE_AUTHORITY_WARNING,
() -> getString(R.string.ssl_ca_cert_info_message)),
Locale.getDefault());
Map<String, Object> arguments = new HashMap<>();
arguments.put("numberOfCertificates", numberOfCertificates);
arguments.put("orgName", dpm.getProfileOwnerNameAsUser(mUserId));
builder.setMessage(msgFormat.format(arguments));
} else if (dpm.getDeviceOwnerComponentOnCallingUser() != null) {
builder.setMessage(getResources().getQuantityString(
R.plurals.ssl_ca_cert_info_message_device_owner, numberOfCertificates,
dpm.getDeviceOwnerNameOnAnyUser()));
MessageFormat msgFormat = new MessageFormat(
dpm.getResources()
.getString(DEVICE_OWNER_INSTALLED_CERTIFICATE_AUTHORITY_WARNING,
() -> getResources().getString(
R.string.ssl_ca_cert_info_message_device_owner)),
Locale.getDefault());
Map<String, Object> arguments = new HashMap<>();
arguments.put("numberOfCertificates", numberOfCertificates);
arguments.put("orgName", dpm.getDeviceOwnerNameOnAnyUser());
builder.setMessage(msgFormat.format(arguments));
} else {
// Consumer case. Show scary warning.
builder.setIcon(android.R.drawable.stat_notify_error);

View File

@@ -18,6 +18,8 @@ package com.android.settings;
import static android.view.HapticFeedbackConstants.CLOCK_TICK;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SETTINGS_SLIDER;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
@@ -30,9 +32,12 @@ import android.util.AttributeSet;
import android.view.View;
import android.widget.SeekBar;
import com.android.internal.jank.InteractionJankMonitor;
public class PointerSpeedPreference extends SeekBarDialogPreference implements
SeekBar.OnSeekBarChangeListener {
private final InputManager mIm;
private final InteractionJankMonitor mJankMonitor = InteractionJankMonitor.getInstance();
private SeekBar mSeekBar;
private int mOldSpeed;
@@ -88,11 +93,15 @@ public class PointerSpeedPreference extends SeekBarDialogPreference implements
public void onStartTrackingTouch(SeekBar seekBar) {
mTouchInProgress = true;
mJankMonitor.begin(InteractionJankMonitor.Configuration.Builder
.withView(CUJ_SETTINGS_SLIDER, seekBar)
.setTag(getKey()));
}
public void onStopTrackingTouch(SeekBar seekBar) {
mTouchInProgress = false;
mIm.tryPointerSpeed(seekBar.getProgress() + InputManager.MIN_POINTER_SPEED);
mJankMonitor.end(CUJ_SETTINGS_SLIDER);
}
private void onSpeedChanged() {

View File

@@ -15,6 +15,11 @@
*/
package com.android.settings;
import static android.app.admin.DevicePolicyResources.Strings.Settings.SHARE_REMOTE_BUGREPORT_DIALOG_TITLE;
import static android.app.admin.DevicePolicyResources.Strings.Settings.SHARE_REMOTE_BUGREPORT_FINISHED_REQUEST_CONSENT;
import static android.app.admin.DevicePolicyResources.Strings.Settings.SHARE_REMOTE_BUGREPORT_NOT_FINISHED_REQUEST_CONSENT;
import static android.app.admin.DevicePolicyResources.Strings.Settings.SHARING_REMOTE_BUGREPORT_MESSAGE;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
@@ -42,12 +47,16 @@ public class RemoteBugreportActivity extends Activity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DevicePolicyManager devicePolicyManager = getSystemService(DevicePolicyManager.class);
final int notificationType = getIntent().getIntExtra(
DevicePolicyManager.EXTRA_BUGREPORT_NOTIFICATION_TYPE, -1);
if (notificationType == DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED) {
AlertDialog dialog = new AlertDialog.Builder(this)
.setMessage(R.string.sharing_remote_bugreport_dialog_message)
.setMessage(devicePolicyManager.getResources().getString(
SHARING_REMOTE_BUGREPORT_MESSAGE,
() -> getString(R.string.sharing_remote_bugreport_dialog_message)))
.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
@@ -65,12 +74,22 @@ public class RemoteBugreportActivity extends Activity {
} else if (notificationType == DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED
|| notificationType
== DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED) {
int defaultMessageId = notificationType
== DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED
? R.string.share_remote_bugreport_dialog_message
: R.string.share_remote_bugreport_dialog_message_finished;
String overrideMessageId = notificationType
== DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED
? SHARE_REMOTE_BUGREPORT_NOT_FINISHED_REQUEST_CONSENT
: SHARE_REMOTE_BUGREPORT_FINISHED_REQUEST_CONSENT;
AlertDialog dialog = new AlertDialog.Builder(this)
.setTitle(R.string.share_remote_bugreport_dialog_title)
.setMessage(notificationType
== DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED
? R.string.share_remote_bugreport_dialog_message
: R.string.share_remote_bugreport_dialog_message_finished)
.setTitle(devicePolicyManager.getResources().getString(
SHARE_REMOTE_BUGREPORT_DIALOG_TITLE,
() -> getString(R.string.share_remote_bugreport_dialog_title)))
.setMessage(devicePolicyManager.getResources().getString(overrideMessageId,
() -> getString(defaultMessageId)))
.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {

View File

@@ -201,7 +201,8 @@ public class ResetNetwork extends InstrumentedFragment {
name = record.getNumber();
}
if (TextUtils.isEmpty(name)) {
name = record.getCarrierName().toString();
CharSequence carrierName = record.getCarrierName();
name = TextUtils.isEmpty(carrierName) ? "" : carrierName.toString();
}
if (TextUtils.isEmpty(name)) {
name = String.format("MCC:%s MNC:%s Slot:%s Id:%s", record.getMcc(),

View File

@@ -30,7 +30,6 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CheckedTextView;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
@@ -53,7 +52,6 @@ public class RestrictedListPreference extends CustomListPreference {
public RestrictedListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setWidgetLayoutResource(R.layout.restricted_icon);
mHelper = new RestrictedPreferenceHelper(context, this, attrs);
}
@@ -67,10 +65,6 @@ public class RestrictedListPreference extends CustomListPreference {
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
mHelper.onBindViewHolder(holder);
final View restrictedIcon = holder.findViewById(R.id.restricted_icon);
if (restrictedIcon != null) {
restrictedIcon.setVisibility(isDisabledByAdmin() ? View.VISIBLE : View.GONE);
}
}
@Override
@@ -187,11 +181,9 @@ public class RestrictedListPreference extends CustomListPreference {
View root = super.getView(position, convertView, parent);
CharSequence entry = getItem(position);
CheckedTextView text = (CheckedTextView) root.findViewById(R.id.text1);
ImageView padlock = (ImageView) root.findViewById(R.id.restricted_lock_icon);
if (isRestrictedForEntry(entry)) {
text.setEnabled(false);
text.setChecked(false);
padlock.setVisibility(View.VISIBLE);
} else {
if (mSelectedIndex != -1) {
text.setChecked(position == mSelectedIndex);
@@ -199,7 +191,6 @@ public class RestrictedListPreference extends CustomListPreference {
if (!text.isEnabled()) {
text.setEnabled(true);
}
padlock.setVisibility(View.GONE);
}
return root;
}

View File

@@ -16,16 +16,24 @@
package com.android.settings;
import static android.provider.Settings.ACTION_PRIVACY_SETTINGS;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.ims.ImsRcsManager;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.biometrics.face.FaceSettings;
import com.android.settings.core.FeatureFlags;
import com.android.settings.enterprise.EnterprisePrivacySettings;
import com.android.settings.network.MobileNetworkIntentConverter;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
import com.android.settings.security.SecuritySettingsFeatureProvider;
import com.google.android.setupdesign.util.ThemeHelper;
@@ -42,6 +50,8 @@ public class Settings extends SettingsActivity {
public static class BluetoothSettingsActivity extends SettingsActivity { /* empty */ }
public static class CreateShortcutActivity extends SettingsActivity { /* empty */ }
public static class FaceSettingsActivity extends SettingsActivity { /* empty */ }
/** Container for {@link FaceSettings} to use with a pre-defined task affinity. */
public static class FaceSettingsInternalActivity extends SettingsActivity { /* empty */ }
public static class FingerprintSettingsActivity extends SettingsActivity { /* empty */ }
public static class CombinedBiometricSettingsActivity extends SettingsActivity { /* empty */ }
public static class CombinedBiometricProfileSettingsActivity extends SettingsActivity { /* empty */ }
@@ -131,6 +141,27 @@ public class Settings extends SettingsActivity {
/** Activity for the security dashboard. */
public static class SecurityDashboardActivity extends SettingsActivity {
private static final String TAG = "SecurityDashboardActivity";
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
handleSafetyCenterRedirection();
}
/** Redirects to SafetyCenter if enabled. */
@VisibleForTesting
public void handleSafetyCenterRedirection() {
if (SafetyCenterManagerWrapper.get().isEnabled(this)) {
try {
startActivity(new Intent(Intent.ACTION_SAFETY_CENTER));
finish();
} catch (ActivityNotFoundException e) {
Log.e(TAG, "Unable to open safety center", e);
}
}
}
/** Whether the given fragment is allowed. */
@VisibleForTesting
@Override
@@ -161,12 +192,39 @@ public class Settings extends SettingsActivity {
return alternativeFragmentClassname;
}
}
/** Activity for the Advanced security settings. */
public static class SecurityAdvancedSettings extends SettingsActivity { /* empty */ }
public static class UsageAccessSettingsActivity extends SettingsActivity { /* empty */ }
public static class AppUsageAccessSettingsActivity extends SettingsActivity { /* empty */ }
public static class LocationSettingsActivity extends SettingsActivity { /* empty */ }
public static class ScanningSettingsActivity extends SettingsActivity { /* empty */ }
public static class WifiScanningSettingsActivity extends SettingsActivity { /* empty */ }
public static class PrivacyDashboardActivity extends SettingsActivity { /* empty */ }
/** Activity for the privacy dashboard. */
public static class PrivacyDashboardActivity extends SettingsActivity {
private static final String TAG = "PrivacyDashboardActivity";
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
handleSafetyCenterRedirection();
}
/** Redirects to SafetyCenter if enabled. */
@VisibleForTesting
public void handleSafetyCenterRedirection() {
if (ACTION_PRIVACY_SETTINGS.equals(getIntent().getAction())
&& SafetyCenterManagerWrapper.get().isEnabled(this)) {
try {
startActivity(new Intent(Intent.ACTION_SAFETY_CENTER));
finish();
} catch (ActivityNotFoundException e) {
Log.e(TAG, "Unable to open safety center", e);
}
}
}
}
public static class PrivacyControlsActivity extends SettingsActivity { /* empty */ }
public static class PrivacySettingsActivity extends SettingsActivity { /* empty */ }
public static class FactoryResetActivity extends SettingsActivity {
@Override
@@ -215,6 +273,7 @@ public class Settings extends SettingsActivity {
public static class VrListenersSettingsActivity extends SettingsActivity { /* empty */ }
public static class PremiumSmsAccessActivity extends SettingsActivity { /* empty */ }
public static class PictureInPictureSettingsActivity extends SettingsActivity { /* empty */ }
public static class TurnScreenOnSettingsActivity extends SettingsActivity { /* empty */ }
public static class AppPictureInPictureSettingsActivity extends SettingsActivity { /* empty */ }
public static class ZenAccessSettingsActivity extends SettingsActivity { /* empty */ }
public static class ZenAccessDetailSettingsActivity extends SettingsActivity {}
@@ -237,6 +296,7 @@ public class Settings extends SettingsActivity {
public static class AppBubbleNotificationSettingsActivity extends SettingsActivity { /* empty */ }
public static class NotificationAssistantSettingsActivity extends SettingsActivity{ /* empty */ }
public static class NotificationAppListActivity extends SettingsActivity { /* empty */ }
public static class NotificationReviewPermissionsActivity extends SettingsActivity { /* empty */ }
public static class AppNotificationSettingsActivity extends SettingsActivity { /* empty */ }
public static class ChannelNotificationSettingsActivity extends SettingsActivity { /* empty */ }
public static class ChannelGroupNotificationSettingsActivity extends SettingsActivity { /* empty */ }
@@ -244,6 +304,8 @@ public class Settings extends SettingsActivity {
public static class AutomaticStorageManagerSettingsActivity extends SettingsActivity { /* empty */ }
public static class GamesStorageActivity extends SettingsActivity { /* empty */ }
public static class GestureNavigationSettingsActivity extends SettingsActivity { /* empty */ }
/** Activity to manage 2-/3-button navigation configuration. */
public static class ButtonNavigationSettingsActivity extends SettingsActivity { /* empty */ }
public static class InteractAcrossProfilesSettingsActivity extends SettingsActivity {
/* empty */
}
@@ -303,6 +365,48 @@ public class Settings extends SettingsActivity {
public static class WifiCallingDisclaimerActivity extends SettingsActivity { /* empty */ }
public static class MobileNetworkListActivity extends SettingsActivity {}
public static class PowerMenuSettingsActivity extends SettingsActivity {}
public static class MobileNetworkActivity extends SettingsActivity {
public static final String TAG = "MobileNetworkActivity";
public static final String EXTRA_MMS_MESSAGE = "mms_message";
public static final String EXTRA_SHOW_CAPABILITY_DISCOVERY_OPT_IN =
"show_capability_discovery_opt_in";
private MobileNetworkIntentConverter mIntentConverter;
/**
* Override of #onNewIntent() requires Activity to have "singleTop" launch mode within
* AndroidManifest.xml
*/
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d(TAG, "Starting onNewIntent");
createUiFromIntent(null /* savedState */, convertIntent(intent));
}
@Override
public Intent getIntent() {
return convertIntent(super.getIntent());
}
private Intent convertIntent(Intent copyFrom) {
if (mIntentConverter == null) {
mIntentConverter = new MobileNetworkIntentConverter(this);
}
Intent intent = mIntentConverter.apply(copyFrom);
return (intent == null) ? copyFrom : intent;
}
public static boolean doesIntentContainOptInAction(Intent intent) {
String intentAction = (intent != null ? intent.getAction() : null);
return TextUtils.equals(intentAction,
ImsRcsManager.ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN);
}
}
/**
* Activity for BugReportHandlerPicker.
*/
@@ -327,4 +431,9 @@ public class Settings extends SettingsActivity {
public static class AppDashboardActivity extends SettingsActivity {}
public static class AdaptiveBrightnessActivity extends SettingsActivity { /* empty */ }
/**
* Activity for OneHandedSettings
*/
public static class OneHandedSettingsActivity extends SettingsActivity { /* empty */ }
}

View File

@@ -24,6 +24,7 @@ import static com.android.settings.applications.appinfo.AppButtonsPreferenceCont
import android.app.ActionBar;
import android.app.ActivityManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -33,6 +34,7 @@ import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.graphics.drawable.Icon;
@@ -64,8 +66,8 @@ import com.android.settings.core.SettingsBaseActivity;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.core.gateway.SettingsGateway;
import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.homepage.DeepLinkHomepageActivityInternal;
import com.android.settings.homepage.SettingsHomepageActivity;
import com.android.settings.homepage.SliceDeepLinkHomepageActivity;
import com.android.settings.homepage.TopLevelSettings;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.wfd.WifiDisplaySettings;
@@ -143,6 +145,7 @@ public class SettingsActivity extends SettingsBaseActivity
public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
":settings:show_fragment_as_subsetting";
public static final String EXTRA_IS_SECOND_LAYER_PAGE = ":settings:is_second_layer_page";
/**
* Additional extra of Settings#ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK.
@@ -150,6 +153,8 @@ public class SettingsActivity extends SettingsBaseActivity
*/
public static final String EXTRA_IS_FROM_SLICE = "is_from_slice";
public static final String EXTRA_USER_HANDLE = "user_handle";
/**
* Personal or Work profile tab of {@link ProfileSelectFragment}
* <p>0: Personal tab.
@@ -251,16 +256,18 @@ public class SettingsActivity extends SettingsBaseActivity
getMetaData();
final Intent intent = getIntent();
if (shouldShowTwoPaneDeepLink(intent)) {
launchHomepageForTwoPaneDeepLink(intent);
finishAndRemoveTask();
if (shouldShowTwoPaneDeepLink(intent) && tryStartTwoPaneDeepLink(intent)) {
finish();
super.onCreate(savedState);
return;
}
super.onCreate(savedState);
Log.d(LOG_TAG, "Starting onCreate");
createUiFromIntent(savedState, intent);
}
protected void createUiFromIntent(Bundle savedState, Intent intent) {
long startTime = System.currentTimeMillis();
final FeatureFactory factory = FeatureFactory.getFactory(this);
@@ -301,12 +308,12 @@ public class SettingsActivity extends SettingsBaseActivity
launchSettingFragment(initialFragmentName, intent);
}
final boolean isInSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
final boolean isActionBarButtonEnabled = isActionBarButtonEnabled(intent);
final ActionBar actionBar = getActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(!isInSetupWizard);
actionBar.setHomeButtonEnabled(!isInSetupWizard);
actionBar.setDisplayHomeAsUpEnabled(isActionBarButtonEnabled);
actionBar.setHomeButtonEnabled(isActionBarButtonEnabled);
actionBar.setDisplayShowTitleEnabled(true);
}
mMainSwitch = findViewById(R.id.switch_bar);
@@ -366,6 +373,18 @@ public class SettingsActivity extends SettingsBaseActivity
}
}
private boolean isActionBarButtonEnabled(Intent intent) {
if (WizardManagerHelper.isAnySetupWizard(intent)) {
return false;
}
final boolean isSecondLayerPage =
intent.getBooleanExtra(EXTRA_IS_SECOND_LAYER_PAGE, false);
// TODO: move Settings's ActivityEmbeddingUtils to SettingsLib.
return !com.android.settingslib.activityembedding.ActivityEmbeddingUtils
.shouldHideNavigateUpButton(this, isSecondLayerPage);
}
private boolean isSubSettings(Intent intent) {
return this instanceof SubSettings ||
intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
@@ -398,7 +417,7 @@ public class SettingsActivity extends SettingsBaseActivity
return trampolineIntent;
}
private void launchHomepageForTwoPaneDeepLink(Intent intent) {
private boolean tryStartTwoPaneDeepLink(Intent intent) {
final Intent trampolineIntent;
if (intent.getBooleanExtra(EXTRA_IS_FROM_SLICE, false)) {
// Get menu key for slice deep link case.
@@ -408,11 +427,26 @@ public class SettingsActivity extends SettingsBaseActivity
mHighlightMenuKey = highlightMenuKey;
}
trampolineIntent = getTrampolineIntent(intent, mHighlightMenuKey);
trampolineIntent.setClass(this, SliceDeepLinkHomepageActivity.class);
trampolineIntent.setClass(this, DeepLinkHomepageActivityInternal.class);
} else {
trampolineIntent = getTrampolineIntent(intent, mHighlightMenuKey);
}
startActivity(trampolineIntent);
try {
final UserManager um = getSystemService(UserManager.class);
final UserInfo userInfo = um.getUserInfo(getUser().getIdentifier());
if (userInfo.isManagedProfile()) {
trampolineIntent.setClass(this, DeepLinkHomepageActivityInternal.class)
.putExtra(EXTRA_USER_HANDLE, getUser());
startActivityAsUser(trampolineIntent, um.getPrimaryUser().getUserHandle());
} else {
startActivity(trampolineIntent);
}
} catch (ActivityNotFoundException e) {
Log.e(LOG_TAG, "Deep link homepage is not available to show 2-pane UI");
return false;
}
return true;
}
private boolean shouldShowTwoPaneDeepLink(Intent intent) {
@@ -420,8 +454,11 @@ public class SettingsActivity extends SettingsBaseActivity
return false;
}
// If the activity is not the task root, it should not start trampoline for deep links.
if (!isTaskRoot()) {
// If the activity is task root, starting trampoline is needed in order to show two-pane UI.
// If FLAG_ACTIVITY_NEW_TASK is set, the activity will become the start of a new task on
// this history stack, so starting trampoline is needed in order to notify the homepage that
// the highlight key is changed.
if (!isTaskRoot() && (intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
return false;
}
@@ -432,6 +469,15 @@ public class SettingsActivity extends SettingsBaseActivity
return false;
}
// If the activity's launch mode is "singleInstance", it can't be embedded in Settings since
// it will be created in a new task.
ActivityInfo info = intent.resolveActivityInfo(getPackageManager(),
PackageManager.MATCH_DEFAULT_ONLY);
if (info.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
Log.w(LOG_TAG, "launchMode: singleInstance");
return false;
}
if (intent.getBooleanExtra(EXTRA_IS_FROM_SLICE, false)) {
// Slice deep link starts the Intent using SubSettingLauncher. Returns true to show
// 2-pane deep link.

View File

@@ -20,6 +20,7 @@ import android.app.Application;
import com.android.settings.activityembedding.ActivityEmbeddingRulesController;
import com.android.settings.homepage.SettingsHomepageActivity;
import com.android.settingslib.applications.AppIconCacheManager;
import java.lang.ref.WeakReference;
@@ -44,4 +45,10 @@ public class SettingsApplication extends Application {
public SettingsHomepageActivity getHomeActivity() {
return mHomeActivity.get();
}
@Override
public void onLowMemory() {
super.onLowMemory();
AppIconCacheManager.getInstance().release();
}
}

View File

@@ -18,6 +18,7 @@ package com.android.settings;
import android.app.Activity;
import android.app.Dialog;
import android.app.admin.DevicePolicyManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
@@ -54,6 +55,7 @@ import com.android.settingslib.search.Indexable;
import com.android.settingslib.widget.LayoutPreference;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.setupcompat.util.WizardManagerHelper;
import java.util.UUID;
@@ -63,12 +65,13 @@ import java.util.UUID;
public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceFragment
implements DialogCreatable, HelpResourceProvider, Indexable {
private static final String TAG = "SettingsPreference";
private static final String TAG = "SettingsPreferenceFragment";
private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
private static final int ORDER_FIRST = -1;
protected DevicePolicyManager mDevicePolicyManager;
private SettingsDialogFragment mDialogFragment;
// Cache the content resolver for async callbacks
private ContentResolver mContentResolver;
@@ -121,10 +124,20 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
public HighlightablePreferenceGroupAdapter mAdapter;
private boolean mPreferenceHighlighted = false;
@Override
public void onAttach(Context context) {
if (shouldSkipForInitialSUW() && !WizardManagerHelper.isDeviceProvisioned(getContext())) {
Log.w(TAG, "Skip " + getClass().getSimpleName() + " before SUW completed.");
finish();
}
super.onAttach(context);
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mDevicePolicyManager = getContext().getSystemService(DevicePolicyManager.class);
if (icicle != null) {
mPreferenceHighlighted = icicle.getBoolean(SAVE_HIGHLIGHTED_KEY);
}
@@ -267,6 +280,16 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
|| (mAdapter.getPreferenceAdapterPosition(preference) != RecyclerView.NO_POSITION));
}
/**
* Whether UI should be skipped in the initial SUW flow.
*
* @return {@code true} when UI should be skipped in the initial SUW flow.
* {@code false} when UI should not be skipped in the initial SUW flow.
*/
protected boolean shouldSkipForInitialSUW() {
return false;
}
protected void onDataSetChanged() {
highlightPreferenceIfNeeded();
updateEmptyView();
@@ -708,4 +731,35 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
final Activity activity = getActivity();
return activity == null || activity.isFinishing() || activity.isDestroyed();
}
protected void replaceEnterprisePreferenceScreenTitle(String overrideKey, int resource) {
getActivity().setTitle(mDevicePolicyManager.getResources().getString(
overrideKey, () -> getString(resource)));
}
protected void replaceEnterpriseStringSummary(
String preferenceKey, String overrideKey, int resource) {
Preference preference = findPreference(preferenceKey);
if (preference == null) {
Log.d(TAG, "Could not find enterprise preference " + preferenceKey);
return;
}
preference.setSummary(
mDevicePolicyManager.getResources().getString(overrideKey,
() -> getString(resource)));
}
protected void replaceEnterpriseStringTitle(
String preferenceKey, String overrideKey, int resource) {
Preference preference = findPreference(preferenceKey);
if (preference == null) {
Log.d(TAG, "Could not find enterprise preference " + preferenceKey);
return;
}
preference.setTitle(
mDevicePolicyManager.getResources().getString(overrideKey,
() -> getString(resource)));
}
}

View File

@@ -48,6 +48,9 @@ public class SetupWizardUtils {
if (WizardManagerHelper.isAnySetupWizard(intent)) {
if (ThemeHelper.isSetupWizardDayNightEnabled(context)) {
switch (theme) {
case ThemeHelper.THEME_GLIF_V4_LIGHT:
case ThemeHelper.THEME_GLIF_V4:
return R.style.GlifV4Theme_DayNight;
case ThemeHelper.THEME_GLIF_V3_LIGHT:
case ThemeHelper.THEME_GLIF_V3:
return R.style.GlifV3Theme_DayNight;
@@ -60,6 +63,10 @@ public class SetupWizardUtils {
}
} else {
switch (theme) {
case ThemeHelper.THEME_GLIF_V4_LIGHT:
return R.style.GlifV4Theme_Light;
case ThemeHelper.THEME_GLIF_V4:
return R.style.GlifV4Theme;
case ThemeHelper.THEME_GLIF_V3_LIGHT:
return R.style.GlifV3Theme_Light;
case ThemeHelper.THEME_GLIF_V3:
@@ -76,6 +83,9 @@ public class SetupWizardUtils {
}
} else {
switch (theme) {
case ThemeHelper.THEME_GLIF_V4_LIGHT:
case ThemeHelper.THEME_GLIF_V4:
return R.style.GlifV4Theme;
case ThemeHelper.THEME_GLIF_V3_LIGHT:
case ThemeHelper.THEME_GLIF_V3:
return R.style.GlifV3Theme;

View File

@@ -416,8 +416,11 @@ public class TetherSettings extends RestrictedSettingsFragment
if (usbTethered) {
mUsbTether.setEnabled(!mDataSaverEnabled);
mUsbTether.setChecked(true);
mUsbTether.setDisabledByAdmin(
checkIfUsbDataSignalingIsDisabled(mContext, UserHandle.myUserId()));
final RestrictedLockUtils.EnforcedAdmin enforcedAdmin =
checkIfUsbDataSignalingIsDisabled(mContext, UserHandle.myUserId());
if (enforcedAdmin != null) {
mUsbTether.setDisabledByAdmin(enforcedAdmin);
}
} else {
mUsbTether.setChecked(false);
updateUsbPreference();

View File

@@ -34,7 +34,7 @@ import android.widget.Spinner;
import androidx.appcompat.app.AlertDialog;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.TrustedCredentialsSettings.CertHolder;
import com.android.settings.TrustedCredentialsFragment.CertHolder;
import com.android.settingslib.RestrictedLockUtils;
import java.security.cert.X509Certificate;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -85,7 +85,6 @@ import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.style.TtsSpan;
import android.util.ArraySet;
import android.util.FeatureFlagUtils;
import android.util.IconDrawableFactory;
import android.util.Log;
import android.view.LayoutInflater;
@@ -419,7 +418,7 @@ public final class Utils extends com.android.settingslib.Utils {
public static UserHandle getManagedProfile(UserManager userManager) {
final List<UserHandle> userProfiles = userManager.getUserProfiles();
for (UserHandle profile : userProfiles) {
if (profile.getIdentifier() == userManager.getUserHandle()) {
if (profile.getIdentifier() == userManager.getProcessUserId()) {
continue;
}
final UserInfo userInfo = userManager.getUserInfo(profile.getIdentifier());
@@ -1220,7 +1219,7 @@ public final class Utils extends com.android.settingslib.Utils {
*/
@ColorInt
public static int getHomepageIconColor(Context context) {
return getColorAttrDefaultColor(context, android.R.attr.textColorSecondary);
return getColorAttrDefaultColor(context, android.R.attr.textColorPrimary);
}
/**
@@ -1228,6 +1227,6 @@ public final class Utils extends com.android.settingslib.Utils {
*/
@ColorInt
public static int getHomepageIconColorHighlight(Context context) {
return getColorAttrDefaultColor(context, android.R.attr.textColorSecondaryInverse);
return context.getColor(R.color.accent_select_primary_text);
}
}

View File

@@ -33,7 +33,7 @@ public class AccessibilityButtonFooterPreferenceController extends
}
@Override
protected String getLearnMoreContentDescription() {
protected String getLearnMoreText() {
return mContext.getString(
R.string.accessibility_button_gesture_footer_learn_more_content_description);
}
@@ -50,9 +50,10 @@ public class AccessibilityButtonFooterPreferenceController extends
final int titleResource = AccessibilityUtil.isGestureNavigateEnabled(mContext)
? R.string.accessibility_button_gesture_description
: R.string.accessibility_button_description;
final CharSequence footerText = mContext.getText(titleResource);
final AccessibilityFooterPreference footerPreference =
screen.findPreference(getPreferenceKey());
footerPreference.setTitle(titleResource);
footerPreference.setTitle(footerText);
super.displayPreference(screen);
}
}

View File

@@ -31,7 +31,7 @@ public class AccessibilityControlTimeoutFooterPreferenceController extends
}
@Override
protected String getLearnMoreContentDescription() {
protected String getLearnMoreText() {
return mContext.getString(
R.string.accessibility_control_timeout_footer_learn_more_content_description);
}

View File

@@ -21,6 +21,7 @@ import static com.android.internal.accessibility.AccessibilityShortcutController
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Activity;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
@@ -49,6 +50,7 @@ import java.util.Set;
public class AccessibilityDetailsSettingsFragment extends InstrumentedFragment {
private final static String TAG = "A11yDetailsSettings";
private AppOpsManager mAppOps;
@Override
public int getMetricsCategory() {
@@ -59,6 +61,8 @@ public class AccessibilityDetailsSettingsFragment extends InstrumentedFragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAppOps = getActivity().getSystemService(AppOpsManager.class);
// In case the Intent doesn't have component name, go to a11y services list.
final String extraComponentName = getActivity().getIntent().getStringExtra(
Intent.EXTRA_COMPONENT_NAME);
@@ -127,10 +131,11 @@ public class AccessibilityDetailsSettingsFragment extends InstrumentedFragment {
}
// In case this accessibility service isn't permitted, go to a11y services list.
if (!isServiceAllowed(componentName.getPackageName())) {
if (!isServiceAllowed(info.getResolveInfo().serviceInfo.applicationInfo.uid,
componentName.getPackageName())) {
Log.w(TAG,
"openAccessibilityDetailsSettingsAndFinish: target accessibility service is"
+ "prohibited by Device Admin.");
+ "prohibited by Device Admin or App Op.");
return false;
}
openSubSettings(ToggleAccessibilityServicePreferenceFragment.class.getName(),
@@ -148,11 +153,23 @@ public class AccessibilityDetailsSettingsFragment extends InstrumentedFragment {
}
@VisibleForTesting
boolean isServiceAllowed(String packageName) {
boolean isServiceAllowed(int uid, String packageName) {
final DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
final List<String> permittedServices = dpm.getPermittedAccessibilityServices(
UserHandle.myUserId());
return (permittedServices == null || permittedServices.contains(packageName));
if (permittedServices != null && !permittedServices.contains(packageName)) {
return false;
}
try {
final int mode = mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
uid, packageName);
final boolean ecmEnabled = getContext().getResources().getBoolean(
com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
return !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
} catch (Exception e) {
// Fallback in case if app ops is not available in testing.
return true;
}
}
private AccessibilityServiceInfo getAccessibilityServiceInfo(ComponentName componentName) {
@@ -206,11 +223,26 @@ public class AccessibilityDetailsSettingsFragment extends InstrumentedFragment {
extras.putString(AccessibilitySettings.EXTRA_SETTINGS_COMPONENT_NAME,
new ComponentName(packageName, settingsClassName).flattenToString());
}
final String tileServiceClassName = info.getTileServiceName();
if (!TextUtils.isEmpty(tileServiceClassName)) {
extras.putString(AccessibilitySettings.EXTRA_TILE_SERVICE_COMPONENT_NAME,
new ComponentName(packageName, tileServiceClassName).flattenToString());
}
extras.putParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME, componentName);
extras.putInt(AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES, info.getAnimatedImageRes());
final String htmlDescription = info.loadHtmlDescription(getActivity().getPackageManager());
extras.putString(AccessibilitySettings.EXTRA_HTML_DESCRIPTION, htmlDescription);
final CharSequence intro = info.loadIntro(getActivity().getPackageManager());
extras.putCharSequence(AccessibilitySettings.EXTRA_INTRO, intro);
// We will log nonA11yTool status from PolicyWarningUIController; others none.
extras.putLong(AccessibilitySettings.EXTRA_TIME_FOR_LOGGING,
getActivity().getIntent().getLongExtra(
AccessibilitySettings.EXTRA_TIME_FOR_LOGGING, 0));
return extras;
}

View File

@@ -36,7 +36,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -119,6 +118,11 @@ public class AccessibilityDialogUtils {
* launch tutorial.
*/
int LAUNCH_ACCESSIBILITY_TUTORIAL = 1008;
/**
* OPEN: Settings > Accessibility > Display size and text > Click 'Reset settings' button.
*/
int DIALOG_RESET_SETTINGS = 1009;
}
/**
@@ -131,7 +135,6 @@ public class AccessibilityDialogUtils {
DialogType.EDIT_SHORTCUT_GENERIC_SUW,
DialogType.EDIT_SHORTCUT_MAGNIFICATION,
DialogType.EDIT_SHORTCUT_MAGNIFICATION_SUW,
DialogType.EDIT_MAGNIFICATION_SWITCH_SHORTCUT,
})
public @interface DialogType {
@@ -139,7 +142,6 @@ public class AccessibilityDialogUtils {
int EDIT_SHORTCUT_GENERIC_SUW = 1;
int EDIT_SHORTCUT_MAGNIFICATION = 2;
int EDIT_SHORTCUT_MAGNIFICATION_SUW = 3;
int EDIT_MAGNIFICATION_SWITCH_SHORTCUT = 4;
}
/**
@@ -159,27 +161,6 @@ public class AccessibilityDialogUtils {
return alertDialog;
}
/**
* Method to show the magnification edit shortcut dialog in Magnification.
*
* @param context A valid context
* @param positiveBtnListener The positive button listener
* @return A magnification edit shortcut dialog in Magnification
*/
public static Dialog createMagnificationSwitchShortcutDialog(Context context,
CustomButtonsClickListener positiveBtnListener) {
final View contentView = createSwitchShortcutDialogContentView(context);
final AlertDialog alertDialog = new AlertDialog.Builder(context)
.setView(contentView)
.setTitle(context.getString(
R.string.accessibility_magnification_switch_shortcut_title))
.create();
setCustomButtonsClickListener(alertDialog, contentView,
positiveBtnListener, /* negativeBtnListener= */ null);
setScrollIndicators(contentView);
return alertDialog;
}
/**
* Updates the software shortcut in edit shortcut dialog.
*
@@ -233,56 +214,6 @@ public class AccessibilityDialogUtils {
View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM);
}
interface CustomButtonsClickListener {
void onClick(@CustomButton int which);
}
/**
* Annotation for customized dialog button type.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
CustomButton.POSITIVE,
CustomButton.NEGATIVE,
})
public @interface CustomButton {
int POSITIVE = 1;
int NEGATIVE = 2;
}
private static void setCustomButtonsClickListener(Dialog dialog, View contentView,
CustomButtonsClickListener positiveBtnListener,
CustomButtonsClickListener negativeBtnListener) {
final Button positiveButton = contentView.findViewById(
R.id.custom_positive_button);
final Button negativeButton = contentView.findViewById(
R.id.custom_negative_button);
if (positiveButton != null) {
positiveButton.setOnClickListener(v -> {
if (positiveBtnListener != null) {
positiveBtnListener.onClick(CustomButton.POSITIVE);
}
dialog.dismiss();
});
}
if (negativeButton != null) {
negativeButton.setOnClickListener(v -> {
if (negativeBtnListener != null) {
negativeBtnListener.onClick(CustomButton.NEGATIVE);
}
dialog.dismiss();
});
}
}
private static View createSwitchShortcutDialogContentView(Context context) {
return createEditDialogContentView(context, DialogType.EDIT_MAGNIFICATION_SWITCH_SHORTCUT);
}
/**
* Get a content View for the edit shortcut dialog.
*
@@ -325,12 +256,6 @@ public class AccessibilityDialogUtils {
initMagnifyShortcut(context, contentView);
initAdvancedWidget(contentView);
break;
case DialogType.EDIT_MAGNIFICATION_SWITCH_SHORTCUT:
contentView = inflater.inflate(
R.layout.accessibility_edit_magnification_shortcut, null);
final ImageView image = contentView.findViewById(R.id.image);
image.setImageResource(retrieveSoftwareShortcutImageResId(context));
break;
default:
throw new IllegalArgumentException();
}
@@ -548,18 +473,24 @@ public class AccessibilityDialogUtils {
* @param context A valid context
* @param dialogTitle The title of the dialog
* @param customView The customized view
* @param listener This listener will be invoked when the positive button in the dialog is
* clicked
* @param positiveButtonText The text of the positive button
* @param positiveListener This listener will be invoked when the positive button in the dialog
* is clicked
* @param negativeButtonText The text of the negative button
* @param negativeListener This listener will be invoked when the negative button in the dialog
* is clicked
* @return the {@link Dialog} with the given view
*/
public static Dialog createCustomDialog(Context context, CharSequence dialogTitle,
View customView, DialogInterface.OnClickListener listener) {
View customView, CharSequence positiveButtonText,
DialogInterface.OnClickListener positiveListener, CharSequence negativeButtonText,
DialogInterface.OnClickListener negativeListener) {
final AlertDialog alertDialog = new AlertDialog.Builder(context)
.setView(customView)
.setTitle(dialogTitle)
.setCancelable(true)
.setPositiveButton(R.string.save, listener)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(positiveButtonText, positiveListener)
.setNegativeButton(negativeButtonText, negativeListener)
.create();
if (customView instanceof ScrollView || customView instanceof AbsListView) {
setScrollIndicators(customView);

View File

@@ -25,9 +25,7 @@ import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.widget.FooterPreference;
/**
* A custom preference acting as footer of a page. Disables the movement method by default.
*/
/** A custom preference acting as footer of a page. Disables the movement method by default. */
public final class AccessibilityFooterPreference extends FooterPreference {
private boolean mLinkEnabled;
@@ -46,12 +44,16 @@ public final class AccessibilityFooterPreference extends FooterPreference {
final TextView title = holder.itemView.findViewById(android.R.id.title);
if (mLinkEnabled) {
// When a TextView has a movement method, it will set the view to clickable. This makes
// View.onTouchEvent always return true and consumes the touch event, essentially
// nullifying any return values of MovementMethod.onTouchEvent.
// When a TextView has a movement method, it will set the view to focusable and
// clickable. This makes View.onTouchEvent always return true and consumes the touch
// event, essentially nullifying any return values of MovementMethod.onTouchEvent.
// To still allow propagating touch events to the parent when this view doesn't have
// links, we only set the movement method here if the text contains links.
title.setMovementMethod(LinkMovementMethod.getInstance());
// Groups of related title and link content by making the container focusable,
// then make all the children inside not focusable.
title.setFocusable(false);
} else {
title.setMovementMethod(/* movement= */ null);
}

View File

@@ -31,7 +31,7 @@ import com.android.settingslib.HelpUtils;
public class AccessibilityFooterPreferenceController extends BasePreferenceController {
private int mHelpResource;
private String mLearnMoreContentDescription;
private String mLearnMoreText;
private String mIntroductionTitle;
public AccessibilityFooterPreferenceController(Context context, String key) {
@@ -56,9 +56,9 @@ public class AccessibilityFooterPreferenceController extends BasePreferenceContr
* Setups a help item in the {@link AccessibilityFooterPreference} with specific content
* description.
*/
public void setupHelpLink(int helpResource, String learnMoreContentDescription) {
public void setupHelpLink(int helpResource, String learnMoreText) {
mHelpResource = helpResource;
mLearnMoreContentDescription = learnMoreContentDescription;
mLearnMoreText = learnMoreText;
}
/**
@@ -73,12 +73,12 @@ public class AccessibilityFooterPreferenceController extends BasePreferenceContr
/**
* Overrides this if showing a help item in the {@link AccessibilityFooterPreference} with
* specific content description.
* specific learn more title.
*
* @return the content description for the help url
* @return learn more title for the help url
*/
protected String getLearnMoreContentDescription() {
return mLearnMoreContentDescription;
protected String getLearnMoreText() {
return mLearnMoreText;
}
/**
@@ -117,10 +117,13 @@ public class AccessibilityFooterPreferenceController extends BasePreferenceContr
footerPreference.setLearnMoreAction(view -> {
view.startActivityForResult(helpIntent, 0);
});
footerPreference.setLearnMoreContentDescription(getLearnMoreContentDescription());
footerPreference.setLearnMoreText(getLearnMoreText());
footerPreference.setLinkEnabled(true);
} else {
footerPreference.setLinkEnabled(false);
}
// Grouping subcomponents to make more accessible.
footerPreference.setSelectable(false);
}
}

View File

@@ -46,6 +46,7 @@ import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RawRes;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
@@ -125,9 +126,15 @@ public final class AccessibilityGestureNavigationTutorial {
}
static AlertDialog createAccessibilityTutorialDialog(Context context, int shortcutTypes) {
return createAccessibilityTutorialDialog(context, shortcutTypes, mOnClickListener);
}
static AlertDialog createAccessibilityTutorialDialog(Context context, int shortcutTypes,
@Nullable DialogInterface.OnClickListener negativeButtonListener) {
return new AlertDialog.Builder(context)
.setView(createShortcutNavigationContentView(context, shortcutTypes))
.setNegativeButton(R.string.accessibility_tutorial_dialog_button, mOnClickListener)
.setNegativeButton(R.string.accessibility_tutorial_dialog_button,
negativeButtonListener)
.create();
}

View File

@@ -38,13 +38,15 @@ import com.android.settings.R;
import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
@@ -53,7 +55,7 @@ import java.util.concurrent.FutureTask;
* Controller that shows and updates the bluetooth device name
*/
public class AccessibilityHearingAidPreferenceController extends BasePreferenceController
implements LifecycleObserver, OnStart, OnStop {
implements LifecycleObserver, OnStart, OnStop, BluetoothCallback {
private static final String TAG = "AccessibilityHearingAidPreferenceController";
private Preference mHearingAidPreference;
@@ -82,15 +84,13 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC
private final LocalBluetoothManager mLocalBluetoothManager;
private final BluetoothAdapter mBluetoothAdapter;
//cache value of supporting hearing aid or not
private boolean mHearingAidProfileSupported;
private FragmentManager mFragmentManager;
public AccessibilityHearingAidPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mLocalBluetoothManager = getLocalBluetoothManager();
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mHearingAidProfileSupported = isHearingAidProfileSupported();
}
@Override
@@ -101,29 +101,27 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC
@Override
public int getAvailabilityStatus() {
return mHearingAidProfileSupported ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
return isHearingAidProfileSupported() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@Override
public void onStart() {
if (mHearingAidProfileSupported) {
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
mContext.registerReceiver(mHearingAidChangedReceiver, filter);
}
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
mContext.registerReceiver(mHearingAidChangedReceiver, filter);
mLocalBluetoothManager.getEventManager().registerCallback(this);
}
@Override
public void onStop() {
if (mHearingAidProfileSupported) {
mContext.unregisterReceiver(mHearingAidChangedReceiver);
}
mContext.unregisterReceiver(mHearingAidChangedReceiver);
mLocalBluetoothManager.getEventManager().unregisterCallback(this);
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (TextUtils.equals(preference.getKey(), getPreferenceKey())){
if (TextUtils.equals(preference.getKey(), getPreferenceKey())) {
final CachedBluetoothDevice device = getConnectedHearingAidDevice();
if (device == null) {
launchHearingAidInstructionDialog();
@@ -141,7 +139,38 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC
if (device == null) {
return mContext.getText(R.string.accessibility_hearingaid_not_connected_summary);
}
return device.getName();
final int connectedNum = getConnectedHearingAidDeviceNum();
final CharSequence name = device.getName();
final int side = device.getDeviceSide();
final CachedBluetoothDevice subDevice = device.getSubDevice();
if (connectedNum > 1) {
return mContext.getString(R.string.accessibility_hearingaid_more_device_summary, name);
}
if (subDevice != null && subDevice.isConnected()) {
return mContext.getString(
R.string.accessibility_hearingaid_left_and_right_side_device_summary, name);
}
if (side == HearingAidProfile.DeviceSide.SIDE_INVALID) {
return mContext.getString(
R.string.accessibility_hearingaid_active_device_summary, name);
}
return (side == HearingAidProfile.DeviceSide.SIDE_LEFT)
? mContext.getString(
R.string.accessibility_hearingaid_left_side_device_summary, name)
: mContext.getString(
R.string.accessibility_hearingaid_right_side_device_summary, name);
}
@Override
public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
if (activeDevice == null) {
return;
}
if (bluetoothProfile == BluetoothProfile.HEARING_AID) {
HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, activeDevice);
}
}
public void setFragmentManager(FragmentManager fragmentManager) {
@@ -150,33 +179,44 @@ public class AccessibilityHearingAidPreferenceController extends BasePreferenceC
@VisibleForTesting
CachedBluetoothDevice getConnectedHearingAidDevice() {
if (!mHearingAidProfileSupported) {
if (!isHearingAidProfileSupported()) {
return null;
}
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
return null;
}
final List<BluetoothDevice> deviceList = mLocalBluetoothManager.getProfileManager()
.getHearingAidProfile().getConnectedDevices();
final Iterator it = deviceList.iterator();
while (it.hasNext()) {
BluetoothDevice obj = (BluetoothDevice)it.next();
if (!mLocalBluetoothManager.getCachedDeviceManager().isSubDevice(obj)) {
return mLocalBluetoothManager.getCachedDeviceManager().findDevice(obj);
final CachedBluetoothDeviceManager deviceManager =
mLocalBluetoothManager.getCachedDeviceManager();
final HearingAidProfile hearingAidProfile =
mLocalBluetoothManager.getProfileManager().getHearingAidProfile();
final List<BluetoothDevice> deviceList = hearingAidProfile.getConnectedDevices();
for (BluetoothDevice obj : deviceList) {
if (!deviceManager.isSubDevice(obj)) {
return deviceManager.findDevice(obj);
}
}
return null;
}
private int getConnectedHearingAidDeviceNum() {
if (!isHearingAidProfileSupported()) {
return 0;
}
final CachedBluetoothDeviceManager deviceManager =
mLocalBluetoothManager.getCachedDeviceManager();
final HearingAidProfile hearingAidProfile =
mLocalBluetoothManager.getProfileManager().getHearingAidProfile();
final List<BluetoothDevice> deviceList = hearingAidProfile.getConnectedDevices();
return (int) deviceList.stream()
.filter(device -> !deviceManager.isSubDevice(device))
.count();
}
private boolean isHearingAidProfileSupported() {
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
return false;
}
final List<Integer> supportedList = mBluetoothAdapter.getSupportedProfiles();
if (supportedList.contains(BluetoothProfile.HEARING_AID)) {
return true;
}
return false;
return supportedList.contains(BluetoothProfile.HEARING_AID);
}
private LocalBluetoothManager getLocalBluetoothManager() {

View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 2022 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 android.content.ComponentName;
import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import java.util.StringJoiner;
/** Provides utility methods to accessibility quick settings only. */
final class AccessibilityQuickSettingUtils {
private static final String ACCESSIBILITY_PERF = "accessibility_prefs";
private static final String KEY_TILE_SERVICE_SHOWN = "tile_service_shown";
private static final char COMPONENT_NAME_SEPARATOR = ':';
private static final TextUtils.SimpleStringSplitter sStringColonSplitter =
new TextUtils.SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
/**
* Opts in component name into {@link AccessibilityQuickSettingUtils#KEY_TILE_SERVICE_SHOWN}
* colon-separated string in {@link SharedPreferences}.
*
* @param context The current context.
* @param componentName The component name that need to be opted in SharedPreferences.
*/
public static void optInValueToSharedPreferences(Context context,
@NonNull ComponentName componentName) {
final String targetString = getFromSharedPreferences(context);
if (hasValueInSharedPreferences(targetString, componentName)) {
return;
}
final StringJoiner joiner = new StringJoiner(String.valueOf(COMPONENT_NAME_SEPARATOR));
if (!TextUtils.isEmpty(targetString)) {
joiner.add(targetString);
}
joiner.add(componentName.flattenToString());
SharedPreferences.Editor editor = getSharedPreferences(context).edit();
editor.putString(KEY_TILE_SERVICE_SHOWN, joiner.toString()).apply();
}
/**
* Returns if component name existed in {@link
* AccessibilityQuickSettingUtils#KEY_TILE_SERVICE_SHOWN} string in {@link SharedPreferences}.
*
* @param context The current context.
* @param componentName The component name that need to be checked existed in SharedPreferences.
* @return {@code true} if componentName existed in SharedPreferences.
*/
public static boolean hasValueInSharedPreferences(Context context,
@NonNull ComponentName componentName) {
final String targetString = getFromSharedPreferences(context);
return hasValueInSharedPreferences(targetString, componentName);
}
private static boolean hasValueInSharedPreferences(String targetString,
@NonNull ComponentName componentName) {
if (TextUtils.isEmpty(targetString)) {
return false;
}
sStringColonSplitter.setString(targetString);
while (sStringColonSplitter.hasNext()) {
final String name = sStringColonSplitter.next();
if (TextUtils.equals(componentName.flattenToString(), name)) {
return true;
}
}
return false;
}
private static String getFromSharedPreferences(Context context) {
return getSharedPreferences(context).getString(KEY_TILE_SERVICE_SHOWN, "");
}
private static SharedPreferences getSharedPreferences(Context context) {
return context.getSharedPreferences(ACCESSIBILITY_PERF, Context.MODE_PRIVATE);
}
private AccessibilityQuickSettingUtils(){}
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright (C) 2022 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 android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnCreate;
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
/** PrimarySwitchPreferenceController that shows quick settings tooltip on first use. */
public abstract class AccessibilityQuickSettingsPrimarySwitchPreferenceController
extends TogglePreferenceController
implements LifecycleObserver, OnCreate, OnSaveInstanceState {
private static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
private final Handler mHandler;
private PrimarySwitchPreference mPreference;
private AccessibilityQuickSettingsTooltipWindow mTooltipWindow;
private boolean mNeedsQSTooltipReshow = false;
/** Returns the accessibility tile component name. */
abstract ComponentName getTileComponentName();
/** Returns the accessibility tile tooltip content. */
abstract CharSequence getTileTooltipContent();
public AccessibilityQuickSettingsPrimarySwitchPreferenceController(Context context,
String preferenceKey) {
super(context, preferenceKey);
mHandler = new Handler(context.getMainLooper());
}
@Override
public void onCreate(Bundle savedInstanceState) {
// Restore the tooltip.
if (savedInstanceState != null) {
if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_RESHOW)) {
mNeedsQSTooltipReshow = savedInstanceState.getBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW);
}
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
if (mTooltipWindow != null) {
outState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, mTooltipWindow.isShowing());
}
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
if (mNeedsQSTooltipReshow) {
mHandler.post(this::showQuickSettingsTooltipIfNeeded);
}
}
@Override
public boolean setChecked(boolean isChecked) {
if (isChecked) {
showQuickSettingsTooltipIfNeeded();
}
return isChecked;
}
@Override
public boolean isChecked() {
return false;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
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(mPreference.getSwitch());
AccessibilityQuickSettingUtils.optInValueToSharedPreferences(mContext, tileComponentName);
mNeedsQSTooltipReshow = false;
}
}

View File

@@ -0,0 +1,186 @@
/*
* Copyright (C) 2022 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 android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.AccessibilityDelegate;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
/**
* UI container for the accessibility quick settings tooltip.
*
* <p> The popup window shows the information about the operation of the quick settings. In
* addition, the arrow is pointing to the top center of the device to display one-off menu within
* {@code mCloseDelayTimeMillis} time.</p>
*/
public class AccessibilityQuickSettingsTooltipWindow extends PopupWindow {
private final Context mContext;
private Handler mHandler;
private long mCloseDelayTimeMillis;
public AccessibilityQuickSettingsTooltipWindow(Context context) {
super(context);
this.mContext = context;
}
private final AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
final AccessibilityAction clickAction = new AccessibilityAction(
AccessibilityNodeInfo.ACTION_CLICK,
mContext.getString(R.string.accessibility_quick_settings_tooltip_dismiss));
info.addAction(clickAction);
}
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if (action == AccessibilityNodeInfo.ACTION_CLICK) {
dismiss();
return true;
}
return super.performAccessibilityAction(host, action, args);
}
};
/**
* Sets up {@link #AccessibilityQuickSettingsTooltipWindow}'s layout and content.
*
* @param text text to be displayed
* @param imageResId the resource ID of the image drawable
*/
public void setup(CharSequence text, @DrawableRes int imageResId) {
this.setup(text, imageResId, /* closeDelayTimeMillis= */ 0);
}
/**
* Sets up {@link #AccessibilityQuickSettingsTooltipWindow}'s layout and content.
*
* <p> The system will attempt to close popup window to the target duration of the threads if
* close delay time is positive number. </p>
*
* @param text text to be displayed
* @param imageResId the resource ID of the image drawable
* @param closeDelayTimeMillis how long the popup window be auto-closed
*/
public void setup(CharSequence text, @DrawableRes int imageResId, long closeDelayTimeMillis) {
this.mCloseDelayTimeMillis = closeDelayTimeMillis;
setBackgroundDrawable(new ColorDrawable(mContext.getColor(android.R.color.transparent)));
final LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class);
final View popupView =
inflater.inflate(R.layout.accessibility_qs_tooltip, /* root= */ null);
popupView.setFocusable(/* focusable= */ true);
popupView.setAccessibilityDelegate(mAccessibilityDelegate);
setContentView(popupView);
final ImageView imageView = getContentView().findViewById(R.id.qs_illustration);
imageView.setImageResource(imageResId);
final TextView textView = getContentView().findViewById(R.id.qs_content);
textView.setText(text);
setWidth(getWindowWidthWith(textView));
setHeight(LinearLayout.LayoutParams.WRAP_CONTENT);
setFocusable(/* focusable= */ true);
setOutsideTouchable(/* touchable= */ true);
}
/**
* Displays the content view in a popup window at the top and center position.
*
* @param targetView a target view to get the {@link View#getWindowToken()} token from.
*/
public void showAtTopCenter(View targetView) {
showAtLocation(targetView, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 0);
}
/**
* Disposes of the popup window.
*
* <p> Remove any pending posts of callbacks and sent messages for closing popup window. </p>
*/
@Override
public void dismiss() {
super.dismiss();
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(/* token= */ null);
}
}
/**
* Displays the content view in a popup window at the specified location.
*
* <p> The system will attempt to close popup window to the target duration of the threads if
* close delay time is positive number. </p>
*
* @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from
* @param gravity the gravity which controls the placement of the popup window
* @param x the popup's x location offset
* @param y the popup's y location offset
*/
@Override
public void showAtLocation(View parent, int gravity, int x, int y) {
super.showAtLocation(parent, gravity, x, y);
scheduleAutoCloseAction();
}
private void scheduleAutoCloseAction() {
if (mCloseDelayTimeMillis <= 0) {
return;
}
if (mHandler == null) {
mHandler = new Handler(mContext.getMainLooper());
}
mHandler.removeCallbacksAndMessages(/* token= */ null);
mHandler.postDelayed(this::dismiss, mCloseDelayTimeMillis);
}
private int getWindowWidthWith(TextView textView) {
final int availableWindowWidth = getAvailableWindowWidth();
final int widthSpec =
View.MeasureSpec.makeMeasureSpec(availableWindowWidth, View.MeasureSpec.AT_MOST);
final int heightSpec =
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
textView.measure(widthSpec, heightSpec);
return textView.getMeasuredWidth();
}
@VisibleForTesting
int getAvailableWindowWidth() {
final Resources res = mContext.getResources();
final int padding = res.getDimensionPixelSize(R.dimen.accessibility_qs_tooltip_margin);
final int screenWidth = res.getDisplayMetrics().widthPixels;
return screenWidth - padding * 2;
}
}

View File

@@ -1,174 +0,0 @@
/*
* 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.accessibility;
import static com.android.settings.core.SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE;
import android.app.settings.SettingsEnums;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceFragmentCompat;
import com.android.settings.R;
import com.android.settings.core.InstrumentedActivity;
import com.android.settings.display.FontSizePreferenceFragmentForSetupWizard;
import com.android.settings.display.ScreenZoomPreferenceFragmentForSetupWizard;
import com.android.settingslib.transition.SettingsTransitionHelper.TransitionType;
import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupdesign.GlifLayout;
import com.google.android.setupdesign.util.ThemeHelper;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/** Settings font/display size activity for SUW. */
public class AccessibilityScreenSizeForSetupWizardActivity extends InstrumentedActivity {
private static final String TAG = "ScreenSizeForSetup";
// A parameter decides which fragment ({@link FontSizePreferenceFragmentForSetupWizard} or
// {@link ScreenZoomPreferenceFragmentForSetupWizard}) will be visioned.
static final String VISION_FRAGMENT_NO = "vision_fragment_no";
/**
* Flags indicating the type of the fragment.
*/
@IntDef({
FragmentType.FONT_SIZE,
FragmentType.SCREEN_SIZE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface FragmentType {
int FONT_SIZE = 1;
int SCREEN_SIZE = 2;
}
// Keep the last height of the scroll view in the {@link GlifLayout}
private int mLastScrollViewHeight;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final int appliedTheme = ThemeHelper.trySetDynamicColor(this)
? R.style.SudDynamicColorThemeGlifV3_DayNight : R.style.SudThemeGlifV3_DayNight;
setTheme(appliedTheme);
setContentView(R.layout.accessibility_screen_size_setup_wizard);
updateHeaderLayout();
scrollToBottom();
initFooterButton();
if (savedInstanceState == null) {
final PreferenceFragmentCompat fragment =
getFragmentType(getIntent()) == FragmentType.FONT_SIZE
? new FontSizePreferenceFragmentForSetupWizard()
: new ScreenZoomPreferenceFragmentForSetupWizard();
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content_frame, fragment)
.commit();
}
}
@Override
protected void onPause() {
// For accessibility activities launched from setup wizard.
if (getTransitionType(getIntent()) == TransitionType.TRANSITION_FADE) {
overridePendingTransition(R.anim.sud_stay, android.R.anim.fade_out);
}
super.onPause();
}
@Override
public int getMetricsCategory() {
return getFragmentType(getIntent()) == FragmentType.FONT_SIZE
? SettingsEnums.SUW_ACCESSIBILITY_FONT_SIZE
: SettingsEnums.SUW_ACCESSIBILITY_DISPLAY_SIZE;
}
@VisibleForTesting
void updateHeaderLayout() {
if (ThemeHelper.shouldApplyExtendedPartnerConfig(this) && isSuwSupportedTwoPanes()) {
final GlifLayout layout = findViewById(R.id.setup_wizard_layout);
final LinearLayout headerLayout = layout.findManagedViewById(R.id.sud_layout_header);
if (headerLayout != null) {
headerLayout.setPadding(0, layout.getPaddingTop(), 0,
layout.getPaddingBottom());
}
}
((TextView) findViewById(R.id.suc_layout_title)).setText(
getFragmentType(getIntent()) == FragmentType.FONT_SIZE
? R.string.title_font_size
: R.string.screen_zoom_title);
((TextView) findViewById(R.id.sud_layout_subtitle)).setText(
getFragmentType(getIntent()) == FragmentType.FONT_SIZE
? R.string.font_size_summary
: R.string.screen_zoom_summary);
}
private boolean isSuwSupportedTwoPanes() {
return getResources().getBoolean(R.bool.config_suw_supported_two_panes);
}
private void initFooterButton() {
final GlifLayout layout = findViewById(R.id.setup_wizard_layout);
final FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
final View.OnClickListener nextButtonListener = v -> onBackPressed();
final FooterButton primaryButton =
new FooterButton.Builder(this)
.setText(R.string.done)
.setListener(nextButtonListener)
.setButtonType(FooterButton.ButtonType.NEXT)
.setTheme(R.style.SudGlifButton_Primary)
.build();
mixin.setPrimaryButton(primaryButton);
}
/**
* Scrolls to bottom while {@link ScrollView} layout changed.
*/
private void scrollToBottom() {
mLastScrollViewHeight = 0;
final GlifLayout layout = findViewById(R.id.setup_wizard_layout);
final ScrollView scrollView = layout.getScrollView();
scrollView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
final int scrollViewHeight = scrollView.getHeight();
if (scrollViewHeight > 0 && scrollViewHeight != mLastScrollViewHeight) {
mLastScrollViewHeight = scrollViewHeight;
scrollView.post(() -> {
// Here is no need to show the scrolling animation. So disabled first and
// then enabled it after scrolling finished.
scrollView.setSmoothScrollingEnabled(false);
scrollView.fullScroll(View.FOCUS_DOWN);
scrollView.setSmoothScrollingEnabled(true);
});
}
});
}
private int getTransitionType(Intent intent) {
return intent.getIntExtra(EXTRA_PAGE_TRANSITION_TYPE, TransitionType.TRANSITION_NONE);
}
private int getFragmentType(Intent intent) {
return intent.getIntExtra(VISION_FRAGMENT_NO, FragmentType.FONT_SIZE);
}
}

View File

@@ -16,21 +16,13 @@
package com.android.settings.accessibility;
import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_MEDIUM;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityShortcutInfo;
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
@@ -40,22 +32,17 @@ import android.util.ArrayMap;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.VisibleForTesting;
import androidx.core.content.ContextCompat;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.internal.content.PackageMonitor;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.accessibility.AccessibilityUtil.AccessibilityServiceFragmentType;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.accessibility.AccessibilityUtils;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.search.SearchIndexableRaw;
@@ -63,7 +50,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** Activity with the accessibility settings. */
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
@@ -71,9 +57,6 @@ public class AccessibilitySettings extends DashboardFragment {
private static final String TAG = "AccessibilitySettings";
// Index of the first preference in a preference category.
private static final int FIRST_PREFERENCE_IN_CATEGORY_INDEX = -1;
// Preference categories
private static final String CATEGORY_SCREEN_READER = "screen_reader_category";
private static final String CATEGORY_CAPTIONS = "captions_category";
@@ -94,13 +77,16 @@ public class AccessibilitySettings extends DashboardFragment {
static final String EXTRA_TITLE_RES = "title_res";
static final String EXTRA_RESOLVE_INFO = "resolve_info";
static final String EXTRA_SUMMARY = "summary";
static final String EXTRA_INTRO = "intro";
static final String EXTRA_SETTINGS_TITLE = "settings_title";
static final String EXTRA_COMPONENT_NAME = "component_name";
static final String EXTRA_SETTINGS_COMPONENT_NAME = "settings_component_name";
static final String EXTRA_TILE_SERVICE_COMPONENT_NAME = "tile_service_component_name";
static final String EXTRA_VIDEO_RAW_RESOURCE_ID = "video_resource";
static final String EXTRA_LAUNCHED_FROM_SUW = "from_suw";
static final String EXTRA_ANIMATED_IMAGE_RES = "animated_image_res";
static final String EXTRA_HTML_DESCRIPTION = "html_description";
static final String EXTRA_TIME_FOR_LOGGING = "start_time_to_log_a11y_tool";
// Timeout before we update the services if packages are added/removed
// since the AccessibilityManagerService has to do that processing first
@@ -146,7 +132,7 @@ public class AccessibilitySettings extends DashboardFragment {
};
@VisibleForTesting
final SettingsContentObserver mSettingsContentObserver;
final AccessibilitySettingsContentObserver mSettingsContentObserver;
private final Map<String, PreferenceCategory> mCategoryToPrefCategoryMap =
new ArrayMap<>();
@@ -170,12 +156,9 @@ public class AccessibilitySettings extends DashboardFragment {
// Observe changes from accessibility selection menu
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
mSettingsContentObserver = new SettingsContentObserver(mHandler, shortcutFeatureKeys) {
@Override
public void onChange(boolean selfChange, Uri uri) {
onContentChanged();
}
};
mSettingsContentObserver = new AccessibilitySettingsContentObserver(mHandler);
mSettingsContentObserver.registerKeysToObserverCallback(shortcutFeatureKeys,
key -> onContentChanged());
}
@Override
@@ -203,6 +186,12 @@ public class AccessibilitySettings extends DashboardFragment {
registerContentMonitors();
}
@Override
public void onResume() {
super.onResume();
updateAllPreferences();
}
@Override
public void onStart() {
if (mNeedPreferencesUpdate) {
@@ -243,8 +232,7 @@ public class AccessibilitySettings extends DashboardFragment {
* @param serviceEnabled Whether the accessibility service is enabled.
* @return The service summary
*/
@VisibleForTesting
static CharSequence getServiceSummary(Context context, AccessibilityServiceInfo info,
public static CharSequence getServiceSummary(Context context, AccessibilityServiceInfo info,
boolean serviceEnabled) {
if (serviceEnabled && info.crashed) {
return context.getText(R.string.accessibility_summary_state_stopped);
@@ -283,8 +271,7 @@ public class AccessibilitySettings extends DashboardFragment {
* @param serviceEnabled Whether the accessibility service is enabled.
* @return The service description
*/
@VisibleForTesting
static CharSequence getServiceDescription(Context context, AccessibilityServiceInfo info,
public static CharSequence getServiceDescription(Context context, AccessibilityServiceInfo info,
boolean serviceEnabled) {
if (serviceEnabled && info.crashed) {
return context.getText(R.string.accessibility_description_state_stopped);
@@ -293,11 +280,6 @@ public class AccessibilitySettings extends DashboardFragment {
return info.loadDescription(context.getPackageManager());
}
static boolean isRampingRingerEnabled(final Context context) {
return Settings.Global.getInt(
context.getContentResolver(), Settings.Global.APPLY_RAMPING_RINGER, 0) == 1;
}
@VisibleForTesting
void onContentChanged() {
// If the fragment is visible then update preferences immediately, else set the flag then
@@ -505,241 +487,4 @@ public class AccessibilitySettings extends DashboardFragment {
context);
}
};
/**
* This class helps setup RestrictedPreference.
*/
@VisibleForTesting
static class RestrictedPreferenceHelper {
private final Context mContext;
private final DevicePolicyManager mDpm;
private final PackageManager mPm;
RestrictedPreferenceHelper(Context context) {
mContext = context;
mDpm = context.getSystemService(DevicePolicyManager.class);
mPm = context.getPackageManager();
}
/**
* Creates the list of {@link RestrictedPreference} with the installedServices arguments.
*
* @param installedServices The list of {@link AccessibilityServiceInfo}s of the
* installed accessibility services
* @return The list of {@link RestrictedPreference}
*/
@VisibleForTesting
List<RestrictedPreference> createAccessibilityServicePreferenceList(
List<AccessibilityServiceInfo> installedServices) {
final Set<ComponentName> enabledServices =
AccessibilityUtils.getEnabledServicesFromSettings(mContext);
final List<String> permittedServices = mDpm.getPermittedAccessibilityServices(
UserHandle.myUserId());
final int installedServicesSize = installedServices.size();
final List<RestrictedPreference> preferenceList = new ArrayList<>(
installedServicesSize);
for (int i = 0; i < installedServicesSize; ++i) {
final AccessibilityServiceInfo info = installedServices.get(i);
final ResolveInfo resolveInfo = info.getResolveInfo();
final String packageName = resolveInfo.serviceInfo.packageName;
final ComponentName componentName = new ComponentName(packageName,
resolveInfo.serviceInfo.name);
final String key = componentName.flattenToString();
final CharSequence title = resolveInfo.loadLabel(mPm);
final boolean serviceEnabled = enabledServices.contains(componentName);
final CharSequence summary = getServiceSummary(mContext, info, serviceEnabled);
final String fragment = getAccessibilityServiceFragmentTypeName(info);
Drawable icon = resolveInfo.loadIcon(mPm);
if (resolveInfo.getIconResource() == 0) {
icon = ContextCompat.getDrawable(mContext,
R.drawable.ic_accessibility_generic);
}
final RestrictedPreference preference = createRestrictedPreference(key, title,
summary, icon, fragment);
// permittedServices null means all accessibility services are allowed.
final boolean serviceAllowed =
permittedServices == null || permittedServices.contains(packageName);
setRestrictedPreferenceEnabled(preference, packageName, serviceAllowed,
serviceEnabled);
final String prefKey = preference.getKey();
final int imageRes = info.getAnimatedImageRes();
final CharSequence description = getServiceDescription(mContext, info,
serviceEnabled);
final String htmlDescription = info.loadHtmlDescription(mPm);
final String settingsClassName = info.getSettingsActivityName();
putBasicExtras(preference, prefKey, title, description, imageRes, htmlDescription,
componentName);
putServiceExtras(preference, resolveInfo, serviceEnabled);
putSettingsExtras(preference, packageName, settingsClassName);
preferenceList.add(preference);
}
return preferenceList;
}
/**
* Create the list of {@link RestrictedPreference} with the installedShortcuts arguments.
*
* @param installedShortcuts The list of {@link AccessibilityShortcutInfo}s of the
* installed accessibility shortcuts
* @return The list of {@link RestrictedPreference}
*/
@VisibleForTesting
List<RestrictedPreference> createAccessibilityActivityPreferenceList(
List<AccessibilityShortcutInfo> installedShortcuts) {
final Set<ComponentName> enabledServices =
AccessibilityUtils.getEnabledServicesFromSettings(mContext);
final List<String> permittedServices = mDpm.getPermittedAccessibilityServices(
UserHandle.myUserId());
final int installedShortcutsSize = installedShortcuts.size();
final List<RestrictedPreference> preferenceList = new ArrayList<>(
installedShortcutsSize);
for (int i = 0; i < installedShortcutsSize; ++i) {
final AccessibilityShortcutInfo info = installedShortcuts.get(i);
final ActivityInfo activityInfo = info.getActivityInfo();
final ComponentName componentName = info.getComponentName();
final String key = componentName.flattenToString();
final CharSequence title = activityInfo.loadLabel(mPm);
final String summary = info.loadSummary(mPm);
final String fragment =
LaunchAccessibilityActivityPreferenceFragment.class.getName();
Drawable icon = activityInfo.loadIcon(mPm);
if (activityInfo.getIconResource() == 0) {
icon = ContextCompat.getDrawable(mContext, R.drawable.ic_accessibility_generic);
}
final RestrictedPreference preference = createRestrictedPreference(key, title,
summary, icon, fragment);
final String packageName = componentName.getPackageName();
// permittedServices null means all accessibility services are allowed.
final boolean serviceAllowed =
permittedServices == null || permittedServices.contains(packageName);
final boolean serviceEnabled = enabledServices.contains(componentName);
setRestrictedPreferenceEnabled(preference, packageName, serviceAllowed,
serviceEnabled);
final String prefKey = preference.getKey();
final String description = info.loadDescription(mPm);
final int imageRes = info.getAnimatedImageRes();
final String htmlDescription = info.loadHtmlDescription(mPm);
final String settingsClassName = info.getSettingsActivityName();
putBasicExtras(preference, prefKey, title, description, imageRes, htmlDescription,
componentName);
putSettingsExtras(preference, packageName, settingsClassName);
preferenceList.add(preference);
}
return preferenceList;
}
private String getAccessibilityServiceFragmentTypeName(AccessibilityServiceInfo info) {
// Shorten the name to avoid exceeding 100 characters in one line.
final String volumeShortcutToggleAccessibilityServicePreferenceFragment =
VolumeShortcutToggleAccessibilityServicePreferenceFragment.class.getName();
switch (AccessibilityUtil.getAccessibilityServiceFragmentType(info)) {
case AccessibilityServiceFragmentType.VOLUME_SHORTCUT_TOGGLE:
return volumeShortcutToggleAccessibilityServicePreferenceFragment;
case AccessibilityServiceFragmentType.INVISIBLE_TOGGLE:
return InvisibleToggleAccessibilityServicePreferenceFragment.class.getName();
case AccessibilityServiceFragmentType.TOGGLE:
return ToggleAccessibilityServicePreferenceFragment.class.getName();
default:
// impossible status
throw new AssertionError();
}
}
private RestrictedPreference createRestrictedPreference(String key, CharSequence title,
CharSequence summary, Drawable icon, String fragment) {
final RestrictedPreference preference = new RestrictedPreference(mContext);
preference.setKey(key);
preference.setTitle(title);
preference.setSummary(summary);
preference.setIcon(Utils.getAdaptiveIcon(mContext, icon, Color.WHITE));
preference.setFragment(fragment);
preference.setIconSize(ICON_SIZE_MEDIUM);
preference.setPersistent(false); // Disable SharedPreferences.
preference.setOrder(FIRST_PREFERENCE_IN_CATEGORY_INDEX);
return preference;
}
private void setRestrictedPreferenceEnabled(RestrictedPreference preference,
String packageName, boolean serviceAllowed, boolean serviceEnabled) {
if (serviceAllowed || serviceEnabled) {
preference.setEnabled(true);
} else {
// Disable accessibility service that are not permitted.
final EnforcedAdmin admin =
RestrictedLockUtilsInternal.checkIfAccessibilityServiceDisallowed(
mContext, packageName, UserHandle.myUserId());
if (admin != null) {
preference.setDisabledByAdmin(admin);
} else {
preference.setEnabled(false);
}
}
}
/** Puts the basic extras into {@link RestrictedPreference}'s getExtras(). */
private void putBasicExtras(RestrictedPreference preference, String prefKey,
CharSequence title, CharSequence summary, int imageRes, String htmlDescription,
ComponentName componentName) {
final Bundle extras = preference.getExtras();
extras.putString(EXTRA_PREFERENCE_KEY, prefKey);
extras.putCharSequence(EXTRA_TITLE, title);
extras.putCharSequence(EXTRA_SUMMARY, summary);
extras.putParcelable(EXTRA_COMPONENT_NAME, componentName);
extras.putInt(EXTRA_ANIMATED_IMAGE_RES, imageRes);
extras.putString(AccessibilitySettings.EXTRA_HTML_DESCRIPTION, htmlDescription);
}
/**
* Puts the service extras into {@link RestrictedPreference}'s getExtras().
*
* Called by {@link AccessibilityServiceInfo} for now.
*/
private void putServiceExtras(RestrictedPreference preference, ResolveInfo resolveInfo,
Boolean serviceEnabled) {
final Bundle extras = preference.getExtras();
extras.putParcelable(EXTRA_RESOLVE_INFO, resolveInfo);
extras.putBoolean(EXTRA_CHECKED, serviceEnabled);
}
/**
* Puts the settings extras into {@link RestrictedPreference}'s getExtras().
*
* Called when settings UI is needed.
*/
private void putSettingsExtras(RestrictedPreference preference, String packageName,
String settingsClassName) {
final Bundle extras = preference.getExtras();
if (!TextUtils.isEmpty(settingsClassName)) {
extras.putString(EXTRA_SETTINGS_TITLE,
mContext.getText(R.string.accessibility_menu_item_settings).toString());
extras.putString(EXTRA_SETTINGS_COMPONENT_NAME,
new ComponentName(packageName, settingsClassName).flattenToString());
}
}
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright (C) 2022 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 android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
import android.util.Log;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class AccessibilitySettingsContentObserver extends ContentObserver {
private static final String TAG = "AccessibilitySettingsContentObserver";
public interface ContentObserverCallback {
void onChange(String key);
}
// Key: Preference key's uri, Value: Preference key
private final Map<Uri, String> mUriToKey = new HashMap<>(2);
// Key: Collection of preference keys, Value: onChange callback for keys
private final Map<List<String>, ContentObserverCallback> mUrisToCallback = new HashMap<>();
AccessibilitySettingsContentObserver(Handler handler) {
super(handler);
// default key to be observed
addDefaultKeysToMap();
}
public void register(ContentResolver contentResolver) {
for (Uri uri : mUriToKey.keySet()) {
contentResolver.registerContentObserver(uri, false, this);
}
}
public void unregister(ContentResolver contentResolver) {
contentResolver.unregisterContentObserver(this);
}
private void addDefaultKeysToMap() {
addKeyToMap(Settings.Secure.ACCESSIBILITY_ENABLED);
addKeyToMap(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
}
private boolean isDefaultKey(String key) {
return Settings.Secure.ACCESSIBILITY_ENABLED.equals(key)
|| Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES.equals(key);
}
private void addKeyToMap(String key) {
mUriToKey.put(Settings.Secure.getUriFor(key), key);
}
/**
* {@link ContentObserverCallback} is added to {@link ContentObserver} to handle the
* onChange event triggered by the key collection of {@code keysToObserve} and the default
* keys.
*
* Note: The following key are default to be observed.
* {@link Settings.Secure.ACCESSIBILITY_ENABLED}
* {@link Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES}
*
* @param keysToObserve A collection of keys which are going to be observed.
* @param observerCallback A callback which is used to handle the onChange event triggered
* by the key collection of {@code keysToObserve}.
*/
public void registerKeysToObserverCallback(List<String> keysToObserve,
ContentObserverCallback observerCallback) {
for (String key: keysToObserve) {
addKeyToMap(key);
}
mUrisToCallback.put(keysToObserve, observerCallback);
}
/**
* {@link ContentObserverCallback} is added to {@link ContentObserver} to handle the
* onChange event triggered by the default keys.
*
* Note: The following key are default to be observed.
* {@link Settings.Secure.ACCESSIBILITY_ENABLED}
* {@link Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES}
*
* @param observerCallback A callback which is used to handle the onChange event triggered
* * by the key collection of {@code keysToObserve}.
*/
public void registerObserverCallback(ContentObserverCallback observerCallback) {
mUrisToCallback.put(Collections.emptyList(), observerCallback);
}
@Override
public final void onChange(boolean selfChange, Uri uri) {
final String key = mUriToKey.get(uri);
if (key == null) {
Log.w(TAG, "AccessibilitySettingsContentObserver can not find the key for "
+ "uri: " + uri);
return;
}
for (List<String> keys : mUrisToCallback.keySet()) {
final boolean isDefaultKey = isDefaultKey(key);
if (isDefaultKey || keys.contains(key)) {
mUrisToCallback.get(keys).onChange(key);
}
}
}
}

View File

@@ -16,12 +16,7 @@
package com.android.settings.accessibility;
import static com.android.settings.accessibility.AccessibilityScreenSizeForSetupWizardActivity.VISION_FRAGMENT_NO;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.accessibility.AccessibilityEvent;
@@ -32,19 +27,16 @@ import androidx.preference.PreferenceFragmentCompat;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SetupWizardUtils;
import com.android.settings.accessibility.AccessibilityScreenSizeForSetupWizardActivity.FragmentType;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.search.actionbar.SearchMenuController;
import com.android.settings.support.actionbar.HelpResourceProvider;
import com.android.settingslib.core.instrumentation.Instrumentable;
import com.android.settingslib.transition.SettingsTransitionHelper;
import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.android.setupdesign.util.ThemeHelper;
public class AccessibilitySettingsForSetupWizardActivity extends SettingsActivity {
private static final String LOG_TAG = "A11ySettingsForSUW";
private static final String SAVE_KEY_TITLE = "activity_title";
@VisibleForTesting
@@ -105,33 +97,12 @@ public class AccessibilitySettingsForSetupWizardActivity extends SettingsActivit
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
applyTheme();
tryLaunchFontSizeSettings();
findViewById(R.id.content_parent).setFitsSystemWindows(false);
}
private void applyTheme() {
if (ThemeHelper.trySetDynamicColor(this)) {
final int appliedTheme = ThemeHelper.isSetupWizardDayNightEnabled(this)
? R.style.SudDynamicColorThemeSettings_SetupWizard_DayNight
: R.style.SudDynamicColorThemeSettings_SetupWizard;
setTheme(appliedTheme);
} else {
setTheme(SetupWizardUtils.getTheme(this, getIntent()));
}
}
@VisibleForTesting
void tryLaunchFontSizeSettings() {
if (WizardManagerHelper.isAnySetupWizard(getIntent())
&& new ComponentName(getPackageName(),
CLASS_NAME_FONT_SIZE_SETTINGS_FOR_SUW).equals(
getIntent().getComponent())) {
final Intent intent = new Intent(this,
AccessibilityScreenSizeForSetupWizardActivity.class);
intent.putExtra(VISION_FRAGMENT_NO, FragmentType.FONT_SIZE);
startActivity(intent);
Log.d(LOG_TAG, "Launch font size settings");
finish();
}
setTheme(SetupWizardUtils.getTheme(this, getIntent()));
setTheme(R.style.SettingsPreferenceTheme_SetupWizard);
ThemeHelper.trySetDynamicColor(this);
}
}

View File

@@ -47,7 +47,7 @@ class AccessibilitySetupWizardUtils {
layout.setIcon(icon);
layout.setDividerInsets(Integer.MAX_VALUE, 0);
if (ThemeHelper.shouldApplyExtendedPartnerConfig(context)) {
if (ThemeHelper.shouldApplyMaterialYouStyle(context)) {
final LinearLayout headerLayout = layout.findManagedViewById(R.id.sud_layout_header);
if (headerLayout != null) {
headerLayout.setPadding(0, layout.getPaddingTop(), 0,

View File

@@ -18,6 +18,7 @@ package com.android.settings.accessibility;
import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_GENERAL_CATEGORY;
import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_SAVED_QS_TOOLTIP_TYPE;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
@@ -25,10 +26,10 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.icu.text.CaseMap;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -41,6 +42,7 @@ import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.utils.LocaleUtils;
@@ -57,6 +59,7 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF
implements ShortcutPreference.OnClickCallback {
private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
protected static final String KEY_SAVED_USER_SHORTCUT_TYPE = "shortcut_type";
protected static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
protected static final int NOT_SET = -1;
// Save user's shortcutType value when savedInstance has value (e.g. device rotated).
protected int mSavedCheckBoxValue = NOT_SET;
@@ -64,9 +67,12 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF
protected ShortcutPreference mShortcutPreference;
private AccessibilityManager.TouchExplorationStateChangeListener
mTouchExplorationStateChangeListener;
private SettingsContentObserver mSettingsContentObserver;
private AccessibilitySettingsContentObserver mSettingsContentObserver;
private CheckBox mSoftwareTypeCheckBox;
private CheckBox mHardwareTypeCheckBox;
private AccessibilityQuickSettingsTooltipWindow mTooltipWindow;
private boolean mNeedsQSTooltipReshow = false;
private int mNeedsQSTooltipType = QuickSettingsTooltipType.GUIDE_TO_EDIT;
/** Returns the accessibility component name. */
protected abstract ComponentName getComponentName();
@@ -74,14 +80,28 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF
/** Returns the accessibility feature name. */
protected abstract CharSequence getLabelName();
/** Returns the accessibility tile component name. */
protected abstract ComponentName getTileComponentName();
/** Returns the accessibility tile tooltip content. */
protected abstract CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Restore the user shortcut type.
if (savedInstanceState != null && savedInstanceState.containsKey(
KEY_SAVED_USER_SHORTCUT_TYPE)) {
mSavedCheckBoxValue = savedInstanceState.getInt(KEY_SAVED_USER_SHORTCUT_TYPE, NOT_SET);
// Restore the user shortcut type and tooltip.
if (savedInstanceState != null) {
if (savedInstanceState.containsKey(KEY_SAVED_USER_SHORTCUT_TYPE)) {
mSavedCheckBoxValue = savedInstanceState.getInt(KEY_SAVED_USER_SHORTCUT_TYPE,
NOT_SET);
}
if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_RESHOW)) {
mNeedsQSTooltipReshow = savedInstanceState.getBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW);
}
if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_TYPE)) {
mNeedsQSTooltipType = savedInstanceState.getInt(KEY_SAVED_QS_TOOLTIP_TYPE);
}
}
final int resId = getPreferenceScreenResId();
@@ -98,13 +118,11 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF
final List<String> shortcutFeatureKeys = new ArrayList<>();
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
mSettingsContentObserver = new SettingsContentObserver(new Handler(), shortcutFeatureKeys) {
@Override
public void onChange(boolean selfChange, Uri uri) {
updateShortcutPreferenceData();
updateShortcutPreference();
}
};
mSettingsContentObserver = new AccessibilitySettingsContentObserver(new Handler());
mSettingsContentObserver.registerKeysToObserverCallback(shortcutFeatureKeys, key -> {
updateShortcutPreferenceData();
updateShortcutPreference();
});
}
@Override
@@ -114,8 +132,8 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF
mShortcutPreference.setPersistent(false);
mShortcutPreference.setKey(getShortcutPreferenceKey());
mShortcutPreference.setOnClickCallback(this);
mShortcutPreference.setTitle(getShortcutTitle());
updateShortcutTitle(mShortcutPreference);
getPreferenceScreen().addPreference(mShortcutPreference);
mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
@@ -126,6 +144,16 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// Reshow tooltip when activity recreate, such as rotate device.
if (mNeedsQSTooltipReshow) {
getView().post(this::showQuickSettingsTooltipIfNeeded);
}
}
@Override
public void onResume() {
super.onResume();
@@ -152,6 +180,10 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF
if (value != NOT_SET) {
outState.putInt(KEY_SAVED_USER_SHORTCUT_TYPE, value);
}
if (mTooltipWindow != null) {
outState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, mTooltipWindow.isShowing());
outState.putInt(KEY_SAVED_QS_TOOLTIP_TYPE, mNeedsQSTooltipType);
}
super.onSaveInstanceState(outState);
}
@@ -160,20 +192,18 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF
final Dialog dialog;
switch (dialogId) {
case DialogEnums.EDIT_SHORTCUT:
final CharSequence dialogTitle = getPrefContext().getString(
R.string.accessibility_shortcut_title, getLabelName());
final int dialogType = WizardManagerHelper.isAnySetupWizard(getIntent())
? AccessibilityDialogUtils.DialogType.EDIT_SHORTCUT_GENERIC_SUW :
AccessibilityDialogUtils.DialogType.EDIT_SHORTCUT_GENERIC;
dialog = AccessibilityDialogUtils.showEditShortcutDialog(
getPrefContext(), dialogType, dialogTitle,
getPrefContext(), dialogType, getShortcutTitle(),
this::callOnAlertDialogCheckboxClicked);
setupEditShortcutDialog(dialog);
return dialog;
case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
dialog = AccessibilityGestureNavigationTutorial
.createAccessibilityTutorialDialog(getPrefContext(),
getUserShortcutTypes());
getUserShortcutTypes(), this::callOnTutorialDialogButtonClicked);
dialog.setCanceledOnTouchOutside(false);
return dialog;
default:
@@ -181,9 +211,8 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF
}
}
protected void updateShortcutTitle(ShortcutPreference shortcutPreference) {
final CharSequence title = getString(R.string.accessibility_shortcut_title, getLabelName());
shortcutPreference.setTitle(title);
protected CharSequence getShortcutTitle() {
return getString(R.string.accessibility_shortcut_title, getLabelName());
}
@Override
@@ -271,6 +300,17 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF
getComponentName());
};
/**
* This method will be invoked when a button in the tutorial dialog is clicked.
*
* @param dialog The dialog that received the click
* @param which The button that was clicked
*/
private void callOnTutorialDialogButtonClicked(DialogInterface dialog, int which) {
dialog.dismiss();
showQuickSettingsTooltipIfNeeded();
}
/**
* This method will be invoked when a button in the edit shortcut dialog is clicked.
*
@@ -283,12 +323,21 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF
}
final int value = getShortcutTypeCheckBoxValue();
saveNonEmptyUserShortcutType(value);
AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), value, getComponentName());
AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), ~value, getComponentName());
mShortcutPreference.setChecked(value != AccessibilityUtil.UserShortcutType.EMPTY);
final boolean shortcutAssigned = value != AccessibilityUtil.UserShortcutType.EMPTY;
mShortcutPreference.setChecked(shortcutAssigned);
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
if (mHardwareTypeCheckBox.isChecked()) {
AccessibilityUtil.skipVolumeShortcutDialogTimeoutRestriction(getPrefContext());
}
// Show the quick setting tooltip if the shortcut assigned in the first time
if (shortcutAssigned) {
showQuickSettingsTooltipIfNeeded();
}
}
@VisibleForTesting
@@ -426,4 +475,45 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF
getComponentName()));
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
}
/**
* Shows the quick settings tooltip if the quick settings feature is assigned. The tooltip only
* shows once.
*
* @param type The quick settings tooltip type
*/
protected void showQuickSettingsTooltipIfNeeded(@QuickSettingsTooltipType int type) {
mNeedsQSTooltipType = type;
showQuickSettingsTooltipIfNeeded();
}
private void showQuickSettingsTooltipIfNeeded() {
final ComponentName tileComponentName = getTileComponentName();
if (tileComponentName == null) {
// Returns if no tile service assigned.
return;
}
if (!mNeedsQSTooltipReshow && AccessibilityQuickSettingUtils.hasValueInSharedPreferences(
getContext(), tileComponentName)) {
// Returns if quick settings tooltip only show once.
return;
}
final CharSequence content = getTileTooltipContent(mNeedsQSTooltipType);
if (TextUtils.isEmpty(content)) {
// Returns if no content of tile tooltip assigned.
return;
}
final int imageResId = mNeedsQSTooltipType == QuickSettingsTooltipType.GUIDE_TO_EDIT
? R.drawable.accessibility_qs_tooltip_illustration
: R.drawable.accessibility_auto_added_qs_tooltip_illustration;
mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(getContext());
mTooltipWindow.setup(content, imageResId);
mTooltipWindow.showAtTopCenter(getView());
AccessibilityQuickSettingUtils.optInValueToSharedPreferences(getContext(),
tileComponentName);
mNeedsQSTooltipReshow = false;
}
}

View File

@@ -16,6 +16,16 @@
package com.android.settings.accessibility;
import static com.android.settings.accessibility.TextReadingPreferenceFragment.BOLD_TEXT_KEY;
import static com.android.settings.accessibility.TextReadingPreferenceFragment.DISPLAY_SIZE_KEY;
import static com.android.settings.accessibility.TextReadingPreferenceFragment.EntryPoint.ACCESSIBILITY_SETTINGS;
import static com.android.settings.accessibility.TextReadingPreferenceFragment.EntryPoint.DISPLAY_SETTINGS;
import static com.android.settings.accessibility.TextReadingPreferenceFragment.EntryPoint.SUW_ANYTHING_ELSE;
import static com.android.settings.accessibility.TextReadingPreferenceFragment.EntryPoint.SUW_VISION_SETTINGS;
import static com.android.settings.accessibility.TextReadingPreferenceFragment.FONT_SIZE_KEY;
import static com.android.settings.accessibility.TextReadingPreferenceFragment.HIGH_TEXT_CONTRAST_KEY;
import static com.android.settings.accessibility.TextReadingPreferenceFragment.RESET_KEY;
import android.content.ComponentName;
import com.android.settings.core.instrumentation.SettingsStatsLog;
@@ -41,4 +51,65 @@ public final class AccessibilityStatsLogUtils {
return enabled ? SettingsStatsLog.ACCESSIBILITY_SERVICE_REPORTED__SERVICE_STATUS__ENABLED
: SettingsStatsLog.ACCESSIBILITY_SERVICE_REPORTED__SERVICE_STATUS__DISABLED;
}
/**
* Logs when the non-a11y category service is disabled. Calls this when the user disables the
* non-a11y category service for the first time.
*
* @param packageName package name of the service
* @param durationMills duration in milliseconds between starting the page and disabling the
* service
*/
static void logDisableNonA11yCategoryService(String packageName, long durationMills) {
com.android.internal.accessibility.util.AccessibilityStatsLogUtils
.logNonA11yToolServiceWarningReported(
packageName,
com.android.internal.accessibility.util.AccessibilityStatsLogUtils
.ACCESSIBILITY_PRIVACY_WARNING_STATUS_SERVICE_DISABLED,
durationMills);
}
/**
* Converts to the key name for logging.
*
* @param prefKey the preference key
* @return the int value which maps to the key name
*/
static int convertToItemKeyName(String prefKey) {
switch (prefKey) {
case FONT_SIZE_KEY:
return SettingsStatsLog.ACCESSIBILITY_TEXT_READING_OPTIONS_CHANGED__NAME__TEXT_READING_FONT_SIZE;
case DISPLAY_SIZE_KEY:
return SettingsStatsLog.ACCESSIBILITY_TEXT_READING_OPTIONS_CHANGED__NAME__TEXT_READING_DISPLAY_SIZE;
case BOLD_TEXT_KEY:
return SettingsStatsLog.ACCESSIBILITY_TEXT_READING_OPTIONS_CHANGED__NAME__TEXT_READING_BOLD_TEXT;
case HIGH_TEXT_CONTRAST_KEY:
return SettingsStatsLog.ACCESSIBILITY_TEXT_READING_OPTIONS_CHANGED__NAME__TEXT_READING_HIGH_CONTRAST_TEXT;
case RESET_KEY:
return SettingsStatsLog.ACCESSIBILITY_TEXT_READING_OPTIONS_CHANGED__NAME__TEXT_READING_RESET;
default:
return SettingsStatsLog.ACCESSIBILITY_TEXT_READING_OPTIONS_CHANGED__NAME__TEXT_READING_UNKNOWN_ITEM;
}
}
/**
* Converts to the entry point for logging.
*
* @param entryPoint the entry point
* @return the int value which maps to the entry point
*/
static int convertToEntryPoint(int entryPoint) {
switch (entryPoint) {
case SUW_VISION_SETTINGS:
return SettingsStatsLog.ACCESSIBILITY_TEXT_READING_OPTIONS_CHANGED__ENTRY_POINT__TEXT_READING_SUW_VISION_SETTINGS;
case SUW_ANYTHING_ELSE:
return SettingsStatsLog.ACCESSIBILITY_TEXT_READING_OPTIONS_CHANGED__ENTRY_POINT__TEXT_READING_SUW_ANYTHING_ELSE;
case DISPLAY_SETTINGS:
return SettingsStatsLog.ACCESSIBILITY_TEXT_READING_OPTIONS_CHANGED__ENTRY_POINT__TEXT_READING_DISPLAY_SETTINGS;
case ACCESSIBILITY_SETTINGS:
return SettingsStatsLog.ACCESSIBILITY_TEXT_READING_OPTIONS_CHANGED__ENTRY_POINT__TEXT_READING_ACCESSIBILITY_SETTINGS;
default:
return SettingsStatsLog.ACCESSIBILITY_TEXT_READING_OPTIONS_CHANGED__ENTRY_POINT__TEXT_READING_UNKNOWN_ENTRY;
}
}
}

View File

@@ -29,7 +29,7 @@ import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.RadioButtonPreference;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
import com.google.common.primitives.Ints;
@@ -40,7 +40,7 @@ import java.util.Map;
* Controller class that control accessibility time out settings.
*/
public class AccessibilityTimeoutController extends AbstractPreferenceController implements
LifecycleObserver, RadioButtonPreference.OnClickListener, PreferenceControllerMixin {
LifecycleObserver, SelectorWithWidgetPreference.OnClickListener, PreferenceControllerMixin {
static final String CONTENT_TIMEOUT_SETTINGS_SECURE =
Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS;
static final String CONTROL_TIMEOUT_SETTINGS_SECURE =
@@ -54,7 +54,7 @@ public class AccessibilityTimeoutController extends AbstractPreferenceController
private final ContentResolver mContentResolver;
private final Resources mResources;
private OnChangeListener mOnChangeListener;
private RadioButtonPreference mPreference;
private SelectorWithWidgetPreference mPreference;
private int mAccessibilityUiTimeoutValue;
public AccessibilityTimeoutController(Context context, Lifecycle lifecycle,
@@ -124,13 +124,13 @@ public class AccessibilityTimeoutController extends AbstractPreferenceController
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = (RadioButtonPreference)
mPreference = (SelectorWithWidgetPreference)
screen.findPreference(getPreferenceKey());
mPreference.setOnClickListener(this);
}
@Override
public void onRadioButtonClicked(RadioButtonPreference preference) {
public void onRadioButtonClicked(SelectorWithWidgetPreference preference) {
int value = getTimeoutValueToKeyMap().get(mPreferenceKey);
handlePreferenceChange(String.valueOf(value));
if (mOnChangeListener != null) {

View File

@@ -46,7 +46,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.StringJoiner;
/** Provides utility methods to accessibility settings only. */
final class AccessibilityUtil {
public final class AccessibilityUtil {
private AccessibilityUtil(){}
@@ -105,6 +105,17 @@ final class AccessibilityUtil {
int TRIPLETAP = 4; // 1 << 2
}
/**
* Denotes the quick setting tooltip type.
*
* {@code GUIDE_TO_EDIT} for QS tiles that need to be added by editing.
* {@code GUIDE_TO_DIRECT_USE} for QS tiles that have been auto-added already.
*/
public @interface QuickSettingsTooltipType {
int GUIDE_TO_EDIT = 0;
int GUIDE_TO_DIRECT_USE = 1;
}
/** Denotes the accessibility enabled status */
@Retention(RetentionPolicy.SOURCE)
public @interface State {
@@ -423,4 +434,15 @@ final class AccessibilityUtil {
public static boolean isSystemApp(@NonNull AccessibilityServiceInfo info) {
return info.getResolveInfo().serviceInfo.applicationInfo.isSystemApp();
}
/**
* Bypasses the timeout restriction if volume key shortcut assigned.
*
* @param context the current context.
*/
public static void skipVolumeShortcutDialogTimeoutRestriction(Context context) {
Settings.Secure.putInt(context.getContentResolver(),
Settings.Secure.SKIP_ACCESSIBILITY_SHORTCUT_DIALOG_TIMEOUT_RESTRICTION, /*
true */ 1);
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2022 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 android.content.Context;
import android.os.VibrationAttributes;
import android.provider.Settings;
/** Preference controller for alarm vibration intensity */
public class AlarmVibrationIntensityPreferenceController
extends VibrationIntensityPreferenceController {
/** General configuration for alarm vibration intensity settings. */
public static final class AlarmVibrationPreferenceConfig extends VibrationPreferenceConfig {
public AlarmVibrationPreferenceConfig(Context context) {
super(context, Settings.System.ALARM_VIBRATION_INTENSITY,
VibrationAttributes.USAGE_ALARM);
}
}
public AlarmVibrationIntensityPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey, new AlarmVibrationPreferenceConfig(context));
}
protected AlarmVibrationIntensityPreferenceController(Context context, String preferenceKey,
int supportedIntensityLevels) {
super(context, preferenceKey, new AlarmVibrationPreferenceConfig(context),
supportedIntensityLevels);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2022 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 android.content.Context;
import com.android.settings.accessibility.AlarmVibrationIntensityPreferenceController.AlarmVibrationPreferenceConfig;
/** Preference controller for alarm vibration with only a toggle for on/off states. */
public class AlarmVibrationTogglePreferenceController extends VibrationTogglePreferenceController {
public AlarmVibrationTogglePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey, new AlarmVibrationPreferenceConfig(context));
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2022 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 android.provider.Settings.Secure.ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT;
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import android.content.Context;
import android.os.UserHandle;
import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
/**
* A toggle preference controller for audio description
*/
public class AudioDescriptionPreferenceController extends TogglePreferenceController {
static final String PREF_KEY = "toggle_audio_description";
public AudioDescriptionPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public boolean isChecked() {
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT,
OFF /* default */,
UserHandle.USER_CURRENT) == ON;
}
@Override
public boolean setChecked(boolean isChecked) {
return Settings.Secure.putIntForUser(mContext.getContentResolver(),
ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT,
isChecked ? ON : OFF,
UserHandle.USER_CURRENT);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
}

View File

@@ -16,12 +16,16 @@
package com.android.settings.accessibility;
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import android.app.settings.SettingsEnums;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.view.View;
import android.view.accessibility.CaptioningManager;
@@ -92,6 +96,7 @@ public class CaptionAppearanceFragment extends DashboardFragment
private final List<Preference> mPreferenceList = new ArrayList<>();
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final View.OnLayoutChangeListener mLayoutChangeListener =
new View.OnLayoutChangeListener() {
@Override
@@ -99,7 +104,7 @@ public class CaptionAppearanceFragment extends DashboardFragment
int oldLeft, int oldTop, int oldRight, int oldBottom) {
// Remove the listener once the callback is triggered.
mPreviewViewport.removeOnLayoutChangeListener(this);
refreshPreviewText();
mHandler.post(() ->refreshPreviewText());
}
};
@@ -397,6 +402,7 @@ public class CaptionAppearanceFragment extends DashboardFragment
}
refreshPreviewText();
enableCaptioningManager();
}
@Override
@@ -406,16 +412,26 @@ public class CaptionAppearanceFragment extends DashboardFragment
Settings.Secure.putString(
cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_TYPEFACE, (String) value);
refreshPreviewText();
enableCaptioningManager();
} else if (mFontSize == preference) {
Settings.Secure.putFloat(
cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE,
Float.parseFloat((String) value));
refreshPreviewText();
enableCaptioningManager();
}
return true;
}
private void enableCaptioningManager() {
if (mCaptioningManager.isEnabled()) {
return;
}
Settings.Secure.putInt(getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, ON);
}
@Override
public int getHelpResource() {
return R.string.help_url_caption;

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2022 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 android.content.Context;
import android.view.accessibility.CaptioningManager;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.google.common.primitives.Floats;
import com.google.common.primitives.Ints;
/** Controller that shows the caption scale and style summary. */
public class CaptionAppearancePreferenceController extends BasePreferenceController {
private final CaptioningManager mCaptioningManager;
public CaptionAppearancePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mCaptioningManager = context.getSystemService(CaptioningManager.class);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public CharSequence getSummary() {
return mContext.getString(R.string.preference_summary_default_combination,
geFontScaleSummary(), getPresetSummary());
}
private float[] getFontScaleValuesArray() {
final String[] fontScaleValuesStrArray = mContext.getResources().getStringArray(
R.array.captioning_font_size_selector_values);
final int length = fontScaleValuesStrArray.length;
final float[] fontScaleValuesArray = new float[length];
for (int i = 0; i < length; ++i) {
fontScaleValuesArray[i] = Float.parseFloat(fontScaleValuesStrArray[i]);
}
return fontScaleValuesArray;
}
private CharSequence geFontScaleSummary() {
final float[] fontScaleValuesArray = getFontScaleValuesArray();
final String[] fontScaleSummaries = mContext.getResources().getStringArray(
R.array.captioning_font_size_selector_titles);
final float fontScale = mCaptioningManager.getFontScale();
final int idx = Floats.indexOf(fontScaleValuesArray, fontScale);
return fontScaleSummaries[idx == /* not exist */ -1 ? 0 : idx];
}
private CharSequence getPresetSummary() {
final int[] presetValuesArray = mContext.getResources().getIntArray(
R.array.captioning_preset_selector_values);
final String[] presetSummaries = mContext.getResources().getStringArray(
R.array.captioning_preset_selector_titles);
final int preset = mCaptioningManager.getRawUserStyle();
final int idx = Ints.indexOf(presetValuesArray, preset);
return presetSummaries[idx];
}
}

View File

@@ -30,7 +30,7 @@ public class CaptionFooterPreferenceController extends AccessibilityFooterPrefer
}
@Override
protected String getLearnMoreContentDescription() {
protected String getLearnMoreText() {
return mContext.getString(
R.string.accessibility_captioning_footer_learn_more_content_description);
}

View File

@@ -34,8 +34,6 @@ import com.android.settings.widget.SettingsMainSwitchPreference;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.widget.OnMainSwitchChangeListener;
import com.google.common.primitives.Floats;
import java.util.ArrayList;
import java.util.List;
@@ -56,7 +54,6 @@ public class CaptionPropertiesFragment extends DashboardFragment
private Preference mMoreOptions;
private final List<Preference> mPreferenceList = new ArrayList<>();
private float[] mFontSizeValuesArray;
@Override
public int getMetricsCategory() {
@@ -71,13 +68,12 @@ public class CaptionPropertiesFragment extends DashboardFragment
initializeAllPreferences();
installUpdateListeners();
initFontSizeValuesArray();
}
@Override
public void onResume() {
super.onResume();
updateAllPreferences();
mSwitch.setChecked(mCaptioningManager.isEnabled());
}
@Override
@@ -105,21 +101,6 @@ public class CaptionPropertiesFragment extends DashboardFragment
}
private void initFontSizeValuesArray() {
final String[] fontSizeValuesStrArray = getPrefContext().getResources().getStringArray(
R.array.captioning_font_size_selector_values);
final int length = fontSizeValuesStrArray.length;
mFontSizeValuesArray = new float[length];
for (int i = 0; i < length; ++i) {
mFontSizeValuesArray[i] = Float.parseFloat(fontSizeValuesStrArray[i]);
}
}
private void updateAllPreferences() {
mSwitch.setChecked(mCaptioningManager.isEnabled());
mTextAppearance.setSummary(geTextAppearanceSummary(getPrefContext()));
}
@Override
public boolean onPreferenceChange(Preference preference, Object value) {
final ContentResolver cr = getActivity().getContentResolver();
@@ -136,16 +117,6 @@ public class CaptionPropertiesFragment extends DashboardFragment
return R.string.help_url_caption;
}
private CharSequence geTextAppearanceSummary(Context context) {
final String[] fontSizeSummaries = context.getResources().getStringArray(
R.array.captioning_font_size_selector_summaries);
final float fontSize = mCaptioningManager.getFontScale();
final int idx = Floats.indexOf(mFontSizeValuesArray, fontSize);
return fontSizeSummaries[idx == /* not exist */ -1 ? 0 : idx];
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.captioning_settings);

View File

@@ -30,11 +30,11 @@ import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
/** Accessibility settings for text and display. */
/** Accessibility settings for color and motion. */
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class TextAndDisplayFragment extends DashboardFragment {
public class ColorAndMotionFragment extends DashboardFragment {
private static final String TAG = "TextAndDisplayFragment";
private static final String TAG = "ColorAndMotionFragment";
private static final String CATEGORY_EXPERIMENTAL = "experimental_category";
@@ -49,7 +49,7 @@ public class TextAndDisplayFragment extends DashboardFragment {
@Override
public int getMetricsCategory() {
return SettingsEnums.ACCESSIBILITY_TEXT_AND_DISPLAY;
return SettingsEnums.ACCESSIBILITY_COLOR_AND_MOTION;
}
@@ -62,7 +62,7 @@ public class TextAndDisplayFragment extends DashboardFragment {
@Override
protected int getPreferenceScreenResId() {
return R.xml.accessibility_text_and_display;
return R.xml.accessibility_color_and_motion;
}
@Override
@@ -104,5 +104,5 @@ public class TextAndDisplayFragment extends DashboardFragment {
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.accessibility_text_and_display);
new BaseSearchIndexProvider(R.xml.accessibility_color_and_motion);
}

View File

@@ -30,7 +30,7 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.RadioButtonPreference;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
import com.google.common.primitives.Ints;
@@ -39,7 +39,7 @@ import java.util.Map;
/** Controller class that control radio button of accessibility daltonizer settings. */
public class DaltonizerRadioButtonPreferenceController extends BasePreferenceController implements
LifecycleObserver, RadioButtonPreference.OnClickListener {
LifecycleObserver, SelectorWithWidgetPreference.OnClickListener {
private static final String TYPE = Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER;
// pair the preference key and daltonizer value.
@@ -49,7 +49,7 @@ public class DaltonizerRadioButtonPreferenceController extends BasePreferenceCon
private final ContentResolver mContentResolver;
private final Resources mResources;
private DaltonizerRadioButtonPreferenceController.OnChangeListener mOnChangeListener;
private RadioButtonPreference mPreference;
private SelectorWithWidgetPreference mPreference;
private int mAccessibilityDaltonizerValue;
public DaltonizerRadioButtonPreferenceController(Context context, String preferenceKey) {
@@ -120,7 +120,7 @@ public class DaltonizerRadioButtonPreferenceController extends BasePreferenceCon
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = (RadioButtonPreference)
mPreference = (SelectorWithWidgetPreference)
screen.findPreference(getPreferenceKey());
mPreference.setOnClickListener(this);
mPreference.setAppendixVisibility(View.GONE);
@@ -128,7 +128,7 @@ public class DaltonizerRadioButtonPreferenceController extends BasePreferenceCon
}
@Override
public void onRadioButtonClicked(RadioButtonPreference preference) {
public void onRadioButtonClicked(SelectorWithWidgetPreference preference) {
final int value = getDaltonizerValueToKeyMap().get(mPreferenceKey);
handlePreferenceChange(String.valueOf(value));
if (mOnChangeListener != null) {

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2022 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 android.content.Context;
import android.content.res.Resources;
import android.view.Display;
import com.android.settingslib.display.DisplayDensityConfiguration;
import com.android.settingslib.display.DisplayDensityUtils;
import java.util.Arrays;
import java.util.Collections;
import java.util.stream.Collectors;
/**
* Data class for storing the configurations related to the display size.
*/
class DisplaySizeData extends PreviewSizeData<Integer> {
DisplaySizeData(Context context) {
super(context);
final DisplayDensityUtils density = new DisplayDensityUtils(getContext());
final int initialIndex = density.getCurrentIndex();
if (initialIndex < 0) {
// Failed to obtain default density, which means we failed to
// connect to the window manager service. Just use the current
// density and don't let the user change anything.
final Resources resources = getContext().getResources();
final int densityDpi = resources.getDisplayMetrics().densityDpi;
setDefaultValue(densityDpi);
setInitialIndex(0);
setValues(Collections.singletonList(densityDpi));
} else {
setDefaultValue(density.getDefaultDensity());
setInitialIndex(initialIndex);
setValues(Arrays.stream(density.getValues()).boxed().collect(Collectors.toList()));
}
}
@Override
void commit(int currentProgress) {
final int densityDpi = getValues().get(currentProgress);
if (densityDpi == getDefaultValue()) {
DisplayDensityConfiguration.clearForcedDisplayDensity(Display.DEFAULT_DISPLAY);
} else {
DisplayDensityConfiguration.setForcedDisplayDensity(Display.DEFAULT_DISPLAY,
densityDpi);
}
}
}

View File

@@ -51,9 +51,7 @@ public class FloatingMenuTransparencyPreferenceController extends SliderPreferen
private final ContentResolver mContentResolver;
@VisibleForTesting
final ContentObserver mContentObserver;
@VisibleForTesting
SeekBarPreference mPreference;
private SeekBarPreference mPreference;
public FloatingMenuTransparencyPreferenceController(Context context,
String preferenceKey) {
@@ -83,6 +81,7 @@ public class FloatingMenuTransparencyPreferenceController extends SliderPreferen
mPreference.setMin(getMin());
mPreference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_ENDS);
updateAvailabilityStatus();
updateState(mPreference);
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2022 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.display.ToggleFontSizePreferenceFragment.fontSizeValueToIndex;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.provider.Settings;
import com.android.settings.R;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* Data class for storing the configurations related to the font size.
*/
final class FontSizeData extends PreviewSizeData<Float> {
private static final float FONT_SCALE_DEF_VALUE = 1.0f;
FontSizeData(Context context) {
super(context);
final Resources resources = getContext().getResources();
final ContentResolver resolver = getContext().getContentResolver();
final List<String> strEntryValues =
Arrays.asList(resources.getStringArray(R.array.entryvalues_font_size));
setDefaultValue(FONT_SCALE_DEF_VALUE);
final float currentScale =
Settings.System.getFloat(resolver, Settings.System.FONT_SCALE, getDefaultValue());
setInitialIndex(fontSizeValueToIndex(currentScale, strEntryValues.toArray(new String[0])));
setValues(strEntryValues.stream().map(Float::valueOf).collect(Collectors.toList()));
}
@Override
void commit(int currentProgress) {
final ContentResolver resolver = getContext().getContentResolver();
Settings.System.putFloat(resolver, Settings.System.FONT_SCALE,
getValues().get(currentProgress));
}
}

View File

@@ -1,56 +0,0 @@
/*
* 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.accessibility;
import static com.android.settings.accessibility.AccessibilityScreenSizeForSetupWizardActivity.VISION_FRAGMENT_NO;
import static com.android.settings.core.SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE;
import android.content.Context;
import android.content.Intent;
import androidx.preference.Preference;
import com.android.settings.accessibility.AccessibilityScreenSizeForSetupWizardActivity.FragmentType;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.transition.SettingsTransitionHelper.TransitionType;
/** PreferenceController for displaying font size page. */
public class FontSizePreferenceController extends BasePreferenceController {
public FontSizePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (!mPreferenceKey.equals(preference.getKey())) {
return false;
}
final Intent intent = new Intent(mContext,
AccessibilityScreenSizeForSetupWizardActivity.class);
intent.putExtra(VISION_FRAGMENT_NO, FragmentType.FONT_SIZE);
intent.putExtra(EXTRA_PAGE_TRANSITION_TYPE, TransitionType.TRANSITION_FADE);
mContext.startActivity(intent);
return true;
}
}

View File

@@ -21,13 +21,19 @@ import android.graphics.fonts.FontStyle;
import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.accessibility.TextReadingPreferenceFragment.EntryPoint;
import com.android.settings.core.TogglePreferenceController;
import com.android.settings.core.instrumentation.SettingsStatsLog;
/** PreferenceController for displaying all text in bold. */
public class FontWeightAdjustmentPreferenceController extends TogglePreferenceController {
public class FontWeightAdjustmentPreferenceController extends TogglePreferenceController implements
TextReadingResetController.ResetStateListener {
static final int BOLD_TEXT_ADJUSTMENT =
FontStyle.FONT_WEIGHT_BOLD - FontStyle.FONT_WEIGHT_NORMAL;
@EntryPoint
private int mEntryPoint;
public FontWeightAdjustmentPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@@ -45,6 +51,12 @@ public class FontWeightAdjustmentPreferenceController extends TogglePreferenceCo
@Override
public boolean setChecked(boolean isChecked) {
SettingsStatsLog.write(
SettingsStatsLog.ACCESSIBILITY_TEXT_READING_OPTIONS_CHANGED,
AccessibilityStatsLogUtils.convertToItemKeyName(getPreferenceKey()),
isChecked ? 1 : 0,
AccessibilityStatsLogUtils.convertToEntryPoint(mEntryPoint));
return Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.FONT_WEIGHT_ADJUSTMENT, (isChecked ? BOLD_TEXT_ADJUSTMENT : 0));
}
@@ -53,4 +65,18 @@ public class FontWeightAdjustmentPreferenceController extends TogglePreferenceCo
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
@Override
public void resetState() {
setChecked(false);
}
/**
* The entry point is used for logging.
*
* @param entryPoint from which settings page
*/
void setEntryPoint(@EntryPoint int entryPoint) {
mEntryPoint = entryPoint;
}
}

View File

@@ -16,30 +16,72 @@
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.content.Context;
import android.os.VibrationAttributes;
import android.os.Vibrator;
import android.provider.Settings;
import androidx.annotation.VisibleForTesting;
/** Preference controller for haptic feedback intensity */
public class HapticFeedbackIntensityPreferenceController
extends VibrationIntensityPreferenceController {
@VisibleForTesting
static final String PREF_KEY = "touch_vibration_preference_screen";
/** General configuration for haptic feedback intensity settings. */
public static final class HapticFeedbackVibrationPreferenceConfig
extends VibrationPreferenceConfig {
public HapticFeedbackIntensityPreferenceController(Context context) {
super(context, PREF_KEY, Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Settings.System.HAPTIC_FEEDBACK_ENABLED);
public HapticFeedbackVibrationPreferenceConfig(Context context) {
super(context, Settings.System.HAPTIC_FEEDBACK_INTENSITY,
VibrationAttributes.USAGE_TOUCH);
}
@Override
public int readIntensity() {
final int hapticFeedbackEnabled = Settings.System.getInt(mContentResolver,
Settings.System.HAPTIC_FEEDBACK_ENABLED, ON);
if (hapticFeedbackEnabled == OFF) {
// HAPTIC_FEEDBACK_ENABLED is deprecated but should still be applied if the user has
// turned it off already.
return Vibrator.VIBRATION_INTENSITY_OFF;
}
return super.readIntensity();
}
@Override
public boolean updateIntensity(int intensity) {
final boolean success = super.updateIntensity(intensity);
final boolean isIntensityOff = intensity == Vibrator.VIBRATION_INTENSITY_OFF;
Settings.System.putInt(mContentResolver, Settings.System.HAPTIC_FEEDBACK_ENABLED,
isIntensityOff ? OFF : ON);
// HAPTIC_FEEDBACK_ENABLED is deprecated but should still reflect the intensity setting.
// HARDWARE_HAPTIC_FEEDBACK_INTENSITY is dependent on this setting, but should not be
// disabled by it.
Settings.System.putInt(mContentResolver,
Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY,
isIntensityOff ? getDefaultIntensity() : intensity);
return success;
}
}
public HapticFeedbackIntensityPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey, new HapticFeedbackVibrationPreferenceConfig(context));
}
protected HapticFeedbackIntensityPreferenceController(Context context, String preferenceKey,
int supportedIntensityLevels) {
super(context, preferenceKey, new HapticFeedbackVibrationPreferenceConfig(context),
supportedIntensityLevels);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
protected int getDefaultIntensity() {
return mVibrator.getDefaultHapticFeedbackIntensity();
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2022 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 android.content.Context;
import com.android.settings.accessibility.HapticFeedbackIntensityPreferenceController.HapticFeedbackVibrationPreferenceConfig;
/** Preference controller for haptic feedback with only a toggle for on/off states. */
public class HapticFeedbackTogglePreferenceController extends VibrationTogglePreferenceController {
public HapticFeedbackTogglePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey, new HapticFeedbackVibrationPreferenceConfig(context));
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
}

View File

@@ -18,7 +18,6 @@ package com.android.settings.accessibility;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.DialogInterface;
import android.os.Bundle;
import androidx.appcompat.app.AlertDialog;
@@ -30,24 +29,17 @@ import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
public class HearingAidDialogFragment extends InstrumentedDialogFragment {
public static HearingAidDialogFragment newInstance() {
HearingAidDialogFragment frag = new HearingAidDialogFragment();
return frag;
return new HearingAidDialogFragment();
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.accessibility_hearingaid_pair_instructions_title)
.setMessage(R.string.accessibility_hearingaid_pair_instructions_message)
.setPositiveButton(R.string.accessibility_hearingaid_instruction_continue_button,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
launchBluetoothAddDeviceSetting();
}
})
.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) { }
})
(dialog, which) -> launchBluetoothAddDeviceSetting())
.setNegativeButton(android.R.string.cancel, /* listener= */ null)
.create();
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2022 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 android.util.Log;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentManager;
import com.android.settings.bluetooth.HearingAidPairingDialogFragment;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HearingAidProfile;
/** Provides utility methods related hearing aids. */
public final class HearingAidUtils {
private static final String TAG = "HearingAidUtils";
private HearingAidUtils(){}
/**
* Launches pairing dialog when hearing aid device needs other side of hearing aid device to
* work.
*
* @param fragmentManager The {@link FragmentManager} used to show dialog fragment
* @param device The {@link CachedBluetoothDevice} need to be hearing aid device
*/
public static void launchHearingAidPairingDialog(FragmentManager fragmentManager,
@NonNull CachedBluetoothDevice device) {
if (device.isConnectedHearingAidDevice()
&& device.getDeviceMode() == HearingAidProfile.DeviceMode.MODE_BINAURAL
&& device.getSubDevice() == null) {
launchHearingAidPairingDialogInternal(fragmentManager, device);
}
}
private static void launchHearingAidPairingDialogInternal(FragmentManager fragmentManager,
@NonNull CachedBluetoothDevice device) {
if (device.getDeviceSide() == HearingAidProfile.DeviceSide.SIDE_INVALID) {
Log.w(TAG, "Can not launch hearing aid pairing dialog for invalid side");
return;
}
HearingAidPairingDialogFragment.newInstance(device).show(fragmentManager,
HearingAidPairingDialogFragment.TAG);
}
}

View File

@@ -19,10 +19,23 @@ package com.android.settings.accessibility;
import android.content.Context;
import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
public class HighTextContrastPreferenceController extends TogglePreferenceController {
import com.android.settings.R;
import com.android.settings.accessibility.TextReadingPreferenceFragment.EntryPoint;
import com.android.settings.core.TogglePreferenceController;
import com.android.settings.core.instrumentation.SettingsStatsLog;
/**
* PreferenceController for displaying all text in high contrast style.
*/
public class HighTextContrastPreferenceController extends TogglePreferenceController implements
TextReadingResetController.ResetStateListener {
private SwitchPreference mSwitchPreference;
@EntryPoint
private int mEntryPoint;
public HighTextContrastPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
@@ -41,6 +54,12 @@ public class HighTextContrastPreferenceController extends TogglePreferenceContro
@Override
public boolean setChecked(boolean isChecked) {
SettingsStatsLog.write(
SettingsStatsLog.ACCESSIBILITY_TEXT_READING_OPTIONS_CHANGED,
AccessibilityStatsLogUtils.convertToItemKeyName(getPreferenceKey()),
isChecked ? 1 : 0,
AccessibilityStatsLogUtils.convertToEntryPoint(mEntryPoint));
return Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, (isChecked ? 1 : 0));
}
@@ -49,4 +68,25 @@ public class HighTextContrastPreferenceController extends TogglePreferenceContro
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mSwitchPreference = screen.findPreference(getPreferenceKey());
}
@Override
public void resetState() {
setChecked(false);
updateState(mSwitchPreference);
}
/**
* The entry point is used for logging.
*
* @param entryPoint from which settings page
*/
void setEntryPoint(@EntryPoint int entryPoint) {
mEntryPoint = entryPoint;
}
}

View File

@@ -41,6 +41,7 @@ import androidx.annotation.Nullable;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.overlay.FeatureFactory;
import java.util.ArrayList;
@@ -51,6 +52,7 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature
private static final String TAG = "LaunchA11yActivity";
private static final String EMPTY_STRING = "";
protected static final String KEY_LAUNCH_PREFERENCE = "launch_preference";
private ComponentName mTileComponentName;
@Override
public int getMetricsCategory() {
@@ -106,6 +108,13 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature
AccessibilitySettings.EXTRA_SETTINGS_TITLE);
mSettingsIntent = TextUtils.isEmpty(settingsTitle) ? null : getSettingsIntent(arguments);
mSettingsTitle = (mSettingsIntent == null) ? null : settingsTitle;
// Tile service.
if (arguments.containsKey(AccessibilitySettings.EXTRA_TILE_SERVICE_COMPONENT_NAME)) {
final String tileServiceComponentName = arguments.getString(
AccessibilitySettings.EXTRA_TILE_SERVICE_COMPONENT_NAME);
mTileComponentName = ComponentName.unflattenFromString(tileServiceComponentName);
}
}
@Override
@@ -114,6 +123,29 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature
mComponentName);
}
@Override
ComponentName getTileComponentName() {
return mTileComponentName;
}
@Override
CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type) {
final ComponentName componentName = getTileComponentName();
if (componentName == null) {
return null;
}
final CharSequence tileName = loadTileLabel(getPrefContext(), componentName);
if (tileName == null) {
return null;
}
final int titleResId = type == QuickSettingsTooltipType.GUIDE_TO_EDIT
? R.string.accessibility_service_qs_tooltip_content
: R.string.accessibility_service_auto_added_qs_tooltip_content;
return getString(titleResId, tileName);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Do not call super. We don't want to see the "Help & feedback" option on this page so as

View File

@@ -25,6 +25,7 @@ import com.android.internal.view.RotationPolicy;
import com.android.internal.view.RotationPolicy.RotationPolicyListener;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settings.display.DeviceStateAutoRotationHelper;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -59,7 +60,9 @@ public class LockScreenRotationPreferenceController extends TogglePreferenceCont
@Override
public int getAvailabilityStatus() {
return RotationPolicy.isRotationSupported(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
return RotationPolicy.isRotationSupported(mContext)
&& !DeviceStateAutoRotationHelper.isDeviceStateRotationEnabledForA11y(mContext)
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@Override

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2022 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.content.Context;
import android.provider.Settings;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
/** Controller that accesses and switches the preference status of following typing feature */
public class MagnificationFollowTypingPreferenceController extends TogglePreferenceController
implements LifecycleObserver {
private static final String TAG =
MagnificationFollowTypingPreferenceController.class.getSimpleName();
static final String PREF_KEY = "magnification_follow_typing";
private SwitchPreference mFollowTypingPreference;
public MagnificationFollowTypingPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public boolean isChecked() {
return Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED, ON) == ON;
}
@Override
public boolean setChecked(boolean isChecked) {
return Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED,
(isChecked ? ON : OFF));
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mFollowTypingPreference = screen.findPreference(getPreferenceKey());
}
// TODO(b/186731461): Remove it when this controller is used in DashBoardFragment only.
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
void onResume() {
updateState();
}
/**
* Updates the state of preference components which has been displayed by
* {@link MagnificationFollowTypingPreferenceController#displayPreference}.
*/
void updateState() {
updateState(mFollowTypingPreference);
}
}

View File

@@ -100,7 +100,10 @@ public class MagnificationGesturesPreferenceController extends TogglePreferenceC
extras.putInt(AccessibilitySettings.EXTRA_TITLE_RES,
R.string.accessibility_screen_magnification_gestures_title);
String summary = context.getString(R.string.accessibility_screen_magnification_summary);
String intro = context.getString(R.string.accessibility_screen_magnification_intro_text);
extras.putCharSequence(AccessibilitySettings.EXTRA_INTRO, intro);
String summary = context.getString(R.string.accessibility_screen_magnification_summary);
final Object[] numberArguments = {1, 2, 3, 4, 5};
summary = MessageFormat.format(summary, numberArguments);
extras.putCharSequence(AccessibilitySettings.EXTRA_HTML_DESCRIPTION, summary);

View File

@@ -16,8 +16,6 @@
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;
@@ -27,12 +25,13 @@ import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
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;
@@ -45,6 +44,7 @@ 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;
@@ -52,7 +52,6 @@ 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
@@ -63,17 +62,18 @@ public class MagnificationModePreferenceController extends BasePreferenceControl
@VisibleForTesting
static final int DIALOG_MAGNIFICATION_MODE = DIALOG_ID_BASE + 1;
@VisibleForTesting
static final int DIALOG_MAGNIFICATION_SWITCH_SHORTCUT = DIALOG_ID_BASE + 2;
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 static final char COMPONENT_NAME_SEPARATOR = ':';
private DialogHelper mDialogHelper;
// The magnification mode in the dialog.
private int mMode = MagnificationMode.NONE;
@MagnificationMode
private int mModeCache = MagnificationMode.NONE;
private Preference mModePreference;
private ShortcutPreference mLinkPreference;
@VisibleForTesting
ListView mMagnificationModesListView;
@@ -113,7 +113,7 @@ public class MagnificationModePreferenceController extends BasePreferenceControl
@Override
public void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) {
mMode = savedInstanceState.getInt(EXTRA_MODE, MagnificationMode.NONE);
mModeCache = savedInstanceState.getInt(EXTRA_MODE, MagnificationMode.NONE);
}
}
@@ -121,8 +121,10 @@ public class MagnificationModePreferenceController extends BasePreferenceControl
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mModePreference = screen.findPreference(getPreferenceKey());
mLinkPreference = screen.findPreference(
ToggleFeaturePreferenceFragment.KEY_SHORTCUT_PREFERENCE);
mModePreference.setOnPreferenceClickListener(preference -> {
mMode = MagnificationCapabilities.getCapabilities(mContext);
mModeCache = MagnificationCapabilities.getCapabilities(mContext);
mDialogHelper.showDialog(DIALOG_MAGNIFICATION_MODE);
return true;
});
@@ -130,7 +132,7 @@ public class MagnificationModePreferenceController extends BasePreferenceControl
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(EXTRA_MODE, mMode);
outState.putInt(EXTRA_MODE, mModeCache);
}
/**
@@ -147,8 +149,8 @@ public class MagnificationModePreferenceController extends BasePreferenceControl
case DIALOG_MAGNIFICATION_MODE:
return createMagnificationModeDialog();
case DIALOG_MAGNIFICATION_SWITCH_SHORTCUT:
return createMagnificationShortCutConfirmDialog();
case DIALOG_MAGNIFICATION_TRIPLE_TAP_WARNING:
return createMagnificationTripleTapWarningDialog();
}
return null;
}
@@ -158,8 +160,8 @@ public class MagnificationModePreferenceController extends BasePreferenceControl
switch (dialogId) {
case DIALOG_MAGNIFICATION_MODE:
return SettingsEnums.DIALOG_MAGNIFICATION_CAPABILITY;
case DIALOG_MAGNIFICATION_SWITCH_SHORTCUT:
return SettingsEnums.DIALOG_MAGNIFICATION_SWITCH_SHORTCUT;
case DIALOG_MAGNIFICATION_TRIPLE_TAP_WARNING:
return SettingsEnums.DIALOG_MAGNIFICATION_TRIPLE_TAP_WARNING;
default:
return 0;
}
@@ -178,29 +180,40 @@ public class MagnificationModePreferenceController extends BasePreferenceControl
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, this::onMagnificationModeDialogPositiveButtonClicked);
mMagnificationModesListView,
positiveBtnText, this::onMagnificationModeDialogPositiveButtonClicked,
negativeBtnText, /* negativeListener= */ null);
}
private void onMagnificationModeDialogPositiveButtonClicked(DialogInterface dialogInterface,
@VisibleForTesting
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 {
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 setMode(int mode) {
mMode = mode;
MagnificationCapabilities.setCapabilities(mContext, mMode);
private void updateCapabilitiesAndSummary(@MagnificationMode int mode) {
mModeCache = mode;
MagnificationCapabilities.setCapabilities(mContext, mModeCache);
mModePreference.setSummary(
MagnificationCapabilities.getSummary(mContext, mMode));
MagnificationCapabilities.getSummary(mContext, mModeCache));
}
private void onMagnificationModeSelected(AdapterView<?> parent, View view, int position,
@@ -208,19 +221,16 @@ public class MagnificationModePreferenceController extends BasePreferenceControl
final MagnificationModeInfo modeInfo =
(MagnificationModeInfo) mMagnificationModesListView.getItemAtPosition(
position);
if (modeInfo.mMagnificationMode == mMode) {
if (modeInfo.mMagnificationMode == mModeCache) {
return;
}
mMode = modeInfo.mMagnificationMode;
if (isTripleTapEnabled(mContext) && mMode != MagnificationMode.FULLSCREEN) {
mDialogHelper.showDialog(DIALOG_MAGNIFICATION_SWITCH_SHORTCUT);
}
mModeCache = modeInfo.mMagnificationMode;
}
private int computeSelectionIndex() {
final int modesSize = mModeInfos.size();
for (int i = 0; i < modesSize; i++) {
if (mModeInfos.get(i).mMagnificationMode == mMode) {
if (mModeInfos.get(i).mMagnificationMode == mModeCache) {
return i + mMagnificationModesListView.getHeaderViewsCount();
}
}
@@ -234,41 +244,57 @@ public class MagnificationModePreferenceController extends BasePreferenceControl
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, OFF) == ON;
}
private Dialog createMagnificationShortCutConfirmDialog() {
return AccessibilityDialogUtils.createMagnificationSwitchShortcutDialog(mContext,
this::onSwitchShortcutDialogButtonClicked);
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 onSwitchShortcutDialogButtonClicked(@CustomButton int which) {
optOutMagnificationFromTripleTap();
//TODO(b/147990389): Merge this function into AccessibilityUtils after the format of
// magnification target is changed to ComponentName.
optInMagnificationToAccessibilityButton();
void onMagnificationTripleTapWarningDialogNegativeButtonClicked(
DialogInterface dialogInterface, int which) {
mModeCache = MagnificationCapabilities.getCapabilities(mContext);
mDialogHelper.showDialog(DIALOG_MAGNIFICATION_MODE);
}
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());
@VisibleForTesting
void onMagnificationTripleTapWarningDialogPositiveButtonClicked(
DialogInterface dialogInterface, int which) {
updateCapabilitiesAndSummary(mModeCache);
}
// TODO(b/186731461): Remove it when this controller is used in DashBoardFragment only.
@@ -277,7 +303,6 @@ public class MagnificationModePreferenceController extends BasePreferenceControl
updateState(mModePreference);
}
/**
* An interface to help the delegate to show the dialog. It will be injected to the delegate.
*/

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2022 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 android.content.Context;
import android.os.VibrationAttributes;
import android.provider.Settings;
/** Preference controller for am vibration intensity */
public class MediaVibrationIntensityPreferenceController
extends VibrationIntensityPreferenceController {
/** General configuration for alarm vibration intensity settings. */
public static final class MediaVibrationPreferenceConfig extends VibrationPreferenceConfig {
public MediaVibrationPreferenceConfig(Context context) {
super(context, Settings.System.MEDIA_VIBRATION_INTENSITY,
VibrationAttributes.USAGE_MEDIA);
}
}
public MediaVibrationIntensityPreferenceController(Context context,
String preferenceKey) {
super(context, preferenceKey, new MediaVibrationPreferenceConfig(context));
}
protected MediaVibrationIntensityPreferenceController(Context context, String preferenceKey,
int supportedIntensityLevels) {
super(context, preferenceKey, new MediaVibrationPreferenceConfig(context),
supportedIntensityLevels);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2022 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 android.content.Context;
import com.android.settings.accessibility.MediaVibrationIntensityPreferenceController.MediaVibrationPreferenceConfig;
/** Preference controller for alarm vibration with only a toggle for on/off states. */
public class MediaVibrationTogglePreferenceController extends VibrationTogglePreferenceController {
public MediaVibrationTogglePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey, new MediaVibrationPreferenceConfig(context));
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
}

View File

@@ -17,27 +17,42 @@
package com.android.settings.accessibility;
import android.content.Context;
import android.os.VibrationAttributes;
import android.provider.Settings;
import androidx.annotation.VisibleForTesting;
/** Preference controller for notification vibration intensity */
public class NotificationVibrationIntensityPreferenceController
extends VibrationIntensityPreferenceController {
@VisibleForTesting
static final String PREF_KEY = "notification_vibration_preference_screen";
/** General configuration for notification vibration intensity settings. */
public static final class NotificationVibrationPreferenceConfig
extends VibrationPreferenceConfig {
public NotificationVibrationIntensityPreferenceController(Context context) {
super(context, PREF_KEY, Settings.System.NOTIFICATION_VIBRATION_INTENSITY, "");
public NotificationVibrationPreferenceConfig(Context context) {
super(context, Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
VibrationAttributes.USAGE_NOTIFICATION);
}
@Override
public boolean isRestrictedByRingerModeSilent() {
// Notifications never vibrate when the phone is in silent mode.
return true;
}
}
public NotificationVibrationIntensityPreferenceController(Context context,
String preferenceKey) {
super(context, preferenceKey, new NotificationVibrationPreferenceConfig(context));
}
protected NotificationVibrationIntensityPreferenceController(Context context,
String preferenceKey, int supportedIntensityLevels) {
super(context, preferenceKey, new NotificationVibrationPreferenceConfig(context),
supportedIntensityLevels);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
protected int getDefaultIntensity() {
return mVibrator.getDefaultNotificationVibrationIntensity();
}
}

View File

@@ -1,62 +0,0 @@
/*
* 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.accessibility;
import android.app.settings.SettingsEnums;
import android.media.AudioAttributes;
import android.os.Vibrator;
import android.provider.Settings;
import com.android.settings.R;
/**
* Fragment for picking accessibility shortcut service
*/
public class NotificationVibrationPreferenceFragment extends VibrationPreferenceFragment {
@Override
public int getMetricsCategory() {
return SettingsEnums.ACCESSIBILITY_VIBRATION_NOTIFICATION;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.accessibility_notification_vibration_settings;
}
/**
* Get the setting string of the vibration intensity setting this preference is dealing with.
*/
@Override
protected String getVibrationIntensitySetting() {
return Settings.System.NOTIFICATION_VIBRATION_INTENSITY;
}
@Override
protected String getVibrationEnabledSetting() {
return "";
}
@Override
protected int getPreviewVibrationAudioAttributesUsage() {
return AudioAttributes.USAGE_NOTIFICATION;
}
@Override
protected int getDefaultVibrationIntensity() {
Vibrator vibrator = getContext().getSystemService(Vibrator.class);
return vibrator.getDefaultNotificationVibrationIntensity();
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2022 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 android.content.Context;
import com.android.settings.accessibility.NotificationVibrationIntensityPreferenceController.NotificationVibrationPreferenceConfig;
/** Preference controller for notification vibration with only a toggle for on/off states. */
public class NotificationVibrationTogglePreferenceController
extends VibrationTogglePreferenceController {
public NotificationVibrationTogglePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey, new NotificationVibrationPreferenceConfig(context));
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
}

View File

@@ -148,6 +148,8 @@ public final class PaletteListPreference extends Preference {
rootView.addView(textView);
}
updateFirstAndLastItemsBackground(context, rootView, paletteData.size());
}
private GradientDrawable createGradientDrawable(ViewGroup rootView, @ColorInt int color) {
@@ -164,6 +166,19 @@ public final class PaletteListPreference extends Preference {
return gradientDrawable;
}
private void updateFirstAndLastItemsBackground(Context context, ViewGroup rootView, int size) {
final int radius =
context.getResources().getDimensionPixelSize(
R.dimen.accessibility_illustration_view_radius);
final int lastIndex = size - 1;
final GradientDrawable firstItem =
(GradientDrawable) rootView.getChildAt(0).getBackground();
final GradientDrawable lastItem =
(GradientDrawable) rootView.getChildAt(lastIndex).getBackground();
firstItem.setCornerRadii(new float[]{radius, radius, radius, radius, 0, 0, 0, 0});
lastItem.setCornerRadii(new float[]{0, 0, 0, 0, radius, radius, radius, radius});
}
private List<Integer> getPaletteColors(Context context) {
final int[] paletteResources =
context.getResources().getIntArray(R.array.setting_palette_colors);

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2022 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 android.content.Context;
import androidx.annotation.NonNull;
import java.util.List;
/**
* Abstract data class for storing and fetching the configurations related to the preview of the
* text and reading options.
*/
abstract class PreviewSizeData<T extends Number> {
private final Context mContext;
private int mInitialIndex;
private T mDefaultValue;
private List<T> mValues;
PreviewSizeData(@NonNull Context context) {
mContext = context;
}
Context getContext() {
return mContext;
}
List<T> getValues() {
return mValues;
}
void setValues(List<T> values) {
mValues = values;
}
T getDefaultValue() {
return mDefaultValue;
}
void setDefaultValue(T defaultValue) {
mDefaultValue = defaultValue;
}
int getInitialIndex() {
return mInitialIndex;
}
void setInitialIndex(int initialIndex) {
mInitialIndex = initialIndex;
}
/**
* Persists the selected size.
*/
abstract void commit(int currentProgress);
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2022 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 android.content.Context;
import android.widget.SeekBar;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceScreen;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.widget.LabeledSeekBarPreference;
/**
* 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 {
private final PreviewSizeData<? extends Number> mSizeData;
private boolean mSeekByTouch;
private ProgressInteractionListener mInteractionListener;
private LabeledSeekBarPreference mSeekBarPreference;
private final SeekBar.OnSeekBarChangeListener mSeekBarChangeListener =
new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
mInteractionListener.notifyPreferenceChanged();
if (!mSeekByTouch && mInteractionListener != null) {
mInteractionListener.onProgressChanged();
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
mSeekByTouch = true;
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mSeekByTouch = false;
if (mInteractionListener != null) {
mInteractionListener.onEndTrackingTouch();
}
}
};
PreviewSizeSeekBarController(Context context, String preferenceKey,
@NonNull PreviewSizeData<? extends Number> sizeData) {
super(context, preferenceKey);
mSizeData = sizeData;
}
void setInteractionListener(ProgressInteractionListener interactionListener) {
mInteractionListener = interactionListener;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
final int dataSize = mSizeData.getValues().size();
final int initialIndex = mSizeData.getInitialIndex();
mSeekBarPreference = screen.findPreference(getPreferenceKey());
mSeekBarPreference.setMax(dataSize - 1);
mSeekBarPreference.setProgress(initialIndex);
mSeekBarPreference.setContinuousUpdates(true);
mSeekBarPreference.setOnSeekBarChangeListener(mSeekBarChangeListener);
}
@Override
public void resetState() {
final int defaultProgress = mSizeData.getValues().indexOf(mSizeData.getDefaultValue());
mSeekBarPreference.setProgress(defaultProgress);
}
/**
* Interface for callbacks when users interact with the seek bar.
*/
interface ProgressInteractionListener {
/**
* Called when the progress is changed.
*/
void notifyPreferenceChanged();
/**
* Called when the progress is changed without tracking touch.
*/
void onProgressChanged();
/**
* Called when the seek bar is end tracking.
*/
void onEndTrackingTouch();
}
}

View File

@@ -16,6 +16,9 @@
package com.android.settings.accessibility;
import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME;
import android.content.ComponentName;
import android.content.Context;
import android.database.ContentObserver;
import android.hardware.display.ColorDisplayManager;
@@ -30,14 +33,14 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settings.widget.PrimarySwitchPreference;
import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
/** PreferenceController that shows the Reduce Bright Colors summary */
public class ReduceBrightColorsPreferenceController extends TogglePreferenceController
public class ReduceBrightColorsPreferenceController
extends AccessibilityQuickSettingsPrimarySwitchPreferenceController
implements LifecycleObserver, OnStart, OnStop {
private ContentObserver mSettingsContentObserver;
private PrimarySwitchPreference mPreference;
@@ -67,6 +70,7 @@ public class ReduceBrightColorsPreferenceController extends TogglePreferenceCont
@Override
public boolean setChecked(boolean isChecked) {
super.setChecked(isChecked);
return mColorDisplayManager.setReduceBrightColorsActivated(isChecked);
}
@@ -105,8 +109,20 @@ public class ReduceBrightColorsPreferenceController extends TogglePreferenceCont
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED),
false, mSettingsContentObserver, UserHandle.USER_CURRENT);
}
@Override
public void onStop() {
mContext.getContentResolver().unregisterContentObserver(mSettingsContentObserver);
}
@Override
protected ComponentName getTileComponentName() {
return REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME;
}
@Override
CharSequence getTileTooltipContent() {
return mContext.getText(
R.string.accessibility_reduce_bright_colors_auto_added_qs_tooltip_content);
}
}

View File

@@ -0,0 +1,339 @@
/*
* Copyright (C) 2022 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.settingslib.widget.TwoTargetPreference.ICON_SIZE_MEDIUM;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityShortcutInfo;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
import android.text.TextUtils;
import androidx.core.content.ContextCompat;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.accessibility.AccessibilityUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* This class helps setup RestrictedPreference for accessibility.
*/
public class RestrictedPreferenceHelper {
// Index of the first preference in a preference category.
private static final int FIRST_PREFERENCE_IN_CATEGORY_INDEX = -1;
private final Context mContext;
private final DevicePolicyManager mDpm;
private final PackageManager mPm;
private final AppOpsManager mAppOps;
public RestrictedPreferenceHelper(Context context) {
mContext = context;
mDpm = context.getSystemService(DevicePolicyManager.class);
mPm = context.getPackageManager();
mAppOps = context.getSystemService(AppOpsManager.class);
}
/**
* Creates the list of {@link RestrictedPreference} with the installedServices arguments.
*
* @param installedServices The list of {@link AccessibilityServiceInfo}s of the
* installed accessibility services
* @return The list of {@link RestrictedPreference}
*/
public List<RestrictedPreference> createAccessibilityServicePreferenceList(
List<AccessibilityServiceInfo> installedServices) {
final Set<ComponentName> enabledServices =
AccessibilityUtils.getEnabledServicesFromSettings(mContext);
final List<String> permittedServices = mDpm.getPermittedAccessibilityServices(
UserHandle.myUserId());
final int installedServicesSize = installedServices.size();
final List<RestrictedPreference> preferenceList = new ArrayList<>(
installedServicesSize);
for (int i = 0; i < installedServicesSize; ++i) {
final AccessibilityServiceInfo info = installedServices.get(i);
final ResolveInfo resolveInfo = info.getResolveInfo();
final String packageName = resolveInfo.serviceInfo.packageName;
final ComponentName componentName = new ComponentName(packageName,
resolveInfo.serviceInfo.name);
final String key = componentName.flattenToString();
final CharSequence title = resolveInfo.loadLabel(mPm);
final boolean serviceEnabled = enabledServices.contains(componentName);
final CharSequence summary = AccessibilitySettings.getServiceSummary(
mContext, info, serviceEnabled);
final String fragment = getAccessibilityServiceFragmentTypeName(info);
Drawable icon = resolveInfo.loadIcon(mPm);
if (resolveInfo.getIconResource() == 0) {
icon = ContextCompat.getDrawable(mContext,
R.drawable.ic_accessibility_generic);
}
final RestrictedPreference preference = createRestrictedPreference(key, title,
summary, icon, fragment, packageName,
resolveInfo.serviceInfo.applicationInfo.uid);
setRestrictedPreferenceEnabled(preference, permittedServices, serviceEnabled);
final String prefKey = preference.getKey();
final int imageRes = info.getAnimatedImageRes();
final CharSequence intro = info.loadIntro(mPm);
final CharSequence description = AccessibilitySettings.getServiceDescription(
mContext, info, serviceEnabled);
final String htmlDescription = info.loadHtmlDescription(mPm);
final String settingsClassName = info.getSettingsActivityName();
final String tileServiceClassName = info.getTileServiceName();
putBasicExtras(preference, prefKey, title, intro, description, imageRes,
htmlDescription, componentName);
putServiceExtras(preference, resolveInfo, serviceEnabled);
putSettingsExtras(preference, packageName, settingsClassName);
putTileServiceExtras(preference, packageName, tileServiceClassName);
preferenceList.add(preference);
}
return preferenceList;
}
/**
* Creates the list of {@link RestrictedPreference} with the installedShortcuts arguments.
*
* @param installedShortcuts The list of {@link AccessibilityShortcutInfo}s of the
* installed accessibility shortcuts
* @return The list of {@link RestrictedPreference}
*/
public List<RestrictedPreference> createAccessibilityActivityPreferenceList(
List<AccessibilityShortcutInfo> installedShortcuts) {
final Set<ComponentName> enabledServices =
AccessibilityUtils.getEnabledServicesFromSettings(mContext);
final List<String> permittedServices = mDpm.getPermittedAccessibilityServices(
UserHandle.myUserId());
final int installedShortcutsSize = installedShortcuts.size();
final List<RestrictedPreference> preferenceList = new ArrayList<>(
installedShortcutsSize);
for (int i = 0; i < installedShortcutsSize; ++i) {
final AccessibilityShortcutInfo info = installedShortcuts.get(i);
final ActivityInfo activityInfo = info.getActivityInfo();
final ComponentName componentName = info.getComponentName();
final String key = componentName.flattenToString();
final CharSequence title = activityInfo.loadLabel(mPm);
final String summary = info.loadSummary(mPm);
final String fragment =
LaunchAccessibilityActivityPreferenceFragment.class.getName();
Drawable icon = activityInfo.loadIcon(mPm);
if (activityInfo.getIconResource() == 0) {
icon = ContextCompat.getDrawable(mContext, R.drawable.ic_accessibility_generic);
}
final RestrictedPreference preference = createRestrictedPreference(key, title,
summary, icon, fragment, componentName.getPackageName(),
activityInfo.applicationInfo.uid);
final boolean serviceEnabled = enabledServices.contains(componentName);
setRestrictedPreferenceEnabled(preference, permittedServices, serviceEnabled);
final String prefKey = preference.getKey();
final CharSequence intro = info.loadIntro(mPm);
final String description = info.loadDescription(mPm);
final int imageRes = info.getAnimatedImageRes();
final String htmlDescription = info.loadHtmlDescription(mPm);
final String settingsClassName = info.getSettingsActivityName();
final String tileServiceClassName = info.getTileServiceName();
putBasicExtras(preference, prefKey, title, intro, description, imageRes,
htmlDescription, componentName);
putSettingsExtras(preference, componentName.getPackageName(), settingsClassName);
putTileServiceExtras(preference, componentName.getPackageName(),
tileServiceClassName);
preferenceList.add(preference);
}
return preferenceList;
}
private String getAccessibilityServiceFragmentTypeName(AccessibilityServiceInfo info) {
final int type = AccessibilityUtil.getAccessibilityServiceFragmentType(info);
switch (type) {
case AccessibilityUtil.AccessibilityServiceFragmentType.VOLUME_SHORTCUT_TOGGLE:
return VolumeShortcutToggleAccessibilityServicePreferenceFragment.class.getName();
case AccessibilityUtil.AccessibilityServiceFragmentType.INVISIBLE_TOGGLE:
return InvisibleToggleAccessibilityServicePreferenceFragment.class.getName();
case AccessibilityUtil.AccessibilityServiceFragmentType.TOGGLE:
return ToggleAccessibilityServicePreferenceFragment.class.getName();
default:
throw new IllegalArgumentException(
"Unsupported accessibility fragment type " + type);
}
}
private RestrictedPreference createRestrictedPreference(String key, CharSequence title,
CharSequence summary, Drawable icon, String fragment, String packageName, int uid) {
final RestrictedPreference preference = new RestrictedPreference(mContext, packageName,
uid);
preference.setKey(key);
preference.setTitle(title);
preference.setSummary(summary);
preference.setIcon(Utils.getAdaptiveIcon(mContext, icon, Color.WHITE));
preference.setFragment(fragment);
preference.setIconSize(ICON_SIZE_MEDIUM);
preference.setPersistent(false); // Disable SharedPreferences.
preference.setOrder(FIRST_PREFERENCE_IN_CATEGORY_INDEX);
return preference;
}
private void setRestrictedPreferenceEnabled(RestrictedPreference preference,
final List<String> permittedServices, boolean serviceEnabled) {
// permittedServices null means all accessibility services are allowed.
boolean serviceAllowed = permittedServices == null || permittedServices.contains(
preference.getPackageName());
boolean appOpsAllowed;
if (serviceAllowed) {
try {
final int mode = mAppOps.noteOpNoThrow(
AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
preference.getUid(), preference.getPackageName());
final boolean ecmEnabled = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
appOpsAllowed = !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
serviceAllowed = appOpsAllowed;
} catch (Exception e) {
// Allow service in case if app ops is not available in testing.
appOpsAllowed = true;
}
} else {
appOpsAllowed = false;
}
if (serviceAllowed || serviceEnabled) {
preference.setEnabled(true);
} else {
// Disable accessibility service that are not permitted.
final RestrictedLockUtils.EnforcedAdmin admin =
RestrictedLockUtilsInternal.checkIfAccessibilityServiceDisallowed(
mContext, preference.getPackageName(), UserHandle.myUserId());
if (admin != null) {
preference.setDisabledByAdmin(admin);
} else if (!appOpsAllowed) {
preference.setDisabledByAppOps(true);
} else {
preference.setEnabled(false);
}
}
}
/** Puts the basic extras into {@link RestrictedPreference}'s getExtras(). */
private void putBasicExtras(RestrictedPreference preference, String prefKey,
CharSequence title, CharSequence intro, CharSequence summary, int imageRes,
String htmlDescription, ComponentName componentName) {
final Bundle extras = preference.getExtras();
extras.putString(AccessibilitySettings.EXTRA_PREFERENCE_KEY, prefKey);
extras.putCharSequence(AccessibilitySettings.EXTRA_TITLE, title);
extras.putCharSequence(AccessibilitySettings.EXTRA_INTRO, intro);
extras.putCharSequence(AccessibilitySettings.EXTRA_SUMMARY, summary);
extras.putParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME, componentName);
extras.putInt(AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES, imageRes);
extras.putString(AccessibilitySettings.EXTRA_HTML_DESCRIPTION, htmlDescription);
}
/**
* Puts the service extras into {@link RestrictedPreference}'s getExtras().
*
* <p><b>Note:</b> Called by {@link AccessibilityServiceInfo}.</p>
*
* @param preference The preference we are configuring.
* @param resolveInfo The service resolve info.
* @param serviceEnabled Whether the accessibility service is enabled.
*/
private void putServiceExtras(RestrictedPreference preference, ResolveInfo resolveInfo,
Boolean serviceEnabled) {
final Bundle extras = preference.getExtras();
extras.putParcelable(AccessibilitySettings.EXTRA_RESOLVE_INFO, resolveInfo);
extras.putBoolean(AccessibilitySettings.EXTRA_CHECKED, serviceEnabled);
}
/**
* Puts the settings extras into {@link RestrictedPreference}'s getExtras().
*
* <p><b>Note:</b> Called when settings UI is needed.</p>
*
* @param preference The preference we are configuring.
* @param packageName Package of accessibility feature.
* @param settingsClassName The component name of an activity that allows the user to modify
* the settings for this accessibility feature.
*/
private void putSettingsExtras(RestrictedPreference preference, String packageName,
String settingsClassName) {
final Bundle extras = preference.getExtras();
if (!TextUtils.isEmpty(settingsClassName)) {
extras.putString(AccessibilitySettings.EXTRA_SETTINGS_TITLE,
mContext.getText(R.string.accessibility_menu_item_settings).toString());
extras.putString(AccessibilitySettings.EXTRA_SETTINGS_COMPONENT_NAME,
new ComponentName(packageName, settingsClassName).flattenToString());
}
}
/**
* Puts the information about a particular application
* {@link android.service.quicksettings.TileService} into {@link RestrictedPreference}'s
* getExtras().
*
* <p><b>Note:</b> Called when a tooltip of
* {@link android.service.quicksettings.TileService} is needed.</p>
*
* @param preference The preference we are configuring.
* @param packageName Package of accessibility feature.
* @param tileServiceClassName The component name of tileService is associated with this
* accessibility feature.
*/
private void putTileServiceExtras(RestrictedPreference preference, String packageName,
String tileServiceClassName) {
final Bundle extras = preference.getExtras();
if (!TextUtils.isEmpty(tileServiceClassName)) {
extras.putString(AccessibilitySettings.EXTRA_TILE_SERVICE_COMPONENT_NAME,
new ComponentName(packageName, tileServiceClassName).flattenToString());
}
}
}

View File

@@ -17,28 +17,23 @@
package com.android.settings.accessibility;
import android.content.Context;
import android.provider.Settings;
import androidx.annotation.VisibleForTesting;
/** Preference controller for ringtone vibration intensity */
public class RingVibrationIntensityPreferenceController
extends VibrationIntensityPreferenceController {
@VisibleForTesting
static final String PREF_KEY = "ring_vibration_preference_screen";
public RingVibrationIntensityPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey, new RingVibrationPreferenceConfig(context));
}
public RingVibrationIntensityPreferenceController(Context context) {
super(context, PREF_KEY, Settings.System.RING_VIBRATION_INTENSITY,
Settings.System.VIBRATE_WHEN_RINGING, /* supportRampingRinger= */ true);
protected RingVibrationIntensityPreferenceController(Context context, String preferenceKey,
int supportedIntensityLevels) {
super(context, preferenceKey, new RingVibrationPreferenceConfig(context),
supportedIntensityLevels);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
protected int getDefaultIntensity() {
return mVibrator.getDefaultRingVibrationIntensity();
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2022 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.content.Context;
import android.media.AudioManager;
import android.os.VibrationAttributes;
import android.os.Vibrator;
import android.provider.Settings;
/** General configuration for ringtone vibration intensity settings. */
public class RingVibrationPreferenceConfig extends VibrationPreferenceConfig {
private final AudioManager mAudioManager;
public RingVibrationPreferenceConfig(Context context) {
super(context, Settings.System.RING_VIBRATION_INTENSITY,
VibrationAttributes.USAGE_RINGTONE);
mAudioManager = context.getSystemService(AudioManager.class);
}
@Override
public boolean isRestrictedByRingerModeSilent() {
// Incoming calls never vibrate when the phone is in silent mode.
return true;
}
@Override
public boolean updateIntensity(int intensity) {
final boolean success = super.updateIntensity(intensity);
// VIBRATE_WHEN_RINGING is deprecated but should still reflect the intensity setting.
// Ramping ringer is independent of the ring intensity and should not be affected.
Settings.System.putInt(mContentResolver, Settings.System.VIBRATE_WHEN_RINGING,
(intensity == Vibrator.VIBRATION_INTENSITY_OFF) ? OFF : ON);
return success;
}
}

View File

@@ -1,66 +0,0 @@
/*
* 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.accessibility;
import android.app.settings.SettingsEnums;
import android.media.AudioAttributes;
import android.os.Vibrator;
import android.provider.Settings;
import com.android.settings.R;
/**
* Fragment for picking accessibility shortcut service
*/
public class RingVibrationPreferenceFragment extends VibrationPreferenceFragment {
@Override
public int getMetricsCategory() {
return SettingsEnums.ACCESSIBILITY_VIBRATION_RING;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.accessibility_ring_vibration_settings;
}
/**
* Get the setting string of the vibration intensity setting this preference is dealing with.
*/
@Override
protected String getVibrationIntensitySetting() {
return Settings.System.RING_VIBRATION_INTENSITY;
}
@Override
protected String getVibrationEnabledSetting() {
if (AccessibilitySettings.isRampingRingerEnabled(getContext())) {
return Settings.Global.APPLY_RAMPING_RINGER;
} else {
return Settings.System.VIBRATE_WHEN_RINGING;
}
}
@Override
protected int getPreviewVibrationAudioAttributesUsage() {
return AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
}
@Override
protected int getDefaultVibrationIntensity() {
Vibrator vibrator = getContext().getSystemService(Vibrator.class);
return vibrator.getDefaultRingVibrationIntensity();
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2022 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 android.content.Context;
/** Preference controller for ringtone vibration with only a toggle for on/off states. */
public class RingVibrationTogglePreferenceController extends VibrationTogglePreferenceController {
public RingVibrationTogglePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey, new RingVibrationPreferenceConfig(context));
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
}

View File

@@ -1,56 +0,0 @@
/*
* 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.accessibility;
import static com.android.settings.accessibility.AccessibilityScreenSizeForSetupWizardActivity.VISION_FRAGMENT_NO;
import static com.android.settings.core.SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE;
import android.content.Context;
import android.content.Intent;
import androidx.preference.Preference;
import com.android.settings.accessibility.AccessibilityScreenSizeForSetupWizardActivity.FragmentType;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.transition.SettingsTransitionHelper.TransitionType;
/** PreferenceController for displaying screen size page. */
public class ScreenSizePreferenceController extends BasePreferenceController {
public ScreenSizePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (!mPreferenceKey.equals(preference.getKey())) {
return false;
}
final Intent intent = new Intent(mContext,
AccessibilityScreenSizeForSetupWizardActivity.class);
intent.putExtra(VISION_FRAGMENT_NO, FragmentType.SCREEN_SIZE);
intent.putExtra(EXTRA_PAGE_TRANSITION_TYPE, TransitionType.TRANSITION_FADE);
mContext.startActivity(intent);
return true;
}
}

View File

@@ -1,55 +0,0 @@
/*
* Copyright (C) 2013 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 android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
import java.util.ArrayList;
import java.util.List;
abstract class SettingsContentObserver extends ContentObserver {
private final List<String> mKeysToObserve = new ArrayList<>(2);
public SettingsContentObserver(Handler handler) {
super(handler);
mKeysToObserve.add(Settings.Secure.ACCESSIBILITY_ENABLED);
mKeysToObserve.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
}
public SettingsContentObserver(Handler handler, List<String> keysToObserve) {
this(handler);
mKeysToObserve.addAll(keysToObserve);
}
public void register(ContentResolver contentResolver) {
for (int i = 0; i < mKeysToObserve.size(); i++) {
contentResolver.registerContentObserver(
Settings.Secure.getUriFor(mKeysToObserve.get(i)), false, this);
}
}
public void unregister(ContentResolver contentResolver) {
contentResolver.unregisterContentObserver(this);
}
@Override
public abstract void onChange(boolean selfChange, Uri uri);
}

View File

@@ -64,6 +64,11 @@ public class ShortcutPreference extends Preference {
setLayoutResource(R.layout.accessibility_shortcut_secondary_action);
setWidgetLayoutResource(R.layout.preference_widget_primary_switch);
setIconSpaceReserved(false);
// Treat onSettingsClicked as this preference's click.
setOnPreferenceClickListener(preference -> {
callOnSettingsClicked();
return true;
});
}
@Override

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2022 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.TextReadingPreferenceFragment.EXTRA_LAUNCHED_FROM;
import android.content.Context;
import android.os.Bundle;
import androidx.preference.Preference;
import com.android.settings.accessibility.TextReadingPreferenceFragment.EntryPoint;
import com.android.settings.core.BasePreferenceController;
/**
* The base controller for the fragment{@link TextReadingPreferenceFragment}.
*/
public class TextReadingFragmentBaseController extends BasePreferenceController {
@EntryPoint
private int mEntryPoint;
private TextReadingFragmentBaseController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
TextReadingFragmentBaseController(Context context, String preferenceKey,
@EntryPoint int entryPoint) {
this(context, preferenceKey);
mEntryPoint = entryPoint;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (getPreferenceKey().equals(preference.getKey())) {
final Bundle extras = preference.getExtras();
extras.putInt(EXTRA_LAUNCHED_FROM, mEntryPoint);
}
return super.handlePreferenceTreeClick(preference);
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2022 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.TextReadingPreferenceFragment.EntryPoint.ACCESSIBILITY_SETTINGS;
import android.content.Context;
/**
* The controller for the {@link TextReadingPreferenceFragment} from the accessibility settings.
*/
public class TextReadingFragmentForA11ySettingsController extends
TextReadingFragmentBaseController {
public TextReadingFragmentForA11ySettingsController(Context context, String preferenceKey) {
// Pass the fixed entry point to the parent controller for logging.
super(context, preferenceKey, ACCESSIBILITY_SETTINGS);
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2022 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.TextReadingPreferenceFragment.EntryPoint.DISPLAY_SETTINGS;
import android.content.Context;
/**
* The controller for the {@link TextReadingPreferenceFragment} from the display settings.
*/
public class TextReadingFragmentForDisplaySettingsController extends
TextReadingFragmentBaseController {
public TextReadingFragmentForDisplaySettingsController(Context context, String preferenceKey) {
// Pass the fixed entry point to the parent controller for logging.
super(context, preferenceKey, DISPLAY_SETTINGS);
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2022 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.TextReadingPreferenceFragment.EntryPoint.SUW_VISION_SETTINGS;
import android.content.Context;
/**
* The controller for the {@link TextReadingPreferenceFragment} during the SetupWizard.
*/
public class TextReadingFragmentForSuwController extends TextReadingFragmentBaseController {
public TextReadingFragmentForSuwController(Context context, String preferenceKey) {
// Pass the fixed entry point to the parent controller for logging.
super(context, preferenceKey, SUW_VISION_SETTINGS);
}
}

View File

@@ -0,0 +1,250 @@
/*
* Copyright (C) 2022 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.TextReadingResetController.ResetStateListener;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.IntDef;
import androidx.appcompat.app.AlertDialog;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.SearchIndexable;
import com.google.common.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Accessibility settings for adjusting the system features which are related to the reading. For
* example, bold text, high contrast text, display size, font size and so on.
*/
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class TextReadingPreferenceFragment extends DashboardFragment {
public static final String EXTRA_LAUNCHED_FROM = "launched_from";
private static final String TAG = "TextReadingPreferenceFragment";
private static final String CATEGORY_FOR_ANYTHING_ELSE =
"com.android.settings.suggested.category.DISPLAY_SETTINGS";
static final String FONT_SIZE_KEY = "font_size";
static final String DISPLAY_SIZE_KEY = "display_size";
static final String BOLD_TEXT_KEY = "toggle_force_bold_text";
static final String HIGH_TEXT_CONTRAST_KEY = "toggle_high_text_contrast_preference";
static final String RESET_KEY = "reset";
private static final String PREVIEW_KEY = "preview";
private static final String NEED_RESET_SETTINGS = "need_reset_settings";
private FontWeightAdjustmentPreferenceController mFontWeightAdjustmentController;
private int mEntryPoint = EntryPoint.UNKNOWN_ENTRY;
/**
* The entry point which launches the {@link TextReadingPreferenceFragment}.
*
* <p>This should only be used for logging.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({
EntryPoint.UNKNOWN_ENTRY,
EntryPoint.SUW_VISION_SETTINGS,
EntryPoint.SUW_ANYTHING_ELSE,
EntryPoint.DISPLAY_SETTINGS,
EntryPoint.ACCESSIBILITY_SETTINGS,
})
@interface EntryPoint {
int UNKNOWN_ENTRY = 0;
int SUW_VISION_SETTINGS = 1;
int SUW_ANYTHING_ELSE = 2;
int DISPLAY_SETTINGS = 3;
int ACCESSIBILITY_SETTINGS = 4;
}
@VisibleForTesting
List<ResetStateListener> mResetStateListeners;
@VisibleForTesting
boolean mNeedResetSettings;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mNeedResetSettings = false;
mResetStateListeners = getResetStateListeners();
if (icicle != null && icicle.getBoolean(NEED_RESET_SETTINGS)) {
mResetStateListeners.forEach(ResetStateListener::resetState);
}
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.accessibility_text_reading_options;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.ACCESSIBILITY_TEXT_READING_OPTIONS;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
updateEntryPoint();
final List<AbstractPreferenceController> controllers = new ArrayList<>();
final FontSizeData fontSizeData = new FontSizeData(context);
final DisplaySizeData displaySizeData = createDisplaySizeData(context);
final TextReadingPreviewController previewController = new TextReadingPreviewController(
context, PREVIEW_KEY, fontSizeData, displaySizeData);
previewController.setEntryPoint(mEntryPoint);
controllers.add(previewController);
final PreviewSizeSeekBarController fontSizeController = new PreviewSizeSeekBarController(
context, FONT_SIZE_KEY, fontSizeData);
fontSizeController.setInteractionListener(previewController);
controllers.add(fontSizeController);
final PreviewSizeSeekBarController displaySizeController = new PreviewSizeSeekBarController(
context, DISPLAY_SIZE_KEY, displaySizeData);
displaySizeController.setInteractionListener(previewController);
controllers.add(displaySizeController);
mFontWeightAdjustmentController =
new FontWeightAdjustmentPreferenceController(context, BOLD_TEXT_KEY);
mFontWeightAdjustmentController.setEntryPoint(mEntryPoint);
controllers.add(mFontWeightAdjustmentController);
final HighTextContrastPreferenceController highTextContrastController =
new HighTextContrastPreferenceController(context, HIGH_TEXT_CONTRAST_KEY);
highTextContrastController.setEntryPoint(mEntryPoint);
controllers.add(highTextContrastController);
final TextReadingResetController resetController =
new TextReadingResetController(context, RESET_KEY,
v -> showDialog(DialogEnums.DIALOG_RESET_SETTINGS));
resetController.setEntryPoint(mEntryPoint);
controllers.add(resetController);
return controllers;
}
@Override
public Dialog onCreateDialog(int dialogId) {
if (dialogId == DialogEnums.DIALOG_RESET_SETTINGS) {
return new AlertDialog.Builder(getPrefContext())
.setTitle(R.string.accessibility_text_reading_confirm_dialog_title)
.setMessage(R.string.accessibility_text_reading_confirm_dialog_message)
.setPositiveButton(
R.string.accessibility_text_reading_confirm_dialog_reset_button,
this::onPositiveButtonClicked)
.setNegativeButton(R.string.cancel, /* listener= */ null)
.create();
}
throw new IllegalArgumentException("Unsupported dialogId " + dialogId);
}
@Override
public int getDialogMetricsCategory(int dialogId) {
if (dialogId == DialogEnums.DIALOG_RESET_SETTINGS) {
return SettingsEnums.DIALOG_RESET_SETTINGS;
}
return super.getDialogMetricsCategory(dialogId);
}
@Override
public void onSaveInstanceState(Bundle outState) {
if (mNeedResetSettings) {
outState.putBoolean(NEED_RESET_SETTINGS, true);
}
}
@VisibleForTesting
DisplaySizeData createDisplaySizeData(Context context) {
return new DisplaySizeData(context);
}
private void updateEntryPoint() {
final Bundle bundle = getArguments();
if (bundle != null && bundle.containsKey(EXTRA_LAUNCHED_FROM)) {
mEntryPoint = bundle.getInt(EXTRA_LAUNCHED_FROM, EntryPoint.UNKNOWN_ENTRY);
return;
}
final Intent intent = getIntent();
if (intent == null) {
mEntryPoint = EntryPoint.UNKNOWN_ENTRY;
return;
}
final Set<String> categories = intent.getCategories();
mEntryPoint = categories != null && categories.contains(CATEGORY_FOR_ANYTHING_ELSE)
? EntryPoint.SUW_ANYTHING_ELSE : EntryPoint.UNKNOWN_ENTRY;
}
private void onPositiveButtonClicked(DialogInterface dialog, int which) {
// To avoid showing the dialog again, probably the onDetach() of SettingsDialogFragment
// was interrupted by unexpectedly recreating the activity.
removeDialog(DialogEnums.DIALOG_RESET_SETTINGS);
if (mFontWeightAdjustmentController.isChecked()) {
// TODO(b/228956791): Consider replacing or removing it once the root cause is
// clarified and the better method is available.
// Probably has the race condition issue between "Bold text" and the other features
// including "Display Size", “Font Size” if they would be enabled at the same time,
// so our workaround is that the “Bold text” would be reset first and then do the
// remaining to avoid flickering problem.
mNeedResetSettings = true;
mFontWeightAdjustmentController.resetState();
} else {
mResetStateListeners.forEach(ResetStateListener::resetState);
}
Toast.makeText(getPrefContext(), R.string.accessibility_text_reading_reset_message,
Toast.LENGTH_SHORT).show();
}
private List<ResetStateListener> getResetStateListeners() {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
getPreferenceControllers().forEach(controllers::addAll);
return controllers.stream().filter(c -> c instanceof ResetStateListener).map(
c -> (ResetStateListener) c).collect(Collectors.toList());
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.accessibility_text_reading_options);
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2022 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 android.app.settings.SettingsEnums;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.android.settingslib.Utils;
import com.android.settingslib.widget.LayoutPreference;
import com.google.android.setupdesign.GlifPreferenceLayout;
import com.google.android.setupdesign.util.LayoutStyler;
/**
* A {@link androidx.preference.PreferenceFragmentCompat} that displays the settings page related
* to the text and reading option in the SetupWizard.
*/
public class TextReadingPreferenceFragmentForSetupWizard extends TextReadingPreferenceFragment {
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final GlifPreferenceLayout layout = (GlifPreferenceLayout) view;
final String title = getContext().getString(
R.string.accessibility_text_reading_options_title);
final Drawable icon = getContext().getDrawable(R.drawable.ic_accessibility_visibility);
icon.setTintList(Utils.getColorAttr(getContext(), android.R.attr.colorPrimary));
AccessibilitySetupWizardUtils.updateGlifPreferenceLayout(getContext(), layout, title,
/* description= */ null, icon);
updateResetButtonPadding();
}
@Override
public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
final GlifPreferenceLayout layout = (GlifPreferenceLayout) parent;
return layout.onCreateRecyclerView(inflater, parent, savedInstanceState);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.SUW_ACCESSIBILITY_TEXT_READING_OPTIONS;
}
@Override
public int getHelpResource() {
// Hides help center in action bar and footer bar in SuW
return 0;
}
/**
* Updates the padding of the reset button to meet for SetupWizard style.
*/
private void updateResetButtonPadding() {
final LayoutPreference resetPreference = (LayoutPreference) findPreference(RESET_KEY);
final ViewGroup parentView =
(ViewGroup) resetPreference.findViewById(R.id.reset_button).getParent();
LayoutStyler.applyPartnerCustomizationLayoutPaddingStyle(parentView);
}
}

View File

@@ -0,0 +1,231 @@
/*
* Copyright (C) 2022 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 android.content.Context;
import android.content.res.Configuration;
import android.os.SystemClock;
import android.util.Log;
import android.view.Choreographer;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.accessibility.TextReadingPreferenceFragment.EntryPoint;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.instrumentation.SettingsStatsLog;
import com.android.settings.display.PreviewPagerAdapter;
import com.android.settings.widget.LabeledSeekBarPreference;
import java.util.Objects;
/**
* A {@link BasePreferenceController} for controlling the preview pager of the text and reading
* options.
*/
class TextReadingPreviewController extends BasePreferenceController implements
PreviewSizeSeekBarController.ProgressInteractionListener {
private static final String TAG = "TextReadingPreviewCtrl";
static final int[] PREVIEW_SAMPLE_RES_IDS = new int[]{
R.layout.accessibility_text_reading_preview_app_grid,
R.layout.screen_zoom_preview_1,
R.layout.accessibility_text_reading_preview_mail_content};
private static final String PREVIEW_KEY = "preview";
private static final String FONT_SIZE_KEY = "font_size";
private static final String DISPLAY_SIZE_KEY = "display_size";
private static final long MIN_COMMIT_INTERVAL_MS = 800;
private static final long CHANGE_BY_SEEKBAR_DELAY_MS = 100;
private static final long CHANGE_BY_BUTTON_DELAY_MS = 300;
private final FontSizeData mFontSizeData;
private final DisplaySizeData mDisplaySizeData;
private int mLastFontProgress;
private int mLastDisplayProgress;
private long mLastCommitTime;
private TextReadingPreviewPreference mPreviewPreference;
private LabeledSeekBarPreference mFontSizePreference;
private LabeledSeekBarPreference mDisplaySizePreference;
@EntryPoint
private int mEntryPoint;
private final Choreographer.FrameCallback mCommit = f -> {
tryCommitFontSizeConfig();
tryCommitDisplaySizeConfig();
mLastCommitTime = SystemClock.elapsedRealtime();
};
TextReadingPreviewController(Context context, String preferenceKey,
@NonNull FontSizeData fontSizeData, @NonNull DisplaySizeData displaySizeData) {
super(context, preferenceKey);
mFontSizeData = fontSizeData;
mDisplaySizeData = displaySizeData;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreviewPreference = screen.findPreference(PREVIEW_KEY);
mFontSizePreference = screen.findPreference(FONT_SIZE_KEY);
mDisplaySizePreference = screen.findPreference(DISPLAY_SIZE_KEY);
Objects.requireNonNull(mFontSizePreference,
/* message= */ "Font size preference is null, the preview controller "
+ "couldn't get the info");
Objects.requireNonNull(mDisplaySizePreference,
/* message= */ "Display size preference is null, the preview controller"
+ " couldn't get the info");
mLastFontProgress = mFontSizeData.getInitialIndex();
mLastDisplayProgress = mDisplaySizeData.getInitialIndex();
final Configuration origConfig = mContext.getResources().getConfiguration();
final boolean isLayoutRtl =
origConfig.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
final PreviewPagerAdapter pagerAdapter = new PreviewPagerAdapter(mContext, isLayoutRtl,
PREVIEW_SAMPLE_RES_IDS, createConfig(origConfig));
mPreviewPreference.setPreviewAdapter(pagerAdapter);
pagerAdapter.setPreviewLayer(/* newLayerIndex= */ 0,
/* currentLayerIndex= */ 0,
/* currentFrameIndex= */ 0, /* animate= */ false);
}
@Override
public void notifyPreferenceChanged() {
final int displayDataSize = mDisplaySizeData.getValues().size();
final int fontSizeProgress = mFontSizePreference.getProgress();
final int displaySizeProgress = mDisplaySizePreference.getProgress();
// To be consistent with the
// {@link PreviewPagerAdapter#setPreviewLayer(int, int, int, boolean)} behavior,
// here also needs the same design. In addition, please also refer to
// the {@link #createConfig(Configuration)}.
final int pagerIndex = fontSizeProgress * displayDataSize + displaySizeProgress;
mPreviewPreference.notifyPreviewPagerChanged(pagerIndex);
}
@Override
public void onProgressChanged() {
postCommitDelayed(CHANGE_BY_BUTTON_DELAY_MS);
}
@Override
public void onEndTrackingTouch() {
postCommitDelayed(CHANGE_BY_SEEKBAR_DELAY_MS);
}
/**
* The entry point is used for logging.
*
* @param entryPoint from which settings page
*/
void setEntryPoint(@EntryPoint int entryPoint) {
mEntryPoint = entryPoint;
}
/**
* Avoids the flicker when switching to the previous or next level.
*
* <p><br>[Flickering problem steps] commit()-> snapshot in framework(old screenshot) ->
* app update the preview -> snapshot(old screen) fade out</p>
*
* <p><br>To prevent flickering problem, we make sure that we update the local preview
* first and then we do the commit later. </p>
*
* <p><br><b>Note:</b> It doesn't matter that we use
* Choreographer or main thread handler since the delay time is longer
* than 1 frame. Use Choreographer to let developer understand it's a
* window update.</p>
*
* @param commitDelayMs the interval time after a action.
*/
void postCommitDelayed(long commitDelayMs) {
if (SystemClock.elapsedRealtime() - mLastCommitTime < MIN_COMMIT_INTERVAL_MS) {
commitDelayMs += MIN_COMMIT_INTERVAL_MS;
}
final Choreographer choreographer = Choreographer.getInstance();
choreographer.removeFrameCallback(mCommit);
choreographer.postFrameCallbackDelayed(mCommit, commitDelayMs);
}
private void tryCommitFontSizeConfig() {
final int fontProgress = mFontSizePreference.getProgress();
if (fontProgress != mLastFontProgress) {
mFontSizeData.commit(fontProgress);
mLastFontProgress = fontProgress;
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Font size: " + fontProgress);
}
SettingsStatsLog.write(
SettingsStatsLog.ACCESSIBILITY_TEXT_READING_OPTIONS_CHANGED,
AccessibilityStatsLogUtils.convertToItemKeyName(mFontSizePreference.getKey()),
fontProgress,
AccessibilityStatsLogUtils.convertToEntryPoint(mEntryPoint));
}
}
private void tryCommitDisplaySizeConfig() {
final int displayProgress = mDisplaySizePreference.getProgress();
if (displayProgress != mLastDisplayProgress) {
mDisplaySizeData.commit(displayProgress);
mLastDisplayProgress = displayProgress;
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Display size: " + displayProgress);
}
SettingsStatsLog.write(
SettingsStatsLog.ACCESSIBILITY_TEXT_READING_OPTIONS_CHANGED,
AccessibilityStatsLogUtils.convertToItemKeyName(
mDisplaySizePreference.getKey()),
displayProgress,
AccessibilityStatsLogUtils.convertToEntryPoint(mEntryPoint));
}
}
private Configuration[] createConfig(Configuration origConfig) {
final int fontDataSize = mFontSizeData.getValues().size();
final int displayDataSize = mDisplaySizeData.getValues().size();
final int totalNum = fontDataSize * displayDataSize;
final Configuration[] configurations = new Configuration[totalNum];
for (int i = 0; i < fontDataSize; ++i) {
for (int j = 0; j < displayDataSize; ++j) {
final Configuration config = new Configuration(origConfig);
config.fontScale = mFontSizeData.getValues().get(i);
config.densityDpi = mDisplaySizeData.getValues().get(j);
configurations[i * displayDataSize + j] = config;
}
}
return configurations;
}
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright (C) 2022 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 android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import androidx.viewpager.widget.ViewPager;
import com.android.internal.util.Preconditions;
import com.android.settings.R;
import com.android.settings.display.PreviewPagerAdapter;
import com.android.settings.widget.DotsPageIndicator;
/**
* A {@link Preference} that could show the preview related to the text and reading options.
*/
public class TextReadingPreviewPreference extends Preference {
private int mCurrentItem;
private int mLastLayerIndex;
private PreviewPagerAdapter mPreviewAdapter;
TextReadingPreviewPreference(Context context) {
super(context);
init();
}
public TextReadingPreviewPreference(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
TextReadingPreviewPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
TextReadingPreviewPreference(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
final ViewPager viewPager = (ViewPager) holder.findViewById(R.id.preview_pager);
final DotsPageIndicator pageIndicator =
(DotsPageIndicator) holder.findViewById(R.id.page_indicator);
updateAdapterIfNeeded(viewPager, pageIndicator, mPreviewAdapter);
updatePagerAndIndicator(viewPager, pageIndicator);
}
void setPreviewAdapter(PreviewPagerAdapter previewAdapter) {
if (previewAdapter != mPreviewAdapter) {
mPreviewAdapter = previewAdapter;
notifyChanged();
}
}
void setCurrentItem(int currentItem) {
Preconditions.checkNotNull(mPreviewAdapter,
"Preview adapter is null, you should init the preview adapter first");
if (currentItem != mCurrentItem) {
mCurrentItem = currentItem;
notifyChanged();
}
}
int getCurrentItem() {
return mCurrentItem;
}
private void updateAdapterIfNeeded(ViewPager viewPager, DotsPageIndicator pageIndicator,
PreviewPagerAdapter previewAdapter) {
if (viewPager.getAdapter() == previewAdapter) {
return;
}
viewPager.setAdapter(previewAdapter);
if (previewAdapter != null) {
pageIndicator.setViewPager(viewPager);
} else {
mCurrentItem = 0;
}
}
private void updatePagerAndIndicator(ViewPager viewPager, DotsPageIndicator pageIndicator) {
if (viewPager.getAdapter() == null) {
return;
}
if (viewPager.getCurrentItem() != mCurrentItem) {
viewPager.setCurrentItem(mCurrentItem);
}
pageIndicator.setVisibility(
viewPager.getAdapter().getCount() > 1 ? View.VISIBLE : View.GONE);
}
private void init() {
setLayoutResource(R.layout.accessibility_text_reading_preview);
}
void notifyPreviewPagerChanged(int pagerIndex) {
Preconditions.checkNotNull(mPreviewAdapter,
"Preview adapter is null, you should init the preview adapter first");
if (pagerIndex != mLastLayerIndex) {
mPreviewAdapter.setPreviewLayer(pagerIndex, mLastLayerIndex, getCurrentItem(),
/* animate= */ false);
}
mLastLayerIndex = pagerIndex;
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2022 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 android.content.Context;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.accessibility.TextReadingPreferenceFragment.EntryPoint;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.instrumentation.SettingsStatsLog;
import com.android.settingslib.widget.LayoutPreference;
/**
* The controller of the reset button in the text and reading options page.
*/
class TextReadingResetController extends BasePreferenceController {
private final View.OnClickListener mOnResetClickListener;
@EntryPoint
private int mEntryPoint;
TextReadingResetController(Context context, String preferenceKey,
@Nullable View.OnClickListener listener) {
super(context, preferenceKey);
mOnResetClickListener = listener;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
final LayoutPreference layoutPreference = screen.findPreference(getPreferenceKey());
final View view = layoutPreference.findViewById(R.id.reset_button);
view.setOnClickListener(v -> {
if (mOnResetClickListener != null) {
mOnResetClickListener.onClick(v);
SettingsStatsLog.write(
SettingsStatsLog.ACCESSIBILITY_TEXT_READING_OPTIONS_CHANGED,
AccessibilityStatsLogUtils.convertToItemKeyName(getPreferenceKey()),
/* reset */ -1,
AccessibilityStatsLogUtils.convertToEntryPoint(mEntryPoint));
}
});
}
/**
* The entry point is used for logging.
*
* @param entryPoint from which settings page
*/
void setEntryPoint(@EntryPoint int entryPoint) {
mEntryPoint = entryPoint;
}
/**
* Interface for resetting to default state.
*/
interface ResetStateListener {
/**
* Called when the reset button was clicked.
*/
void resetState();
}
}

View File

@@ -35,7 +35,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
@@ -47,6 +47,7 @@ import android.widget.Switch;
import androidx.annotation.Nullable;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.accessibility.AccessibilityUtils;
@@ -59,20 +60,16 @@ public class ToggleAccessibilityServicePreferenceFragment extends
ToggleFeaturePreferenceFragment {
private static final String TAG = "ToggleAccessibilityServicePreferenceFragment";
private static final String KEY_HAS_LOGGED = "has_logged";
private AtomicBoolean mIsDialogShown = new AtomicBoolean(/* initialValue= */ false);
private static final String EMPTY_STRING = "";
private final SettingsContentObserver mSettingsContentObserver =
new SettingsContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange, Uri uri) {
updateSwitchBarToggleSwitch();
}
};
private Dialog mDialog;
private Dialog mWarningDialog;
private ComponentName mTileComponentName;
private BroadcastReceiver mPackageRemovedReceiver;
private boolean mDisabledStateLogged = false;
private long mStartTimeMillsForLogging = 0;
@Override
public int getMetricsCategory() {
@@ -97,6 +94,18 @@ public class ToggleAccessibilityServicePreferenceFragment extends
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
if (savedInstanceState.containsKey(KEY_HAS_LOGGED)) {
mDisabledStateLogged = savedInstanceState.getBoolean(KEY_HAS_LOGGED);
}
}
}
@Override
protected void registerKeysToObserverCallback(
AccessibilitySettingsContentObserver contentObserver) {
super.registerKeysToObserverCallback(contentObserver);
contentObserver.registerObserverCallback(key -> updateSwitchBarToggleSwitch());
}
@Override
@@ -114,13 +123,28 @@ public class ToggleAccessibilityServicePreferenceFragment extends
public void onResume() {
super.onResume();
updateSwitchBarToggleSwitch();
mSettingsContentObserver.register(getContentResolver());
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onSaveInstanceState(Bundle outState) {
if (mStartTimeMillsForLogging > 0) {
outState.putBoolean(KEY_HAS_LOGGED, mDisabledStateLogged);
}
super.onSaveInstanceState(outState);
}
@Override
public void onPreferenceToggled(String preferenceKey, boolean enabled) {
ComponentName toggledService = ComponentName.unflattenFromString(preferenceKey);
logAccessibilityServiceEnabled(toggledService, enabled);
if (!enabled) {
logDisabledState(toggledService.getPackageName());
}
AccessibilityUtils.setAccessibilityServiceState(getPrefContext(), toggledService, enabled);
}
@@ -146,55 +170,46 @@ public class ToggleAccessibilityServicePreferenceFragment extends
@Override
public Dialog onCreateDialog(int dialogId) {
final AccessibilityServiceInfo info = getAccessibilityServiceInfo();
switch (dialogId) {
case DialogEnums.ENABLE_WARNING_FROM_TOGGLE: {
final AccessibilityServiceInfo info = getAccessibilityServiceInfo();
case DialogEnums.ENABLE_WARNING_FROM_TOGGLE:
if (info == null) {
return null;
}
mDialog = AccessibilityServiceWarning
mWarningDialog = AccessibilityServiceWarning
.createCapabilitiesDialog(getPrefContext(), info,
this::onDialogButtonFromEnableToggleClicked,
this::onDialogButtonFromUninstallClicked);
break;
}
case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE: {
final AccessibilityServiceInfo info = getAccessibilityServiceInfo();
return mWarningDialog;
case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE:
if (info == null) {
return null;
}
mDialog = AccessibilityServiceWarning
mWarningDialog = AccessibilityServiceWarning
.createCapabilitiesDialog(getPrefContext(), info,
this::onDialogButtonFromShortcutToggleClicked,
this::onDialogButtonFromUninstallClicked);
break;
}
case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT: {
final AccessibilityServiceInfo info = getAccessibilityServiceInfo();
return mWarningDialog;
case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT:
if (info == null) {
return null;
}
mDialog = AccessibilityServiceWarning
mWarningDialog = AccessibilityServiceWarning
.createCapabilitiesDialog(getPrefContext(), info,
this::onDialogButtonFromShortcutClicked,
this::onDialogButtonFromUninstallClicked);
break;
}
case DialogEnums.DISABLE_WARNING_FROM_TOGGLE: {
final AccessibilityServiceInfo info = getAccessibilityServiceInfo();
return mWarningDialog;
case DialogEnums.DISABLE_WARNING_FROM_TOGGLE:
if (info == null) {
return null;
}
mDialog = AccessibilityServiceWarning
mWarningDialog = AccessibilityServiceWarning
.createDisableDialog(getPrefContext(), info,
this::onDialogButtonFromDisableToggleClicked);
break;
}
default: {
mDialog = super.onCreateDialog(dialogId);
}
return mWarningDialog;
default:
return super.onCreateDialog(dialogId);
}
return mDialog;
}
@Override
@@ -219,6 +234,29 @@ public class ToggleAccessibilityServicePreferenceFragment extends
mComponentName);
}
@Override
ComponentName getTileComponentName() {
return mTileComponentName;
}
@Override
CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type) {
final ComponentName componentName = getTileComponentName();
if (componentName == null) {
return null;
}
final CharSequence tileName = loadTileLabel(getPrefContext(), componentName);
if (tileName == null) {
return null;
}
final int titleResId = type == QuickSettingsTooltipType.GUIDE_TO_EDIT
? R.string.accessibility_service_qs_tooltip_content
: R.string.accessibility_service_auto_added_qs_tooltip_content;
return getString(titleResId, tileName);
}
@Override
protected void updateSwitchBarToggleSwitch() {
final boolean checked = isAccessibilityServiceEnabled();
@@ -353,6 +391,14 @@ public class ToggleAccessibilityServicePreferenceFragment extends
// Get Accessibility service name.
mPackageName = getAccessibilityServiceInfo().getResolveInfo().loadLabel(
getPackageManager());
if (arguments.containsKey(AccessibilitySettings.EXTRA_TILE_SERVICE_COMPONENT_NAME)) {
final String tileServiceComponentName = arguments.getString(
AccessibilitySettings.EXTRA_TILE_SERVICE_COMPONENT_NAME);
mTileComponentName = ComponentName.unflattenFromString(tileServiceComponentName);
}
mStartTimeMillsForLogging = arguments.getLong(AccessibilitySettings.EXTRA_TIME_FOR_LOGGING);
}
private void onDialogButtonFromDisableToggleClicked(DialogInterface dialog, int which) {
@@ -380,7 +426,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends
}
private void onDialogButtonFromUninstallClicked() {
mDialog.dismiss();
mWarningDialog.dismiss();
final Intent uninstallIntent = createUninstallPackageActivityIntent();
if (uninstallIntent == null) {
return;
@@ -414,12 +460,12 @@ public class ToggleAccessibilityServicePreferenceFragment extends
mIsDialogShown.set(false);
showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL);
}
mDialog.dismiss();
mWarningDialog.dismiss();
}
private void onDenyButtonFromEnableToggleClicked() {
handleConfirmServiceEnabled(/* confirmed= */ false);
mDialog.dismiss();
mWarningDialog.dismiss();
}
void onDialogButtonFromShortcutToggleClicked(View view) {
@@ -443,7 +489,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends
mIsDialogShown.set(false);
showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL);
mDialog.dismiss();
mWarningDialog.dismiss();
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
}
@@ -451,7 +497,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends
private void onDenyButtonFromShortcutToggleClicked() {
mShortcutPreference.setChecked(false);
mDialog.dismiss();
mWarningDialog.dismiss();
}
void onDialogButtonFromShortcutClicked(View view) {
@@ -469,11 +515,11 @@ public class ToggleAccessibilityServicePreferenceFragment extends
mIsDialogShown.set(false);
showPopupDialog(DialogEnums.EDIT_SHORTCUT);
mDialog.dismiss();
mWarningDialog.dismiss();
}
private void onDenyButtonFromShortcutClicked() {
mDialog.dismiss();
mWarningDialog.dismiss();
}
private boolean onPreferenceClick(boolean isChecked) {
@@ -506,4 +552,13 @@ public class ToggleAccessibilityServicePreferenceFragment extends
false));
}
}
private void logDisabledState(String packageName) {
if (mStartTimeMillsForLogging > 0 && !mDisabledStateLogged) {
AccessibilityStatsLogUtils.logDisableNonA11yCategoryService(
packageName,
SystemClock.elapsedRealtime() - mStartTimeMillsForLogging);
mDisabledStateLogged = true;
}
}
}

View File

@@ -31,7 +31,7 @@ public class ToggleAutoclickFooterPreferenceController extends
}
@Override
protected String getLearnMoreContentDescription() {
protected String getLearnMoreText() {
return mContext.getString(
R.string.accessibility_autoclick_footer_learn_more_content_description);
}

View File

@@ -35,7 +35,7 @@ import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.LayoutPreference;
import com.android.settingslib.widget.RadioButtonPreference;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
import java.util.Map;
@@ -43,7 +43,7 @@ import java.util.Map;
* Controller class that controls accessibility autoclick settings.
*/
public class ToggleAutoclickPreferenceController extends BasePreferenceController implements
LifecycleObserver, RadioButtonPreference.OnClickListener, PreferenceControllerMixin {
LifecycleObserver, SelectorWithWidgetPreference.OnClickListener, PreferenceControllerMixin {
@VisibleForTesting
static final String CONTROL_AUTOCLICK_DELAY_SECURE =
@@ -67,7 +67,7 @@ public class ToggleAutoclickPreferenceController extends BasePreferenceControlle
private final ContentResolver mContentResolver;
private final Resources mResources;
private OnChangeListener mOnChangeListener;
private RadioButtonPreference mDelayModePref;
private SelectorWithWidgetPreference mDelayModePref;
/**
* Seek bar preference for autoclick delay value. The seek bar has values between 0 and
@@ -109,7 +109,7 @@ public class ToggleAutoclickPreferenceController extends BasePreferenceControlle
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mDelayModePref = (RadioButtonPreference)
mDelayModePref = (SelectorWithWidgetPreference)
screen.findPreference(getPreferenceKey());
mDelayModePref.setOnClickListener(this);
mSeekBerPreference = (LayoutPreference) screen.findPreference(KEY_AUTOCLICK_CUSTOM_SEEKBAR);
@@ -117,7 +117,7 @@ public class ToggleAutoclickPreferenceController extends BasePreferenceControlle
}
@Override
public void onRadioButtonClicked(RadioButtonPreference preference) {
public void onRadioButtonClicked(SelectorWithWidgetPreference preference) {
final int value = mAccessibilityAutoclickKeyToValueMap.get(mPreferenceKey);
handleRadioButtonPreferenceChange(value);
if (mOnChangeListener != null) {

View File

@@ -17,21 +17,23 @@
package com.android.settings.accessibility;
import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_TILE_COMPONENT_NAME;
import static com.android.settings.accessibility.AccessibilityStatsLogUtils.logAccessibilityServiceEnabled;
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.widget.SettingsMainSwitchPreference;
import java.util.ArrayList;
@@ -41,8 +43,6 @@ import java.util.List;
public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePreferenceFragment {
private static final String ENABLED = Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED;
private final Handler mHandler = new Handler();
private SettingsContentObserver mSettingsContentObserver;
@Override
public int getMetricsCategory() {
@@ -51,6 +51,14 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere
@Override
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
final boolean isEnabled = Settings.Secure.getInt(getContentResolver(), ENABLED, OFF) == ON;
if (enabled == isEnabled) {
return;
}
if (enabled) {
showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE);
}
logAccessibilityServiceEnabled(mComponentName, enabled);
Settings.Secure.putInt(getContentResolver(), ENABLED, enabled ? ON : OFF);
}
@@ -72,8 +80,8 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere
}
@Override
protected void updateShortcutTitle(ShortcutPreference shortcutPreference) {
shortcutPreference.setTitle(R.string.accessibility_display_inversion_shortcut_title);
protected CharSequence getShortcutTitle() {
return getText(R.string.accessibility_display_inversion_shortcut_title);
}
@Override
@@ -82,31 +90,34 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere
mComponentName = COLOR_INVERSION_COMPONENT_NAME;
mPackageName = getText(R.string.accessibility_display_inversion_preference_title);
mHtmlDescription = getText(R.string.accessibility_display_inversion_preference_subtitle);
mTopIntroTitle = getText(R.string.accessibility_display_inversion_preference_intro_text);
mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(getPrefContext().getPackageName())
.appendPath(String.valueOf(R.raw.accessibility_color_inversion_banner))
.build();
final List<String> enableServiceFeatureKeys = new ArrayList<>(/* initialCapacity= */ 1);
enableServiceFeatureKeys.add(ENABLED);
mSettingsContentObserver = new SettingsContentObserver(mHandler, enableServiceFeatureKeys) {
@Override
public void onChange(boolean selfChange, Uri uri) {
updateSwitchBarToggleSwitch();
}
};
final View view = super.onCreateView(inflater, container, savedInstanceState);
updateFooterPreference();
return view;
}
@Override
protected void registerKeysToObserverCallback(
AccessibilitySettingsContentObserver contentObserver) {
super.registerKeysToObserverCallback(contentObserver);
final List<String> enableServiceFeatureKeys = new ArrayList<>(/* initialCapacity= */ 1);
enableServiceFeatureKeys.add(ENABLED);
contentObserver.registerKeysToObserverCallback(enableServiceFeatureKeys,
key -> updateSwitchBarToggleSwitch());
}
private void updateFooterPreference() {
final String title = getPrefContext().getString(
R.string.accessibility_color_inversion_about_title);
final String learnMoreContentDescription = getPrefContext().getString(
final String learnMoreText = getPrefContext().getString(
R.string.accessibility_color_inversion_footer_learn_more_content_description);
mFooterPreferenceController.setIntroductionTitle(title);
mFooterPreferenceController.setupHelpLink(getHelpResource(), learnMoreContentDescription);
mFooterPreferenceController.setupHelpLink(getHelpResource(), learnMoreText);
mFooterPreferenceController.displayPreference(getPreferenceScreen());
}
@@ -114,12 +125,10 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere
public void onResume() {
super.onResume();
updateSwitchBarToggleSwitch();
mSettingsContentObserver.register(getContentResolver());
}
@Override
public void onPause() {
mSettingsContentObserver.unregister(getContentResolver());
super.onPause();
}
@@ -134,6 +143,18 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere
mComponentName);
}
@Override
ComponentName getTileComponentName() {
return COLOR_INVERSION_TILE_COMPONENT_NAME;
}
@Override
CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type) {
return getText(type == QuickSettingsTooltipType.GUIDE_TO_EDIT
? R.string.accessibility_color_inversion_qs_tooltip_content
: R.string.accessibility_color_inversion_auto_added_qs_tooltip_content);
}
@Override
protected void updateSwitchBarToggleSwitch() {
final boolean checked = Settings.Secure.getInt(getContentResolver(), ENABLED, OFF) == ON;

View File

@@ -17,16 +17,16 @@
package com.android.settings.accessibility;
import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_TILE_COMPONENT_NAME;
import static com.android.settings.accessibility.AccessibilityStatsLogUtils.logAccessibilityServiceEnabled;
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
@@ -35,6 +35,7 @@ import android.view.ViewGroup;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.SettingsMainSwitchPreference;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -51,10 +52,11 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe
private static final String ENABLED = Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED;
private static final String KEY_PREVIEW = "daltonizer_preview";
private static final String KEY_CATEGORY_MODE = "daltonizer_mode_category";
private static final String KEY_DEUTERANOMALY = "daltonizer_mode_deuteranomaly";
private static final String KEY_PROTANOMALY = "daltonizer_mode_protanomaly";
private static final String KEY_TRITANOMEALY = "daltonizer_mode_tritanomaly";
private static final String KEY_GRAYSCALE = "daltonizer_mode_grayscale";
private static final List<AbstractPreferenceController> sControllers = new ArrayList<>();
private final Handler mHandler = new Handler();
private SettingsContentObserver mSettingsContentObserver;
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
Lifecycle lifecycle) {
@@ -84,36 +86,43 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe
mComponentName = DALTONIZER_COMPONENT_NAME;
mPackageName = getText(R.string.accessibility_display_daltonizer_preference_title);
mHtmlDescription = getText(R.string.accessibility_display_daltonizer_preference_subtitle);
final List<String> enableServiceFeatureKeys = new ArrayList<>(/* initialCapacity= */ 1);
enableServiceFeatureKeys.add(ENABLED);
mSettingsContentObserver = new SettingsContentObserver(mHandler, enableServiceFeatureKeys) {
@Override
public void onChange(boolean selfChange, Uri uri) {
updateSwitchBarToggleSwitch();
}
};
mTopIntroTitle = getText(R.string.accessibility_daltonizer_about_intro_text);
final View view = super.onCreateView(inflater, container, savedInstanceState);
updateFooterPreference();
return view;
}
@Override
protected void registerKeysToObserverCallback(
AccessibilitySettingsContentObserver contentObserver) {
super.registerKeysToObserverCallback(contentObserver);
final List<String> enableServiceFeatureKeys = new ArrayList<>(/* initialCapacity= */ 1);
enableServiceFeatureKeys.add(ENABLED);
contentObserver.registerKeysToObserverCallback(enableServiceFeatureKeys,
key -> updateSwitchBarToggleSwitch());
}
private void updateFooterPreference() {
final String title = getPrefContext()
.getString(R.string.accessibility_daltonizer_about_title);
final String learnMoreContentDescription = getPrefContext()
final String learnMoreText = getPrefContext()
.getString(R.string.accessibility_daltonizer_footer_learn_more_content_description);
mFooterPreferenceController.setIntroductionTitle(title);
mFooterPreferenceController.setupHelpLink(getHelpResource(), learnMoreContentDescription);
mFooterPreferenceController.setupHelpLink(getHelpResource(), learnMoreText);
mFooterPreferenceController.displayPreference(getPreferenceScreen());
}
/** Customizes the order by preference key. */
protected List<String> getPreferenceOrderList() {
final List<String> lists = new ArrayList<>();
lists.add(KEY_TOP_INTRO_PREFERENCE);
lists.add(KEY_PREVIEW);
lists.add(KEY_USE_SERVICE_PREFERENCE);
lists.add(KEY_CATEGORY_MODE);
lists.add(KEY_DEUTERANOMALY);
lists.add(KEY_PROTANOMALY);
lists.add(KEY_TRITANOMEALY);
lists.add(KEY_GRAYSCALE);
lists.add(KEY_GENERAL_CATEGORY);
lists.add(KEY_HTML_DESCRIPTION_PREFERENCE);
return lists;
@@ -123,8 +132,6 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe
public void onResume() {
super.onResume();
updateSwitchBarToggleSwitch();
mSettingsContentObserver.register(getContentResolver());
for (AbstractPreferenceController controller :
buildPreferenceControllers(getPrefContext(), getSettingsLifecycle())) {
((DaltonizerRadioButtonPreferenceController) controller).setOnChangeListener(this);
@@ -135,7 +142,6 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe
@Override
public void onPause() {
mSettingsContentObserver.unregister(getContentResolver());
for (AbstractPreferenceController controller :
buildPreferenceControllers(getPrefContext(), getSettingsLifecycle())) {
((DaltonizerRadioButtonPreferenceController) controller).setOnChangeListener(null);
@@ -160,6 +166,14 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe
@Override
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
final boolean isEnabled = Settings.Secure.getInt(getContentResolver(), ENABLED, OFF) == ON;
if (enabled == isEnabled) {
return;
}
if (enabled) {
showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE);
}
logAccessibilityServiceEnabled(mComponentName, enabled);
Settings.Secure.putInt(getContentResolver(), ENABLED, enabled ? ON : OFF);
}
@@ -176,8 +190,8 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe
}
@Override
protected void updateShortcutTitle(ShortcutPreference shortcutPreference) {
shortcutPreference.setTitle(R.string.accessibility_daltonizer_shortcut_title);
protected CharSequence getShortcutTitle() {
return getText(R.string.accessibility_daltonizer_shortcut_title);
}
@Override
@@ -186,6 +200,18 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe
mComponentName);
}
@Override
ComponentName getTileComponentName() {
return DALTONIZER_TILE_COMPONENT_NAME;
}
@Override
CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type) {
return getText(type == QuickSettingsTooltipType.GUIDE_TO_EDIT
? R.string.accessibility_color_correction_qs_tooltip_content
: R.string.accessibility_color_correction_auto_added_qs_tooltip_content);
}
@Override
protected void updateSwitchBarToggleSwitch() {
final boolean checked = Settings.Secure.getInt(getContentResolver(), ENABLED, OFF) == ON;

View File

@@ -25,7 +25,9 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.graphics.drawable.Drawable;
import android.icu.text.CaseMap;
import android.net.Uri;
@@ -33,6 +35,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.quicksettings.TileService;
import android.text.Html;
import android.text.TextUtils;
import android.view.LayoutInflater;
@@ -53,6 +56,7 @@ import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.accessibility.AccessibilityDialogUtils.DialogType;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
import com.android.settings.utils.LocaleUtils;
import com.android.settings.widget.SettingsMainSwitchBar;
@@ -60,6 +64,7 @@ import com.android.settings.widget.SettingsMainSwitchPreference;
import com.android.settingslib.accessibility.AccessibilityUtils;
import com.android.settingslib.widget.IllustrationPreference;
import com.android.settingslib.widget.OnMainSwitchChangeListener;
import com.android.settingslib.widget.TopIntroPreference;
import com.google.android.setupcompat.util.WizardManagerHelper;
@@ -74,6 +79,7 @@ import java.util.Locale;
public abstract class ToggleFeaturePreferenceFragment extends SettingsPreferenceFragment
implements ShortcutPreference.OnClickCallback, OnMainSwitchChangeListener {
protected TopIntroPreference mTopIntroPreference;
protected SettingsMainSwitchPreference mToggleServiceSwitchPreference;
protected ShortcutPreference mShortcutPreference;
protected Preference mSettingsPreference;
@@ -89,21 +95,29 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
protected Uri mImageUri;
private CharSequence mDescription;
protected CharSequence mHtmlDescription;
protected CharSequence mTopIntroTitle;
private static final String DRAWABLE_FOLDER = "drawable";
protected static final String KEY_TOP_INTRO_PREFERENCE = "top_intro";
protected static final String KEY_USE_SERVICE_PREFERENCE = "use_service";
public static final String KEY_GENERAL_CATEGORY = "general_categories";
protected static final String KEY_HTML_DESCRIPTION_PREFERENCE = "html_description";
private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
public static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
protected static final String KEY_SAVED_USER_SHORTCUT_TYPE = "shortcut_type";
protected static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
protected static final String KEY_SAVED_QS_TOOLTIP_TYPE = "qs_tooltip_type";
protected static final String KEY_ANIMATED_IMAGE = "animated_image";
private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
private SettingsContentObserver mSettingsContentObserver;
private AccessibilitySettingsContentObserver mSettingsContentObserver;
private CheckBox mSoftwareTypeCheckBox;
private CheckBox mHardwareTypeCheckBox;
private AccessibilityQuickSettingsTooltipWindow mTooltipWindow;
private boolean mNeedsQSTooltipReshow = false;
private int mNeedsQSTooltipType = QuickSettingsTooltipType.GUIDE_TO_EDIT;
public static final int NOT_SET = -1;
// Save user's shortcutType value when savedInstance has value (e.g. device rotated).
protected int mSavedCheckBoxValue = NOT_SET;
@@ -129,13 +143,20 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Restore the user shortcut type.
if (savedInstanceState != null && savedInstanceState.containsKey(
KEY_SAVED_USER_SHORTCUT_TYPE)) {
mSavedCheckBoxValue = savedInstanceState.getInt(KEY_SAVED_USER_SHORTCUT_TYPE, NOT_SET);
// Restore the user shortcut type and tooltip.
if (savedInstanceState != null) {
if (savedInstanceState.containsKey(KEY_SAVED_USER_SHORTCUT_TYPE)) {
mSavedCheckBoxValue = savedInstanceState.getInt(KEY_SAVED_USER_SHORTCUT_TYPE,
NOT_SET);
}
if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_RESHOW)) {
mNeedsQSTooltipReshow = savedInstanceState.getBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW);
}
if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_TYPE)) {
mNeedsQSTooltipType = savedInstanceState.getInt(KEY_SAVED_QS_TOOLTIP_TYPE);
}
}
setupDefaultShortcutIfNecessary(getPrefContext());
final int resId = getPreferenceScreenResId();
if (resId <= 0) {
final PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(
@@ -143,17 +164,21 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
setPreferenceScreen(preferenceScreen);
}
final List<String> shortcutFeatureKeys = getFeatureSettingsKeys();
mSettingsContentObserver = new SettingsContentObserver(new Handler(), shortcutFeatureKeys) {
@Override
public void onChange(boolean selfChange, Uri uri) {
updateShortcutPreferenceData();
updateShortcutPreference();
}
};
mSettingsContentObserver = new AccessibilitySettingsContentObserver(new Handler());
registerKeysToObserverCallback(mSettingsContentObserver);
}
protected List<String> getFeatureSettingsKeys() {
protected void registerKeysToObserverCallback(
AccessibilitySettingsContentObserver contentObserver) {
final List<String> shortcutFeatureKeys = getShortcutFeatureSettingsKeys();
contentObserver.registerKeysToObserverCallback(shortcutFeatureKeys, key -> {
updateShortcutPreferenceData();
updateShortcutPreference();
});
}
protected List<String> getShortcutFeatureSettingsKeys() {
final List<String> shortcutFeatureKeys = new ArrayList<>();
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
@@ -166,6 +191,7 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
// Need to be called as early as possible. Protected variables will be assigned here.
onProcessArguments(getArguments());
initTopIntroPreference();
initAnimatedImagePreference();
initToggleServiceSwitchPreference();
initGeneralCategory();
@@ -194,6 +220,13 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
switchBar.hide();
updatePreferenceOrder();
// Reshow tooltip when activity recreate, such as rotate device.
if (mNeedsQSTooltipReshow) {
getView().post(this::showQuickSettingsTooltipIfNeeded);
}
writeDefaultShortcutTargetServiceToSettingsIfNeeded(getPrefContext());
}
@Override
@@ -227,6 +260,10 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
if (value != NOT_SET) {
outState.putInt(KEY_SAVED_USER_SHORTCUT_TYPE, value);
}
if (mTooltipWindow != null) {
outState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, mTooltipWindow.isShowing());
outState.putInt(KEY_SAVED_QS_TOOLTIP_TYPE, mNeedsQSTooltipType);
}
super.onSaveInstanceState(outState);
}
@@ -234,19 +271,17 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
public Dialog onCreateDialog(int dialogId) {
switch (dialogId) {
case DialogEnums.EDIT_SHORTCUT:
final CharSequence dialogTitle = getPrefContext().getString(
R.string.accessibility_shortcut_title, mPackageName);
final int dialogType = WizardManagerHelper.isAnySetupWizard(getIntent())
? DialogType.EDIT_SHORTCUT_GENERIC_SUW : DialogType.EDIT_SHORTCUT_GENERIC;
mDialog = AccessibilityDialogUtils.showEditShortcutDialog(
getPrefContext(), dialogType, dialogTitle,
getPrefContext(), dialogType, getShortcutTitle(),
this::callOnAlertDialogCheckboxClicked);
setupEditShortcutDialog(mDialog);
return mDialog;
case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
mDialog = AccessibilityGestureNavigationTutorial
.createAccessibilityTutorialDialog(getPrefContext(),
getUserShortcutTypes());
getUserShortcutTypes(), this::callOnTutorialDialogButtonClicked);
mDialog.setCanceledOnTouchOutside(false);
return mDialog;
default:
@@ -292,18 +327,27 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
*/
abstract int getUserShortcutTypes();
/** Returns the accessibility tile component name. */
abstract ComponentName getTileComponentName();
/** Returns the accessibility tile tooltip content. */
abstract CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type);
protected void updateToggleServiceTitle(SettingsMainSwitchPreference switchPreference) {
final CharSequence title =
getString(R.string.accessibility_service_primary_switch_title, mPackageName);
switchPreference.setTitle(title);
}
protected void updateShortcutTitle(ShortcutPreference shortcutPreference) {
final CharSequence title = getString(R.string.accessibility_shortcut_title, mPackageName);
shortcutPreference.setTitle(title);
protected CharSequence getShortcutTitle() {
return getString(R.string.accessibility_shortcut_title, mPackageName);
}
protected abstract void onPreferenceToggled(String preferenceKey, boolean enabled);
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
if (enabled) {
showQuickSettingsTooltipIfNeeded();
}
}
protected void onInstallSwitchPreferenceToggleSwitch() {
// Implement this to set a checked listener.
@@ -354,11 +398,17 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
mHtmlDescription = arguments.getCharSequence(
AccessibilitySettings.EXTRA_HTML_DESCRIPTION);
}
// Intro.
if (arguments.containsKey(AccessibilitySettings.EXTRA_INTRO)) {
mTopIntroTitle = arguments.getCharSequence(AccessibilitySettings.EXTRA_INTRO);
}
}
/** Customizes the order by preference key. */
protected List<String> getPreferenceOrderList() {
final List<String> lists = new ArrayList<>();
lists.add(KEY_TOP_INTRO_PREFERENCE);
lists.add(KEY_ANIMATED_IMAGE);
lists.add(KEY_USE_SERVICE_PREFERENCE);
lists.add(KEY_GENERAL_CATEGORY);
@@ -427,6 +477,17 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
getPreferenceScreen().addPreference(illustrationPreference);
}
@VisibleForTesting
void initTopIntroPreference() {
if (TextUtils.isEmpty(mTopIntroTitle)) {
return;
}
mTopIntroPreference = new TopIntroPreference(getPrefContext());
mTopIntroPreference.setKey(KEY_TOP_INTRO_PREFERENCE);
mTopIntroPreference.setTitle(mTopIntroTitle);
getPreferenceScreen().addPreference(mTopIntroPreference);
}
private void initToggleServiceSwitchPreference() {
mToggleServiceSwitchPreference = new SettingsMainSwitchPreference(getPrefContext());
mToggleServiceSwitchPreference.setKey(KEY_USE_SERVICE_PREFERENCE);
@@ -452,8 +513,7 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
mShortcutPreference.setPersistent(false);
mShortcutPreference.setKey(getShortcutPreferenceKey());
mShortcutPreference.setOnClickCallback(this);
updateShortcutTitle(mShortcutPreference);
mShortcutPreference.setTitle(getShortcutTitle());
final PreferenceCategory generalCategory = findPreference(KEY_GENERAL_CATEGORY);
generalCategory.addPreference(mShortcutPreference);
@@ -501,13 +561,6 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
createFooterPreference(getPreferenceScreen(), mDescription,
getString(R.string.accessibility_introduction_title, mPackageName));
}
if (TextUtils.isEmpty(mHtmlDescription) && TextUtils.isEmpty(mDescription)) {
final CharSequence defaultDescription =
getText(R.string.accessibility_service_default_description);
createFooterPreference(getPreferenceScreen(), defaultDescription,
getString(R.string.accessibility_introduction_title, mPackageName));
}
}
@@ -639,6 +692,17 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
null, LocaleUtils.getConcatenatedString(list));
}
/**
* This method will be invoked when a button in the tutorial dialog is clicked.
*
* @param dialog The dialog that received the click
* @param which The button that was clicked
*/
private void callOnTutorialDialogButtonClicked(DialogInterface dialog, int which) {
dialog.dismiss();
showQuickSettingsTooltipIfNeeded();
}
/**
* This method will be invoked when a button in the edit shortcut dialog is clicked.
*
@@ -651,12 +715,21 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
}
final int value = getShortcutTypeCheckBoxValue();
saveNonEmptyUserShortcutType(value);
AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), value, mComponentName);
AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), ~value, mComponentName);
mShortcutPreference.setChecked(value != UserShortcutType.EMPTY);
final boolean shortcutAssigned = value != UserShortcutType.EMPTY;
mShortcutPreference.setChecked(shortcutAssigned);
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
if (mHardwareTypeCheckBox.isChecked()) {
AccessibilityUtil.skipVolumeShortcutDialogTimeoutRestriction(getPrefContext());
}
// Show the quick setting tooltip if the shortcut assigned in the first time
if (shortcutAssigned) {
showQuickSettingsTooltipIfNeeded();
}
}
protected void updateShortcutPreferenceData() {
@@ -716,8 +789,22 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
/**
* Setups a configurable default if the setting has never been set.
*
* TODO(b/228562075): Remove this function when correcting the format in config file
* `config_defaultAccessibilityService`.
*/
private static void setupDefaultShortcutIfNecessary(Context context) {
private void writeDefaultShortcutTargetServiceToSettingsIfNeeded(Context context) {
if (mComponentName == null) {
return;
}
final ComponentName defaultService = ComponentName.unflattenFromString(context.getString(
com.android.internal.R.string.config_defaultAccessibilityService));
// write default accessibility service only when user enter into corresponding page.
if (!mComponentName.equals(defaultService)) {
return;
}
final String targetKey = Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
String targetString = Settings.Secure.getString(context.getContentResolver(), targetKey);
if (!TextUtils.isEmpty(targetString)) {
@@ -766,4 +853,61 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
mComponentName.flattenToString(), type);
PreferredShortcuts.saveUserShortcutType(getPrefContext(), shortcut);
}
/**
* Shows the quick settings tooltip if the quick settings feature is assigned. The tooltip only
* shows once.
*
* @param type The quick settings tooltip type
*/
protected void showQuickSettingsTooltipIfNeeded(@QuickSettingsTooltipType int type) {
mNeedsQSTooltipType = type;
showQuickSettingsTooltipIfNeeded();
}
private void showQuickSettingsTooltipIfNeeded() {
final ComponentName tileComponentName = getTileComponentName();
if (tileComponentName == null) {
// Returns if no tile service assigned.
return;
}
if (!mNeedsQSTooltipReshow && AccessibilityQuickSettingUtils.hasValueInSharedPreferences(
getContext(), tileComponentName)) {
// Returns if quick settings tooltip only show once.
return;
}
final CharSequence content = getTileTooltipContent(mNeedsQSTooltipType);
if (TextUtils.isEmpty(content)) {
// Returns if no content of tile tooltip assigned.
return;
}
final int imageResId = mNeedsQSTooltipType == QuickSettingsTooltipType.GUIDE_TO_EDIT
? R.drawable.accessibility_qs_tooltip_illustration
: R.drawable.accessibility_auto_added_qs_tooltip_illustration;
mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(getContext());
mTooltipWindow.setup(content, imageResId);
mTooltipWindow.showAtTopCenter(getView());
AccessibilityQuickSettingUtils.optInValueToSharedPreferences(getContext(),
tileComponentName);
mNeedsQSTooltipReshow = false;
}
/** Returns user visible name of the tile by given {@link ComponentName}. */
protected CharSequence loadTileLabel(Context context, ComponentName componentName) {
final PackageManager packageManager = context.getPackageManager();
final Intent queryIntent = new Intent(TileService.ACTION_QS_TILE);
final List<ResolveInfo> resolveInfos =
packageManager.queryIntentServices(queryIntent, PackageManager.GET_META_DATA);
for (ResolveInfo info : resolveInfos) {
final ServiceInfo serviceInfo = info.serviceInfo;
if (TextUtils.equals(componentName.getPackageName(), serviceInfo.packageName)
&& TextUtils.equals(componentName.getClassName(), serviceInfo.name)) {
return serviceInfo.loadLabel(packageManager);
}
}
return null;
}
}

View File

@@ -16,13 +16,17 @@
package com.android.settings.accessibility;
import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME;
import static com.android.settings.accessibility.AccessibilityStatsLogUtils.logAccessibilityServiceEnabled;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.hardware.display.ColorDisplayManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
@@ -31,8 +35,8 @@ import android.view.ViewGroup;
import androidx.preference.PreferenceCategory;
import androidx.preference.SwitchPreference;
import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.SeekBarPreference;
import com.android.settings.widget.SettingsMainSwitchPreference;
@@ -50,8 +54,6 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
private static final String KEY_INTENSITY = "rbc_intensity";
private static final String KEY_PERSIST = "rbc_persist";
private final Handler mHandler = new Handler();
private SettingsContentObserver mSettingsContentObserver;
private ReduceBrightColorsIntensityPreferenceController mRbcIntensityPreferenceController;
private ReduceBrightColorsPersistencePreferenceController mRbcPersistencePreferenceController;
private ColorDisplayManager mColorDisplayManager;
@@ -64,23 +66,16 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
.authority(getPrefContext().getPackageName())
.appendPath(String.valueOf(R.raw.extra_dim_banner))
.build();
mComponentName = AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME;
mComponentName = REDUCE_BRIGHT_COLORS_COMPONENT_NAME;
mPackageName = getText(R.string.reduce_bright_colors_preference_title);
mHtmlDescription = getText(R.string.reduce_bright_colors_preference_subtitle);
final List<String> enableServiceFeatureKeys = new ArrayList<>(/* initialCapacity= */ 1);
enableServiceFeatureKeys.add(REDUCE_BRIGHT_COLORS_ACTIVATED_KEY);
mTopIntroTitle = getText(R.string.reduce_bright_colors_preference_intro_text);
mRbcIntensityPreferenceController =
new ReduceBrightColorsIntensityPreferenceController(getContext(), KEY_INTENSITY);
mRbcPersistencePreferenceController =
new ReduceBrightColorsPersistencePreferenceController(getContext(), KEY_PERSIST);
mRbcIntensityPreferenceController.displayPreference(getPreferenceScreen());
mRbcPersistencePreferenceController.displayPreference(getPreferenceScreen());
mSettingsContentObserver = new SettingsContentObserver(mHandler, enableServiceFeatureKeys) {
@Override
public void onChange(boolean selfChange, Uri uri) {
updateSwitchBarToggleSwitch();
}
};
mColorDisplayManager = getContext().getSystemService(ColorDisplayManager.class);
final View view = super.onCreateView(inflater, container, savedInstanceState);
// Parent sets the title when creating the view, so set it after calling super
@@ -90,6 +85,17 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
return view;
}
@Override
protected void registerKeysToObserverCallback(
AccessibilitySettingsContentObserver contentObserver) {
super.registerKeysToObserverCallback(contentObserver);
final List<String> enableServiceFeatureKeys = new ArrayList<>(/* initialCapacity= */ 1);
enableServiceFeatureKeys.add(REDUCE_BRIGHT_COLORS_ACTIVATED_KEY);
contentObserver.registerKeysToObserverCallback(enableServiceFeatureKeys,
key -> updateSwitchBarToggleSwitch());
}
private void updateGeneralCategoryOrder() {
final PreferenceCategory generalCategory = findPreference(KEY_GENERAL_CATEGORY);
final SeekBarPreference intensity = findPreference(KEY_INTENSITY);
@@ -117,12 +123,10 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
public void onResume() {
super.onResume();
updateSwitchBarToggleSwitch();
mSettingsContentObserver.register(getContentResolver());
}
@Override
public void onPause() {
mSettingsContentObserver.unregister(getContentResolver());
super.onPause();
}
@@ -144,7 +148,10 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
@Override
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
AccessibilityStatsLogUtils.logAccessibilityServiceEnabled(mComponentName, enabled);
if (enabled) {
showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE);
}
logAccessibilityServiceEnabled(mComponentName, enabled);
mColorDisplayManager.setReduceBrightColorsActivated(enabled);
}
@@ -161,8 +168,8 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
}
@Override
protected void updateShortcutTitle(ShortcutPreference shortcutPreference) {
shortcutPreference.setTitle(R.string.reduce_bright_colors_shortcut_title);
protected CharSequence getShortcutTitle() {
return getText(R.string.reduce_bright_colors_shortcut_title);
}
@Override
@@ -171,6 +178,18 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
mComponentName);
}
@Override
ComponentName getTileComponentName() {
return REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME;
}
@Override
CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type) {
return getText(type == QuickSettingsTooltipType.GUIDE_TO_EDIT
? R.string.accessibility_reduce_bright_colors_qs_tooltip_content
: R.string.accessibility_reduce_bright_colors_auto_added_qs_tooltip_content);
}
@Override
protected void updateSwitchBarToggleSwitch() {
final boolean checked = mColorDisplayManager.isReduceBrightColorsActivated();

View File

@@ -23,9 +23,11 @@ import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.icu.text.CaseMap;
import android.net.Uri;
import android.os.Bundle;
@@ -40,11 +42,13 @@ import android.widget.CheckBox;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.SwitchPreference;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.DialogCreatable;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityDialogUtils.DialogType;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
import com.android.settings.utils.LocaleUtils;
@@ -73,8 +77,10 @@ public class ToggleScreenMagnificationPreferenceFragment extends
private static final TextUtils.SimpleStringSplitter sStringColonSplitter =
new TextUtils.SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
private MagnificationModePreferenceController mModePreferenceController;
private DialogCreatable mDialogDelegate;
private MagnificationFollowTypingPreferenceController mFollowTypingPreferenceController;
protected SwitchPreference mFollowingTypingSwitchPreference;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -103,10 +109,10 @@ public class ToggleScreenMagnificationPreferenceFragment extends
private void updateFooterPreference() {
final String title = getPrefContext().getString(
R.string.accessibility_screen_magnification_about_title);
final String learnMoreContentDescription = getPrefContext().getString(
final String learnMoreText = getPrefContext().getString(
R.string.accessibility_screen_magnification_footer_learn_more_content_description);
mFooterPreferenceController.setIntroductionTitle(title);
mFooterPreferenceController.setupHelpLink(getHelpResource(), learnMoreContentDescription);
mFooterPreferenceController.setupHelpLink(getHelpResource(), learnMoreText);
mFooterPreferenceController.displayPreference(getPreferenceScreen());
}
@@ -141,8 +147,7 @@ public class ToggleScreenMagnificationPreferenceFragment extends
return AccessibilityGestureNavigationTutorial
.showAccessibilityGestureTutorialDialog(getPrefContext());
case DialogEnums.MAGNIFICATION_EDIT_SHORTCUT:
final CharSequence dialogTitle = getPrefContext().getString(
R.string.accessibility_shortcut_title, mPackageName);
final CharSequence dialogTitle = getShortcutTitle();
final int dialogType = WizardManagerHelper.isAnySetupWizard(getIntent())
? DialogType.EDIT_SHORTCUT_MAGNIFICATION_SUW
: DialogType.EDIT_SHORTCUT_MAGNIFICATION;
@@ -157,9 +162,14 @@ public class ToggleScreenMagnificationPreferenceFragment extends
@Override
protected void initSettingsPreference() {
// If the device doesn't support magnification area, it should hide the settings preference.
if (!getContext().getResources().getBoolean(
com.android.internal.R.bool.config_magnification_area)) {
// If the device doesn't support window magnification feature, it should hide the
// settings preference.
final boolean supportWindowMagnification =
getContext().getResources().getBoolean(
com.android.internal.R.bool.config_magnification_area)
&& getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WINDOW_MAGNIFICATION);
if (!supportWindowMagnification) {
return;
}
mSettingsPreference = new Preference(getPrefContext());
@@ -170,11 +180,27 @@ public class ToggleScreenMagnificationPreferenceFragment extends
final PreferenceCategory generalCategory = findPreference(KEY_GENERAL_CATEGORY);
generalCategory.addPreference(mSettingsPreference);
mModePreferenceController = new MagnificationModePreferenceController(getContext(),
MagnificationModePreferenceController.PREF_KEY);
mModePreferenceController.setDialogHelper(this);
getSettingsLifecycle().addObserver(mModePreferenceController);
mModePreferenceController.displayPreference(getPreferenceScreen());
final MagnificationModePreferenceController magnificationModePreferenceController =
new MagnificationModePreferenceController(getContext(),
MagnificationModePreferenceController.PREF_KEY);
magnificationModePreferenceController.setDialogHelper(this);
getSettingsLifecycle().addObserver(magnificationModePreferenceController);
magnificationModePreferenceController.displayPreference(getPreferenceScreen());
mFollowingTypingSwitchPreference =
new SwitchPreference(getPrefContext());
mFollowingTypingSwitchPreference.setTitle(
R.string.accessibility_screen_magnification_follow_typing_title);
mFollowingTypingSwitchPreference.setSummary(
R.string.accessibility_screen_magnification_follow_typing_summary);
mFollowingTypingSwitchPreference.setKey(
MagnificationFollowTypingPreferenceController.PREF_KEY);
generalCategory.addPreference(mFollowingTypingSwitchPreference);
mFollowTypingPreferenceController = new MagnificationFollowTypingPreferenceController(
getContext(), MagnificationFollowTypingPreferenceController.PREF_KEY);
getSettingsLifecycle().addObserver(mFollowTypingPreferenceController);
mFollowTypingPreferenceController.displayPreference(getPreferenceScreen());
}
@Override
@@ -276,8 +302,23 @@ public class ToggleScreenMagnificationPreferenceFragment extends
}
@Override
protected List<String> getFeatureSettingsKeys() {
final List<String> shortcutKeys = super.getFeatureSettingsKeys();
protected void registerKeysToObserverCallback(
AccessibilitySettingsContentObserver contentObserver) {
super.registerKeysToObserverCallback(contentObserver);
final List<String> followingTypingKeys = new ArrayList<>();
followingTypingKeys.add(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED);
contentObserver.registerKeysToObserverCallback(followingTypingKeys,
key -> updateFollowTypingState());
}
private void updateFollowTypingState() {
mFollowTypingPreferenceController.updateState();
}
@Override
protected List<String> getShortcutFeatureSettingsKeys() {
final List<String> shortcutKeys = super.getShortcutFeatureSettingsKeys();
shortcutKeys.add(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
return shortcutKeys;
}
@@ -325,6 +366,10 @@ public class ToggleScreenMagnificationPreferenceFragment extends
mShortcutPreference.setChecked(value != UserShortcutType.EMPTY);
mShortcutPreference.setSummary(
getShortcutTypeSummary(getPrefContext()));
if (mHardwareTypeCheckBox.isChecked()) {
AccessibilityUtil.skipVolumeShortcutDialogTimeoutRestriction(getPrefContext());
}
}
@Override
@@ -364,6 +409,16 @@ public class ToggleScreenMagnificationPreferenceFragment extends
return getUserShortcutTypeFromSettings(getPrefContext());
}
@Override
ComponentName getTileComponentName() {
return null;
}
@Override
CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type) {
return null;
}
@Override
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
if (enabled && TextUtils.equals(
@@ -414,17 +469,15 @@ public class ToggleScreenMagnificationPreferenceFragment extends
mShortcutPreference.setKey(getShortcutPreferenceKey());
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
mShortcutPreference.setOnClickCallback(this);
final CharSequence title = getString(R.string.accessibility_shortcut_title, mPackageName);
mShortcutPreference.setTitle(title);
mShortcutPreference.setTitle(getShortcutTitle());
final PreferenceCategory generalCategory = findPreference(KEY_GENERAL_CATEGORY);
generalCategory.addPreference(mShortcutPreference);
}
@Override
protected void updateShortcutTitle(ShortcutPreference shortcutPreference) {
shortcutPreference.setTitle(R.string.accessibility_screen_magnification_shortcut_title);
protected CharSequence getShortcutTitle() {
return getText(R.string.accessibility_screen_magnification_shortcut_title);
}
@Override

Some files were not shown because too many files have changed in this diff Show More