Merge "Merge sc-dev-plus-aosp-without-vendor@7634622" into stage-aosp-master
This commit is contained in:
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.android.settings;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
@@ -73,8 +71,6 @@ public class ActivityPicker extends AlertActivity implements
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
getWindow().addPrivateFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
|
||||
|
||||
final Intent intent = getIntent();
|
||||
|
||||
|
||||
@@ -192,3 +192,4 @@ public class AirplaneModeEnabler extends GlobalSettingsChangeListener {
|
||||
return WirelessUtils.isAirplaneModeOn(mContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,8 +49,8 @@ import com.android.settings.network.TetherEnabler;
|
||||
import com.android.settings.network.UsbTetherPreferenceController;
|
||||
import com.android.settings.network.WifiTetherDisablePreferenceController;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settings.widget.SwitchBar;
|
||||
import com.android.settings.widget.SwitchBarController;
|
||||
import com.android.settings.widget.MainSwitchBarController;
|
||||
import com.android.settings.widget.SettingsMainSwitchBar;
|
||||
import com.android.settings.wifi.tether.WifiTetherApBandPreferenceController;
|
||||
import com.android.settings.wifi.tether.WifiTetherAutoOffPreferenceController;
|
||||
import com.android.settings.wifi.tether.WifiTetherBasePreferenceController;
|
||||
@@ -227,15 +227,15 @@ public class AllInOneTetherSettings extends RestrictedDashboardFragment
|
||||
adapter.getProfileProxy(activity.getApplicationContext(), mProfileServiceListener,
|
||||
BluetoothProfile.PAN);
|
||||
}
|
||||
final SwitchBar switchBar = activity.getSwitchBar();
|
||||
final SettingsMainSwitchBar mainSwitch = activity.getSwitchBar();
|
||||
mTetherEnabler = new TetherEnabler(activity,
|
||||
new SwitchBarController(switchBar), mBluetoothPan);
|
||||
new MainSwitchBarController(mainSwitch), mBluetoothPan);
|
||||
getSettingsLifecycle().addObserver(mTetherEnabler);
|
||||
use(UsbTetherPreferenceController.class).setTetherEnabler(mTetherEnabler);
|
||||
use(BluetoothTetherPreferenceController.class).setTetherEnabler(mTetherEnabler);
|
||||
use(EthernetTetherPreferenceController.class).setTetherEnabler(mTetherEnabler);
|
||||
use(WifiTetherDisablePreferenceController.class).setTetherEnabler(mTetherEnabler);
|
||||
switchBar.show();
|
||||
mainSwitch.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -304,12 +304,12 @@ public class AllInOneTetherSettings extends RestrictedDashboardFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWhitelistStatusChanged(int uid, boolean isWhitelisted) {
|
||||
public void onAllowlistStatusChanged(int uid, boolean isAllowlisted) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlacklistStatusChanged(int uid, boolean isBlacklisted) {
|
||||
public void onDenylistStatusChanged(int uid, boolean isDenylisted) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@@ -355,7 +355,7 @@ public class AllInOneTetherSettings extends RestrictedDashboardFragment
|
||||
@Override
|
||||
public void onTetherConfigUpdated(AbstractPreferenceController controller) {
|
||||
final SoftApConfiguration config = buildNewConfig();
|
||||
mPasswordPreferenceController.updateVisibility(config.getSecurityType());
|
||||
mPasswordPreferenceController.setSecurityType(config.getSecurityType());
|
||||
mWifiManager.setSoftApConfiguration(config);
|
||||
|
||||
if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED) {
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.settings;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
|
||||
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
@@ -91,6 +93,7 @@ public class AllowBindAppWidgetActivity extends AlertActivity implements
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().addPrivateFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
|
||||
setResult(RESULT_CANCELED); // By default, set the result to cancelled
|
||||
Intent intent = getIntent();
|
||||
CharSequence label = "";
|
||||
|
||||
66
src/com/android/settings/AsyncTaskSidecar.java
Normal file
66
src/com/android/settings/AsyncTaskSidecar.java
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.os.AsyncTask;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/** A {@link SidecarFragment} which uses an {@link AsyncTask} to perform background work. */
|
||||
public abstract class AsyncTaskSidecar<Param, Result> extends SidecarFragment {
|
||||
|
||||
private Future<Result> mAsyncTask;
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (mAsyncTask != null) {
|
||||
mAsyncTask.cancel(true /* mayInterruptIfRunning */);
|
||||
}
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the background task.
|
||||
*
|
||||
* @param param parameters passed in from {@link #run}
|
||||
*/
|
||||
protected abstract Result doInBackground(@Nullable Param param);
|
||||
|
||||
/** Handles the background task's result. */
|
||||
protected void onPostExecute(Result result) {}
|
||||
|
||||
/** Runs the sidecar and sets the state to RUNNING. */
|
||||
public void run(@Nullable final Param param) {
|
||||
setState(State.RUNNING, Substate.UNUSED);
|
||||
|
||||
if (mAsyncTask != null) {
|
||||
mAsyncTask.cancel(true /* mayInterruptIfRunning */);
|
||||
}
|
||||
|
||||
mAsyncTask =
|
||||
ThreadUtils.postOnBackgroundThread(
|
||||
() -> {
|
||||
Result result = doInBackground(param);
|
||||
ThreadUtils.postOnMainThread(() -> onPostExecute(result));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ public class CustomListPreference extends ListPreference {
|
||||
|
||||
public CustomListPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setSingleLineTitle(true);
|
||||
}
|
||||
|
||||
public CustomListPreference(Context context, AttributeSet attrs, int defStyleAttr,
|
||||
|
||||
@@ -24,13 +24,10 @@ import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.display.BrightnessLevelPreferenceController;
|
||||
import com.android.settings.display.CameraGesturePreferenceController;
|
||||
import com.android.settings.display.LiftToWakePreferenceController;
|
||||
import com.android.settings.display.NightDisplayPreferenceController;
|
||||
import com.android.settings.display.NightModePreferenceController;
|
||||
import com.android.settings.display.ScreenSaverPreferenceController;
|
||||
import com.android.settings.display.ShowOperatorNamePreferenceController;
|
||||
import com.android.settings.display.TapToWakePreferenceController;
|
||||
import com.android.settings.display.ThemePreferenceController;
|
||||
import com.android.settings.display.TimeoutPreferenceController;
|
||||
import com.android.settings.display.VrDisplayPreferenceController;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
@@ -44,8 +41,6 @@ import java.util.List;
|
||||
public class DisplaySettings extends DashboardFragment {
|
||||
private static final String TAG = "DisplaySettings";
|
||||
|
||||
private static final String KEY_SCREEN_TIMEOUT = "screen_timeout";
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.DISPLAY;
|
||||
@@ -81,11 +76,8 @@ public class DisplaySettings extends DashboardFragment {
|
||||
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
||||
controllers.add(new CameraGesturePreferenceController(context));
|
||||
controllers.add(new LiftToWakePreferenceController(context));
|
||||
controllers.add(new NightDisplayPreferenceController(context));
|
||||
controllers.add(new NightModePreferenceController(context));
|
||||
controllers.add(new ScreenSaverPreferenceController(context));
|
||||
controllers.add(new TapToWakePreferenceController(context));
|
||||
controllers.add(new TimeoutPreferenceController(context, KEY_SCREEN_TIMEOUT));
|
||||
controllers.add(new VrDisplayPreferenceController(context));
|
||||
controllers.add(new ShowOperatorNamePreferenceController(context));
|
||||
controllers.add(new ThemePreferenceController(context));
|
||||
|
||||
@@ -62,8 +62,8 @@ public class EncryptionInterstitial extends SettingsActivity {
|
||||
|
||||
@Override
|
||||
protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
|
||||
resid = SetupWizardUtils.getTheme(getIntent());
|
||||
super.onApplyThemeResource(theme, resid, first);
|
||||
final int new_resid = SetupWizardUtils.getTheme(this, getIntent());
|
||||
super.onApplyThemeResource(theme, new_resid, first);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -111,6 +111,8 @@ public class EncryptionInterstitial extends SettingsActivity {
|
||||
ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false);
|
||||
final boolean forFace = getActivity().getIntent()
|
||||
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE, false);
|
||||
final boolean forBiometrics = getActivity().getIntent()
|
||||
.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS, false);
|
||||
Intent intent = getActivity().getIntent();
|
||||
mRequestedPasswordQuality = intent.getIntExtra(EXTRA_PASSWORD_QUALITY, 0);
|
||||
mUnlockMethodIntent = intent.getParcelableExtra(EXTRA_UNLOCK_METHOD_INTENT);
|
||||
@@ -121,6 +123,8 @@ public class EncryptionInterstitial extends SettingsActivity {
|
||||
R.string.encryption_interstitial_message_pattern_for_fingerprint :
|
||||
forFace ?
|
||||
R.string.encryption_interstitial_message_pattern_for_face :
|
||||
forBiometrics ?
|
||||
R.string.encryption_interstitial_message_pattern_for_biometrics :
|
||||
R.string.encryption_interstitial_message_pattern;
|
||||
break;
|
||||
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
|
||||
@@ -129,6 +133,8 @@ public class EncryptionInterstitial extends SettingsActivity {
|
||||
R.string.encryption_interstitial_message_pin_for_fingerprint :
|
||||
forFace ?
|
||||
R.string.encryption_interstitial_message_pin_for_face :
|
||||
forBiometrics ?
|
||||
R.string.encryption_interstitial_message_pin_for_biometrics :
|
||||
R.string.encryption_interstitial_message_pin;
|
||||
break;
|
||||
default:
|
||||
@@ -136,6 +142,8 @@ public class EncryptionInterstitial extends SettingsActivity {
|
||||
R.string.encryption_interstitial_message_password_for_fingerprint :
|
||||
forFace ?
|
||||
R.string.encryption_interstitial_message_password_for_face :
|
||||
forBiometrics ?
|
||||
R.string.encryption_interstitial_message_password_for_biometrics :
|
||||
R.string.encryption_interstitial_message_password;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.PowerManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
@@ -166,12 +165,6 @@ public class FallbackHome extends Activity {
|
||||
.addCategory(Intent.CATEGORY_HOME);
|
||||
final ResolveInfo homeInfo = getPackageManager().resolveActivity(homeIntent, 0);
|
||||
if (Objects.equals(getPackageName(), homeInfo.activityInfo.packageName)) {
|
||||
if (UserManager.isSplitSystemUser()
|
||||
&& UserHandle.myUserId() == UserHandle.USER_SYSTEM) {
|
||||
// This avoids the situation where the system user has no home activity after
|
||||
// SUW and this activity continues to throw out warnings. See b/28870689.
|
||||
return;
|
||||
}
|
||||
Log.d(TAG, "User unlocked but no home; let's hope someone enables one soon?");
|
||||
mHandler.sendEmptyMessageDelayed(0, 500);
|
||||
} else {
|
||||
|
||||
@@ -56,6 +56,7 @@ import androidx.preference.Preference;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import com.android.settings.network.ProxySubscriptionManager;
|
||||
import com.android.settings.network.SubscriptionUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -310,7 +311,8 @@ public class IccLockSettings extends SettingsPreferenceFragment
|
||||
mTabHost.addTab(buildTabSpec(tag,
|
||||
String.valueOf(subInfo == null
|
||||
? getContext().getString(R.string.sim_editor_title, slot + 1)
|
||||
: subInfo.getDisplayName())));
|
||||
: SubscriptionUtil.getUniqueSubscriptionDisplayName(
|
||||
subInfo, getContext()))));
|
||||
}
|
||||
|
||||
mTabHost.setCurrentTabByTag(getTagForSlotId(mSlotId));
|
||||
|
||||
@@ -59,11 +59,11 @@ import android.widget.TextView;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settings.core.InstrumentedFragment;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.enterprise.ActionDisabledByAdminDialogHelper;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settings.password.ConfirmLockPattern;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
import com.android.settingslib.development.DevelopmentSettingsEnabler;
|
||||
|
||||
import com.google.android.setupcompat.template.FooterBarMixin;
|
||||
@@ -83,16 +83,16 @@ import java.util.List;
|
||||
*
|
||||
* This is the initial screen.
|
||||
*/
|
||||
public class MasterClear extends InstrumentedFragment implements OnGlobalLayoutListener {
|
||||
private static final String TAG = "MasterClear";
|
||||
public class MainClear extends InstrumentedFragment implements OnGlobalLayoutListener {
|
||||
private static final String TAG = "MainClear";
|
||||
|
||||
@VisibleForTesting
|
||||
static final int KEYGUARD_REQUEST = 55;
|
||||
@VisibleForTesting
|
||||
static final int CREDENTIAL_CONFIRM_REQUEST = 56;
|
||||
|
||||
private static final String KEY_SHOW_ESIM_RESET_CHECKBOX
|
||||
= "masterclear.allow_retain_esim_profiles_after_fdr";
|
||||
private static final String KEY_SHOW_ESIM_RESET_CHECKBOX =
|
||||
"masterclear.allow_retain_esim_profiles_after_fdr";
|
||||
|
||||
static final String ERASE_EXTERNAL_EXTRA = "erase_sd";
|
||||
static final String ERASE_ESIMS_EXTRA = "erase_esim";
|
||||
@@ -139,8 +139,11 @@ public class MasterClear extends InstrumentedFragment implements OnGlobalLayoutL
|
||||
*/
|
||||
private boolean runKeyguardConfirmation(int request) {
|
||||
Resources res = getActivity().getResources();
|
||||
return new ChooseLockSettingsHelper(getActivity(), this).launchConfirmationActivity(
|
||||
request, res.getText(R.string.master_clear_short_title));
|
||||
final ChooseLockSettingsHelper.Builder builder =
|
||||
new ChooseLockSettingsHelper.Builder(getActivity(), this);
|
||||
return builder.setRequestCode(request)
|
||||
.setTitle(res.getText(R.string.main_clear_short_title))
|
||||
.show();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -184,12 +187,15 @@ public class MasterClear extends InstrumentedFragment implements OnGlobalLayoutL
|
||||
final Bundle args = new Bundle();
|
||||
args.putBoolean(ERASE_EXTERNAL_EXTRA, mExternalStorage.isChecked());
|
||||
args.putBoolean(ERASE_ESIMS_EXTRA, mEsimStorage.isChecked());
|
||||
new SubSettingLauncher(getContext())
|
||||
.setDestination(MasterClearConfirm.class.getName())
|
||||
.setArguments(args)
|
||||
.setTitleRes(R.string.master_clear_confirm_title)
|
||||
.setSourceMetricsCategory(getMetricsCategory())
|
||||
.launch();
|
||||
final Intent intent = new Intent();
|
||||
intent.setClass(getContext(),
|
||||
com.android.settings.Settings.FactoryResetConfirmActivity.class);
|
||||
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, MainClearConfirm.class.getName());
|
||||
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
|
||||
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID,
|
||||
R.string.main_clear_confirm_title);
|
||||
intent.putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, getMetricsCategory());
|
||||
getContext().startActivity(intent);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -293,7 +299,7 @@ public class MasterClear extends InstrumentedFragment implements OnGlobalLayoutL
|
||||
if (mScrollView != null) {
|
||||
mScrollView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
}
|
||||
mScrollView = mContentView.findViewById(R.id.master_clear_scrollview);
|
||||
mScrollView = mContentView.findViewById(R.id.main_clear_scrollview);
|
||||
|
||||
/*
|
||||
* If the external storage is emulated, it will be erased with a factory
|
||||
@@ -351,9 +357,9 @@ public class MasterClear extends InstrumentedFragment implements OnGlobalLayoutL
|
||||
final UserManager um = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
|
||||
loadAccountList(um);
|
||||
final StringBuffer contentDescription = new StringBuffer();
|
||||
final View masterClearContainer = mContentView.findViewById(R.id.master_clear_container);
|
||||
getContentDescription(masterClearContainer, contentDescription);
|
||||
masterClearContainer.setContentDescription(contentDescription);
|
||||
final View mainClearContainer = mContentView.findViewById(R.id.main_clear_container);
|
||||
getContentDescription(mainClearContainer, contentDescription);
|
||||
mainClearContainer.setContentDescription(contentDescription);
|
||||
|
||||
// Set the status of initiateButton based on scrollview
|
||||
mScrollView.setOnScrollChangeListener(new OnScrollChangeListener() {
|
||||
@@ -421,7 +427,7 @@ public class MasterClear extends InstrumentedFragment implements OnGlobalLayoutL
|
||||
final FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
|
||||
mixin.setPrimaryButton(
|
||||
new FooterButton.Builder(getActivity())
|
||||
.setText(R.string.master_clear_button_text)
|
||||
.setText(R.string.main_clear_button_text)
|
||||
.setListener(mInitiateListener)
|
||||
.setButtonType(ButtonType.OTHER)
|
||||
.setTheme(R.style.SudGlifButton_Primary)
|
||||
@@ -472,15 +478,15 @@ public class MasterClear extends InstrumentedFragment implements OnGlobalLayoutL
|
||||
final int profileId = userInfo.id;
|
||||
final UserHandle userHandle = new UserHandle(profileId);
|
||||
Account[] accounts = mgr.getAccountsAsUser(profileId);
|
||||
final int N = accounts.length;
|
||||
if (N == 0) {
|
||||
final int accountLength = accounts.length;
|
||||
if (accountLength == 0) {
|
||||
continue;
|
||||
}
|
||||
accountsCount += N;
|
||||
accountsCount += accountLength;
|
||||
|
||||
AuthenticatorDescription[] descs = AccountManager.get(context)
|
||||
.getAuthenticatorTypesAsUser(profileId);
|
||||
final int M = descs.length;
|
||||
final int descLength = descs.length;
|
||||
|
||||
if (profilesSize > 1) {
|
||||
View titleView = Utils.inflateCategoryHeader(inflater, contents);
|
||||
@@ -490,10 +496,10 @@ public class MasterClear extends InstrumentedFragment implements OnGlobalLayoutL
|
||||
contents.addView(titleView);
|
||||
}
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
for (int i = 0; i < accountLength; i++) {
|
||||
Account account = accounts[i];
|
||||
AuthenticatorDescription desc = null;
|
||||
for (int j = 0; j < M; j++) {
|
||||
for (int j = 0; j < descLength; j++) {
|
||||
if (account.type.equals(descs[j].type)) {
|
||||
desc = descs[j];
|
||||
break;
|
||||
@@ -521,7 +527,7 @@ public class MasterClear extends InstrumentedFragment implements OnGlobalLayoutL
|
||||
icon = context.getPackageManager().getDefaultActivityIcon();
|
||||
}
|
||||
|
||||
View child = inflater.inflate(R.layout.master_clear_account, contents, false);
|
||||
View child = inflater.inflate(R.layout.main_clear_account, contents, false);
|
||||
((ImageView) child.findViewById(android.R.id.icon)).setImageDrawable(icon);
|
||||
((TextView) child.findViewById(android.R.id.title)).setText(account.name);
|
||||
contents.addView(child);
|
||||
@@ -549,7 +555,7 @@ public class MasterClear extends InstrumentedFragment implements OnGlobalLayoutL
|
||||
.hasBaseUserRestriction(context, UserManager.DISALLOW_FACTORY_RESET,
|
||||
UserHandle.myUserId());
|
||||
if (disallow && !Utils.isDemoUser(context)) {
|
||||
return inflater.inflate(R.layout.master_clear_disallowed_screen, null);
|
||||
return inflater.inflate(R.layout.main_clear_disallowed_screen, null);
|
||||
} else if (admin != null) {
|
||||
new ActionDisabledByAdminDialogHelper(getActivity())
|
||||
.prepareDialogBuilder(UserManager.DISALLOW_FACTORY_RESET, admin)
|
||||
@@ -558,7 +564,7 @@ public class MasterClear extends InstrumentedFragment implements OnGlobalLayoutL
|
||||
return new View(getContext());
|
||||
}
|
||||
|
||||
mContentView = inflater.inflate(R.layout.master_clear, null);
|
||||
mContentView = inflater.inflate(R.layout.main_clear, null);
|
||||
|
||||
establishInitialState();
|
||||
return mContentView;
|
||||
@@ -21,26 +21,20 @@ import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.admin.FactoryResetProtectionPolicy;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.graphics.Color;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.PowerManager;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.service.oemlock.OemLockManager;
|
||||
import android.service.persistentdata.PersistentDataBlockManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.telephony.UiccSlotInfo;
|
||||
import android.telephony.euicc.EuiccManager;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -60,8 +54,6 @@ import com.google.android.setupcompat.template.FooterButton.ButtonType;
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
import com.google.android.setupdesign.GlifLayout;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Confirm and execute a reset of the device to a clean "just out of the box"
|
||||
* state. Multiple confirmations are required: first, a general "are you sure
|
||||
@@ -72,8 +64,8 @@ import java.util.Arrays;
|
||||
*
|
||||
* This is the confirmation screen.
|
||||
*/
|
||||
public class MasterClearConfirm extends InstrumentedFragment {
|
||||
private final static String TAG = "MasterClearConfirm";
|
||||
public class MainClearConfirm extends InstrumentedFragment {
|
||||
private static final String TAG = "MainClearConfirm";
|
||||
|
||||
@VisibleForTesting View mContentView;
|
||||
private boolean mEraseSdCard;
|
||||
@@ -91,89 +83,6 @@ public class MasterClearConfirm extends InstrumentedFragment {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the eSIM slot is in an error state, display a dialog to warn users that their eSIM
|
||||
// profiles may not be fully deleted during FDR.
|
||||
if (shouldShowEsimEraseFailureDialog()) {
|
||||
Log.e(TAG, "eUICC card is in an error state. Display a dialog to warn the user.");
|
||||
showEsimErrorDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
performFactoryReset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the user choose to erase eSIM profile but the eUICC card is in an error
|
||||
* state.
|
||||
*/
|
||||
private boolean shouldShowEsimEraseFailureDialog() {
|
||||
EuiccManager euiccManager = getActivity().getSystemService(EuiccManager.class);
|
||||
TelephonyManager telephonyManager =
|
||||
getActivity().getSystemService(TelephonyManager.class);
|
||||
|
||||
if (euiccManager == null || !euiccManager.isEnabled()) {
|
||||
Log.i(
|
||||
TAG,
|
||||
"eSIM manager is disabled. No need to check eSIM slot before FDR.");
|
||||
return false;
|
||||
}
|
||||
if (!mEraseEsims) {
|
||||
Log.i(
|
||||
TAG,
|
||||
"eSIM does not need to be reset. No need to check eSIM slot before FDR.");
|
||||
return false;
|
||||
}
|
||||
UiccSlotInfo[] slotInfos = telephonyManager.getUiccSlotsInfo();
|
||||
if (slotInfos == null) {
|
||||
Log.i(TAG, "Unable to get UICC slots.");
|
||||
return false;
|
||||
}
|
||||
// If getIsEuicc() returns false for an eSIM slot, it means the eSIM is in the error
|
||||
// state.
|
||||
return Arrays.stream(slotInfos).anyMatch(
|
||||
slot -> slot != null && !slot.isRemovable() && !slot.getIsEuicc());
|
||||
}
|
||||
|
||||
private void showEsimErrorDialog() {
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.fdr_esim_failure_title)
|
||||
.setMessage(R.string.fdr_esim_failure_text)
|
||||
.setNeutralButton(R.string.dlg_cancel,
|
||||
(DialogInterface.OnClickListener) (dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setNegativeButton(R.string.fdr_esim_failure_reboot_btn,
|
||||
(DialogInterface.OnClickListener) (dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
PowerManager pm = (PowerManager) getActivity()
|
||||
.getSystemService(Context.POWER_SERVICE);
|
||||
pm.reboot(null);
|
||||
})
|
||||
.setPositiveButton(R.string.lockpassword_continue_label,
|
||||
(DialogInterface.OnClickListener) (dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
showContinueFdrDialog();
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showContinueFdrDialog() {
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.fdr_continue_title)
|
||||
.setMessage(R.string.fdr_continue_text)
|
||||
.setNegativeButton(R.string.dlg_cancel,
|
||||
(DialogInterface.OnClickListener) (dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setPositiveButton(R.string.fdr_continue_btn,
|
||||
(DialogInterface.OnClickListener) (dialog, which) -> {
|
||||
dialog.dismiss();
|
||||
performFactoryReset();
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private void performFactoryReset() {
|
||||
final PersistentDataBlockManager pdbManager = (PersistentDataBlockManager)
|
||||
getActivity().getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
|
||||
|
||||
@@ -194,7 +103,7 @@ public class MasterClearConfirm extends InstrumentedFragment {
|
||||
mProgressDialog.hide();
|
||||
if (getActivity() != null) {
|
||||
getActivity().setRequestedOrientation(mOldOrientation);
|
||||
doMasterClear();
|
||||
doMainClear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +121,7 @@ public class MasterClearConfirm extends InstrumentedFragment {
|
||||
}
|
||||
}.execute();
|
||||
} else {
|
||||
doMasterClear();
|
||||
doMainClear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,9 +130,9 @@ public class MasterClearConfirm extends InstrumentedFragment {
|
||||
progressDialog.setIndeterminate(true);
|
||||
progressDialog.setCancelable(false);
|
||||
progressDialog.setTitle(
|
||||
getActivity().getString(R.string.master_clear_progress_title));
|
||||
getActivity().getString(R.string.main_clear_progress_title));
|
||||
progressDialog.setMessage(
|
||||
getActivity().getString(R.string.master_clear_progress_text));
|
||||
getActivity().getString(R.string.main_clear_progress_text));
|
||||
return progressDialog;
|
||||
}
|
||||
};
|
||||
@@ -271,11 +180,11 @@ public class MasterClearConfirm extends InstrumentedFragment {
|
||||
return !WizardManagerHelper.isDeviceProvisioned(getActivity());
|
||||
}
|
||||
|
||||
private void doMasterClear() {
|
||||
private void doMainClear() {
|
||||
Intent intent = new Intent(Intent.ACTION_FACTORY_RESET);
|
||||
intent.setPackage("android");
|
||||
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
|
||||
intent.putExtra(Intent.EXTRA_REASON, "MasterClearConfirm");
|
||||
intent.putExtra(Intent.EXTRA_REASON, "MainClearConfirm");
|
||||
intent.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, mEraseSdCard);
|
||||
intent.putExtra(Intent.EXTRA_WIPE_ESIMS, mEraseEsims);
|
||||
getActivity().sendBroadcast(intent);
|
||||
@@ -291,7 +200,7 @@ public class MasterClearConfirm extends InstrumentedFragment {
|
||||
final FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
|
||||
mixin.setPrimaryButton(
|
||||
new FooterButton.Builder(getActivity())
|
||||
.setText(R.string.master_clear_button_text)
|
||||
.setText(R.string.main_clear_button_text)
|
||||
.setListener(mFinalClickListener)
|
||||
.setButtonType(ButtonType.OTHER)
|
||||
.setTheme(R.style.SudGlifButton_Primary)
|
||||
@@ -321,7 +230,7 @@ public class MasterClearConfirm extends InstrumentedFragment {
|
||||
getActivity(), UserManager.DISALLOW_FACTORY_RESET, UserHandle.myUserId());
|
||||
if (RestrictedLockUtilsInternal.hasBaseUserRestriction(getActivity(),
|
||||
UserManager.DISALLOW_FACTORY_RESET, UserHandle.myUserId())) {
|
||||
return inflater.inflate(R.layout.master_clear_disallowed_screen, null);
|
||||
return inflater.inflate(R.layout.main_clear_disallowed_screen, null);
|
||||
} else if (admin != null) {
|
||||
new ActionDisabledByAdminDialogHelper(getActivity())
|
||||
.prepareDialogBuilder(UserManager.DISALLOW_FACTORY_RESET, admin)
|
||||
@@ -329,7 +238,7 @@ public class MasterClearConfirm extends InstrumentedFragment {
|
||||
.show();
|
||||
return new View(getActivity());
|
||||
}
|
||||
mContentView = inflater.inflate(R.layout.master_clear_confirm, null);
|
||||
mContentView = inflater.inflate(R.layout.main_clear_confirm, null);
|
||||
setUpActionBarAndTitle();
|
||||
establishFinalConfirmationState();
|
||||
setAccessibilityTitle();
|
||||
@@ -351,7 +260,7 @@ public class MasterClearConfirm extends InstrumentedFragment {
|
||||
void setSubtitle() {
|
||||
if (mEraseEsims) {
|
||||
((TextView) mContentView.findViewById(R.id.sud_layout_description))
|
||||
.setText(R.string.master_clear_final_desc_esim);
|
||||
.setText(R.string.main_clear_final_desc_esim);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,9 +270,9 @@ public class MasterClearConfirm extends InstrumentedFragment {
|
||||
|
||||
Bundle args = getArguments();
|
||||
mEraseSdCard = args != null
|
||||
&& args.getBoolean(MasterClear.ERASE_EXTERNAL_EXTRA);
|
||||
&& args.getBoolean(MainClear.ERASE_EXTERNAL_EXTRA);
|
||||
mEraseEsims = args != null
|
||||
&& args.getBoolean(MasterClear.ERASE_ESIMS_EXTRA);
|
||||
&& args.getBoolean(MainClear.ERASE_ESIMS_EXTRA);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.settings;
|
||||
|
||||
import static android.view.HapticFeedbackConstants.CLOCK_TICK;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
@@ -37,6 +39,7 @@ public class PointerSpeedPreference extends SeekBarDialogPreference implements
|
||||
private boolean mRestoredOldState;
|
||||
|
||||
private boolean mTouchInProgress;
|
||||
private int mLastProgress = -1;
|
||||
|
||||
private ContentObserver mSpeedObserver = new ContentObserver(new Handler()) {
|
||||
@Override
|
||||
@@ -70,12 +73,17 @@ public class PointerSpeedPreference extends SeekBarDialogPreference implements
|
||||
mOldSpeed = mIm.getPointerSpeed(getContext());
|
||||
mSeekBar.setProgress(mOldSpeed - InputManager.MIN_POINTER_SPEED);
|
||||
mSeekBar.setOnSeekBarChangeListener(this);
|
||||
mSeekBar.setContentDescription(getTitle());
|
||||
}
|
||||
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
|
||||
if (!mTouchInProgress) {
|
||||
mIm.tryPointerSpeed(progress + InputManager.MIN_POINTER_SPEED);
|
||||
}
|
||||
if (progress != mLastProgress) {
|
||||
seekBar.performHapticFeedback(CLOCK_TICK);
|
||||
mLastProgress = progress;
|
||||
}
|
||||
}
|
||||
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
@@ -62,7 +62,8 @@ public class RegulatoryInfoDisplayActivity extends Activity implements
|
||||
super.onCreate(savedInstanceState);
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.regulatory_labels)
|
||||
.setOnDismissListener(this);
|
||||
.setOnDismissListener(this)
|
||||
.setPositiveButton(android.R.string.ok, null /* onClickListener */);
|
||||
|
||||
boolean regulatoryInfoDrawableExists = false;
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.euicc.EuiccManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
@@ -46,6 +47,7 @@ import androidx.annotation.VisibleForTesting;
|
||||
import com.android.settings.core.InstrumentedFragment;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.enterprise.ActionDisabledByAdminDialogHelper;
|
||||
import com.android.settings.network.SubscriptionUtil;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settings.password.ConfirmLockPattern;
|
||||
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||
@@ -53,7 +55,9 @@ import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
import com.android.settingslib.development.DevelopmentSettingsEnabler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Confirm and execute a reset of the device's network settings to a clean "just out of the box"
|
||||
@@ -92,8 +96,11 @@ public class ResetNetwork extends InstrumentedFragment {
|
||||
*/
|
||||
private boolean runKeyguardConfirmation(int request) {
|
||||
Resources res = getActivity().getResources();
|
||||
return new ChooseLockSettingsHelper(getActivity(), this).launchConfirmationActivity(
|
||||
request, res.getText(R.string.reset_network_title));
|
||||
final ChooseLockSettingsHelper.Builder builder =
|
||||
new ChooseLockSettingsHelper.Builder(getActivity(), this);
|
||||
return builder.setRequestCode(request)
|
||||
.setTitle(res.getText(R.string.reset_network_title))
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,7 +116,7 @@ public class ResetNetwork extends InstrumentedFragment {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
showFinalConfirmation();
|
||||
} else {
|
||||
establishInitialState();
|
||||
establishInitialState(getActiveSubscriptionInfoList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +129,7 @@ public class ResetNetwork extends InstrumentedFragment {
|
||||
args.putInt(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
|
||||
subscription.getSubscriptionId());
|
||||
}
|
||||
args.putBoolean(MasterClear.ERASE_ESIMS_EXTRA,
|
||||
args.putBoolean(MainClear.ERASE_ESIMS_EXTRA,
|
||||
mEsimContainer.getVisibility() == View.VISIBLE && mEsimCheckbox.isChecked());
|
||||
new SubSettingLauncher(getContext())
|
||||
.setDestination(ResetNetworkConfirm.class.getName())
|
||||
@@ -158,14 +165,15 @@ public class ResetNetwork extends InstrumentedFragment {
|
||||
* inflate each view, caching all of the widget pointers we'll need at the
|
||||
* time, then simply reuse the inflated views directly whenever we need
|
||||
* to change contents.
|
||||
*
|
||||
* @param subscriptionsList is a list of SubscriptionInfo(s) which allow user to select from
|
||||
*/
|
||||
private void establishInitialState() {
|
||||
private void establishInitialState(List<SubscriptionInfo> subscriptionsList) {
|
||||
mSubscriptionSpinner = (Spinner) mContentView.findViewById(R.id.reset_network_subscription);
|
||||
mEsimContainer = mContentView.findViewById(R.id.erase_esim_container);
|
||||
mEsimCheckbox = mContentView.findViewById(R.id.erase_esim);
|
||||
|
||||
mSubscriptions = SubscriptionManager.from(getActivity())
|
||||
.getActiveSubscriptionInfoList();
|
||||
mSubscriptions = subscriptionsList;
|
||||
if (mSubscriptions != null && mSubscriptions.size() > 0) {
|
||||
// Get the default subscription in the order of data, voice, sms, first up.
|
||||
int defaultSubscription = SubscriptionManager.getDefaultDataSubscriptionId();
|
||||
@@ -187,7 +195,8 @@ public class ResetNetwork extends InstrumentedFragment {
|
||||
// Set the first selected value to the default
|
||||
selectedIndex = subscriptionNames.size();
|
||||
}
|
||||
String name = record.getDisplayName().toString();
|
||||
String name = SubscriptionUtil.getUniqueSubscriptionDisplayName(
|
||||
record, getContext()).toString();
|
||||
if (TextUtils.isEmpty(name)) {
|
||||
name = record.getNumber();
|
||||
}
|
||||
@@ -228,6 +237,31 @@ public class ResetNetwork extends InstrumentedFragment {
|
||||
}
|
||||
}
|
||||
|
||||
private List<SubscriptionInfo> getActiveSubscriptionInfoList() {
|
||||
SubscriptionManager mgr = getActivity().getSystemService(SubscriptionManager.class);
|
||||
if (mgr == null) {
|
||||
Log.w(TAG, "No SubscriptionManager");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Optional.ofNullable(mgr.getActiveSubscriptionInfoList())
|
||||
.orElse(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// update options if subcription has been changed
|
||||
List<SubscriptionInfo> updatedSubscriptions = getActiveSubscriptionInfoList();
|
||||
if ((mSubscriptions != null)
|
||||
&& (mSubscriptions.size() == updatedSubscriptions.size())
|
||||
&& mSubscriptions.containsAll(updatedSubscriptions)) {
|
||||
return;
|
||||
}
|
||||
Log.d(TAG, "subcription list changed");
|
||||
establishInitialState(updatedSubscriptions);
|
||||
}
|
||||
|
||||
private boolean showEuiccSettings(Context context) {
|
||||
EuiccManager euiccManager =
|
||||
(EuiccManager) context.getSystemService(Context.EUICC_SERVICE);
|
||||
@@ -258,7 +292,7 @@ public class ResetNetwork extends InstrumentedFragment {
|
||||
|
||||
mContentView = inflater.inflate(R.layout.reset_network, null);
|
||||
|
||||
establishInitialState();
|
||||
establishInitialState(getActiveSubscriptionInfoList());
|
||||
return mContentView;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,11 +33,14 @@ import android.net.wifi.WifiManager;
|
||||
import android.net.wifi.p2p.WifiP2pManager;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Looper;
|
||||
import android.os.RecoverySystem;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -50,7 +53,7 @@ import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.android.settings.core.InstrumentedFragment;
|
||||
import com.android.settings.enterprise.ActionDisabledByAdminDialogHelper;
|
||||
import com.android.settings.network.ApnSettings;
|
||||
import com.android.settings.network.apn.ApnSettings;
|
||||
import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
|
||||
/**
|
||||
@@ -64,6 +67,7 @@ import com.android.settingslib.RestrictedLockUtilsInternal;
|
||||
* This is the confirmation screen.
|
||||
*/
|
||||
public class ResetNetworkConfirm extends InstrumentedFragment {
|
||||
private static final String TAG = "ResetNetworkConfirm";
|
||||
|
||||
@VisibleForTesting View mContentView;
|
||||
@VisibleForTesting boolean mEraseEsim;
|
||||
@@ -72,12 +76,15 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
|
||||
private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||
private ProgressDialog mProgressDialog;
|
||||
private AlertDialog mAlertDialog;
|
||||
private OnSubscriptionsChangedListener mSubscriptionsChangedListener;
|
||||
|
||||
/**
|
||||
* Async task used to do all reset task. If error happens during
|
||||
* erasing eSIM profiles or timeout, an error msg is shown.
|
||||
*/
|
||||
private class ResetNetworkTask extends AsyncTask<Void, Void, Boolean> {
|
||||
private static final String TAG = "ResetNetworkTask";
|
||||
|
||||
private final Context mContext;
|
||||
private final String mPackageName;
|
||||
|
||||
@@ -136,6 +143,8 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
|
||||
}
|
||||
|
||||
restoreDefaultApn(mContext);
|
||||
Log.d(TAG, "network factoryReset complete. succeeded: "
|
||||
+ String.valueOf(isResetSucceed));
|
||||
return isResetSucceed;
|
||||
}
|
||||
|
||||
@@ -168,6 +177,18 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
|
||||
return;
|
||||
}
|
||||
|
||||
// abandon execution if subscription no longer active
|
||||
if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
|
||||
SubscriptionManager mgr = getSubscriptionManager();
|
||||
// always remove listener
|
||||
stopMonitorSubscriptionChange(mgr);
|
||||
if (!isSubscriptionRemainActive(mgr, mSubId)) {
|
||||
Log.w(TAG, "subId " + mSubId + " disappear when confirm");
|
||||
mActivity.finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mProgressDialog = getProgressDialog(mActivity);
|
||||
mProgressDialog.show();
|
||||
|
||||
@@ -195,7 +216,7 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
|
||||
progressDialog.setIndeterminate(true);
|
||||
progressDialog.setCancelable(false);
|
||||
progressDialog.setMessage(
|
||||
context.getString(R.string.master_clear_progress_text));
|
||||
context.getString(R.string.main_clear_progress_text));
|
||||
return progressDialog;
|
||||
}
|
||||
|
||||
@@ -258,10 +279,60 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
|
||||
if (args != null) {
|
||||
mSubId = args.getInt(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
|
||||
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
||||
mEraseEsim = args.getBoolean(MasterClear.ERASE_ESIMS_EXTRA);
|
||||
mEraseEsim = args.getBoolean(MainClear.ERASE_ESIMS_EXTRA);
|
||||
}
|
||||
|
||||
mActivity = getActivity();
|
||||
|
||||
if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
|
||||
return;
|
||||
}
|
||||
// close confirmation dialog when reset specific subscription
|
||||
// but removed priori to the confirmation button been pressed
|
||||
startMonitorSubscriptionChange(getSubscriptionManager());
|
||||
}
|
||||
|
||||
private SubscriptionManager getSubscriptionManager() {
|
||||
SubscriptionManager mgr = mActivity.getSystemService(SubscriptionManager.class);
|
||||
if (mgr == null) {
|
||||
Log.w(TAG, "No SubscriptionManager");
|
||||
}
|
||||
return mgr;
|
||||
}
|
||||
|
||||
private void startMonitorSubscriptionChange(SubscriptionManager mgr) {
|
||||
if (mgr == null) {
|
||||
return;
|
||||
}
|
||||
// update monitor listener
|
||||
mSubscriptionsChangedListener = new OnSubscriptionsChangedListener(
|
||||
Looper.getMainLooper()) {
|
||||
@Override
|
||||
public void onSubscriptionsChanged() {
|
||||
SubscriptionManager mgr = getSubscriptionManager();
|
||||
if (isSubscriptionRemainActive(mgr, mSubId)) {
|
||||
return;
|
||||
}
|
||||
// close UI if subscription no longer active
|
||||
Log.w(TAG, "subId " + mSubId + " no longer active.");
|
||||
stopMonitorSubscriptionChange(mgr);
|
||||
mActivity.finish();
|
||||
}
|
||||
};
|
||||
mgr.addOnSubscriptionsChangedListener(
|
||||
mActivity.getMainExecutor(), mSubscriptionsChangedListener);
|
||||
}
|
||||
|
||||
private boolean isSubscriptionRemainActive(SubscriptionManager mgr, int subscriptionId) {
|
||||
return (mgr == null) ? false : (mgr.getActiveSubscriptionInfo(subscriptionId) != null);
|
||||
}
|
||||
|
||||
private void stopMonitorSubscriptionChange(SubscriptionManager mgr) {
|
||||
if ((mgr == null) || (mSubscriptionsChangedListener == null)) {
|
||||
return;
|
||||
}
|
||||
mgr.removeOnSubscriptionsChangedListener(mSubscriptionsChangedListener);
|
||||
mSubscriptionsChangedListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -276,6 +347,7 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
|
||||
if (mAlertDialog != null) {
|
||||
mAlertDialog.dismiss();
|
||||
}
|
||||
stopMonitorSubscriptionChange(getSubscriptionManager());
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,16 @@ package com.android.settings;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.FeatureFlagUtils;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.settings.core.FeatureFlags;
|
||||
import com.android.settings.enterprise.EnterprisePrivacySettings;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.security.SecuritySettingsFeatureProvider;
|
||||
|
||||
import com.google.android.setupdesign.util.ThemeHelper;
|
||||
|
||||
/**
|
||||
* Top-level Settings activity
|
||||
@@ -37,6 +43,8 @@ public class Settings extends SettingsActivity {
|
||||
public static class CreateShortcutActivity extends SettingsActivity { /* empty */ }
|
||||
public static class FaceSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class FingerprintSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class CombinedBiometricSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class CombinedBiometricProfileSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class TetherSettingsActivity extends SettingsActivity {
|
||||
// TODO(b/147675042): Clean the override up when we enable the new Fragment persistently.
|
||||
@Override
|
||||
@@ -78,10 +86,10 @@ public class Settings extends SettingsActivity {
|
||||
public static class DataSaverSummaryActivity extends SettingsActivity{ /* empty */ }
|
||||
public static class DateTimeSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class PrivateVolumeForgetActivity extends SettingsActivity { /* empty */ }
|
||||
public static class PrivateVolumeSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class PublicVolumeSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class WifiSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class WifiSettings2Activity extends SettingsActivity { /* empty */ }
|
||||
public static class NetworkProviderSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class WifiP2pSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class AvailableVirtualKeyboardActivity extends SettingsActivity { /* empty */ }
|
||||
public static class KeyboardLayoutPickerActivity extends SettingsActivity { /* empty */ }
|
||||
@@ -95,6 +103,7 @@ public class Settings extends SettingsActivity {
|
||||
public static class DisplaySettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class NightDisplaySettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class NightDisplaySuggestionActivity extends NightDisplaySettingsActivity { /* empty */ }
|
||||
public static class SmartAutoRotateSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class MyDeviceInfoActivity extends SettingsActivity { /* empty */ }
|
||||
public static class ModuleLicensesActivity extends SettingsActivity { /* empty */ }
|
||||
public static class ApplicationSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
@@ -110,14 +119,79 @@ public class Settings extends SettingsActivity {
|
||||
public static class AccessibilityInversionSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class AccessibilityContrastSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class AccessibilityDaltonizerSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class SecurityDashboardActivity extends SettingsActivity { /* empty */ }
|
||||
/**
|
||||
* Activity for lockscreen settings.
|
||||
*/
|
||||
public static class LockScreenSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
/**
|
||||
* Activity for Reduce Bright Colors.
|
||||
*/
|
||||
public static class ReduceBrightColorsSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
/** Activity for the security dashboard. */
|
||||
public static class SecurityDashboardActivity extends SettingsActivity {
|
||||
|
||||
/** Whether the given fragment is allowed. */
|
||||
@VisibleForTesting
|
||||
@Override
|
||||
public boolean isValidFragment(String fragmentName) {
|
||||
return super.isValidFragment(fragmentName)
|
||||
|| (fragmentName != null
|
||||
&& TextUtils.equals(fragmentName, getAlternativeFragmentName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInitialFragmentName(Intent intent) {
|
||||
final String alternativeFragmentName = getAlternativeFragmentName();
|
||||
if (alternativeFragmentName != null) {
|
||||
return alternativeFragmentName;
|
||||
}
|
||||
|
||||
return super.getInitialFragmentName(intent);
|
||||
}
|
||||
|
||||
private String getAlternativeFragmentName() {
|
||||
String alternativeFragmentClassname = null;
|
||||
final SecuritySettingsFeatureProvider securitySettingsFeatureProvider =
|
||||
FeatureFactory.getFactory(this).getSecuritySettingsFeatureProvider();
|
||||
if (securitySettingsFeatureProvider.hasAlternativeSecuritySettingsFragment()) {
|
||||
alternativeFragmentClassname = securitySettingsFeatureProvider
|
||||
.getAlternativeSecuritySettingsFragmentClassname();
|
||||
}
|
||||
return alternativeFragmentClassname;
|
||||
}
|
||||
}
|
||||
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 PrivacyDashboardActivity extends SettingsActivity { /* empty */ }
|
||||
public static class PrivacySettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class FactoryResetActivity extends SettingsActivity { /* empty */ }
|
||||
public static class FactoryResetActivity extends SettingsActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedState) {
|
||||
setTheme(SetupWizardUtils.getTheme(this, getIntent()));
|
||||
ThemeHelper.trySetDynamicColor(this);
|
||||
super.onCreate(savedState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isToolbarEnabled() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static class FactoryResetConfirmActivity extends SettingsActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedState) {
|
||||
setTheme(SetupWizardUtils.getTheme(this, getIntent()));
|
||||
ThemeHelper.trySetDynamicColor(this);
|
||||
super.onCreate(savedState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isToolbarEnabled() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static class RunningServicesActivity extends SettingsActivity { /* empty */ }
|
||||
public static class BatterySaverSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class BatterySaverScheduleSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
@@ -168,10 +242,6 @@ public class Settings extends SettingsActivity {
|
||||
public static class ManageDomainUrlsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class AutomaticStorageManagerSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class GamesStorageActivity extends SettingsActivity { /* empty */ }
|
||||
public static class MoviesStorageActivity extends SettingsActivity { /* empty */ }
|
||||
public static class PhotosStorageActivity extends SettingsActivity {
|
||||
/* empty */
|
||||
}
|
||||
public static class GestureNavigationSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class InteractAcrossProfilesSettingsActivity extends SettingsActivity {
|
||||
/* empty */
|
||||
@@ -187,6 +257,8 @@ public class Settings extends SettingsActivity {
|
||||
public static class OverlaySettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class ManageExternalStorageActivity extends SettingsActivity { /* empty */ }
|
||||
public static class AppManageExternalStorageActivity extends SettingsActivity { /* empty */ }
|
||||
public static class MediaManagementAppsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class AppMediaManagementAppsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class WriteSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class ChangeWifiStateActivity extends SettingsActivity { /* empty */ }
|
||||
public static class AppDrawOverlaySettingsActivity extends SettingsActivity { /* empty */ }
|
||||
@@ -198,6 +270,10 @@ public class Settings extends SettingsActivity {
|
||||
public static class ManagedProfileSettingsActivity extends SettingsActivity { /* empty */ }
|
||||
public static class DeletionHelperActivity extends SettingsActivity { /* empty */ }
|
||||
|
||||
/** Actviity to manage apps with {@link android.Manifest.permission#SCHEDULE_EXACT_ALARM} */
|
||||
public static class AlarmsAndRemindersActivity extends SettingsActivity {/* empty */ }
|
||||
/** App specific version of {@link AlarmsAndRemindersActivity} */
|
||||
public static class AlarmsAndRemindersAppActivity extends SettingsActivity {/* empty */ }
|
||||
|
||||
public static class ApnEditorActivity extends SettingsActivity { /* empty */ }
|
||||
public static class ChooseAccountActivity extends SettingsActivity { /* empty */ }
|
||||
@@ -209,7 +285,11 @@ public class Settings extends SettingsActivity {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (!EnterprisePrivacySettings.isPageEnabled(this)) {
|
||||
if (FeatureFactory.getFactory(this)
|
||||
.getEnterprisePrivacyFeatureProvider(this)
|
||||
.showParentalControls()) {
|
||||
finish();
|
||||
} else if (!EnterprisePrivacySettings.isPageEnabled(this)) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -219,9 +299,7 @@ public class Settings extends SettingsActivity {
|
||||
public static class BluetoothDeviceDetailActivity extends SettingsActivity { /* empty */ }
|
||||
public static class WifiCallingDisclaimerActivity extends SettingsActivity { /* empty */ }
|
||||
public static class MobileNetworkListActivity extends SettingsActivity {}
|
||||
public static class GlobalActionsPanelSettingsActivity extends SettingsActivity {}
|
||||
public static class PowerMenuSettingsActivity extends SettingsActivity {}
|
||||
public static class QuickControlsSettingsActivity extends SettingsActivity {}
|
||||
/**
|
||||
* Activity for BugReportHandlerPicker.
|
||||
*/
|
||||
@@ -231,7 +309,6 @@ public class Settings extends SettingsActivity {
|
||||
public static class NetworkDashboardActivity extends SettingsActivity {}
|
||||
public static class ConnectedDeviceDashboardActivity extends SettingsActivity {}
|
||||
public static class PowerUsageSummaryActivity extends SettingsActivity { /* empty */ }
|
||||
public static class AppAndNotificationDashboardActivity extends SettingsActivity {}
|
||||
public static class StorageDashboardActivity extends SettingsActivity {}
|
||||
public static class AccountDashboardActivity extends SettingsActivity {}
|
||||
public static class SystemDashboardActivity extends SettingsActivity {}
|
||||
@@ -241,4 +318,8 @@ public class Settings extends SettingsActivity {
|
||||
*/
|
||||
public static class MediaControlsSettingsActivity extends SettingsActivity {}
|
||||
|
||||
/**
|
||||
* Activity for AppDashboard.
|
||||
*/
|
||||
public static class AppDashboardActivity extends SettingsActivity {}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ import com.android.settings.dashboard.DashboardFeatureProvider;
|
||||
import com.android.settings.homepage.TopLevelSettings;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.wfd.WifiDisplaySettings;
|
||||
import com.android.settings.widget.SwitchBar;
|
||||
import com.android.settings.widget.SettingsMainSwitchBar;
|
||||
import com.android.settingslib.core.instrumentation.Instrumentable;
|
||||
import com.android.settingslib.core.instrumentation.SharedPreferencesLogger;
|
||||
import com.android.settingslib.development.DevelopmentSettingsEnabler;
|
||||
@@ -172,7 +172,7 @@ public class SettingsActivity extends SettingsBaseActivity
|
||||
}
|
||||
};
|
||||
|
||||
private SwitchBar mSwitchBar;
|
||||
private SettingsMainSwitchBar mMainSwitch;
|
||||
|
||||
private Button mNextButton;
|
||||
|
||||
@@ -181,8 +181,8 @@ public class SettingsActivity extends SettingsBaseActivity
|
||||
|
||||
private DashboardFeatureProvider mDashboardFeatureProvider;
|
||||
|
||||
public SwitchBar getSwitchBar() {
|
||||
return mSwitchBar;
|
||||
public SettingsMainSwitchBar getSwitchBar() {
|
||||
return mMainSwitch;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -215,7 +215,7 @@ public class SettingsActivity extends SettingsBaseActivity
|
||||
private String getMetricsTag() {
|
||||
String tag = null;
|
||||
if (getIntent() != null && getIntent().hasExtra(EXTRA_SHOW_FRAGMENT)) {
|
||||
tag = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
|
||||
tag = getInitialFragmentName(getIntent());
|
||||
}
|
||||
if (TextUtils.isEmpty(tag)) {
|
||||
Log.w(LOG_TAG, "MetricsTag is invalid " + tag);
|
||||
@@ -246,7 +246,7 @@ public class SettingsActivity extends SettingsBaseActivity
|
||||
}
|
||||
|
||||
// Getting Intent properties can only be done after the super.onCreate(...)
|
||||
final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
|
||||
final String initialFragmentName = getInitialFragmentName(intent);
|
||||
|
||||
// This is a "Sub Settings" when:
|
||||
// - this is a real SubSettings
|
||||
@@ -290,9 +290,10 @@ public class SettingsActivity extends SettingsBaseActivity
|
||||
actionBar.setHomeButtonEnabled(!isInSetupWizard);
|
||||
actionBar.setDisplayShowTitleEnabled(true);
|
||||
}
|
||||
mSwitchBar = findViewById(R.id.switch_bar);
|
||||
if (mSwitchBar != null) {
|
||||
mSwitchBar.setMetricsTag(getMetricsTag());
|
||||
mMainSwitch = findViewById(R.id.switch_bar);
|
||||
if (mMainSwitch != null) {
|
||||
mMainSwitch.setMetricsTag(getMetricsTag());
|
||||
mMainSwitch.setTranslationZ(findViewById(R.id.main_content).getTranslationZ() + 1);
|
||||
}
|
||||
|
||||
// see if we should show Back/Next buttons
|
||||
@@ -346,6 +347,12 @@ public class SettingsActivity extends SettingsBaseActivity
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the initial fragment name that the activity will launch. */
|
||||
@VisibleForTesting
|
||||
public String getInitialFragmentName(Intent intent) {
|
||||
return intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onApplyThemeResource(Theme theme, int resid, boolean first) {
|
||||
theme.applyStyle(R.style.SetupWizardPartnerResource, true);
|
||||
@@ -567,7 +574,7 @@ public class SettingsActivity extends SettingsBaseActivity
|
||||
/**
|
||||
* Switch to a specific Fragment with taking care of validation, Title and BackStack
|
||||
*/
|
||||
private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
|
||||
private void switchToFragment(String fragmentName, Bundle args, boolean validate,
|
||||
int titleResId, CharSequence title) {
|
||||
Log.d(LOG_TAG, "Switching to fragment " + fragmentName);
|
||||
if (validate && !isValidFragment(fragmentName)) {
|
||||
@@ -575,6 +582,9 @@ public class SettingsActivity extends SettingsBaseActivity
|
||||
+ fragmentName);
|
||||
}
|
||||
Fragment f = Utils.getTargetFragment(this, fragmentName, args);
|
||||
if (f == null) {
|
||||
return;
|
||||
}
|
||||
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
|
||||
transaction.replace(R.id.main_content, f);
|
||||
if (titleResId > 0) {
|
||||
@@ -585,7 +595,6 @@ public class SettingsActivity extends SettingsBaseActivity
|
||||
transaction.commitAllowingStateLoss();
|
||||
getSupportFragmentManager().executePendingTransactions();
|
||||
Log.d(LOG_TAG, "Executed frag manager pendingTransactions");
|
||||
return f;
|
||||
}
|
||||
|
||||
private void updateTilesList() {
|
||||
@@ -678,7 +687,7 @@ public class SettingsActivity extends SettingsBaseActivity
|
||||
if (somethingChanged) {
|
||||
Log.d(LOG_TAG, "Enabled state changed for some tiles, reloading all categories "
|
||||
+ changedList.toString());
|
||||
updateCategories();
|
||||
mCategoryMixin.updateCategories();
|
||||
} else {
|
||||
Log.d(LOG_TAG, "No enabled state changed, skipping updateCategory call");
|
||||
}
|
||||
|
||||
@@ -111,9 +111,13 @@ public class SettingsDumpService extends Service {
|
||||
for (SubscriptionInfo info : manager.getAvailableSubscriptionInfoList()) {
|
||||
telephonyManager = telephonyManager
|
||||
.createForSubscriptionId(info.getSubscriptionId());
|
||||
NetworkTemplate carrier = NetworkTemplate.buildTemplateCarrierMetered(
|
||||
telephonyManager.getSubscriberId());
|
||||
final JSONObject usage = dumpDataUsage(carrier, controller);
|
||||
String subscriberId = telephonyManager.getSubscriberId();
|
||||
// The null subscriberId means that no any mobile/carrier network will be matched.
|
||||
// Using old API: buildTemplateMobileAll for the null subscriberId to avoid NPE.
|
||||
NetworkTemplate template = subscriberId != null
|
||||
? NetworkTemplate.buildTemplateCarrierMetered(subscriberId)
|
||||
: NetworkTemplate.buildTemplateMobileAll(subscriberId);
|
||||
final JSONObject usage = dumpDataUsage(template, controller);
|
||||
usage.put("subId", info.getSubscriptionId());
|
||||
array.put(usage);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import android.util.Log;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settings.Settings.CreateShortcutActivity;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -62,7 +63,7 @@ public class SettingsInitialize extends BroadcastReceiver {
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
managedProfileSetup(context, pm, broadcast, userInfo);
|
||||
webviewSettingSetup(context, pm, userInfo);
|
||||
refreshExistingShortcuts(context);
|
||||
ThreadUtils.postOnBackgroundThread(() -> refreshExistingShortcuts(context));
|
||||
}
|
||||
|
||||
private void managedProfileSetup(Context context, final PackageManager pm, Intent broadcast,
|
||||
@@ -142,5 +143,4 @@ public class SettingsInitialize extends BroadcastReceiver {
|
||||
}
|
||||
shortcutManager.updateShortcuts(updates);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -44,8 +44,6 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.settings.core.InstrumentedPreferenceFragment;
|
||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
import com.android.settings.search.actionbar.SearchMenuController;
|
||||
import com.android.settings.support.actionbar.HelpMenuController;
|
||||
import com.android.settings.support.actionbar.HelpResourceProvider;
|
||||
import com.android.settings.widget.HighlightablePreferenceGroupAdapter;
|
||||
import com.android.settings.widget.LoadingViewController;
|
||||
@@ -55,6 +53,8 @@ import com.android.settingslib.core.instrumentation.Instrumentable;
|
||||
import com.android.settingslib.search.Indexable;
|
||||
import com.android.settingslib.widget.LayoutPreference;
|
||||
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@@ -110,9 +110,8 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
|
||||
|
||||
@VisibleForTesting
|
||||
ViewGroup mPinnedHeaderFrameLayout;
|
||||
|
||||
private AppBarLayout mAppBarLayout;
|
||||
private LayoutPreference mHeader;
|
||||
|
||||
private View mEmptyView;
|
||||
private LinearLayoutManager mLayoutManager;
|
||||
private ArrayMap<String, Preference> mPreferenceCache;
|
||||
@@ -126,8 +125,6 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
SearchMenuController.init(this /* host */);
|
||||
HelpMenuController.init(this /* host */);
|
||||
|
||||
if (icicle != null) {
|
||||
mPreferenceHighlighted = icicle.getBoolean(SAVE_HIGHLIGHTED_KEY);
|
||||
@@ -140,6 +137,7 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
|
||||
Bundle savedInstanceState) {
|
||||
final View root = super.onCreateView(inflater, container, savedInstanceState);
|
||||
mPinnedHeaderFrameLayout = root.findViewById(R.id.pinned_header);
|
||||
mAppBarLayout = getActivity().findViewById(R.id.app_bar);
|
||||
return root;
|
||||
}
|
||||
|
||||
@@ -245,7 +243,7 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
|
||||
return;
|
||||
}
|
||||
if (mAdapter != null) {
|
||||
mAdapter.requestHighlight(getView(), getListView());
|
||||
mAdapter.requestHighlight(getView(), getListView(), mAppBarLayout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,6 +436,13 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
|
||||
return getActivity().getSystemService(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified system service from the owning Activity.
|
||||
*/
|
||||
protected <T> T getSystemService(final Class<T> serviceClass) {
|
||||
return getActivity().getSystemService(serviceClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the PackageManager from the owning Activity.
|
||||
*/
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.settings;
|
||||
import static com.google.android.setupcompat.util.WizardManagerHelper.EXTRA_IS_FIRST_RUN;
|
||||
import static com.google.android.setupcompat.util.WizardManagerHelper.EXTRA_IS_SETUP_FLOW;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.sysprop.SetupWizardProperties;
|
||||
@@ -39,25 +40,39 @@ public class SetupWizardUtils {
|
||||
return theme;
|
||||
}
|
||||
|
||||
public static int getTheme(Intent intent) {
|
||||
public static int getTheme(Context context, Intent intent) {
|
||||
String theme = getThemeString(intent);
|
||||
// TODO(yukl): Move to ThemeResolver and add any additional required attributes in
|
||||
// onApplyThemeResource using Theme overlays
|
||||
if (theme != null) {
|
||||
if (WizardManagerHelper.isAnySetupWizard(intent)) {
|
||||
switch (theme) {
|
||||
case ThemeHelper.THEME_GLIF_V3_LIGHT:
|
||||
return R.style.GlifV3Theme_Light;
|
||||
case ThemeHelper.THEME_GLIF_V3:
|
||||
return R.style.GlifV3Theme;
|
||||
case ThemeHelper.THEME_GLIF_V2_LIGHT:
|
||||
return R.style.GlifV2Theme_Light;
|
||||
case ThemeHelper.THEME_GLIF_V2:
|
||||
return R.style.GlifV2Theme;
|
||||
case ThemeHelper.THEME_GLIF_LIGHT:
|
||||
return R.style.GlifTheme_Light;
|
||||
case ThemeHelper.THEME_GLIF:
|
||||
return R.style.GlifTheme;
|
||||
if (ThemeHelper.isSetupWizardDayNightEnabled(context)) {
|
||||
switch (theme) {
|
||||
case ThemeHelper.THEME_GLIF_V3_LIGHT:
|
||||
case ThemeHelper.THEME_GLIF_V3:
|
||||
return R.style.GlifV3Theme_DayNight;
|
||||
case ThemeHelper.THEME_GLIF_V2_LIGHT:
|
||||
case ThemeHelper.THEME_GLIF_V2:
|
||||
return R.style.GlifV2Theme_DayNight;
|
||||
case ThemeHelper.THEME_GLIF_LIGHT:
|
||||
case ThemeHelper.THEME_GLIF:
|
||||
return R.style.GlifTheme_DayNight;
|
||||
}
|
||||
} else {
|
||||
switch (theme) {
|
||||
case ThemeHelper.THEME_GLIF_V3_LIGHT:
|
||||
return R.style.GlifV3Theme_Light;
|
||||
case ThemeHelper.THEME_GLIF_V3:
|
||||
return R.style.GlifV3Theme;
|
||||
case ThemeHelper.THEME_GLIF_V2_LIGHT:
|
||||
return R.style.GlifV2Theme_Light;
|
||||
case ThemeHelper.THEME_GLIF_V2:
|
||||
return R.style.GlifV2Theme;
|
||||
case ThemeHelper.THEME_GLIF_LIGHT:
|
||||
return R.style.GlifTheme_Light;
|
||||
case ThemeHelper.THEME_GLIF:
|
||||
return R.style.GlifTheme;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch (theme) {
|
||||
@@ -76,17 +91,30 @@ public class SetupWizardUtils {
|
||||
return R.style.GlifTheme;
|
||||
}
|
||||
|
||||
public static int getTransparentTheme(Intent intent) {
|
||||
final int suwTheme = getTheme(intent);
|
||||
int transparentTheme = R.style.GlifV2Theme_Light_Transparent;
|
||||
if (suwTheme == R.style.GlifV3Theme) {
|
||||
transparentTheme = R.style.GlifV3Theme_Transparent;
|
||||
public static int getTransparentTheme(Context context, Intent intent) {
|
||||
int transparentTheme;
|
||||
final int suwTheme = getTheme(context, intent);
|
||||
if (ThemeHelper.isSetupWizardDayNightEnabled(context)) {
|
||||
transparentTheme = R.style.GlifV2Theme_DayNight_Transparent;
|
||||
} else {
|
||||
transparentTheme = R.style.GlifV2Theme_Light_Transparent;
|
||||
}
|
||||
if (suwTheme == R.style.GlifV3Theme_DayNight) {
|
||||
transparentTheme = R.style.GlifV3Theme_DayNight_Transparent;
|
||||
} else if (suwTheme == R.style.GlifV3Theme_Light) {
|
||||
transparentTheme = R.style.GlifV3Theme_Light_Transparent;
|
||||
} else if (suwTheme == R.style.GlifV2Theme) {
|
||||
transparentTheme = R.style.GlifV2Theme_Transparent;
|
||||
} else if (suwTheme == R.style.GlifV2Theme_DayNight) {
|
||||
transparentTheme = R.style.GlifV2Theme_DayNight_Transparent;
|
||||
} else if (suwTheme == R.style.GlifV2Theme_Light) {
|
||||
transparentTheme = R.style.GlifV2Theme_Light_Transparent;
|
||||
} else if (suwTheme == R.style.GlifTheme_DayNight) {
|
||||
transparentTheme = R.style.SetupWizardTheme_DayNight_Transparent;
|
||||
} else if (suwTheme == R.style.GlifTheme_Light) {
|
||||
transparentTheme = R.style.SetupWizardTheme_Light_Transparent;
|
||||
} else if (suwTheme == R.style.GlifV3Theme) {
|
||||
transparentTheme = R.style.GlifV3Theme_Transparent;
|
||||
} else if (suwTheme == R.style.GlifV2Theme) {
|
||||
transparentTheme = R.style.GlifV2Theme_Transparent;
|
||||
} else if (suwTheme == R.style.GlifTheme) {
|
||||
transparentTheme = R.style.SetupWizardTheme_Transparent;
|
||||
}
|
||||
|
||||
364
src/com/android/settings/SidecarFragment.java
Normal file
364
src/com/android/settings/SidecarFragment.java
Normal file
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
/**
|
||||
* A headless fragment encapsulating a long-running action such as a network RPC surviving rotation.
|
||||
*
|
||||
* <p>Subclasses should implement their own state machine, updating the state on each state change
|
||||
* via {@link #setState(int, int)}. They can define their own states, however, it is suggested that
|
||||
* the pre-defined {@link @State} constants are used and customizations are implemented via
|
||||
* substates. Custom states must be outside the range of pre-defined states.
|
||||
*
|
||||
* <p>It is safe to update the state at any time, but state updates must originate from the main
|
||||
* thread.
|
||||
*
|
||||
* <p>A listener can be attached that receives state updates while it's registered. Note that state
|
||||
* change events can occur at any point in time and hence a registered listener should unregister if
|
||||
* it cannot act upon the state change (typically a non-resumed fragment).
|
||||
*
|
||||
* <p>Listeners can receive state changes for the same state/substate combination, so listeners
|
||||
* should make sure to be idempotent during state change events.
|
||||
*
|
||||
* <p>If a SidecarFragment is only relevant during the lifetime of another fragment (for example, a
|
||||
* sidecar performing a details request for a DetailsFragment), that fragment needs to become the
|
||||
* managing fragment of the sidecar.
|
||||
*
|
||||
* <h2>Managing fragment responsibilities</h2>
|
||||
*
|
||||
* <ol>
|
||||
* <li>Instantiates the sidecar fragment when necessary, preferably in {@link #onStart}.
|
||||
* <li>Removes the sidecar fragment when it's no longer used or when itself is removed. Removal of
|
||||
* the managing fragment can be detected by checking {@link #isRemoving} in {@link #onStop}.
|
||||
* <br>
|
||||
* <li>Registers as a listener in {@link #onResume()}, unregisters in {@link #onPause()}.
|
||||
* <li>Starts the long-running operation by calling into the sidecar.
|
||||
* <li>Receives state updates via {@link Listener#onStateChange(SidecarFragment)} and updates the
|
||||
* UI accordingly.
|
||||
* </ol>
|
||||
*
|
||||
* <h2>Managing fragment example</h2>
|
||||
*
|
||||
* <pre>
|
||||
* public class MainFragment implements SidecarFragment.Listener {
|
||||
* private static final String TAG_SOME_SIDECAR = ...;
|
||||
* private static final String KEY_SOME_SIDECAR_STATE = ...;
|
||||
*
|
||||
* private SomeSidecarFragment mSidecar;
|
||||
*
|
||||
* @Override
|
||||
* public void onStart() {
|
||||
* super.onStart();
|
||||
* Bundle args = ...; // optional args
|
||||
* mSidecar = SidecarFragment.get(getFragmentManager(), TAG_SOME_SIDECAR,
|
||||
* SidecarFragment.class, args);
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public void onResume() {
|
||||
* mSomeSidecar.addListener(this);
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public void onPause() {
|
||||
* mSomeSidecar.removeListener(this):
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public class SidecarFragment extends Fragment {
|
||||
|
||||
private static final String TAG = "SidecarFragment";
|
||||
|
||||
/**
|
||||
* Get an instance of this sidecar.
|
||||
*
|
||||
* <p>Will return the existing instance if one is already present. Note that the args will not
|
||||
* be used in this situation, so args must be constant for any particular fragment manager and
|
||||
* tag.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static <T extends SidecarFragment> T get(
|
||||
FragmentManager fm, String tag, Class<T> clazz, Bundle args) {
|
||||
T fragment = (T) fm.findFragmentByTag(tag);
|
||||
if (fragment == null) {
|
||||
try {
|
||||
fragment = clazz.newInstance();
|
||||
} catch (java.lang.InstantiationException e) {
|
||||
throw new InstantiationException("Unable to create fragment", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalArgumentException("Unable to create fragment", e);
|
||||
}
|
||||
if (args != null) {
|
||||
fragment.setArguments(args);
|
||||
}
|
||||
fm.beginTransaction().add(fragment, tag).commit();
|
||||
// No real harm in doing this here - get() should generally only be called from onCreate
|
||||
// which is on the main thread - and it allows us to start running the sidecar on this
|
||||
// instance immediately rather than having to wait until the transaction commits.
|
||||
fm.executePendingTransactions();
|
||||
}
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
/** State definitions. @see {@link #getState} */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({State.INIT, State.RUNNING, State.SUCCESS, State.ERROR})
|
||||
public @interface State {
|
||||
/** Initial idling state. */
|
||||
int INIT = 0;
|
||||
|
||||
/** The long-running operation is in progress. */
|
||||
int RUNNING = 1;
|
||||
|
||||
/** The long-running operation has succeeded. */
|
||||
int SUCCESS = 2;
|
||||
|
||||
/** The long-running operation has failed. */
|
||||
int ERROR = 3;
|
||||
}
|
||||
|
||||
/** Substate definitions. @see {@link #getSubstate} */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({
|
||||
Substate.UNUSED,
|
||||
Substate.RUNNING_BIND_SERVICE,
|
||||
Substate.RUNNING_GET_ACTIVATION_CODE,
|
||||
})
|
||||
public @interface Substate {
|
||||
// Unknown/unused substate.
|
||||
int UNUSED = 0;
|
||||
int RUNNING_BIND_SERVICE = 1;
|
||||
int RUNNING_GET_ACTIVATION_CODE = 2;
|
||||
|
||||
// Future tags: 3+
|
||||
}
|
||||
|
||||
/** **************************************** */
|
||||
private Set<Listener> mListeners = new CopyOnWriteArraySet<>();
|
||||
|
||||
// Used to track whether onCreate has been called yet.
|
||||
private boolean mCreated;
|
||||
|
||||
@State private int mState;
|
||||
@Substate private int mSubstate;
|
||||
|
||||
/** A listener receiving state change events. */
|
||||
public interface Listener {
|
||||
|
||||
/**
|
||||
* Called upon any state or substate change.
|
||||
*
|
||||
* <p>The new state can be queried through {@link #getState} and {@link #getSubstate}.
|
||||
*
|
||||
* <p>Called from the main thread.
|
||||
*
|
||||
* @param fragment the SidecarFragment that changed its state
|
||||
*/
|
||||
void onStateChange(SidecarFragment fragment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setRetainInstance(true);
|
||||
mCreated = true;
|
||||
setState(State.INIT, Substate.UNUSED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
mCreated = false;
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener that will receive subsequent state changes.
|
||||
*
|
||||
* <p>A {@link Listener#onStateChange(SidecarFragment)} event is fired as part of this call
|
||||
* unless {@link #onCreate} has not yet been called (which means that it's unsafe to access this
|
||||
* fragment as it has not been setup or restored completely). In that case, the future call to
|
||||
* onCreate will trigger onStateChange on registered listener.
|
||||
*
|
||||
* <p>Must be called from the main thread.
|
||||
*
|
||||
* @param listener a listener, or null for unregistering the current listener
|
||||
*/
|
||||
public void addListener(Listener listener) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
mListeners.add(listener);
|
||||
if (mCreated) {
|
||||
notifyListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a previously registered listener.
|
||||
*
|
||||
* @return {@code true} if the listener was removed, {@code false} if there was no such listener
|
||||
* registered.
|
||||
*/
|
||||
public boolean removeListener(Listener listener) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
return mListeners.remove(listener);
|
||||
}
|
||||
|
||||
/** Returns the current state. */
|
||||
@State
|
||||
public int getState() {
|
||||
return mState;
|
||||
}
|
||||
|
||||
/** Returns the current substate. */
|
||||
@Substate
|
||||
public int getSubstate() {
|
||||
return mSubstate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the sidecar to its initial state.
|
||||
*
|
||||
* <p>Implementers can override this method to perform additional reset tasks, but must call the
|
||||
* super method.
|
||||
*/
|
||||
@CallSuper
|
||||
public void reset() {
|
||||
setState(State.INIT, Substate.UNUSED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the state and substate and notifies the registered listener.
|
||||
*
|
||||
* <p>Must be called from the main thread.
|
||||
*
|
||||
* @param state the state to transition to
|
||||
* @param substate the substate to transition to
|
||||
*/
|
||||
protected void setState(@State int state, @Substate int substate) {
|
||||
ThreadUtils.ensureMainThread();
|
||||
|
||||
mState = state;
|
||||
mSubstate = substate;
|
||||
notifyAllListeners();
|
||||
printState();
|
||||
}
|
||||
|
||||
private void notifyAllListeners() {
|
||||
for (Listener listener : mListeners) {
|
||||
notifyListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyListener(Listener listener) {
|
||||
listener.onStateChange(this);
|
||||
}
|
||||
|
||||
/** Prints the state of the sidecar. */
|
||||
public void printState() {
|
||||
StringBuilder sb =
|
||||
new StringBuilder("SidecarFragment.setState(): Sidecar Class: ")
|
||||
.append(getClass().getCanonicalName());
|
||||
sb.append(", State: ");
|
||||
switch (mState) {
|
||||
case SidecarFragment.State.INIT:
|
||||
sb.append("State.INIT");
|
||||
break;
|
||||
case SidecarFragment.State.RUNNING:
|
||||
sb.append("State.RUNNING");
|
||||
break;
|
||||
case SidecarFragment.State.SUCCESS:
|
||||
sb.append("State.SUCCESS");
|
||||
break;
|
||||
case SidecarFragment.State.ERROR:
|
||||
sb.append("State.ERROR");
|
||||
break;
|
||||
default:
|
||||
sb.append(mState);
|
||||
break;
|
||||
}
|
||||
switch (mSubstate) {
|
||||
case SidecarFragment.Substate.UNUSED:
|
||||
sb.append(", Substate.UNUSED");
|
||||
break;
|
||||
default:
|
||||
sb.append(", ").append(mSubstate);
|
||||
break;
|
||||
}
|
||||
|
||||
Log.v(TAG, sb.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
Locale.US,
|
||||
"SidecarFragment[mState=%d, mSubstate=%d]: %s",
|
||||
mState,
|
||||
mSubstate,
|
||||
super.toString());
|
||||
}
|
||||
|
||||
/** The State of the sidecar status. */
|
||||
public static final class States {
|
||||
public static final States SUCCESS = States.create(State.SUCCESS, Substate.UNUSED);
|
||||
public static final States ERROR = States.create(State.ERROR, Substate.UNUSED);
|
||||
|
||||
@State public final int state;
|
||||
@Substate public final int substate;
|
||||
|
||||
/** Creates a new sidecar state. */
|
||||
public static States create(@State int state, @Substate int substate) {
|
||||
return new States(state, substate);
|
||||
}
|
||||
|
||||
public States(@State int state, @Substate int substate) {
|
||||
this.state = state;
|
||||
this.substate = substate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof States)) {
|
||||
return false;
|
||||
}
|
||||
States other = (States) o;
|
||||
return this.state == other.state && this.substate == other.substate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return state * 31 + substate;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
@@ -15,7 +31,8 @@ public class TestingSettingsBroadcastReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals(TelephonyManager.ACTION_SECRET_CODE)) {
|
||||
if (intent != null && intent.getAction() != null
|
||||
&& intent.getAction().equals(TelephonyManager.ACTION_SECRET_CODE)) {
|
||||
Intent i = new Intent(Intent.ACTION_MAIN);
|
||||
i.setClass(context, TestingSettingsActivity.class);
|
||||
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
@@ -20,6 +20,8 @@ import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
|
||||
import static android.net.ConnectivityManager.TETHERING_USB;
|
||||
import static android.net.TetheringManager.TETHERING_ETHERNET;
|
||||
|
||||
import static com.android.settingslib.RestrictedLockUtilsInternal.checkIfUsbDataSignalingIsDisabled;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
@@ -38,10 +40,12 @@ import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerExecutor;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.SearchIndexableResource;
|
||||
import android.text.TextUtils;
|
||||
import android.util.FeatureFlagUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
@@ -51,6 +55,7 @@ import com.android.settings.core.FeatureFlags;
|
||||
import com.android.settings.datausage.DataSaverBackend;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settings.wifi.tether.WifiTetherPreferenceController;
|
||||
import com.android.settingslib.RestrictedSwitchPreference;
|
||||
import com.android.settingslib.TetherUtil;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
|
||||
@@ -78,15 +83,12 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
private static final String KEY_ENABLE_ETHERNET_TETHERING = "enable_ethernet_tethering";
|
||||
private static final String KEY_DATA_SAVER_FOOTER = "disabled_on_data_saver";
|
||||
@VisibleForTesting
|
||||
static final String KEY_TETHER_PREFS_FOOTER = "tether_prefs_footer";
|
||||
|
||||
@VisibleForTesting
|
||||
static final String BLUETOOTH_TETHERING_STATE_CHANGED =
|
||||
"android.bluetooth.pan.profile.action.TETHERING_STATE_CHANGED";
|
||||
static final String KEY_TETHER_PREFS_TOP_INTRO = "tether_prefs_top_intro";
|
||||
|
||||
private static final String TAG = "TetheringSettings";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
private SwitchPreference mUsbTether;
|
||||
private RestrictedSwitchPreference mUsbTether;
|
||||
|
||||
private SwitchPreference mBluetoothTether;
|
||||
|
||||
@@ -94,7 +96,6 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
|
||||
private BroadcastReceiver mTetherChangeReceiver;
|
||||
|
||||
private String[] mUsbRegexs;
|
||||
private String[] mBluetoothRegexs;
|
||||
private String mEthernetRegex;
|
||||
private AtomicReference<BluetoothPan> mBluetoothPan = new AtomicReference<>();
|
||||
@@ -103,7 +104,6 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
private OnStartTetheringCallback mStartTetheringCallback;
|
||||
private ConnectivityManager mCm;
|
||||
private EthernetManager mEm;
|
||||
private TetheringManager mTm;
|
||||
private TetheringEventCallback mTetheringEventCallback;
|
||||
private EthernetListener mEthernetListener;
|
||||
|
||||
@@ -119,6 +119,13 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
private boolean mDataSaverEnabled;
|
||||
private Preference mDataSaverFooter;
|
||||
|
||||
@VisibleForTesting
|
||||
String[] mUsbRegexs;
|
||||
@VisibleForTesting
|
||||
Context mContext;
|
||||
@VisibleForTesting
|
||||
TetheringManager mTm;
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.TETHER;
|
||||
@@ -140,7 +147,8 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
super.onCreate(icicle);
|
||||
|
||||
addPreferencesFromResource(R.xml.tether_prefs);
|
||||
mDataSaverBackend = new DataSaverBackend(getContext());
|
||||
mContext = getContext();
|
||||
mDataSaverBackend = new DataSaverBackend(mContext);
|
||||
mDataSaverEnabled = mDataSaverBackend.isDataSaverEnabled();
|
||||
mDataSaverFooter = findPreference(KEY_DATA_SAVER_FOOTER);
|
||||
|
||||
@@ -159,7 +167,7 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
}
|
||||
|
||||
setupTetherPreference();
|
||||
setFooterPreferenceTitle();
|
||||
setTopIntroPreferenceTitle();
|
||||
|
||||
mDataSaverBackend.addListener(this);
|
||||
|
||||
@@ -169,7 +177,7 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
|
||||
mUsbRegexs = mTm.getTetherableUsbRegexs();
|
||||
mBluetoothRegexs = mTm.getTetherableBluetoothRegexs();
|
||||
mEthernetRegex = getContext().getResources().getString(
|
||||
mEthernetRegex = mContext.getResources().getString(
|
||||
com.android.internal.R.string.config_ethernet_iface_regex);
|
||||
|
||||
final boolean usbAvailable = mUsbRegexs.length != 0;
|
||||
@@ -212,7 +220,7 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
|
||||
@VisibleForTesting
|
||||
void setupTetherPreference() {
|
||||
mUsbTether = (SwitchPreference) findPreference(KEY_USB_TETHER_SETTINGS);
|
||||
mUsbTether = (RestrictedSwitchPreference) findPreference(KEY_USB_TETHER_SETTINGS);
|
||||
mBluetoothTether = (SwitchPreference) findPreference(KEY_ENABLE_BLUETOOTH_TETHERING);
|
||||
mEthernetTether = (SwitchPreference) findPreference(KEY_ENABLE_ETHERNET_TETHERING);
|
||||
}
|
||||
@@ -227,22 +235,21 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWhitelistStatusChanged(int uid, boolean isWhitelisted) {
|
||||
public void onAllowlistStatusChanged(int uid, boolean isAllowlisted) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlacklistStatusChanged(int uid, boolean isBlacklisted) {
|
||||
public void onDenylistStatusChanged(int uid, boolean isDenylisted) {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setFooterPreferenceTitle() {
|
||||
final Preference footerPreference = findPreference(KEY_TETHER_PREFS_FOOTER);
|
||||
final WifiManager wifiManager =
|
||||
(WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
|
||||
void setTopIntroPreferenceTitle() {
|
||||
final Preference topIntroPreference = findPreference(KEY_TETHER_PREFS_TOP_INTRO);
|
||||
final WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
|
||||
if (wifiManager.isStaApConcurrencySupported()) {
|
||||
footerPreference.setTitle(R.string.tethering_footer_info_sta_ap_concurrency);
|
||||
topIntroPreference.setTitle(R.string.tethering_footer_info_sta_ap_concurrency);
|
||||
} else {
|
||||
footerPreference.setTitle(R.string.tethering_footer_info);
|
||||
topIntroPreference.setTitle(R.string.tethering_footer_info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,27 +257,32 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
@Override
|
||||
public void onReceive(Context content, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
// TODO: stop using ACTION_TETHER_STATE_CHANGED and use mTetheringEventCallback instead.
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onReceive() action : " + action);
|
||||
}
|
||||
// TODO(b/194961339): Stop using ACTION_TETHER_STATE_CHANGED and use
|
||||
// mTetheringEventCallback instead.
|
||||
if (action.equals(TetheringManager.ACTION_TETHER_STATE_CHANGED)) {
|
||||
// TODO - this should understand the interface types
|
||||
ArrayList<String> available = intent.getStringArrayListExtra(
|
||||
TetheringManager.EXTRA_AVAILABLE_TETHER);
|
||||
ArrayList<String> active = intent.getStringArrayListExtra(
|
||||
TetheringManager.EXTRA_ACTIVE_TETHER);
|
||||
ArrayList<String> errored = intent.getStringArrayListExtra(
|
||||
TetheringManager.EXTRA_ERRORED_TETHER);
|
||||
updateState(available.toArray(new String[available.size()]),
|
||||
active.toArray(new String[active.size()]),
|
||||
errored.toArray(new String[errored.size()]));
|
||||
updateBluetoothState();
|
||||
updateEthernetState(available.toArray(new String[available.size()]),
|
||||
active.toArray(new String[active.size()]));
|
||||
} else if (action.equals(Intent.ACTION_MEDIA_SHARED)) {
|
||||
mMassStorageActive = true;
|
||||
updateState();
|
||||
updateBluetoothAndEthernetState();
|
||||
updateUsbPreference();
|
||||
} else if (action.equals(Intent.ACTION_MEDIA_UNSHARED)) {
|
||||
mMassStorageActive = false;
|
||||
updateState();
|
||||
updateBluetoothAndEthernetState();
|
||||
updateUsbPreference();
|
||||
} else if (action.equals(UsbManager.ACTION_USB_STATE)) {
|
||||
mUsbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
|
||||
updateState();
|
||||
updateBluetoothAndEthernetState();
|
||||
updateUsbPreference();
|
||||
} else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
|
||||
if (mBluetoothEnableForTether) {
|
||||
switch (intent
|
||||
@@ -289,9 +301,9 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
// ignore transition states
|
||||
}
|
||||
}
|
||||
updateState();
|
||||
} else if (action.equals(BLUETOOTH_TETHERING_STATE_CHANGED)) {
|
||||
updateState();
|
||||
updateBluetoothAndEthernetState();
|
||||
} else if (action.equals(BluetoothPan.ACTION_TETHERING_STATE_CHANGED)) {
|
||||
updateBluetoothAndEthernetState();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -320,7 +332,8 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
if (mEm != null)
|
||||
mEm.addListener(mEthernetListener);
|
||||
|
||||
updateState();
|
||||
updateUsbState();
|
||||
updateBluetoothAndEthernetState();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -360,64 +373,66 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
|
||||
filter = new IntentFilter();
|
||||
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
|
||||
filter.addAction(BLUETOOTH_TETHERING_STATE_CHANGED);
|
||||
filter.addAction(BluetoothPan.ACTION_TETHERING_STATE_CHANGED);
|
||||
activity.registerReceiver(mTetherChangeReceiver, filter);
|
||||
|
||||
if (intent != null) mTetherChangeReceiver.onReceive(activity, intent);
|
||||
}
|
||||
|
||||
private void updateState() {
|
||||
final TetheringManager tm = getContext().getSystemService(TetheringManager.class);
|
||||
final String[] available = tm.getTetherableIfaces();
|
||||
final String[] tethered = tm.getTetheredIfaces();
|
||||
final String[] errored = tm.getTetheringErroredIfaces();
|
||||
updateState(available, tethered, errored);
|
||||
// TODO(b/194961339): Separate the updateBluetoothAndEthernetState() to two methods,
|
||||
// updateBluetoothAndEthernetState() and updateBluetoothAndEthernetPreference().
|
||||
// Because we should update the state when only receiving tethering
|
||||
// state changes and update preference when usb or media share changed.
|
||||
private void updateBluetoothAndEthernetState() {
|
||||
String[] tethered = mTm.getTetheredIfaces();
|
||||
updateBluetoothAndEthernetState(tethered);
|
||||
}
|
||||
|
||||
private void updateState(String[] available, String[] tethered,
|
||||
String[] errored) {
|
||||
updateUsbState(available, tethered, errored);
|
||||
private void updateBluetoothAndEthernetState(String[] tethered) {
|
||||
String[] available = mTm.getTetherableIfaces();
|
||||
updateBluetoothState();
|
||||
updateEthernetState(available, tethered);
|
||||
}
|
||||
|
||||
private void updateUsbState() {
|
||||
String[] tethered = mTm.getTetheredIfaces();
|
||||
updateUsbState(tethered);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void updateUsbState(String[] available, String[] tethered,
|
||||
String[] errored) {
|
||||
boolean usbAvailable = mUsbConnected && !mMassStorageActive;
|
||||
int usbError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
||||
for (String s : available) {
|
||||
for (String regex : mUsbRegexs) {
|
||||
if (s.matches(regex)) {
|
||||
if (usbError == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
|
||||
usbError = mTm.getLastTetherError(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void updateUsbState(String[] tethered) {
|
||||
boolean usbTethered = false;
|
||||
for (String s : tethered) {
|
||||
for (String regex : mUsbRegexs) {
|
||||
if (s.matches(regex)) usbTethered = true;
|
||||
}
|
||||
}
|
||||
boolean usbErrored = false;
|
||||
for (String s: errored) {
|
||||
for (String regex : mUsbRegexs) {
|
||||
if (s.matches(regex)) usbErrored = true;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "updateUsbState() mUsbConnected : " + mUsbConnected
|
||||
+ ", mMassStorageActive : " + mMassStorageActive
|
||||
+ ", usbTethered : " + usbTethered);
|
||||
}
|
||||
|
||||
if (usbTethered) {
|
||||
mUsbTether.setEnabled(!mDataSaverEnabled);
|
||||
mUsbTether.setChecked(true);
|
||||
} else if (usbAvailable) {
|
||||
mUsbTether.setEnabled(!mDataSaverEnabled);
|
||||
mUsbTether.setDisabledByAdmin(
|
||||
checkIfUsbDataSignalingIsDisabled(mContext, UserHandle.myUserId()));
|
||||
} else {
|
||||
mUsbTether.setChecked(false);
|
||||
updateUsbPreference();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUsbPreference() {
|
||||
boolean usbAvailable = mUsbConnected && !mMassStorageActive;
|
||||
|
||||
if (usbAvailable) {
|
||||
mUsbTether.setEnabled(!mDataSaverEnabled);
|
||||
} else {
|
||||
mUsbTether.setEnabled(false);
|
||||
mUsbTether.setChecked(false);
|
||||
}
|
||||
mUsbTether.setDisabledByAdmin(
|
||||
checkIfUsbDataSignalingIsDisabled(mContext, UserHandle.myUserId()));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -437,7 +452,11 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
|
||||
private void updateBluetoothState() {
|
||||
final int btState = getBluetoothState();
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "updateBluetoothState() btState : " + btState);
|
||||
}
|
||||
if (btState == BluetoothAdapter.ERROR) {
|
||||
Log.w(TAG, "updateBluetoothState() Bluetooth state is error!");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -458,7 +477,6 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
|
||||
@VisibleForTesting
|
||||
void updateEthernetState(String[] available, String[] tethered) {
|
||||
|
||||
boolean isAvailable = false;
|
||||
boolean isTethered = false;
|
||||
|
||||
@@ -470,6 +488,11 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
if (s.matches(mEthernetRegex)) isTethered = true;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "updateEthernetState() isAvailable : " + isAvailable
|
||||
+ ", isTethered : " + isTethered);
|
||||
}
|
||||
|
||||
if (isTethered) {
|
||||
mEthernetTether.setEnabled(!mDataSaverEnabled);
|
||||
mEthernetTether.setChecked(true);
|
||||
@@ -606,7 +629,7 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
private void update() {
|
||||
TetherSettings settings = mTetherSettings.get();
|
||||
if (settings != null) {
|
||||
settings.updateState();
|
||||
settings.updateBluetoothAndEthernetState();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -614,13 +637,16 @@ public class TetherSettings extends RestrictedSettingsFragment
|
||||
private final class TetheringEventCallback implements TetheringManager.TetheringEventCallback {
|
||||
@Override
|
||||
public void onTetheredInterfacesChanged(List<String> interfaces) {
|
||||
updateState();
|
||||
Log.d(TAG, "onTetheredInterfacesChanged() interfaces : " + interfaces.toString());
|
||||
String[] tethered = interfaces.toArray(new String[interfaces.size()]);
|
||||
updateUsbState(tethered);
|
||||
updateBluetoothAndEthernetState(tethered);
|
||||
}
|
||||
}
|
||||
|
||||
private final class EthernetListener implements EthernetManager.Listener {
|
||||
public void onAvailabilityChanged(String iface, boolean isAvailable) {
|
||||
mHandler.post(TetherSettings.this::updateState);
|
||||
mHandler.post(() -> updateBluetoothAndEthernetState());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,6 @@ package com.android.settings;
|
||||
|
||||
import static android.content.Intent.EXTRA_USER;
|
||||
import static android.content.Intent.EXTRA_USER_ID;
|
||||
import static android.media.MediaRoute2Info.TYPE_GROUP;
|
||||
import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
|
||||
import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
|
||||
import static android.media.MediaRoute2Info.TYPE_UNKNOWN;
|
||||
import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
|
||||
import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
|
||||
|
||||
@@ -52,13 +48,12 @@ import android.content.res.TypedArray;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.AdaptiveIconDrawable;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.VectorDrawable;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRouter2Manager;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
@@ -90,6 +85,7 @@ 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;
|
||||
@@ -99,6 +95,7 @@ import android.widget.EditText;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TabWidget;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
@@ -116,6 +113,7 @@ import com.android.settings.dashboard.profileselector.ProfileFragmentBridge;
|
||||
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settingslib.widget.ActionBarShadowController;
|
||||
import com.android.settingslib.widget.AdaptiveIcon;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -147,6 +145,24 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
*/
|
||||
public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
|
||||
|
||||
/**
|
||||
* Whether to show location indicators.
|
||||
*/
|
||||
public static final String PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_enabled";
|
||||
|
||||
/**
|
||||
* Whether to show location indicator settings in developer options.
|
||||
*/
|
||||
public static final String PROPERTY_LOCATION_INDICATOR_SETTINGS_ENABLED =
|
||||
"location_indicator_settings_enabled";
|
||||
|
||||
/** Whether or not app hibernation is enabled on the device **/
|
||||
public static final String PROPERTY_APP_HIBERNATION_ENABLED = "app_hibernation_enabled";
|
||||
|
||||
/** Whether or not app hibernation targets apps that target a pre-S SDK **/
|
||||
public static final String PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS =
|
||||
"app_hibernation_targets_pre_s_apps";
|
||||
|
||||
/**
|
||||
* Finds a matching activity for a preference's intent. If a matching
|
||||
* activity is not found, it will remove the preference.
|
||||
@@ -452,6 +468,19 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
return UserHandle.USER_NULL;
|
||||
}
|
||||
|
||||
/** Returns user ID of current user, throws IllegalStateException if it's not available. */
|
||||
public static int getCurrentUserId(UserManager userManager, boolean isWorkProfile)
|
||||
throws IllegalStateException {
|
||||
if (isWorkProfile) {
|
||||
final UserHandle managedUserHandle = getManagedProfile(userManager);
|
||||
if (managedUserHandle == null) {
|
||||
throw new IllegalStateException("Work profile user ID is not available.");
|
||||
}
|
||||
return managedUserHandle.getIdentifier();
|
||||
}
|
||||
return UserHandle.myUserId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the target user for a Settings activity.
|
||||
* <p>
|
||||
@@ -540,7 +569,7 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
* @return UserInfo of the user or null for non-existent user.
|
||||
*/
|
||||
public static UserInfo getExistingUser(UserManager userManager, UserHandle checkUser) {
|
||||
final List<UserInfo> users = userManager.getUsers(true /* excludeDying */);
|
||||
final List<UserInfo> users = userManager.getAliveUsers();
|
||||
final int checkUserId = checkUser.getIdentifier();
|
||||
for (UserInfo user : users) {
|
||||
if (user.id == checkUserId) {
|
||||
@@ -649,7 +678,7 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
*
|
||||
* @param isInternal indicating if the caller is "internal" to the system,
|
||||
* meaning we're willing to trust extras like
|
||||
* {@link ChooseLockSettingsHelper#EXTRA_ALLOW_ANY_USER}.
|
||||
* {@link ChooseLockSettingsHelper#EXTRA_KEY_ALLOW_ANY_USER}.
|
||||
* @throws SecurityException if the given userId does not belong to the
|
||||
* current user group.
|
||||
*/
|
||||
@@ -658,7 +687,7 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
return getCredentialOwnerUserId(context);
|
||||
}
|
||||
final boolean allowAnyUser = isInternal
|
||||
&& bundle.getBoolean(ChooseLockSettingsHelper.EXTRA_ALLOW_ANY_USER, false);
|
||||
&& bundle.getBoolean(ChooseLockSettingsHelper.EXTRA_KEY_ALLOW_ANY_USER, false);
|
||||
final int userId = bundle.getInt(Intent.EXTRA_USER_ID, UserHandle.myUserId());
|
||||
if (userId == LockPatternUtils.USER_FRP) {
|
||||
return allowAnyUser ? userId : enforceSystemUser(context, userId);
|
||||
@@ -832,6 +861,13 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
return faceManager != null && faceManager.isHardwareDetected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the device supports multiple biometrics authentications.
|
||||
*/
|
||||
public static boolean isMultipleBiometricsSupported(Context context) {
|
||||
return hasFingerprintHardware(context) && hasFaceHardware(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches an intent which may optionally have a user id defined.
|
||||
* @param fragment Fragment to use to launch the activity.
|
||||
@@ -951,15 +987,36 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the preference icon with a drawable that is scaled down to to avoid crashing Settings if
|
||||
* it's too big.
|
||||
* Gets the adaptive icon with a drawable that wrapped with an adaptive background using {@code
|
||||
* backgroundColor} if it is not a {@link AdaptiveIconDrawable}
|
||||
*
|
||||
* If the given {@code icon} is too big, it will be auto scaled down to to avoid crashing
|
||||
* Settings.
|
||||
*/
|
||||
public static void setSafeIcon(Preference pref, Drawable icon) {
|
||||
public static Drawable getAdaptiveIcon(Context context, Drawable icon,
|
||||
@ColorInt int backgroundColor) {
|
||||
Drawable adaptiveIcon = getSafeIcon(icon);
|
||||
|
||||
if (!(adaptiveIcon instanceof AdaptiveIconDrawable)) {
|
||||
adaptiveIcon = new AdaptiveIcon(context, adaptiveIcon);
|
||||
((AdaptiveIcon) adaptiveIcon).setBackgroundColor(backgroundColor);
|
||||
}
|
||||
|
||||
return adaptiveIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the icon with a drawable that is scaled down to to avoid crashing Settings if it's too
|
||||
* big and not a {@link VectorDrawable}.
|
||||
*/
|
||||
public static Drawable getSafeIcon(Drawable icon) {
|
||||
Drawable safeIcon = icon;
|
||||
|
||||
if ((icon != null) && !(icon instanceof VectorDrawable)) {
|
||||
safeIcon = getSafeDrawable(icon, 500, 500);
|
||||
}
|
||||
pref.setIcon(safeIcon);
|
||||
|
||||
return safeIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -969,7 +1026,7 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
* @param maxWidth maximum width, in pixels.
|
||||
* @param maxHeight maximum height, in pixels.
|
||||
*/
|
||||
public static Drawable getSafeDrawable(Drawable original, int maxWidth, int maxHeight) {
|
||||
private static Drawable getSafeDrawable(Drawable original, int maxWidth, int maxHeight) {
|
||||
final int actualWidth = original.getMinimumWidth();
|
||||
final int actualHeight = original.getMinimumHeight();
|
||||
|
||||
@@ -1101,13 +1158,17 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
== ProfileSelectFragment.ProfileType.PERSONAL : false;
|
||||
final boolean isWork = args != null ? args.getInt(ProfileSelectFragment.EXTRA_PROFILE)
|
||||
== ProfileSelectFragment.ProfileType.WORK : false;
|
||||
if (activity.getSystemService(UserManager.class).getUserProfiles().size() > 1
|
||||
&& ProfileFragmentBridge.FRAGMENT_MAP.get(fragmentName) != null
|
||||
&& !isWork && !isPersonal) {
|
||||
f = Fragment.instantiate(activity, ProfileFragmentBridge.FRAGMENT_MAP.get(fragmentName),
|
||||
args);
|
||||
} else {
|
||||
f = Fragment.instantiate(activity, fragmentName, args);
|
||||
try {
|
||||
if (activity.getSystemService(UserManager.class).getUserProfiles().size() > 1
|
||||
&& ProfileFragmentBridge.FRAGMENT_MAP.get(fragmentName) != null
|
||||
&& !isWork && !isPersonal) {
|
||||
f = Fragment.instantiate(activity,
|
||||
ProfileFragmentBridge.FRAGMENT_MAP.get(fragmentName), args);
|
||||
} else {
|
||||
f = Fragment.instantiate(activity, fragmentName, args);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Unable to get target fragment", e);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
@@ -1154,29 +1215,14 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if needed to disable media output, otherwise returns {@code false}.
|
||||
* Returns the color of homepage preference icons.
|
||||
*/
|
||||
public static boolean isMediaOutputDisabled(
|
||||
MediaRouter2Manager router2Manager, String packageName) {
|
||||
boolean isMediaOutputDisabled = false;
|
||||
if (!TextUtils.isEmpty(packageName)) {
|
||||
final List<MediaRoute2Info> infos = router2Manager.getAvailableRoutes(packageName);
|
||||
if (infos.size() == 1) {
|
||||
final MediaRoute2Info info = infos.get(0);
|
||||
final int deviceType = info.getType();
|
||||
switch (deviceType) {
|
||||
case TYPE_UNKNOWN:
|
||||
case TYPE_REMOTE_TV:
|
||||
case TYPE_REMOTE_SPEAKER:
|
||||
case TYPE_GROUP:
|
||||
isMediaOutputDisabled = true;
|
||||
break;
|
||||
default:
|
||||
isMediaOutputDisabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isMediaOutputDisabled;
|
||||
@ColorInt
|
||||
public static int getHomepageIconColor(Context context) {
|
||||
return getColorAttrDefaultColor(context, android.R.attr.textColorSecondary);
|
||||
}
|
||||
|
||||
public static boolean isProviderModelEnabled(Context context) {
|
||||
return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
/**
|
||||
* Preference controller for accessibility button footer.
|
||||
*/
|
||||
public class AccessibilityButtonFooterPreferenceController extends
|
||||
AccessibilityFooterPreferenceController {
|
||||
|
||||
public AccessibilityButtonFooterPreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLabelName() {
|
||||
return mContext.getString(R.string.accessibility_button_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
// Need to update footerPreference's data before super.displayPreference(), then it will use
|
||||
// data to update related property of footerPreference.
|
||||
if (AccessibilityUtil.isGestureNavigateEnabled(mContext)) {
|
||||
final AccessibilityFooterPreference footerPreference =
|
||||
screen.findPreference(getPreferenceKey());
|
||||
footerPreference.setTitle(
|
||||
mContext.getString(R.string.accessibility_button_gesture_description));
|
||||
}
|
||||
|
||||
super.displayPreference(screen);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 android.app.settings.SettingsEnums;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
|
||||
/** Settings fragment containing accessibility button properties. */
|
||||
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
||||
public class AccessibilityButtonFragment extends DashboardFragment {
|
||||
|
||||
private static final String TAG = "AccessibilityButtonFragment";
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.accessibility_button_settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLogTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.ACCESSIBILITY_BUTTON_SETTINGS;
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider(R.xml.accessibility_button_settings);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
import android.provider.Settings;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
|
||||
/** Preference controller that controls the preferred location in accessibility button page. */
|
||||
public class AccessibilityButtonLocationPreferenceController extends BasePreferenceController
|
||||
implements Preference.OnPreferenceChangeListener {
|
||||
|
||||
private final ArrayMap<String, String> mValueTitleMap = new ArrayMap<>();
|
||||
private int mDefaultLocation;
|
||||
|
||||
public AccessibilityButtonLocationPreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
initValueTitleMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return AccessibilityUtil.isGestureNavigateEnabled(mContext)
|
||||
? CONDITIONALLY_UNAVAILABLE : AVAILABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final ListPreference listPreference = (ListPreference) preference;
|
||||
final Integer value = Ints.tryParse((String) newValue);
|
||||
if (value != null) {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, value);
|
||||
updateState(listPreference);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
final ListPreference listPreference = (ListPreference) preference;
|
||||
|
||||
listPreference.setValue(getCurrentAccessibilityButtonMode());
|
||||
}
|
||||
|
||||
private String getCurrentAccessibilityButtonMode() {
|
||||
final int mode = Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, mDefaultLocation);
|
||||
return String.valueOf(mode);
|
||||
}
|
||||
|
||||
private void initValueTitleMap() {
|
||||
if (mValueTitleMap.size() == 0) {
|
||||
final String[] values = mContext.getResources().getStringArray(
|
||||
R.array.accessibility_button_location_selector_values);
|
||||
final String[] titles = mContext.getResources().getStringArray(
|
||||
R.array.accessibility_button_location_selector_titles);
|
||||
final int mapSize = values.length;
|
||||
|
||||
mDefaultLocation = Integer.parseInt(values[0]);
|
||||
for (int i = 0; i < mapSize; i++) {
|
||||
mValueTitleMap.put(values[i], titles[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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 android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
import com.android.settingslib.core.lifecycle.events.OnPause;
|
||||
import com.android.settingslib.core.lifecycle.events.OnResume;
|
||||
import com.android.settingslib.widget.LayoutPreference;
|
||||
|
||||
/** Preference controller that controls the preview effect in accessibility button page. */
|
||||
public class AccessibilityButtonPreviewPreferenceController extends BasePreferenceController
|
||||
implements LifecycleObserver, OnResume, OnPause {
|
||||
|
||||
private static final int SMALL_SIZE = 0;
|
||||
private static final float DEFAULT_OPACITY = 0.55f;
|
||||
private static final int DEFAULT_SIZE = 0;
|
||||
|
||||
private final ContentResolver mContentResolver;
|
||||
@VisibleForTesting
|
||||
final ContentObserver mContentObserver;
|
||||
private FloatingMenuLayerDrawable mFloatingMenuPreviewDrawable;
|
||||
|
||||
@VisibleForTesting
|
||||
ImageView mPreview;
|
||||
|
||||
public AccessibilityButtonPreviewPreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
mContentResolver = context.getContentResolver();
|
||||
mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
updatePreviewPreference();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return AVAILABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
final LayoutPreference preference = screen.findPreference(getPreferenceKey());
|
||||
mPreview = preference.findViewById(R.id.preview_image);
|
||||
|
||||
updatePreviewPreference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
mContentResolver.registerContentObserver(
|
||||
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE),
|
||||
/* notifyForDescendants= */ false, mContentObserver);
|
||||
mContentResolver.registerContentObserver(
|
||||
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE),
|
||||
/* notifyForDescendants= */ false, mContentObserver);
|
||||
mContentResolver.registerContentObserver(
|
||||
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY),
|
||||
/* notifyForDescendants= */ false, mContentObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
mContentResolver.unregisterContentObserver(mContentObserver);
|
||||
}
|
||||
|
||||
private void updatePreviewPreference() {
|
||||
if (AccessibilityUtil.isFloatingMenuEnabled(mContext)) {
|
||||
final int size = Settings.Secure.getInt(mContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, DEFAULT_SIZE);
|
||||
final int opacity = (int) (Settings.Secure.getFloat(mContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, DEFAULT_OPACITY) * 100);
|
||||
final int floatingMenuIconId = (size == SMALL_SIZE)
|
||||
? R.drawable.accessibility_button_preview_small_floating_menu
|
||||
: R.drawable.accessibility_button_preview_large_floating_menu;
|
||||
|
||||
mPreview.setImageDrawable(getFloatingMenuPreviewDrawable(floatingMenuIconId, opacity));
|
||||
// Only change opacity(alpha) would not invoke redraw view, need to invalidate manually.
|
||||
mPreview.invalidate();
|
||||
} else {
|
||||
mPreview.setImageDrawable(
|
||||
mContext.getDrawable(R.drawable.accessibility_button_navigation));
|
||||
}
|
||||
}
|
||||
|
||||
private Drawable getFloatingMenuPreviewDrawable(int resId, int opacity) {
|
||||
if (mFloatingMenuPreviewDrawable == null) {
|
||||
mFloatingMenuPreviewDrawable = FloatingMenuLayerDrawable.createLayerDrawable(
|
||||
mContext, resId, opacity);
|
||||
} else {
|
||||
mFloatingMenuPreviewDrawable.updateLayerDrawable(mContext, resId, opacity);
|
||||
}
|
||||
|
||||
return mFloatingMenuPreviewDrawable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
/**
|
||||
* Preference controller for accessibility control timeout footer.
|
||||
*/
|
||||
public class AccessibilityControlTimeoutFooterPreferenceController extends
|
||||
AccessibilityFooterPreferenceController {
|
||||
|
||||
public AccessibilityControlTimeoutFooterPreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLabelName() {
|
||||
return mContext.getString(R.string.accessibility_setting_item_control_timeout_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getHelpResource() {
|
||||
return R.string.help_url_timeout;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_BUTTON_COMPONENT_NAME;
|
||||
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.app.Activity;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
@@ -30,6 +33,9 @@ import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.InstrumentedFragment;
|
||||
@@ -37,6 +43,7 @@ import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settingslib.accessibility.AccessibilityUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public class AccessibilityDetailsSettingsFragment extends InstrumentedFragment {
|
||||
@@ -61,44 +68,83 @@ public class AccessibilityDetailsSettingsFragment extends InstrumentedFragment {
|
||||
return;
|
||||
}
|
||||
|
||||
// In case the A11yServiceInfo doesn't exist, go to ally services list.
|
||||
final ComponentName componentName = ComponentName.unflattenFromString(extraComponentName);
|
||||
if (openSystemAccessibilitySettingsAndFinish(componentName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (openAccessibilityDetailsSettingsAndFinish(componentName)) {
|
||||
return;
|
||||
}
|
||||
// Fall back to open accessibility services list.
|
||||
openAccessibilitySettingsAndFinish();
|
||||
}
|
||||
|
||||
private boolean openSystemAccessibilitySettingsAndFinish(
|
||||
@Nullable ComponentName componentName) {
|
||||
final LaunchFragmentArguments launchArguments =
|
||||
getSystemAccessibilitySettingsLaunchArguments(componentName);
|
||||
if (launchArguments == null) {
|
||||
return false;
|
||||
}
|
||||
openSubSettings(launchArguments.mDestination, launchArguments.mArguments);
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private LaunchFragmentArguments getSystemAccessibilitySettingsLaunchArguments(
|
||||
@Nullable ComponentName componentName) {
|
||||
if (MAGNIFICATION_COMPONENT_NAME.equals(componentName)) {
|
||||
final String destination = ToggleScreenMagnificationPreferenceFragment.class.getName();
|
||||
final Bundle arguments = new Bundle();
|
||||
MagnificationGesturesPreferenceController.populateMagnificationGesturesPreferenceExtras(
|
||||
arguments, getContext());
|
||||
return new LaunchFragmentArguments(destination, arguments);
|
||||
}
|
||||
|
||||
if (ACCESSIBILITY_BUTTON_COMPONENT_NAME.equals(componentName)) {
|
||||
final String destination = AccessibilityButtonFragment.class.getName();
|
||||
return new LaunchFragmentArguments(destination, /* arguments= */ null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private void openAccessibilitySettingsAndFinish() {
|
||||
openSubSettings(AccessibilitySettings.class.getName(), /* arguments= */ null);
|
||||
finish();
|
||||
}
|
||||
|
||||
private boolean openAccessibilityDetailsSettingsAndFinish(
|
||||
@Nullable ComponentName componentName) {
|
||||
// In case the A11yServiceInfo doesn't exist, go to ally services list.
|
||||
final AccessibilityServiceInfo info = getAccessibilityServiceInfo(componentName);
|
||||
if (info == null) {
|
||||
Log.w(TAG, "Open accessibility services list due to invalid component name.");
|
||||
openAccessibilitySettingsAndFinish();
|
||||
return;
|
||||
Log.w(TAG, "openAccessibilityDetailsSettingsAndFinish : invalid component name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// In case this accessibility service isn't permitted, go to a11y services list.
|
||||
if (!isServiceAllowed(componentName.getPackageName())) {
|
||||
Log.w(TAG,
|
||||
"Open accessibility services list due to target accessibility service is "
|
||||
"openAccessibilityDetailsSettingsAndFinish: target accessibility service is"
|
||||
+ "prohibited by Device Admin.");
|
||||
openAccessibilitySettingsAndFinish();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
openAccessibilityDetailsSettingsAndFinish(buildArguments(info));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void openAccessibilitySettingsAndFinish() {
|
||||
new SubSettingLauncher(getActivity())
|
||||
.setDestination(AccessibilitySettings.class.getName())
|
||||
.setSourceMetricsCategory(getMetricsCategory())
|
||||
.launch();
|
||||
openSubSettings(ToggleAccessibilityServicePreferenceFragment.class.getName(),
|
||||
buildArguments(info));
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void openAccessibilityDetailsSettingsAndFinish(Bundle arguments) {
|
||||
private void openSubSettings(@NonNull String destination, @Nullable Bundle arguments) {
|
||||
new SubSettingLauncher(getActivity())
|
||||
.setDestination(ToggleAccessibilityServicePreferenceFragment.class.getName())
|
||||
.setDestination(destination)
|
||||
.setSourceMetricsCategory(getMetricsCategory())
|
||||
.setArguments(arguments)
|
||||
.launch();
|
||||
finish();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -175,4 +221,13 @@ public class AccessibilityDetailsSettingsFragment extends InstrumentedFragment {
|
||||
}
|
||||
activity.finish();
|
||||
}
|
||||
|
||||
private static class LaunchFragmentArguments {
|
||||
final String mDestination;
|
||||
final Bundle mArguments;
|
||||
LaunchFragmentArguments(@NonNull String destination, @Nullable Bundle arguments) {
|
||||
mDestination = Objects.requireNonNull(destination);
|
||||
mArguments = arguments;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,515 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import static com.android.settings.accessibility.ItemInfoArrayAdapter.ItemInfo;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.icu.text.MessageFormat;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ImageSpan;
|
||||
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;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.utils.AnnotationSpan;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Utility class for creating the edit dialog.
|
||||
*/
|
||||
public class AccessibilityDialogUtils {
|
||||
|
||||
/** Denotes the dialog emuns for show dialog. */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface DialogEnums {
|
||||
|
||||
/** OPEN: Settings > Accessibility > Any toggle service > Shortcut > Settings. */
|
||||
int EDIT_SHORTCUT = 1;
|
||||
|
||||
/** OPEN: Settings > Accessibility > Magnification > Shortcut > Settings. */
|
||||
int MAGNIFICATION_EDIT_SHORTCUT = 1001;
|
||||
|
||||
/**
|
||||
* OPEN: Settings > Accessibility > Downloaded toggle service > Toggle use service to
|
||||
* enable service.
|
||||
*/
|
||||
int ENABLE_WARNING_FROM_TOGGLE = 1002;
|
||||
|
||||
/** OPEN: Settings > Accessibility > Downloaded toggle service > Shortcut checkbox. */
|
||||
int ENABLE_WARNING_FROM_SHORTCUT = 1003;
|
||||
|
||||
/**
|
||||
* OPEN: Settings > Accessibility > Downloaded toggle service > Shortcut checkbox
|
||||
* toggle.
|
||||
*/
|
||||
int ENABLE_WARNING_FROM_SHORTCUT_TOGGLE = 1004;
|
||||
|
||||
/**
|
||||
* OPEN: Settings > Accessibility > Downloaded toggle service > Toggle use service to
|
||||
* disable service.
|
||||
*/
|
||||
int DISABLE_WARNING_FROM_TOGGLE = 1005;
|
||||
|
||||
/**
|
||||
* OPEN: Settings > Accessibility > Magnification > Toggle user service in button
|
||||
* navigation.
|
||||
*/
|
||||
int ACCESSIBILITY_BUTTON_TUTORIAL = 1006;
|
||||
|
||||
/**
|
||||
* OPEN: Settings > Accessibility > Magnification > Toggle user service in gesture
|
||||
* navigation.
|
||||
*/
|
||||
int GESTURE_NAVIGATION_TUTORIAL = 1007;
|
||||
|
||||
/**
|
||||
* OPEN: Settings > Accessibility > Downloaded toggle service > Toggle user service > Show
|
||||
* launch tutorial.
|
||||
*/
|
||||
int LAUNCH_ACCESSIBILITY_TUTORIAL = 1008;
|
||||
}
|
||||
|
||||
/**
|
||||
* IntDef enum for dialog type that indicates different dialog for user to choose the shortcut
|
||||
* type.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({
|
||||
DialogType.EDIT_SHORTCUT_GENERIC,
|
||||
DialogType.EDIT_SHORTCUT_GENERIC_SUW,
|
||||
DialogType.EDIT_SHORTCUT_MAGNIFICATION,
|
||||
DialogType.EDIT_SHORTCUT_MAGNIFICATION_SUW,
|
||||
DialogType.EDIT_MAGNIFICATION_SWITCH_SHORTCUT,
|
||||
})
|
||||
|
||||
public @interface DialogType {
|
||||
int EDIT_SHORTCUT_GENERIC = 0;
|
||||
int EDIT_SHORTCUT_GENERIC_SUW = 1;
|
||||
int EDIT_SHORTCUT_MAGNIFICATION = 2;
|
||||
int EDIT_SHORTCUT_MAGNIFICATION_SUW = 3;
|
||||
int EDIT_MAGNIFICATION_SWITCH_SHORTCUT = 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to show the edit shortcut dialog.
|
||||
*
|
||||
* @param context A valid context
|
||||
* @param dialogType The type of edit shortcut dialog
|
||||
* @param dialogTitle The title of edit shortcut dialog
|
||||
* @param listener The listener to determine the action of edit shortcut dialog
|
||||
* @return A edit shortcut dialog for showing
|
||||
*/
|
||||
public static AlertDialog showEditShortcutDialog(Context context, int dialogType,
|
||||
CharSequence dialogTitle, DialogInterface.OnClickListener listener) {
|
||||
final AlertDialog alertDialog = createDialog(context, dialogType, dialogTitle, listener);
|
||||
alertDialog.show();
|
||||
setScrollIndicators(alertDialog);
|
||||
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;
|
||||
}
|
||||
|
||||
private static AlertDialog createDialog(Context context, int dialogType,
|
||||
CharSequence dialogTitle, DialogInterface.OnClickListener listener) {
|
||||
|
||||
final AlertDialog alertDialog = new AlertDialog.Builder(context)
|
||||
.setView(createEditDialogContentView(context, dialogType))
|
||||
.setTitle(dialogTitle)
|
||||
.setPositiveButton(R.string.save, listener)
|
||||
.setNegativeButton(R.string.cancel,
|
||||
(DialogInterface dialog, int which) -> dialog.dismiss())
|
||||
.create();
|
||||
|
||||
return alertDialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scroll indicators for dialog view. The indicators appears while content view is
|
||||
* out of vision for vertical scrolling.
|
||||
*/
|
||||
private static void setScrollIndicators(AlertDialog dialog) {
|
||||
final ScrollView scrollView = dialog.findViewById(R.id.container_layout);
|
||||
setScrollIndicators(scrollView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scroll indicators for dialog view. The indicators appear while content view is
|
||||
* out of vision for vertical scrolling.
|
||||
*
|
||||
* @param view The view contains customized dialog content. Usually it is {@link ScrollView} or
|
||||
* {@link AbsListView}
|
||||
*/
|
||||
private static void setScrollIndicators(@NonNull View view) {
|
||||
view.setScrollIndicators(
|
||||
View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM,
|
||||
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.
|
||||
*
|
||||
* @param context A valid context
|
||||
* @param dialogType The type of edit shortcut dialog
|
||||
* @return A content view suitable for viewing
|
||||
*/
|
||||
private static View createEditDialogContentView(Context context, int dialogType) {
|
||||
final LayoutInflater inflater = (LayoutInflater) context.getSystemService(
|
||||
Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
View contentView = null;
|
||||
|
||||
switch (dialogType) {
|
||||
case DialogType.EDIT_SHORTCUT_GENERIC:
|
||||
contentView = inflater.inflate(
|
||||
R.layout.accessibility_edit_shortcut, null);
|
||||
initSoftwareShortcut(context, contentView);
|
||||
initHardwareShortcut(context, contentView);
|
||||
break;
|
||||
case DialogType.EDIT_SHORTCUT_GENERIC_SUW:
|
||||
contentView = inflater.inflate(
|
||||
R.layout.accessibility_edit_shortcut, null);
|
||||
initSoftwareShortcutForSUW(context, contentView);
|
||||
initHardwareShortcut(context, contentView);
|
||||
break;
|
||||
case DialogType.EDIT_SHORTCUT_MAGNIFICATION:
|
||||
contentView = inflater.inflate(
|
||||
R.layout.accessibility_edit_shortcut_magnification, null);
|
||||
initSoftwareShortcut(context, contentView);
|
||||
initHardwareShortcut(context, contentView);
|
||||
initMagnifyShortcut(context, contentView);
|
||||
initAdvancedWidget(contentView);
|
||||
break;
|
||||
case DialogType.EDIT_SHORTCUT_MAGNIFICATION_SUW:
|
||||
contentView = inflater.inflate(
|
||||
R.layout.accessibility_edit_shortcut_magnification, null);
|
||||
initSoftwareShortcutForSUW(context, contentView);
|
||||
initHardwareShortcut(context, contentView);
|
||||
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();
|
||||
}
|
||||
|
||||
return contentView;
|
||||
}
|
||||
|
||||
private static void setupShortcutWidget(View view, CharSequence titleText,
|
||||
CharSequence summaryText, int imageResId) {
|
||||
final CheckBox checkBox = view.findViewById(R.id.checkbox);
|
||||
checkBox.setText(titleText);
|
||||
final TextView summary = view.findViewById(R.id.summary);
|
||||
if (TextUtils.isEmpty(summaryText)) {
|
||||
summary.setVisibility(View.GONE);
|
||||
} else {
|
||||
summary.setText(summaryText);
|
||||
summary.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
summary.setFocusable(false);
|
||||
}
|
||||
final ImageView image = view.findViewById(R.id.image);
|
||||
image.setImageResource(imageResId);
|
||||
}
|
||||
|
||||
private static void initSoftwareShortcutForSUW(Context context, View view) {
|
||||
final View dialogView = view.findViewById(R.id.software_shortcut);
|
||||
final CharSequence title = context.getText(
|
||||
R.string.accessibility_shortcut_edit_dialog_title_software);
|
||||
final TextView summary = dialogView.findViewById(R.id.summary);
|
||||
final int lineHeight = summary.getLineHeight();
|
||||
|
||||
setupShortcutWidget(dialogView, title,
|
||||
retrieveSoftwareShortcutSummaryForSUW(context, lineHeight),
|
||||
retrieveSoftwareShortcutImageResId(context));
|
||||
}
|
||||
|
||||
private static void initSoftwareShortcut(Context context, View view) {
|
||||
final View dialogView = view.findViewById(R.id.software_shortcut);
|
||||
final CharSequence title = context.getText(
|
||||
R.string.accessibility_shortcut_edit_dialog_title_software);
|
||||
final TextView summary = dialogView.findViewById(R.id.summary);
|
||||
final int lineHeight = summary.getLineHeight();
|
||||
|
||||
setupShortcutWidget(dialogView, title,
|
||||
retrieveSoftwareShortcutSummary(context, lineHeight),
|
||||
retrieveSoftwareShortcutImageResId(context));
|
||||
}
|
||||
|
||||
private static void initHardwareShortcut(Context context, View view) {
|
||||
final View dialogView = view.findViewById(R.id.hardware_shortcut);
|
||||
final CharSequence title = context.getText(
|
||||
R.string.accessibility_shortcut_edit_dialog_title_hardware);
|
||||
final CharSequence summary = context.getText(
|
||||
R.string.accessibility_shortcut_edit_dialog_summary_hardware);
|
||||
setupShortcutWidget(dialogView, title, summary,
|
||||
R.drawable.accessibility_shortcut_type_hardware);
|
||||
// TODO(b/142531156): Use vector drawable instead of temporal png file to avoid distorted.
|
||||
}
|
||||
|
||||
private static void initMagnifyShortcut(Context context, View view) {
|
||||
final View dialogView = view.findViewById(R.id.triple_tap_shortcut);
|
||||
final CharSequence title = context.getText(
|
||||
R.string.accessibility_shortcut_edit_dialog_title_triple_tap);
|
||||
String summary = context.getString(
|
||||
R.string.accessibility_shortcut_edit_dialog_summary_triple_tap);
|
||||
// Format the number '3' in the summary.
|
||||
final Object[] arguments = {3};
|
||||
summary = MessageFormat.format(summary, arguments);
|
||||
|
||||
setupShortcutWidget(dialogView, title, summary,
|
||||
R.drawable.accessibility_shortcut_type_triple_tap);
|
||||
// TODO(b/142531156): Use vector drawable instead of temporal png file to avoid distorted.
|
||||
}
|
||||
|
||||
private static void initAdvancedWidget(View view) {
|
||||
final LinearLayout advanced = view.findViewById(R.id.advanced_shortcut);
|
||||
final View tripleTap = view.findViewById(R.id.triple_tap_shortcut);
|
||||
advanced.setOnClickListener((View v) -> {
|
||||
advanced.setVisibility(View.GONE);
|
||||
tripleTap.setVisibility(View.VISIBLE);
|
||||
});
|
||||
}
|
||||
|
||||
private static CharSequence retrieveSoftwareShortcutSummaryForSUW(Context context,
|
||||
int lineHeight) {
|
||||
final SpannableStringBuilder sb = new SpannableStringBuilder();
|
||||
if (!AccessibilityUtil.isFloatingMenuEnabled(context)) {
|
||||
sb.append(getSummaryStringWithIcon(context, lineHeight));
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
private static CharSequence retrieveSoftwareShortcutSummary(Context context, int lineHeight) {
|
||||
final SpannableStringBuilder sb = new SpannableStringBuilder();
|
||||
if (!AccessibilityUtil.isFloatingMenuEnabled(context)) {
|
||||
sb.append(getSummaryStringWithIcon(context, lineHeight));
|
||||
sb.append("\n\n");
|
||||
}
|
||||
sb.append(getCustomizeAccessibilityButtonLink(context));
|
||||
return sb;
|
||||
}
|
||||
|
||||
private static int retrieveSoftwareShortcutImageResId(Context context) {
|
||||
return AccessibilityUtil.isFloatingMenuEnabled(context)
|
||||
? R.drawable.accessibility_shortcut_type_software_floating
|
||||
: R.drawable.accessibility_shortcut_type_software;
|
||||
}
|
||||
|
||||
private static CharSequence getCustomizeAccessibilityButtonLink(Context context) {
|
||||
final View.OnClickListener linkListener = v -> new SubSettingLauncher(context)
|
||||
.setDestination(AccessibilityButtonFragment.class.getName())
|
||||
.setSourceMetricsCategory(
|
||||
SettingsEnums.SWITCH_SHORTCUT_DIALOG_ACCESSIBILITY_BUTTON_SETTINGS)
|
||||
.launch();
|
||||
final AnnotationSpan.LinkInfo linkInfo = new AnnotationSpan.LinkInfo(
|
||||
AnnotationSpan.LinkInfo.DEFAULT_ANNOTATION, linkListener);
|
||||
|
||||
return AnnotationSpan.linkify(context.getText(
|
||||
R.string.accessibility_shortcut_edit_dialog_summary_software_floating), linkInfo);
|
||||
}
|
||||
|
||||
private static SpannableString getSummaryStringWithIcon(Context context, int lineHeight) {
|
||||
final String summary = context
|
||||
.getString(R.string.accessibility_shortcut_edit_dialog_summary_software);
|
||||
final SpannableString spannableMessage = SpannableString.valueOf(summary);
|
||||
|
||||
// Icon
|
||||
final int indexIconStart = summary.indexOf("%s");
|
||||
final int indexIconEnd = indexIconStart + 2;
|
||||
final Drawable icon = context.getDrawable(R.drawable.ic_accessibility_new);
|
||||
final ImageSpan imageSpan = new ImageSpan(icon);
|
||||
imageSpan.setContentDescription("");
|
||||
icon.setBounds(0, 0, lineHeight, lineHeight);
|
||||
spannableMessage.setSpan(
|
||||
imageSpan, indexIconStart, indexIconEnd,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return spannableMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color associated with the specified attribute in the context's theme.
|
||||
*/
|
||||
@ColorInt
|
||||
private static int getThemeAttrColor(final Context context, final int attributeColor) {
|
||||
final int colorResId = getAttrResourceId(context, attributeColor);
|
||||
return ContextCompat.getColor(context, colorResId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier of the resolved resource assigned to the given attribute.
|
||||
*/
|
||||
private static int getAttrResourceId(final Context context, final int attributeColor) {
|
||||
final int[] attrs = {attributeColor};
|
||||
final TypedArray typedArray = context.obtainStyledAttributes(attrs);
|
||||
final int colorResId = typedArray.getResourceId(0, 0);
|
||||
typedArray.recycle();
|
||||
return colorResId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a dialog with the given view.
|
||||
*
|
||||
* @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
|
||||
* @return the {@link Dialog} with the given view
|
||||
*/
|
||||
public static Dialog createCustomDialog(Context context, CharSequence dialogTitle,
|
||||
View customView, DialogInterface.OnClickListener listener) {
|
||||
final AlertDialog alertDialog = new AlertDialog.Builder(context)
|
||||
.setView(customView)
|
||||
.setTitle(dialogTitle)
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(R.string.save, listener)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.create();
|
||||
if (customView instanceof ScrollView || customView instanceof AbsListView) {
|
||||
setScrollIndicators(customView);
|
||||
}
|
||||
return alertDialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a single choice {@link ListView} with given {@link ItemInfo} list.
|
||||
*
|
||||
* @param context A context.
|
||||
* @param itemInfoList A {@link ItemInfo} list.
|
||||
* @param itemListener The listener will be invoked when the item is clicked.
|
||||
*/
|
||||
@NonNull
|
||||
public static ListView createSingleChoiceListView(@NonNull Context context,
|
||||
@NonNull List<? extends ItemInfo> itemInfoList,
|
||||
@Nullable AdapterView.OnItemClickListener itemListener) {
|
||||
final ListView list = new ListView(context);
|
||||
// Set an id to save its state.
|
||||
list.setId(android.R.id.list);
|
||||
list.setDivider(/* divider= */ null);
|
||||
list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
||||
final ItemInfoArrayAdapter
|
||||
adapter = new ItemInfoArrayAdapter(context, itemInfoList);
|
||||
list.setAdapter(adapter);
|
||||
list.setOnItemClickListener(itemListener);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -1,327 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.ImageSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Utility class for creating the edit dialog.
|
||||
*/
|
||||
public class AccessibilityEditDialogUtils {
|
||||
|
||||
/**
|
||||
* IntDef enum for dialog type that indicates different dialog for user to choose the shortcut
|
||||
* type.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({
|
||||
DialogType.EDIT_SHORTCUT_GENERIC,
|
||||
DialogType.EDIT_SHORTCUT_MAGNIFICATION,
|
||||
DialogType.EDIT_MAGNIFICATION_MODE,
|
||||
})
|
||||
|
||||
private @interface DialogType {
|
||||
int EDIT_SHORTCUT_GENERIC = 0;
|
||||
int EDIT_SHORTCUT_MAGNIFICATION = 1;
|
||||
int EDIT_MAGNIFICATION_MODE = 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to show the edit shortcut dialog.
|
||||
*
|
||||
* @param context A valid context
|
||||
* @param dialogTitle The title of edit shortcut dialog
|
||||
* @param listener The listener to determine the action of edit shortcut dialog
|
||||
* @return A edit shortcut dialog for showing
|
||||
*/
|
||||
public static AlertDialog showEditShortcutDialog(Context context, CharSequence dialogTitle,
|
||||
DialogInterface.OnClickListener listener) {
|
||||
final AlertDialog alertDialog = createDialog(context, DialogType.EDIT_SHORTCUT_GENERIC,
|
||||
dialogTitle, listener);
|
||||
alertDialog.show();
|
||||
setScrollIndicators(alertDialog);
|
||||
return alertDialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to show the edit shortcut dialog in Magnification.
|
||||
*
|
||||
* @param context A valid context
|
||||
* @param dialogTitle The title of edit shortcut dialog
|
||||
* @param listener The listener to determine the action of edit shortcut dialog
|
||||
* @return A edit shortcut dialog for showing in Magnification
|
||||
*/
|
||||
public static AlertDialog showMagnificationEditShortcutDialog(Context context,
|
||||
CharSequence dialogTitle, DialogInterface.OnClickListener listener) {
|
||||
final AlertDialog alertDialog = createDialog(context,
|
||||
DialogType.EDIT_SHORTCUT_MAGNIFICATION, dialogTitle, listener);
|
||||
alertDialog.show();
|
||||
setScrollIndicators(alertDialog);
|
||||
return alertDialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to show the magnification mode dialog in Magnification.
|
||||
*
|
||||
* @param context A valid context
|
||||
* @param dialogTitle The title of magnify mode dialog
|
||||
* @param listener The listener to determine the action of magnify mode dialog
|
||||
* @return A magnification mode dialog in Magnification
|
||||
*/
|
||||
public static AlertDialog showMagnificationModeDialog(Context context,
|
||||
CharSequence dialogTitle, DialogInterface.OnClickListener listener) {
|
||||
final AlertDialog alertDialog = createDialog(context,
|
||||
DialogType.EDIT_MAGNIFICATION_MODE, dialogTitle, listener);
|
||||
alertDialog.show();
|
||||
setScrollIndicators(alertDialog);
|
||||
return alertDialog;
|
||||
}
|
||||
|
||||
private static AlertDialog createDialog(Context context, int dialogType,
|
||||
CharSequence dialogTitle, DialogInterface.OnClickListener listener) {
|
||||
|
||||
final AlertDialog alertDialog = new AlertDialog.Builder(context)
|
||||
.setView(createEditDialogContentView(context, dialogType))
|
||||
.setTitle(dialogTitle)
|
||||
.setPositiveButton(R.string.save, listener)
|
||||
.setNegativeButton(R.string.cancel,
|
||||
(DialogInterface dialog, int which) -> dialog.dismiss())
|
||||
.create();
|
||||
|
||||
return alertDialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scroll indicators for dialog view. The indicators appears while content view is
|
||||
* out of vision for vertical scrolling.
|
||||
*/
|
||||
private static void setScrollIndicators(AlertDialog dialog) {
|
||||
final ScrollView scrollView = dialog.findViewById(R.id.container_layout);
|
||||
scrollView.setScrollIndicators(
|
||||
View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM,
|
||||
View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a content View for the edit shortcut dialog.
|
||||
*
|
||||
* @param context A valid context
|
||||
* @param dialogType The type of edit shortcut dialog
|
||||
* @return A content view suitable for viewing
|
||||
*/
|
||||
private static View createEditDialogContentView(Context context, int dialogType) {
|
||||
final LayoutInflater inflater = (LayoutInflater) context.getSystemService(
|
||||
Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
View contentView = null;
|
||||
|
||||
switch (dialogType) {
|
||||
case DialogType.EDIT_SHORTCUT_GENERIC:
|
||||
contentView = inflater.inflate(
|
||||
R.layout.accessibility_edit_shortcut, null);
|
||||
initSoftwareShortcut(context, contentView);
|
||||
initHardwareShortcut(context, contentView);
|
||||
break;
|
||||
case DialogType.EDIT_SHORTCUT_MAGNIFICATION:
|
||||
contentView = inflater.inflate(
|
||||
R.layout.accessibility_edit_shortcut_magnification, null);
|
||||
initSoftwareShortcut(context, contentView);
|
||||
initHardwareShortcut(context, contentView);
|
||||
initMagnifyShortcut(context, contentView);
|
||||
initAdvancedWidget(contentView);
|
||||
break;
|
||||
case DialogType.EDIT_MAGNIFICATION_MODE:
|
||||
contentView = inflater.inflate(
|
||||
R.layout.accessibility_edit_magnification_mode, null);
|
||||
initMagnifyFullScreen(context, contentView);
|
||||
initMagnifyWindowScreen(context, contentView);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
return contentView;
|
||||
}
|
||||
|
||||
private static void initMagnifyFullScreen(Context context, View view) {
|
||||
final View dialogView = view.findViewById(R.id.magnify_full_screen);
|
||||
final CharSequence title = context.getText(
|
||||
R.string.accessibility_magnification_area_settings_full_screen);
|
||||
// TODO(b/146019459): Use vector drawable instead of temporal png file to avoid distorted.
|
||||
setupShortcutWidget(dialogView, title, R.drawable.accessibility_magnification_full_screen);
|
||||
}
|
||||
|
||||
private static void initMagnifyWindowScreen(Context context, View view) {
|
||||
final View dialogView = view.findViewById(R.id.magnify_window_screen);
|
||||
final CharSequence title = context.getText(
|
||||
R.string.accessibility_magnification_area_settings_window_screen);
|
||||
// TODO(b/146019459): Use vector drawable instead of temporal png file to avoid distorted.
|
||||
setupShortcutWidget(dialogView, title,
|
||||
R.drawable.accessibility_magnification_window_screen);
|
||||
}
|
||||
|
||||
private static void setupShortcutWidget(View view, CharSequence titleText, int imageResId) {
|
||||
setupShortcutWidget(view, titleText, null, imageResId);
|
||||
}
|
||||
|
||||
private static void setupShortcutWidget(View view, CharSequence titleText,
|
||||
CharSequence summaryText, int imageResId) {
|
||||
final CheckBox checkBox = view.findViewById(R.id.checkbox);
|
||||
checkBox.setText(titleText);
|
||||
final TextView summary = view.findViewById(R.id.summary);
|
||||
if (TextUtils.isEmpty(summaryText)) {
|
||||
summary.setVisibility(View.GONE);
|
||||
} else {
|
||||
summary.setText(summaryText);
|
||||
}
|
||||
final ImageView image = view.findViewById(R.id.image);
|
||||
image.setImageResource(imageResId);
|
||||
}
|
||||
|
||||
private static void initSoftwareShortcut(Context context, View view) {
|
||||
final View dialogView = view.findViewById(R.id.software_shortcut);
|
||||
final TextView summary = dialogView.findViewById(R.id.summary);
|
||||
final int lineHeight = summary.getLineHeight();
|
||||
setupShortcutWidget(dialogView, retrieveTitle(context),
|
||||
retrieveSummary(context, lineHeight), retrieveImageResId(context));
|
||||
}
|
||||
|
||||
private static void initHardwareShortcut(Context context, View view) {
|
||||
final View dialogView = view.findViewById(R.id.hardware_shortcut);
|
||||
final CharSequence title = context.getText(
|
||||
R.string.accessibility_shortcut_edit_dialog_title_hardware);
|
||||
final CharSequence summary = context.getText(
|
||||
R.string.accessibility_shortcut_edit_dialog_summary_hardware);
|
||||
setupShortcutWidget(dialogView, title, summary,
|
||||
R.drawable.accessibility_shortcut_type_hardware);
|
||||
// TODO(b/142531156): Use vector drawable instead of temporal png file to avoid distorted.
|
||||
}
|
||||
|
||||
private static void initMagnifyShortcut(Context context, View view) {
|
||||
final View dialogView = view.findViewById(R.id.triple_tap_shortcut);
|
||||
final CharSequence title = context.getText(
|
||||
R.string.accessibility_shortcut_edit_dialog_title_triple_tap);
|
||||
final CharSequence summary = context.getText(
|
||||
R.string.accessibility_shortcut_edit_dialog_summary_triple_tap);
|
||||
setupShortcutWidget(dialogView, title, summary,
|
||||
R.drawable.accessibility_shortcut_type_triple_tap);
|
||||
// TODO(b/142531156): Use vector drawable instead of temporal png file to avoid distorted.
|
||||
}
|
||||
|
||||
private static void initAdvancedWidget(View view) {
|
||||
final LinearLayout advanced = view.findViewById(R.id.advanced_shortcut);
|
||||
final View tripleTap = view.findViewById(R.id.triple_tap_shortcut);
|
||||
advanced.setOnClickListener((View v) -> {
|
||||
advanced.setVisibility(View.GONE);
|
||||
tripleTap.setVisibility(View.VISIBLE);
|
||||
});
|
||||
}
|
||||
|
||||
private static CharSequence retrieveTitle(Context context) {
|
||||
int resId = R.string.accessibility_shortcut_edit_dialog_title_software;
|
||||
if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
|
||||
resId = AccessibilityUtil.isTouchExploreEnabled(context)
|
||||
? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
|
||||
: R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
|
||||
}
|
||||
return context.getText(resId);
|
||||
}
|
||||
|
||||
private static CharSequence retrieveSummary(Context context, int lineHeight) {
|
||||
if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
|
||||
final int resId = AccessibilityUtil.isTouchExploreEnabled(context)
|
||||
? R.string.accessibility_shortcut_edit_dialog_summary_software_gesture_talkback
|
||||
: R.string.accessibility_shortcut_edit_dialog_summary_software_gesture;
|
||||
return context.getText(resId);
|
||||
}
|
||||
return getSummaryStringWithIcon(context, lineHeight);
|
||||
}
|
||||
|
||||
private static int retrieveImageResId(Context context) {
|
||||
// TODO(b/142531156): Use vector drawable instead of temporal png file to avoid distorted.
|
||||
int resId = R.drawable.accessibility_shortcut_type_software;
|
||||
if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
|
||||
resId = AccessibilityUtil.isTouchExploreEnabled(context)
|
||||
? R.drawable.accessibility_shortcut_type_software_gesture_talkback
|
||||
: R.drawable.accessibility_shortcut_type_software_gesture;
|
||||
}
|
||||
return resId;
|
||||
}
|
||||
|
||||
private static SpannableString getSummaryStringWithIcon(Context context, int lineHeight) {
|
||||
final String summary = context
|
||||
.getString(R.string.accessibility_shortcut_edit_dialog_summary_software);
|
||||
final SpannableString spannableMessage = SpannableString.valueOf(summary);
|
||||
|
||||
// Icon
|
||||
final int indexIconStart = summary.indexOf("%s");
|
||||
final int indexIconEnd = indexIconStart + 2;
|
||||
final Drawable icon = context.getDrawable(R.drawable.ic_accessibility_new);
|
||||
final ImageSpan imageSpan = new ImageSpan(icon);
|
||||
imageSpan.setContentDescription("");
|
||||
icon.setBounds(0, 0, lineHeight, lineHeight);
|
||||
spannableMessage.setSpan(
|
||||
imageSpan, indexIconStart, indexIconEnd,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
return spannableMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color associated with the specified attribute in the context's theme.
|
||||
*/
|
||||
@ColorInt
|
||||
private static int getThemeAttrColor(final Context context, final int attributeColor) {
|
||||
final int colorResId = getAttrResourceId(context, attributeColor);
|
||||
return ContextCompat.getColor(context, colorResId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier of the resolved resource assigned to the given attribute.
|
||||
*/
|
||||
private static int getAttrResourceId(final Context context, final int attributeColor) {
|
||||
final int[] attrs = {attributeColor};
|
||||
final TypedArray typedArray = context.obtainStyledAttributes(attrs);
|
||||
final int colorResId = typedArray.getResourceId(0, 0);
|
||||
typedArray.recycle();
|
||||
return colorResId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
|
||||
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.
|
||||
*/
|
||||
public final class AccessibilityFooterPreference extends FooterPreference {
|
||||
|
||||
private boolean mLinkEnabled;
|
||||
|
||||
public AccessibilityFooterPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public AccessibilityFooterPreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||
super.onBindViewHolder(holder);
|
||||
|
||||
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.
|
||||
// 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());
|
||||
} else {
|
||||
title.setMovementMethod(/* movement= */ null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the title field supports movement method.
|
||||
*/
|
||||
public void setLinkEnabled(boolean enabled) {
|
||||
if (mLinkEnabled != enabled) {
|
||||
mLinkEnabled = enabled;
|
||||
notifyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the title field supports movement method.
|
||||
*/
|
||||
public boolean isLinkEnabled() {
|
||||
return mLinkEnabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settingslib.HelpUtils;
|
||||
|
||||
/**
|
||||
* Base class for accessibility preference footer.
|
||||
*/
|
||||
public abstract class AccessibilityFooterPreferenceController extends BasePreferenceController {
|
||||
|
||||
public AccessibilityFooterPreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return AVAILABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
|
||||
final AccessibilityFooterPreference footerPreference =
|
||||
screen.findPreference(getPreferenceKey());
|
||||
updateFooterPreferences(footerPreference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this if showing a help item in the footer bar, by returning the resource id.
|
||||
*
|
||||
* @return the resource id for the help url
|
||||
*/
|
||||
protected int getHelpResource() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Returns the accessibility feature name. */
|
||||
protected abstract String getLabelName();
|
||||
|
||||
private void updateFooterPreferences(AccessibilityFooterPreference footerPreference) {
|
||||
final StringBuffer sb = new StringBuffer();
|
||||
sb.append(mContext.getString(
|
||||
R.string.accessibility_introduction_title, getLabelName()))
|
||||
.append("\n\n")
|
||||
.append(footerPreference.getTitle());
|
||||
footerPreference.setContentDescription(sb);
|
||||
|
||||
if (getHelpResource() != 0) {
|
||||
footerPreference.setLearnMoreAction(view -> {
|
||||
final Intent helpIntent = HelpUtils.getHelpIntent(
|
||||
mContext, mContext.getString(getHelpResource()),
|
||||
mContext.getClass().getName());
|
||||
view.startActivityForResult(helpIntent, 0);
|
||||
});
|
||||
|
||||
final String learnMoreContentDescription = mContext.getString(
|
||||
R.string.footer_learn_more_content_description, getLabelName());
|
||||
footerPreference.setLearnMoreContentDescription(learnMoreContentDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,12 +24,10 @@ import static com.android.settings.accessibility.AccessibilityUtil.UserShortcutT
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.ImageSpan;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.TextureView;
|
||||
@@ -49,11 +47,11 @@ import androidx.annotation.VisibleForTesting;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.util.Preconditions;
|
||||
import androidx.core.widget.TextViewCompat;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -310,30 +308,23 @@ public final class AccessibilityGestureNavigationTutorial {
|
||||
}
|
||||
|
||||
private static View makeTitleView(Context context) {
|
||||
final String familyName =
|
||||
context.getString(
|
||||
com.android.internal.R.string.config_headlineFontFamilyMedium);
|
||||
final TextView textView = new TextView(context);
|
||||
|
||||
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, /* size= */ 20);
|
||||
textView.setTextColor(Utils.getColorAttr(context, android.R.attr.textColorPrimary));
|
||||
// Sets the text color, size, style, hint color, and highlight color from the specified
|
||||
// TextAppearance resource.
|
||||
TextViewCompat.setTextAppearance(textView, R.style.AccessibilityDialogTitle);
|
||||
textView.setGravity(Gravity.CENTER);
|
||||
textView.setTypeface(Typeface.create(familyName, Typeface.NORMAL));
|
||||
|
||||
return textView;
|
||||
}
|
||||
|
||||
private static View makeInstructionView(Context context) {
|
||||
final TextView textView = new TextView(context);
|
||||
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, /* size= */ 16);
|
||||
textView.setTextColor(Utils.getColorAttr(context, android.R.attr.textColorPrimary));
|
||||
textView.setTypeface(
|
||||
Typeface.create(/* familyName= */ "sans-serif", Typeface.NORMAL));
|
||||
TextViewCompat.setTextAppearance(textView, R.style.AccessibilityDialogDescription);
|
||||
return textView;
|
||||
}
|
||||
|
||||
private static TutorialPage createSoftwareTutorialPage(@NonNull Context context) {
|
||||
final CharSequence title = getSoftwareTitle(context);
|
||||
final CharSequence title = context.getText(
|
||||
R.string.accessibility_tutorial_dialog_title_button);
|
||||
final ImageView image = createSoftwareImage(context);
|
||||
final CharSequence instruction = getSoftwareInstruction(context);
|
||||
final ImageView indicatorIcon =
|
||||
@@ -390,44 +381,19 @@ public final class AccessibilityGestureNavigationTutorial {
|
||||
return tutorialPages;
|
||||
}
|
||||
|
||||
private static CharSequence getSoftwareTitle(Context context) {
|
||||
final boolean isGestureNavigationEnabled =
|
||||
AccessibilityUtil.isGestureNavigateEnabled(context);
|
||||
final int resId = isGestureNavigationEnabled
|
||||
? R.string.accessibility_tutorial_dialog_title_gesture
|
||||
: R.string.accessibility_tutorial_dialog_title_button;
|
||||
|
||||
return context.getText(resId);
|
||||
}
|
||||
|
||||
private static ImageView createSoftwareImage(Context context) {
|
||||
int resId = R.drawable.accessibility_shortcut_type_software;
|
||||
if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
|
||||
resId = AccessibilityUtil.isTouchExploreEnabled(context)
|
||||
? R.drawable.accessibility_shortcut_type_software_gesture_talkback
|
||||
: R.drawable.accessibility_shortcut_type_software_gesture;
|
||||
}
|
||||
final int resId = AccessibilityUtil.isFloatingMenuEnabled(context)
|
||||
? R.drawable.accessibility_shortcut_type_software_floating
|
||||
: R.drawable.accessibility_shortcut_type_software;
|
||||
|
||||
return createImageView(context, resId);
|
||||
}
|
||||
|
||||
private static CharSequence getSoftwareInstruction(Context context) {
|
||||
final boolean isGestureNavigateEnabled =
|
||||
AccessibilityUtil.isGestureNavigateEnabled(context);
|
||||
final boolean isTouchExploreEnabled = AccessibilityUtil.isTouchExploreEnabled(context);
|
||||
int resId = R.string.accessibility_tutorial_dialog_message_button;
|
||||
if (isGestureNavigateEnabled) {
|
||||
resId = isTouchExploreEnabled
|
||||
? R.string.accessibility_tutorial_dialog_message_gesture_talkback
|
||||
: R.string.accessibility_tutorial_dialog_message_gesture;
|
||||
}
|
||||
|
||||
CharSequence text = context.getText(resId);
|
||||
if (resId == R.string.accessibility_tutorial_dialog_message_button) {
|
||||
text = getSoftwareInstructionWithIcon(context, text);
|
||||
}
|
||||
|
||||
return text;
|
||||
return AccessibilityUtil.isFloatingMenuEnabled(context)
|
||||
? context.getText(R.string.accessibility_tutorial_dialog_message_floating_button)
|
||||
: getSoftwareInstructionWithIcon(context,
|
||||
context.getText(R.string.accessibility_tutorial_dialog_message_button));
|
||||
}
|
||||
|
||||
private static CharSequence getSoftwareInstructionWithIcon(Context context, CharSequence text) {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
|
||||
import com.android.settingslib.search.SearchIndexableRaw;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provider for Accessibility Search related features.
|
||||
*/
|
||||
public interface AccessibilitySearchFeatureProvider {
|
||||
|
||||
/**
|
||||
* Returns a list of raw data for indexing. See {@link SearchIndexableRaw}
|
||||
*
|
||||
* @param context a valid context {@link Context} instance
|
||||
* @return a list of {@link SearchIndexableRaw} references. Can be null.
|
||||
*/
|
||||
List<SearchIndexableRaw> getSearchIndexableRawData(Context context);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
|
||||
import com.android.settingslib.search.SearchIndexableRaw;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provider implementation for Accessibility Search related features.
|
||||
*/
|
||||
public class AccessibilitySearchFeatureProviderImpl implements AccessibilitySearchFeatureProvider {
|
||||
|
||||
@Override
|
||||
public List<SearchIndexableRaw> getSearchIndexableRawData(Context context) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
@@ -60,11 +61,19 @@ public class AccessibilityServiceWarning {
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* The interface to execute the uninstallation action.
|
||||
*/
|
||||
interface UninstallActionPerformer {
|
||||
void uninstallPackage();
|
||||
}
|
||||
|
||||
/** Returns a {@link Dialog} to be shown to confirm that they want to enable a service. */
|
||||
public static Dialog createCapabilitiesDialog(Context context,
|
||||
AccessibilityServiceInfo info, View.OnClickListener listener) {
|
||||
public static Dialog createCapabilitiesDialog(@NonNull Context context,
|
||||
@NonNull AccessibilityServiceInfo info, @NonNull View.OnClickListener listener,
|
||||
@NonNull UninstallActionPerformer performer) {
|
||||
final AlertDialog ad = new AlertDialog.Builder(context)
|
||||
.setView(createEnableDialogContentView(context, info, listener))
|
||||
.setView(createEnableDialogContentView(context, info, listener, performer))
|
||||
.create();
|
||||
|
||||
Window window = ad.getWindow();
|
||||
@@ -88,7 +97,8 @@ public class AccessibilityServiceWarning {
|
||||
}
|
||||
|
||||
private static View createEnableDialogContentView(Context context,
|
||||
AccessibilityServiceInfo info, View.OnClickListener listener) {
|
||||
@NonNull AccessibilityServiceInfo info, View.OnClickListener listener,
|
||||
UninstallActionPerformer performer) {
|
||||
LayoutInflater inflater = (LayoutInflater) context.getSystemService(
|
||||
Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
@@ -129,6 +139,14 @@ public class AccessibilityServiceWarning {
|
||||
permissionAllowButton.setOnTouchListener(filterTouchListener);
|
||||
permissionDenyButton.setOnClickListener(listener);
|
||||
|
||||
final Button uninstallButton = content.findViewById(
|
||||
R.id.permission_enable_uninstall_button);
|
||||
// Shows an uninstall button to help users quickly remove the non-system App due to the
|
||||
// required permissions.
|
||||
if (!AccessibilityUtil.isSystemApp(info)) {
|
||||
uninstallButton.setVisibility(View.VISIBLE);
|
||||
uninstallButton.setOnClickListener(v -> performer.uninstallPackage());
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import static com.android.settingslib.TwoTargetPreference.ICON_SIZE_MEDIUM;
|
||||
import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_MEDIUM;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.accessibilityservice.AccessibilityShortcutInfo;
|
||||
@@ -28,8 +28,8 @@ 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.hardware.display.ColorDisplayManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
@@ -43,7 +43,6 @@ import androidx.annotation.VisibleForTesting;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import com.android.internal.accessibility.AccessibilityShortcutController;
|
||||
import com.android.internal.content.PackageMonitor;
|
||||
@@ -51,13 +50,14 @@ 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.display.DarkUIPreferenceController;
|
||||
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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -76,28 +76,17 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
|
||||
// Preference categories
|
||||
private static final String CATEGORY_SCREEN_READER = "screen_reader_category";
|
||||
private static final String CATEGORY_AUDIO_AND_CAPTIONS = "audio_and_captions_category";
|
||||
private static final String CATEGORY_CAPTIONS = "captions_category";
|
||||
private static final String CATEGORY_AUDIO = "audio_category";
|
||||
private static final String CATEGORY_DISPLAY = "display_category";
|
||||
private static final String CATEGORY_INTERACTION_CONTROL = "interaction_control_category";
|
||||
private static final String CATEGORY_EXPERIMENTAL = "experimental_category";
|
||||
private static final String CATEGORY_DOWNLOADED_SERVICES = "user_installed_services_category";
|
||||
|
||||
private static final String[] CATEGORIES = new String[] {
|
||||
CATEGORY_SCREEN_READER, CATEGORY_AUDIO_AND_CAPTIONS, CATEGORY_DISPLAY,
|
||||
CATEGORY_INTERACTION_CONTROL, CATEGORY_EXPERIMENTAL, CATEGORY_DOWNLOADED_SERVICES
|
||||
private static final String[] CATEGORIES = new String[]{
|
||||
CATEGORY_SCREEN_READER, CATEGORY_CAPTIONS, CATEGORY_AUDIO, CATEGORY_DISPLAY,
|
||||
CATEGORY_INTERACTION_CONTROL, CATEGORY_DOWNLOADED_SERVICES
|
||||
};
|
||||
|
||||
// Preferences
|
||||
private static final String TOGGLE_INVERSION_PREFERENCE =
|
||||
"toggle_inversion_preference";
|
||||
private static final String TOGGLE_LARGE_POINTER_ICON =
|
||||
"toggle_large_pointer_icon";
|
||||
private static final String TOGGLE_DISABLE_ANIMATIONS = "toggle_disable_animations";
|
||||
private static final String DISPLAY_MAGNIFICATION_PREFERENCE_SCREEN =
|
||||
"magnification_preference_screen";
|
||||
private static final String DISPLAY_DALTONIZER_PREFERENCE_SCREEN =
|
||||
"daltonizer_preference";
|
||||
|
||||
// Extras passed to sub-fragments.
|
||||
static final String EXTRA_PREFERENCE_KEY = "preference_key";
|
||||
static final String EXTRA_CHECKED = "checked";
|
||||
@@ -125,7 +114,7 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
@Override
|
||||
public void run() {
|
||||
if (getActivity() != null) {
|
||||
updateServicePreferences();
|
||||
onContentChanged();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -156,7 +145,8 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
}
|
||||
};
|
||||
|
||||
private final SettingsContentObserver mSettingsContentObserver;
|
||||
@VisibleForTesting
|
||||
final SettingsContentObserver mSettingsContentObserver;
|
||||
|
||||
private final Map<String, PreferenceCategory> mCategoryToPrefCategoryMap =
|
||||
new ArrayMap<>();
|
||||
@@ -165,22 +155,8 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
private final Map<ComponentName, PreferenceCategory> mPreBundledServiceComponentToCategoryMap =
|
||||
new ArrayMap<>();
|
||||
|
||||
private SwitchPreference mToggleLargePointerIconPreference;
|
||||
private SwitchPreference mToggleDisableAnimationsPreference;
|
||||
private Preference mDisplayMagnificationPreferenceScreen;
|
||||
private Preference mDisplayDaltonizerPreferenceScreen;
|
||||
private Preference mToggleInversionPreference;
|
||||
|
||||
/**
|
||||
* Check if the color transforms are color accelerated. Some transforms are experimental only
|
||||
* on non-accelerated platforms due to the performance implications.
|
||||
*
|
||||
* @param context The current context
|
||||
*/
|
||||
public static boolean isColorTransformAccelerated(Context context) {
|
||||
return context.getResources()
|
||||
.getBoolean(com.android.internal.R.bool.config_setColorTransformAccelerated);
|
||||
}
|
||||
private boolean mNeedPreferencesUpdate = false;
|
||||
private boolean mIsForeground = true;
|
||||
|
||||
public AccessibilitySettings() {
|
||||
// Observe changes to anything that the shortcut can toggle, so we can reflect updates
|
||||
@@ -197,7 +173,7 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
mSettingsContentObserver = new SettingsContentObserver(mHandler, shortcutFeatureKeys) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
updateAllPreferences();
|
||||
onContentChanged();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -212,36 +188,43 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
return R.string.help_uri_accessibility;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
initializeAllPreferences();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
use(DarkUIPreferenceController.class).setParentFragment(this);
|
||||
use(AccessibilityHearingAidPreferenceController.class)
|
||||
.setFragmentManager(getFragmentManager());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
initializeAllPreferences();
|
||||
updateAllPreferences();
|
||||
registerContentMonitors();
|
||||
}
|
||||
|
||||
mSettingsPackageMonitor.register(getActivity(), getActivity().getMainLooper(), false);
|
||||
mSettingsContentObserver.register(getContentResolver());
|
||||
@Override
|
||||
public void onStart() {
|
||||
if (mNeedPreferencesUpdate) {
|
||||
updateAllPreferences();
|
||||
mNeedPreferencesUpdate = false;
|
||||
}
|
||||
mIsForeground = true;
|
||||
super.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
mSettingsPackageMonitor.unregister();
|
||||
mSettingsContentObserver.unregister(getContentResolver());
|
||||
mIsForeground = false;
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
unregisterContentMonitors();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.accessibility_settings;
|
||||
@@ -255,8 +238,8 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
/**
|
||||
* Returns the summary for the current state of this accessibilityService.
|
||||
*
|
||||
* @param context A valid context
|
||||
* @param info The accessibilityService's info
|
||||
* @param context A valid context
|
||||
* @param info The accessibilityService's info
|
||||
* @param serviceEnabled Whether the accessibility service is enabled.
|
||||
* @return The service summary
|
||||
*/
|
||||
@@ -295,8 +278,8 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
/**
|
||||
* Returns the description for the current state of this accessibilityService.
|
||||
*
|
||||
* @param context A valid context
|
||||
* @param info The accessibilityService's info
|
||||
* @param context A valid context
|
||||
* @param info The accessibilityService's info
|
||||
* @param serviceEnabled Whether the accessibility service is enabled.
|
||||
* @return The service description
|
||||
*/
|
||||
@@ -315,33 +298,43 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
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
|
||||
// wait for the fragment to show up to update preferences.
|
||||
if (mIsForeground) {
|
||||
updateAllPreferences();
|
||||
} else {
|
||||
mNeedPreferencesUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeAllPreferences() {
|
||||
for (int i = 0; i < CATEGORIES.length; i++) {
|
||||
PreferenceCategory prefCategory = findPreference(CATEGORIES[i]);
|
||||
mCategoryToPrefCategoryMap.put(CATEGORIES[i], prefCategory);
|
||||
}
|
||||
|
||||
// Display inversion.
|
||||
mToggleInversionPreference = findPreference(TOGGLE_INVERSION_PREFERENCE);
|
||||
|
||||
// Large pointer icon.
|
||||
mToggleLargePointerIconPreference = findPreference(TOGGLE_LARGE_POINTER_ICON);
|
||||
|
||||
mToggleDisableAnimationsPreference = findPreference(TOGGLE_DISABLE_ANIMATIONS);
|
||||
|
||||
// Display magnification.
|
||||
mDisplayMagnificationPreferenceScreen = findPreference(
|
||||
DISPLAY_MAGNIFICATION_PREFERENCE_SCREEN);
|
||||
|
||||
// Display color adjustments.
|
||||
mDisplayDaltonizerPreferenceScreen = findPreference(DISPLAY_DALTONIZER_PREFERENCE_SCREEN);
|
||||
}
|
||||
|
||||
private void updateAllPreferences() {
|
||||
@VisibleForTesting
|
||||
void updateAllPreferences() {
|
||||
updateSystemPreferences();
|
||||
updateServicePreferences();
|
||||
}
|
||||
|
||||
private void registerContentMonitors() {
|
||||
final Context context = getActivity();
|
||||
|
||||
mSettingsPackageMonitor.register(context, context.getMainLooper(), /* externalStorage= */
|
||||
false);
|
||||
mSettingsContentObserver.register(getContentResolver());
|
||||
}
|
||||
|
||||
private void unregisterContentMonitors() {
|
||||
mSettingsPackageMonitor.unregister();
|
||||
mSettingsContentObserver.unregister(getContentResolver());
|
||||
}
|
||||
|
||||
protected void updateServicePreferences() {
|
||||
// Since services category is auto generated we have to do a pass
|
||||
// to generate it since services can come and go and then based on
|
||||
@@ -356,8 +349,10 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
|
||||
initializePreBundledServicesMapFromArray(CATEGORY_SCREEN_READER,
|
||||
R.array.config_preinstalled_screen_reader_services);
|
||||
initializePreBundledServicesMapFromArray(CATEGORY_AUDIO_AND_CAPTIONS,
|
||||
R.array.config_preinstalled_audio_and_caption_services);
|
||||
initializePreBundledServicesMapFromArray(CATEGORY_CAPTIONS,
|
||||
R.array.config_preinstalled_captions_services);
|
||||
initializePreBundledServicesMapFromArray(CATEGORY_AUDIO,
|
||||
R.array.config_preinstalled_audio_services);
|
||||
initializePreBundledServicesMapFromArray(CATEGORY_DISPLAY,
|
||||
R.array.config_preinstalled_display_services);
|
||||
initializePreBundledServicesMapFromArray(CATEGORY_INTERACTION_CONTROL,
|
||||
@@ -384,13 +379,15 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
|
||||
// Update the order of all the category according to the order defined in xml file.
|
||||
updateCategoryOrderFromArray(CATEGORY_SCREEN_READER,
|
||||
R.array.config_order_screen_reader_services);
|
||||
updateCategoryOrderFromArray(CATEGORY_AUDIO_AND_CAPTIONS,
|
||||
R.array.config_order_audio_and_caption_services);
|
||||
R.array.config_order_screen_reader_services);
|
||||
updateCategoryOrderFromArray(CATEGORY_CAPTIONS,
|
||||
R.array.config_order_captions_services);
|
||||
updateCategoryOrderFromArray(CATEGORY_AUDIO,
|
||||
R.array.config_order_audio_services);
|
||||
updateCategoryOrderFromArray(CATEGORY_INTERACTION_CONTROL,
|
||||
R.array.config_order_interaction_control_services);
|
||||
R.array.config_order_interaction_control_services);
|
||||
updateCategoryOrderFromArray(CATEGORY_DISPLAY,
|
||||
R.array.config_order_display_services);
|
||||
R.array.config_order_display_services);
|
||||
|
||||
// Need to check each time when updateServicePreferences() called.
|
||||
if (downloadedServicesCategory.getPreferenceCount() == 0) {
|
||||
@@ -398,6 +395,9 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
} else {
|
||||
getPreferenceScreen().addPreference(downloadedServicesCategory);
|
||||
}
|
||||
|
||||
// Hide screen reader category if it is empty.
|
||||
updatePreferenceCategoryVisibility(CATEGORY_SCREEN_READER);
|
||||
}
|
||||
|
||||
private List<RestrictedPreference> getInstalledAccessibilityList(Context context) {
|
||||
@@ -460,7 +460,7 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
* key with the string array of preference order which is defined in the xml.
|
||||
*
|
||||
* @param categoryKey The key of the category need to update the order
|
||||
* @param key The key of the string array which defines the order of category
|
||||
* @param key The key of the string array which defines the order of category
|
||||
*/
|
||||
private void updateCategoryOrderFromArray(String categoryKey, int key) {
|
||||
String[] services = getResources().getStringArray(key);
|
||||
@@ -478,37 +478,33 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the visibility of a category according to its child preference count.
|
||||
*
|
||||
* @param categoryKey The key of the category which needs to check
|
||||
*/
|
||||
private void updatePreferenceCategoryVisibility(String categoryKey) {
|
||||
final PreferenceCategory category = mCategoryToPrefCategoryMap.get(categoryKey);
|
||||
category.setVisible(category.getPreferenceCount() != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates preferences related to system configurations.
|
||||
*/
|
||||
protected void updateSystemPreferences() {
|
||||
// Move color inversion and color correction preferences to Display category if device
|
||||
// supports HWC hardware-accelerated color transform.
|
||||
if (ColorDisplayManager.isColorTransformAccelerated(getContext())) {
|
||||
PreferenceCategory experimentalCategory =
|
||||
mCategoryToPrefCategoryMap.get(CATEGORY_EXPERIMENTAL);
|
||||
PreferenceCategory displayCategory =
|
||||
mCategoryToPrefCategoryMap.get(CATEGORY_DISPLAY);
|
||||
experimentalCategory.removePreference(mToggleInversionPreference);
|
||||
experimentalCategory.removePreference(mDisplayDaltonizerPreferenceScreen);
|
||||
mDisplayMagnificationPreferenceScreen.setSummary(
|
||||
ToggleScreenMagnificationPreferenceFragment.getServiceSummary(getContext()));
|
||||
mDisplayDaltonizerPreferenceScreen.setOrder(
|
||||
mDisplayMagnificationPreferenceScreen.getOrder() + 1);
|
||||
mDisplayDaltonizerPreferenceScreen.setSummary(AccessibilityUtil.getSummary(
|
||||
getContext(), Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED));
|
||||
mToggleInversionPreference.setOrder(
|
||||
mDisplayDaltonizerPreferenceScreen.getOrder() + 1);
|
||||
mToggleLargePointerIconPreference.setOrder(
|
||||
mToggleInversionPreference.getOrder() + 1);
|
||||
mToggleDisableAnimationsPreference.setOrder(
|
||||
mToggleLargePointerIconPreference.getOrder() + 1);
|
||||
mToggleInversionPreference.setSummary(AccessibilityUtil.getSummary(
|
||||
getContext(), Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED));
|
||||
displayCategory.addPreference(mToggleInversionPreference);
|
||||
displayCategory.addPreference(mDisplayDaltonizerPreferenceScreen);
|
||||
}
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider(R.xml.accessibility_settings);
|
||||
new BaseSearchIndexProvider(R.xml.accessibility_settings) {
|
||||
@Override
|
||||
public List<SearchIndexableRaw> getRawDataToIndex(Context context,
|
||||
boolean enabled) {
|
||||
return FeatureFactory.getFactory(context)
|
||||
.getAccessibilitySearchFeatureProvider().getSearchIndexableRawData(
|
||||
context);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This class helps setup RestrictedPreference.
|
||||
@@ -532,6 +528,7 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
* installed accessibility services
|
||||
* @return The list of {@link RestrictedPreference}
|
||||
*/
|
||||
@VisibleForTesting
|
||||
List<RestrictedPreference> createAccessibilityServicePreferenceList(
|
||||
List<AccessibilityServiceInfo> installedServices) {
|
||||
|
||||
@@ -572,7 +569,6 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
|
||||
setRestrictedPreferenceEnabled(preference, packageName, serviceAllowed,
|
||||
serviceEnabled);
|
||||
|
||||
final String prefKey = preference.getKey();
|
||||
final int imageRes = info.getAnimatedImageRes();
|
||||
final CharSequence description = getServiceDescription(mContext, info,
|
||||
@@ -597,6 +593,7 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
* installed accessibility shortcuts
|
||||
* @return The list of {@link RestrictedPreference}
|
||||
*/
|
||||
@VisibleForTesting
|
||||
List<RestrictedPreference> createAccessibilityActivityPreferenceList(
|
||||
List<AccessibilityShortcutInfo> installedShortcuts) {
|
||||
final Set<ComponentName> enabledServices =
|
||||
@@ -676,7 +673,7 @@ public class AccessibilitySettings extends DashboardFragment {
|
||||
preference.setKey(key);
|
||||
preference.setTitle(title);
|
||||
preference.setSummary(summary);
|
||||
Utils.setSafeIcon(preference, icon);
|
||||
preference.setIcon(Utils.getAdaptiveIcon(mContext, icon, Color.WHITE));
|
||||
preference.setFragment(fragment);
|
||||
preference.setIconSize(ICON_SIZE_MEDIUM);
|
||||
preference.setPersistent(false); // Disable SharedPreferences.
|
||||
|
||||
@@ -16,26 +16,33 @@
|
||||
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import static com.android.settings.Utils.getAdaptiveIcon;
|
||||
import static com.android.settings.accessibility.AccessibilityUtil.AccessibilityServiceFragmentType.VOLUME_SHORTCUT_TOGGLE;
|
||||
import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_MEDIUM;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settingslib.RestrictedPreference;
|
||||
|
||||
import com.google.android.setupdesign.GlifPreferenceLayout;
|
||||
import com.google.android.setupdesign.util.ThemeHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -61,8 +68,8 @@ public class AccessibilitySettingsForSetupWizard extends SettingsPreferenceFragm
|
||||
|
||||
// Preference controls.
|
||||
private Preference mDisplayMagnificationPreference;
|
||||
private Preference mScreenReaderPreference;
|
||||
private Preference mSelectToSpeakPreference;
|
||||
private RestrictedPreference mScreenReaderPreference;
|
||||
private RestrictedPreference mSelectToSpeakPreference;
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
@@ -73,16 +80,23 @@ public class AccessibilitySettingsForSetupWizard extends SettingsPreferenceFragm
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
GlifPreferenceLayout layout = (GlifPreferenceLayout) view;
|
||||
final GlifPreferenceLayout layout = (GlifPreferenceLayout) view;
|
||||
layout.setDividerInsets(Integer.MAX_VALUE, 0);
|
||||
|
||||
layout.setDescriptionText(R.string.vision_settings_description);
|
||||
layout.setHeaderText(R.string.vision_settings_title);
|
||||
layout.setIcon(getPrefContext().getDrawable(R.drawable.ic_accessibility_visibility));
|
||||
|
||||
if (ThemeHelper.shouldApplyExtendedPartnerConfig(getActivity())) {
|
||||
final LinearLayout headerLayout = layout.findManagedViewById(R.id.sud_layout_header);
|
||||
headerLayout.setPadding(0, headerLayout.getPaddingTop(), 0,
|
||||
headerLayout.getPaddingBottom());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
|
||||
Bundle savedInstanceState) {
|
||||
GlifPreferenceLayout layout = (GlifPreferenceLayout) parent;
|
||||
final GlifPreferenceLayout layout = (GlifPreferenceLayout) parent;
|
||||
return layout.onCreateRecyclerView(inflater, parent, savedInstanceState);
|
||||
}
|
||||
|
||||
@@ -145,7 +159,7 @@ public class AccessibilitySettingsForSetupWizard extends SettingsPreferenceFragm
|
||||
return null;
|
||||
}
|
||||
|
||||
private void updateAccessibilityServicePreference(Preference preference,
|
||||
private void updateAccessibilityServicePreference(RestrictedPreference preference,
|
||||
String packageName, String serviceName, String targetFragment) {
|
||||
final AccessibilityServiceInfo info = findService(packageName, serviceName);
|
||||
if (info == null) {
|
||||
@@ -153,24 +167,28 @@ public class AccessibilitySettingsForSetupWizard extends SettingsPreferenceFragm
|
||||
return;
|
||||
}
|
||||
|
||||
ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo;
|
||||
String title = info.getResolveInfo().loadLabel(getPackageManager()).toString();
|
||||
final ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo;
|
||||
final Drawable icon = info.getResolveInfo().loadIcon(getPackageManager());
|
||||
preference.setIcon(getAdaptiveIcon(getContext(), icon, Color.WHITE));
|
||||
preference.setIconSize(ICON_SIZE_MEDIUM);
|
||||
final String title = info.getResolveInfo().loadLabel(getPackageManager()).toString();
|
||||
preference.setTitle(title);
|
||||
ComponentName componentName = new ComponentName(serviceInfo.packageName, serviceInfo.name);
|
||||
final ComponentName componentName =
|
||||
new ComponentName(serviceInfo.packageName, serviceInfo.name);
|
||||
preference.setKey(componentName.flattenToString());
|
||||
if (AccessibilityUtil.getAccessibilityServiceFragmentType(info) == VOLUME_SHORTCUT_TOGGLE) {
|
||||
preference.setFragment(targetFragment);
|
||||
}
|
||||
|
||||
// Update the extras.
|
||||
Bundle extras = preference.getExtras();
|
||||
final Bundle extras = preference.getExtras();
|
||||
extras.putParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME, componentName);
|
||||
|
||||
extras.putString(AccessibilitySettings.EXTRA_PREFERENCE_KEY,
|
||||
preference.getKey());
|
||||
extras.putString(AccessibilitySettings.EXTRA_TITLE, title);
|
||||
|
||||
String description = info.loadDescription(getPackageManager());
|
||||
final String description = info.loadDescription(getPackageManager());
|
||||
extras.putString(AccessibilitySettings.EXTRA_SUMMARY, description);
|
||||
|
||||
extras.putInt(AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES, info.getAnimatedImageRes());
|
||||
|
||||
@@ -34,8 +34,10 @@ import com.android.settings.display.FontSizePreferenceFragmentForSetupWizard;
|
||||
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 {
|
||||
|
||||
@@ -89,6 +91,9 @@ public class AccessibilitySettingsForSetupWizardActivity extends SettingsActivit
|
||||
.setSourceMetricsCategory(caller instanceof Instrumentable
|
||||
? ((Instrumentable) caller).getMetricsCategory()
|
||||
: Instrumentable.METRICS_CATEGORY_UNKNOWN)
|
||||
.setExtras(SetupWizardUtils.copyLifecycleExtra(getIntent().getExtras(),
|
||||
new Bundle()))
|
||||
.setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_FADE)
|
||||
.launch();
|
||||
return true;
|
||||
}
|
||||
@@ -96,11 +101,22 @@ public class AccessibilitySettingsForSetupWizardActivity extends SettingsActivit
|
||||
@Override
|
||||
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())
|
||||
@@ -115,7 +131,8 @@ public class AccessibilitySettingsForSetupWizardActivity extends SettingsActivit
|
||||
.setArguments(args)
|
||||
.setSourceMetricsCategory(Instrumentable.METRICS_CATEGORY_UNKNOWN)
|
||||
.setExtras(SetupWizardUtils.copyLifecycleExtra(getIntent().getExtras(),
|
||||
new Bundle()));
|
||||
new Bundle()))
|
||||
.setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_FADE);
|
||||
|
||||
Log.d(LOG_TAG, "Launch font size settings");
|
||||
subSettingLauncher.launch();
|
||||
|
||||
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
* 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.AccessibilityDialogUtils.DialogEnums;
|
||||
import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_GENERAL_CATEGORY;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.settings.SettingsEnums;
|
||||
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.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.widget.CheckBox;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.utils.LocaleUtils;
|
||||
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Base class for accessibility fragments shortcut functions and dialog management.
|
||||
*/
|
||||
public abstract class AccessibilityShortcutPreferenceFragment extends DashboardFragment
|
||||
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 int NOT_SET = -1;
|
||||
// Save user's shortcutType value when savedInstance has value (e.g. device rotated).
|
||||
protected int mSavedCheckBoxValue = NOT_SET;
|
||||
|
||||
protected ShortcutPreference mShortcutPreference;
|
||||
private AccessibilityManager.TouchExplorationStateChangeListener
|
||||
mTouchExplorationStateChangeListener;
|
||||
private SettingsContentObserver mSettingsContentObserver;
|
||||
private CheckBox mSoftwareTypeCheckBox;
|
||||
private CheckBox mHardwareTypeCheckBox;
|
||||
|
||||
/** Returns the accessibility component name. */
|
||||
protected abstract ComponentName getComponentName();
|
||||
|
||||
/** Returns the accessibility feature name. */
|
||||
protected abstract CharSequence getLabelName();
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
final int resId = getPreferenceScreenResId();
|
||||
if (resId <= 0) {
|
||||
final PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(
|
||||
getPrefContext());
|
||||
setPreferenceScreen(preferenceScreen);
|
||||
}
|
||||
|
||||
if (showGeneralCategory()) {
|
||||
initGeneralCategory();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
mShortcutPreference = new ShortcutPreference(getPrefContext(), /* attrs= */ null);
|
||||
mShortcutPreference.setPersistent(false);
|
||||
mShortcutPreference.setKey(getShortcutPreferenceKey());
|
||||
mShortcutPreference.setOnClickCallback(this);
|
||||
|
||||
final CharSequence title = getString(R.string.accessibility_shortcut_title, getLabelName());
|
||||
mShortcutPreference.setTitle(title);
|
||||
getPreferenceScreen().addPreference(mShortcutPreference);
|
||||
|
||||
mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
|
||||
removeDialog(DialogEnums.EDIT_SHORTCUT);
|
||||
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
|
||||
};
|
||||
|
||||
return super.onCreateView(inflater, container, savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
final AccessibilityManager am = getPrefContext().getSystemService(
|
||||
AccessibilityManager.class);
|
||||
am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
|
||||
mSettingsContentObserver.register(getContentResolver());
|
||||
updateShortcutPreferenceData();
|
||||
updateShortcutPreference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
final AccessibilityManager am = getPrefContext().getSystemService(
|
||||
AccessibilityManager.class);
|
||||
am.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
|
||||
mSettingsContentObserver.unregister(getContentResolver());
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
final int value = getShortcutTypeCheckBoxValue();
|
||||
if (value != NOT_SET) {
|
||||
outState.putInt(KEY_SAVED_USER_SHORTCUT_TYPE, value);
|
||||
}
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(int dialogId) {
|
||||
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,
|
||||
this::callOnAlertDialogCheckboxClicked);
|
||||
setupEditShortcutDialog(dialog);
|
||||
return dialog;
|
||||
case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
|
||||
dialog = AccessibilityGestureNavigationTutorial
|
||||
.createAccessibilityTutorialDialog(getPrefContext(),
|
||||
getUserShortcutTypes());
|
||||
dialog.setCanceledOnTouchOutside(false);
|
||||
return dialog;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported dialogId " + dialogId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDialogMetricsCategory(int dialogId) {
|
||||
switch (dialogId) {
|
||||
case DialogEnums.EDIT_SHORTCUT:
|
||||
return SettingsEnums.DIALOG_ACCESSIBILITY_SERVICE_EDIT_SHORTCUT;
|
||||
case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
|
||||
return SettingsEnums.DIALOG_ACCESSIBILITY_TUTORIAL;
|
||||
default:
|
||||
return SettingsEnums.ACTION_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettingsClicked(ShortcutPreference preference) {
|
||||
showDialog(DialogEnums.EDIT_SHORTCUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToggleClicked(ShortcutPreference preference) {
|
||||
if (getComponentName() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(getPrefContext(),
|
||||
getComponentName().flattenToString(), AccessibilityUtil.UserShortcutType.SOFTWARE);
|
||||
if (preference.isChecked()) {
|
||||
AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes,
|
||||
getComponentName());
|
||||
showDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL);
|
||||
} else {
|
||||
AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), shortcutTypes,
|
||||
getComponentName());
|
||||
}
|
||||
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides to return specific shortcut preference key
|
||||
*
|
||||
* @return String The specific shortcut preference key
|
||||
*/
|
||||
protected String getShortcutPreferenceKey() {
|
||||
return KEY_SHORTCUT_PREFERENCE;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setupEditShortcutDialog(Dialog dialog) {
|
||||
final View dialogSoftwareView = dialog.findViewById(R.id.software_shortcut);
|
||||
mSoftwareTypeCheckBox = dialogSoftwareView.findViewById(R.id.checkbox);
|
||||
setDialogTextAreaClickListener(dialogSoftwareView, mSoftwareTypeCheckBox);
|
||||
|
||||
final View dialogHardwareView = dialog.findViewById(R.id.hardware_shortcut);
|
||||
mHardwareTypeCheckBox = dialogHardwareView.findViewById(R.id.checkbox);
|
||||
setDialogTextAreaClickListener(dialogHardwareView, mHardwareTypeCheckBox);
|
||||
|
||||
updateEditShortcutDialogCheckBox();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns accumulated {@link AccessibilityUtil.UserShortcutType} checkbox value or
|
||||
* {@code NOT_SET} if checkboxes did not exist.
|
||||
*/
|
||||
protected int getShortcutTypeCheckBoxValue() {
|
||||
if (mSoftwareTypeCheckBox == null || mHardwareTypeCheckBox == null) {
|
||||
return NOT_SET;
|
||||
}
|
||||
|
||||
int value = AccessibilityUtil.UserShortcutType.EMPTY;
|
||||
if (mSoftwareTypeCheckBox.isChecked()) {
|
||||
value |= AccessibilityUtil.UserShortcutType.SOFTWARE;
|
||||
}
|
||||
if (mHardwareTypeCheckBox.isChecked()) {
|
||||
value |= AccessibilityUtil.UserShortcutType.HARDWARE;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shortcut type list which has been checked by user.
|
||||
*/
|
||||
protected int getUserShortcutTypes() {
|
||||
return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(),
|
||||
getComponentName());
|
||||
};
|
||||
|
||||
/**
|
||||
* This method will be invoked when a button in the edit shortcut dialog is clicked.
|
||||
*
|
||||
* @param dialog The dialog that received the click
|
||||
* @param which The button that was clicked
|
||||
*/
|
||||
protected void callOnAlertDialogCheckboxClicked(DialogInterface dialog, int which) {
|
||||
if (getComponentName() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int value = getShortcutTypeCheckBoxValue();
|
||||
|
||||
saveNonEmptyUserShortcutType(value);
|
||||
AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), value, getComponentName());
|
||||
AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), ~value, getComponentName());
|
||||
mShortcutPreference.setChecked(value != AccessibilityUtil.UserShortcutType.EMPTY);
|
||||
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void initGeneralCategory() {
|
||||
final PreferenceCategory generalCategory = new PreferenceCategory(getPrefContext());
|
||||
generalCategory.setKey(KEY_GENERAL_CATEGORY);
|
||||
generalCategory.setTitle(getGeneralCategoryDescription(null));
|
||||
|
||||
getPreferenceScreen().addPreference(generalCategory);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void saveNonEmptyUserShortcutType(int type) {
|
||||
if (type == AccessibilityUtil.UserShortcutType.EMPTY) {
|
||||
return;
|
||||
}
|
||||
|
||||
final PreferredShortcut shortcut = new PreferredShortcut(
|
||||
getComponentName().flattenToString(), type);
|
||||
PreferredShortcuts.saveUserShortcutType(getPrefContext(), shortcut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides to return customized description for general category above shortcut
|
||||
*
|
||||
* @return CharSequence The customized description for general category
|
||||
*/
|
||||
protected CharSequence getGeneralCategoryDescription(@Nullable CharSequence title) {
|
||||
if (title == null || title.toString().isEmpty()) {
|
||||
// Return default 'Options' string for category
|
||||
return getContext().getString(R.string.accessibility_screen_option);
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides to determinate if showing additional category description above shortcut
|
||||
*
|
||||
* @return boolean true to show category, false otherwise.
|
||||
*/
|
||||
protected boolean showGeneralCategory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setDialogTextAreaClickListener(View dialogView, CheckBox checkBox) {
|
||||
final View dialogTextArea = dialogView.findViewById(R.id.container);
|
||||
dialogTextArea.setOnClickListener(v -> checkBox.toggle());
|
||||
}
|
||||
|
||||
protected CharSequence getShortcutTypeSummary(Context context) {
|
||||
if (!mShortcutPreference.isSettingsEditable()) {
|
||||
return context.getText(R.string.accessibility_shortcut_edit_dialog_title_hardware);
|
||||
}
|
||||
|
||||
if (!mShortcutPreference.isChecked()) {
|
||||
return context.getText(R.string.switch_off_text);
|
||||
}
|
||||
|
||||
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(context,
|
||||
getComponentName().flattenToString(), AccessibilityUtil.UserShortcutType.SOFTWARE);
|
||||
|
||||
final List<CharSequence> list = new ArrayList<>();
|
||||
final CharSequence softwareTitle = context.getText(
|
||||
R.string.accessibility_shortcut_edit_summary_software);
|
||||
|
||||
if (hasShortcutType(shortcutTypes, AccessibilityUtil.UserShortcutType.SOFTWARE)) {
|
||||
list.add(softwareTitle);
|
||||
}
|
||||
if (hasShortcutType(shortcutTypes, AccessibilityUtil.UserShortcutType.HARDWARE)) {
|
||||
final CharSequence hardwareTitle = context.getText(
|
||||
R.string.accessibility_shortcut_hardware_keyword);
|
||||
list.add(hardwareTitle);
|
||||
}
|
||||
|
||||
// Show software shortcut if first time to use.
|
||||
if (list.isEmpty()) {
|
||||
list.add(softwareTitle);
|
||||
}
|
||||
|
||||
return CaseMap.toTitle().wholeString().noLowercase().apply(Locale.getDefault(), /* iter= */
|
||||
null, LocaleUtils.getConcatenatedString(list));
|
||||
}
|
||||
|
||||
private void updateEditShortcutDialogCheckBox() {
|
||||
// If it is during onConfigChanged process then restore the value, or get the saved value
|
||||
// when shortcutPreference is checked.
|
||||
int value = restoreOnConfigChangedValue();
|
||||
if (value == NOT_SET) {
|
||||
final int lastNonEmptyUserShortcutType = PreferredShortcuts.retrieveUserShortcutType(
|
||||
getPrefContext(), getComponentName().flattenToString(),
|
||||
AccessibilityUtil.UserShortcutType.SOFTWARE);
|
||||
value = mShortcutPreference.isChecked() ? lastNonEmptyUserShortcutType
|
||||
: AccessibilityUtil.UserShortcutType.EMPTY;
|
||||
}
|
||||
|
||||
mSoftwareTypeCheckBox.setChecked(
|
||||
hasShortcutType(value, AccessibilityUtil.UserShortcutType.SOFTWARE));
|
||||
mHardwareTypeCheckBox.setChecked(
|
||||
hasShortcutType(value, AccessibilityUtil.UserShortcutType.HARDWARE));
|
||||
}
|
||||
|
||||
private int restoreOnConfigChangedValue() {
|
||||
final int savedValue = mSavedCheckBoxValue;
|
||||
mSavedCheckBoxValue = NOT_SET;
|
||||
return savedValue;
|
||||
}
|
||||
|
||||
private boolean hasShortcutType(int value, @AccessibilityUtil.UserShortcutType int type) {
|
||||
return (value & type) == type;
|
||||
}
|
||||
|
||||
protected void updateShortcutPreferenceData() {
|
||||
if (getComponentName() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int shortcutTypes = AccessibilityUtil.getUserShortcutTypesFromSettings(
|
||||
getPrefContext(), getComponentName());
|
||||
if (shortcutTypes != AccessibilityUtil.UserShortcutType.EMPTY) {
|
||||
final PreferredShortcut shortcut = new PreferredShortcut(
|
||||
getComponentName().flattenToString(), shortcutTypes);
|
||||
PreferredShortcuts.saveUserShortcutType(getPrefContext(), shortcut);
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateShortcutPreference() {
|
||||
if (getComponentName() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(getPrefContext(),
|
||||
getComponentName().flattenToString(), AccessibilityUtil.UserShortcutType.SOFTWARE);
|
||||
mShortcutPreference.setChecked(
|
||||
AccessibilityUtil.hasValuesInSettings(getPrefContext(), shortcutTypes,
|
||||
getComponentName()));
|
||||
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
|
||||
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
@@ -143,6 +144,13 @@ final class AccessibilityUtil {
|
||||
== NAV_BAR_MODE_GESTURAL;
|
||||
}
|
||||
|
||||
/** Determines if a accessibility floating menu is being used. */
|
||||
public static boolean isFloatingMenuEnabled(Context context) {
|
||||
return Settings.Secure.getInt(context.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, /* def= */ -1)
|
||||
== ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
|
||||
}
|
||||
|
||||
/** Determines if a touch explore is being used. */
|
||||
public static boolean isTouchExploreEnabled(Context context) {
|
||||
final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
|
||||
@@ -381,4 +389,13 @@ final class AccessibilityUtil {
|
||||
return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, screenHeightDp,
|
||||
resources.getDisplayMetrics()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the accessibility service belongs to a system App.
|
||||
* @param info AccessibilityServiceInfo
|
||||
* @return {@code true} if the App is a system App.
|
||||
*/
|
||||
public static boolean isSystemApp(@NonNull AccessibilityServiceInfo info) {
|
||||
return info.getResolveInfo().serviceInfo.applicationInfo.isSystemApp();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.AnimatedImageDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
/**
|
||||
* A custom {@link ImageView} preference for showing animated or static image, such as <a
|
||||
* href="https://developers.google.com/speed/webp/">animated webp</a> and static png.
|
||||
*/
|
||||
public class AnimatedImagePreference extends Preference {
|
||||
|
||||
private Uri mImageUri;
|
||||
private int mMaxHeight = -1;
|
||||
|
||||
AnimatedImagePreference(Context context) {
|
||||
super(context);
|
||||
setLayoutResource(R.layout.preference_animated_image);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||
super.onBindViewHolder(holder);
|
||||
|
||||
final ImageView imageView = holder.itemView.findViewById(R.id.animated_img);
|
||||
if (imageView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mImageUri != null) {
|
||||
imageView.setImageURI(mImageUri);
|
||||
|
||||
final Drawable drawable = imageView.getDrawable();
|
||||
if (drawable instanceof AnimatedImageDrawable) {
|
||||
((AnimatedImageDrawable) drawable).start();
|
||||
}
|
||||
}
|
||||
|
||||
if (mMaxHeight > -1) {
|
||||
imageView.setMaxHeight(mMaxHeight);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets image uri to display image in {@link ImageView}
|
||||
*
|
||||
* @param imageUri the Uri of an image
|
||||
*/
|
||||
public void setImageUri(Uri imageUri) {
|
||||
if (imageUri != null && !imageUri.equals(mImageUri)) {
|
||||
mImageUri = imageUri;
|
||||
notifyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum height of the view.
|
||||
*
|
||||
* @param maxHeight the maximum height of ImageView in terms of pixels.
|
||||
*/
|
||||
public void setMaxHeight(int maxHeight) {
|
||||
if (maxHeight != mMaxHeight) {
|
||||
mMaxHeight = maxHeight;
|
||||
notifyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 android.app.settings.SettingsEnums;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
|
||||
/** Accessibility settings for audio adjustment. */
|
||||
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
||||
public class AudioAdjustmentFragment extends DashboardFragment {
|
||||
|
||||
private static final String TAG = "AudioAdjustmentFragment";
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.ACCESSIBILITY_AUDIO_ADJUSTMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.accessibility_audio_adjustment;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLogTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider(R.xml.accessibility_audio_adjustment);
|
||||
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import static android.view.HapticFeedbackConstants.CLOCK_TICK;
|
||||
import static com.android.settings.Utils.isNightMode;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -44,6 +45,7 @@ public class BalanceSeekBar extends SeekBar {
|
||||
private final Context mContext;
|
||||
private final Object mListenerLock = new Object();
|
||||
private OnSeekBarChangeListener mOnSeekBarChangeListener;
|
||||
private int mLastProgress = -1;
|
||||
private final OnSeekBarChangeListener mProxySeekBarListener = new OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
@@ -73,6 +75,12 @@ public class BalanceSeekBar extends SeekBar {
|
||||
progress = mCenter;
|
||||
seekBar.setProgress(progress); // direct update (fromUser becomes false)
|
||||
}
|
||||
if (progress != mLastProgress) {
|
||||
if (progress == mCenter || progress == getMin() || progress == getMax()) {
|
||||
seekBar.performHapticFeedback(CLOCK_TICK);
|
||||
}
|
||||
mLastProgress = progress;
|
||||
}
|
||||
final float balance = (progress - mCenter) * 0.01f;
|
||||
Settings.System.putFloatForUser(mContext.getContentResolver(),
|
||||
Settings.System.MASTER_BALANCE, balance, UserHandle.USER_CURRENT);
|
||||
@@ -115,6 +123,7 @@ public class BalanceSeekBar extends SeekBar {
|
||||
res.getDimensionPixelSize(R.dimen.balance_seekbar_center_marker_width),
|
||||
res.getDimensionPixelSize(R.dimen.balance_seekbar_center_marker_height));
|
||||
mCenterMarkerPaint = new Paint();
|
||||
// TODO use a more suitable colour?
|
||||
mCenterMarkerPaint.setColor(isNightMode(context) ? Color.WHITE : Color.BLACK);
|
||||
mCenterMarkerPaint.setStyle(Paint.Style.FILL);
|
||||
// Remove the progress colour
|
||||
@@ -151,10 +160,5 @@ public class BalanceSeekBar extends SeekBar {
|
||||
canvas.restore();
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
OnSeekBarChangeListener getProxySeekBarListener() {
|
||||
return mProxySeekBarListener;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ import androidx.preference.PreferenceCategory;
|
||||
|
||||
import com.android.internal.widget.SubtitleView;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.accessibility.ListDialogPreference.OnValueChangedListener;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settingslib.accessibility.AccessibilityUtils;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
@@ -46,8 +46,10 @@ import java.util.Locale;
|
||||
|
||||
/** Settings fragment containing font style of captioning properties. */
|
||||
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
||||
public class CaptionAppearanceFragment extends SettingsPreferenceFragment
|
||||
public class CaptionAppearanceFragment extends DashboardFragment
|
||||
implements OnPreferenceChangeListener, OnValueChangedListener {
|
||||
|
||||
private static final String TAG = "CaptionAppearanceFragment";
|
||||
private static final String PREF_CAPTION_PREVIEW = "caption_preview";
|
||||
private static final String PREF_BACKGROUND_COLOR = "captioning_background_color";
|
||||
private static final String PREF_BACKGROUND_OPACITY = "captioning_background_opacity";
|
||||
@@ -107,12 +109,11 @@ public class CaptionAppearanceFragment extends SettingsPreferenceFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
super.onCreatePreferences(savedInstanceState, rootKey);
|
||||
|
||||
mCaptioningManager = (CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE);
|
||||
|
||||
addPreferencesFromResource(R.xml.captioning_appearance);
|
||||
initializeAllPreferences();
|
||||
updateAllPreferences();
|
||||
refreshShowingCustom();
|
||||
@@ -120,6 +121,16 @@ public class CaptionAppearanceFragment extends SettingsPreferenceFragment
|
||||
refreshPreviewText();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.captioning_appearance;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLogTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
private void refreshPreviewText() {
|
||||
final Context context = getActivity();
|
||||
if (context == null) {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
/**
|
||||
* Preference controller for caption footer.
|
||||
*/
|
||||
public class CaptionFooterPreferenceController extends AccessibilityFooterPreferenceController {
|
||||
|
||||
public CaptionFooterPreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLabelName() {
|
||||
return mContext.getString(R.string.accessibility_captioning_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getHelpResource() {
|
||||
return R.string.help_url_caption;
|
||||
}
|
||||
}
|
||||
@@ -26,18 +26,19 @@ import android.view.accessibility.CaptioningManager;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
|
||||
/** Settings fragment containing more options of captioning properties. */
|
||||
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
||||
public class CaptionMoreOptionsFragment extends SettingsPreferenceFragment
|
||||
public class CaptionMoreOptionsFragment extends DashboardFragment
|
||||
implements Preference.OnPreferenceChangeListener {
|
||||
|
||||
private static final String TAG = "CaptionMoreOptionsFragment";
|
||||
private static final String PREF_LOCALE = "captioning_locale";
|
||||
|
||||
private CaptioningManager mCaptioningManager;
|
||||
|
||||
private LocalePreference mLocale;
|
||||
|
||||
@Override
|
||||
@@ -46,17 +47,26 @@ public class CaptionMoreOptionsFragment extends SettingsPreferenceFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
super.onCreatePreferences(savedInstanceState, rootKey);
|
||||
|
||||
mCaptioningManager = (CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE);
|
||||
|
||||
addPreferencesFromResource(R.xml.captioning_more_options);
|
||||
initializeAllPreferences();
|
||||
updateAllPreferences();
|
||||
installUpdateListeners();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.captioning_more_options;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLogTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
private void initializeAllPreferences() {
|
||||
mLocale = (LocalePreference) findPreference(PREF_LOCALE);
|
||||
}
|
||||
|
||||
@@ -22,15 +22,17 @@ import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.view.accessibility.CaptioningManager;
|
||||
import android.widget.Switch;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.Preference.OnPreferenceChangeListener;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settings.widget.SettingsMainSwitchPreference;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
import com.android.settingslib.widget.OnMainSwitchChangeListener;
|
||||
|
||||
import com.google.common.primitives.Floats;
|
||||
|
||||
@@ -39,15 +41,17 @@ import java.util.List;
|
||||
|
||||
/** Settings fragment containing captioning properties. */
|
||||
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
||||
public class CaptionPropertiesFragment extends SettingsPreferenceFragment
|
||||
implements OnPreferenceChangeListener {
|
||||
public class CaptionPropertiesFragment extends DashboardFragment
|
||||
implements OnPreferenceChangeListener, OnMainSwitchChangeListener {
|
||||
|
||||
private static final String TAG = "CaptionPropertiesFragment";
|
||||
private static final String PREF_SWITCH = "captioning_preference_switch";
|
||||
private static final String PREF_TEXT = "captioning_caption_appearance";
|
||||
private static final String PREF_MORE = "captioning_more_options";
|
||||
|
||||
private CaptioningManager mCaptioningManager;
|
||||
|
||||
private SwitchPreference mSwitch;
|
||||
private SettingsMainSwitchPreference mSwitch;
|
||||
private Preference mTextAppearance;
|
||||
private Preference mMoreOptions;
|
||||
|
||||
@@ -60,12 +64,11 @@ public class CaptionPropertiesFragment extends SettingsPreferenceFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
super.onCreatePreferences(savedInstanceState, rootKey);
|
||||
|
||||
mCaptioningManager = (CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE);
|
||||
|
||||
addPreferencesFromResource(R.xml.captioning_settings);
|
||||
initializeAllPreferences();
|
||||
installUpdateListeners();
|
||||
initFontSizeValuesArray();
|
||||
@@ -77,8 +80,18 @@ public class CaptionPropertiesFragment extends SettingsPreferenceFragment
|
||||
updateAllPreferences();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.captioning_settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLogTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
private void initializeAllPreferences() {
|
||||
mSwitch = (SwitchPreference) findPreference(PREF_SWITCH);
|
||||
mSwitch = (SettingsMainSwitchPreference) findPreference(PREF_SWITCH);
|
||||
mTextAppearance = (Preference) findPreference(PREF_TEXT);
|
||||
mMoreOptions = (Preference) findPreference(PREF_MORE);
|
||||
|
||||
@@ -88,6 +101,8 @@ public class CaptionPropertiesFragment extends SettingsPreferenceFragment
|
||||
|
||||
private void installUpdateListeners() {
|
||||
mSwitch.setOnPreferenceChangeListener(this);
|
||||
mSwitch.addOnSwitchChangeListener(this);
|
||||
|
||||
}
|
||||
|
||||
private void initFontSizeValuesArray() {
|
||||
@@ -133,4 +148,11 @@ public class CaptionPropertiesFragment extends SettingsPreferenceFragment
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider(R.xml.captioning_settings);
|
||||
|
||||
@Override
|
||||
public void onSwitchChanged(Switch switchView, boolean isChecked) {
|
||||
final ContentResolver cr = getActivity().getContentResolver();
|
||||
Settings.Secure.putInt(
|
||||
cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, isChecked ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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 android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
import com.android.settingslib.core.lifecycle.events.OnPause;
|
||||
import com.android.settingslib.core.lifecycle.events.OnResume;
|
||||
|
||||
/** Preference controller that controls the fade switch button in accessibility button page. */
|
||||
public class FloatingMenuFadePreferenceController extends BasePreferenceController implements
|
||||
Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
|
||||
|
||||
private static final int OFF = 0;
|
||||
private static final int ON = 1;
|
||||
|
||||
private final ContentResolver mContentResolver;
|
||||
@VisibleForTesting
|
||||
final ContentObserver mContentObserver;
|
||||
|
||||
@VisibleForTesting
|
||||
SwitchPreference mPreference;
|
||||
|
||||
public FloatingMenuFadePreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
mContentResolver = context.getContentResolver();
|
||||
mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
updateAvailabilityStatus();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return AccessibilityUtil.isFloatingMenuEnabled(mContext)
|
||||
? AVAILABLE : DISABLED_DEPENDENT_SETTING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
|
||||
mPreference = screen.findPreference(getPreferenceKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final boolean isEnabled = (boolean) newValue;
|
||||
putFloatingMenuFadeValue(isEnabled);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
final SwitchPreference switchPreference = (SwitchPreference) preference;
|
||||
|
||||
switchPreference.setChecked(getFloatingMenuFadeValue() == ON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
mContentResolver.registerContentObserver(
|
||||
Settings.Secure.getUriFor(
|
||||
Settings.Secure.ACCESSIBILITY_BUTTON_MODE),
|
||||
/* notifyForDescendants= */ false, mContentObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
mContentResolver.unregisterContentObserver(mContentObserver);
|
||||
}
|
||||
|
||||
private void updateAvailabilityStatus() {
|
||||
mPreference.setEnabled(AccessibilityUtil.isFloatingMenuEnabled(mContext));
|
||||
}
|
||||
|
||||
private int getFloatingMenuFadeValue() {
|
||||
return Settings.Secure.getInt(mContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, ON);
|
||||
}
|
||||
|
||||
private void putFloatingMenuFadeValue(boolean isEnabled) {
|
||||
Settings.Secure.putInt(mContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
|
||||
isEnabled ? ON : OFF);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/** LayerDrawable that contains device icon as background and floating menu icon as foreground. */
|
||||
public class FloatingMenuLayerDrawable extends LayerDrawable {
|
||||
|
||||
private FloatingMenuLayerDrawableState mState;
|
||||
|
||||
/**
|
||||
* Creates a new layer drawable with the list of specified layers.
|
||||
*
|
||||
* @param layers a list of drawables to use as layers in this new drawable,
|
||||
* must be non-null
|
||||
*/
|
||||
private FloatingMenuLayerDrawable(@NonNull Drawable[] layers) {
|
||||
super(layers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the {@link LayerDrawable} that contains device icon as background and floating menu
|
||||
* icon with given {@code opacity} value as foreground.
|
||||
*
|
||||
* @param context the valid context used to get the icon
|
||||
* @param resId the resource ID of the floating menu icon
|
||||
* @param opacity the opacity to apply to the given icon
|
||||
* @return the drawable that combines the device icon and the floating menu icon
|
||||
*/
|
||||
public static FloatingMenuLayerDrawable createLayerDrawable(Context context, int resId,
|
||||
int opacity) {
|
||||
final Drawable bg = context.getDrawable(R.drawable.accessibility_button_preview_base);
|
||||
final FloatingMenuLayerDrawable basicDrawable = new FloatingMenuLayerDrawable(
|
||||
new Drawable[]{bg, null});
|
||||
|
||||
basicDrawable.updateLayerDrawable(context, resId, opacity);
|
||||
return basicDrawable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the drawable with given {@code resId} drawable and {@code opacity}(alpha)
|
||||
* value at index 1 layer.
|
||||
*
|
||||
* @param context the valid context used to get the icon
|
||||
* @param resId the resource ID of the floating menu icon
|
||||
* @param opacity the opacity to apply to the given icon
|
||||
*/
|
||||
public void updateLayerDrawable(Context context, int resId, int opacity) {
|
||||
final Drawable icon = context.getDrawable(resId);
|
||||
icon.setAlpha(opacity);
|
||||
this.setDrawable(/* index= */ 1, icon);
|
||||
this.setConstantState(context, resId, opacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConstantState getConstantState() {
|
||||
return mState;
|
||||
}
|
||||
|
||||
/** Stores the constant state and data to the given drawable. */
|
||||
private void setConstantState(Context context, int resId, int opacity) {
|
||||
mState = new FloatingMenuLayerDrawableState(context, resId, opacity);
|
||||
}
|
||||
|
||||
/** {@link ConstantState} to store the data of {@link FloatingMenuLayerDrawable}. */
|
||||
@VisibleForTesting
|
||||
static class FloatingMenuLayerDrawableState extends ConstantState {
|
||||
|
||||
private final Context mContext;
|
||||
private final int mResId;
|
||||
private final int mOpacity;
|
||||
|
||||
FloatingMenuLayerDrawableState(Context context, int resId, int opacity) {
|
||||
mContext = context;
|
||||
mResId = resId;
|
||||
mOpacity = opacity;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Drawable newDrawable() {
|
||||
return createLayerDrawable(mContext, mResId, mOpacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChangingConfigurations() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final FloatingMenuLayerDrawableState that = (FloatingMenuLayerDrawableState) o;
|
||||
return mResId == that.mResId
|
||||
&& mOpacity == that.mOpacity
|
||||
&& Objects.equals(mContext, that.mContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mContext, mResId, mOpacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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 android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
import com.android.settingslib.core.lifecycle.events.OnPause;
|
||||
import com.android.settingslib.core.lifecycle.events.OnResume;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/** Preference controller that controls the preferred size in accessibility button page. */
|
||||
public class FloatingMenuSizePreferenceController extends BasePreferenceController
|
||||
implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
|
||||
|
||||
private final ContentResolver mContentResolver;
|
||||
@VisibleForTesting
|
||||
final ContentObserver mContentObserver;
|
||||
|
||||
@VisibleForTesting
|
||||
ListPreference mPreference;
|
||||
|
||||
private final ArrayMap<String, String> mValueTitleMap = new ArrayMap<>();
|
||||
private int mDefaultSize;
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({
|
||||
Size.SMALL,
|
||||
Size.LARGE,
|
||||
})
|
||||
@VisibleForTesting
|
||||
@interface Size {
|
||||
int SMALL = 0;
|
||||
int LARGE = 1;
|
||||
}
|
||||
|
||||
public FloatingMenuSizePreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
mContentResolver = context.getContentResolver();
|
||||
mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
updateAvailabilityStatus();
|
||||
}
|
||||
};
|
||||
|
||||
initValueTitleMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return AccessibilityUtil.isFloatingMenuEnabled(mContext)
|
||||
? AVAILABLE : DISABLED_DEPENDENT_SETTING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
|
||||
mPreference = screen.findPreference(getPreferenceKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final ListPreference listPreference = (ListPreference) preference;
|
||||
final Integer value = Ints.tryParse((String) newValue);
|
||||
if (value != null) {
|
||||
putAccessibilityFloatingMenuSize(value);
|
||||
updateState(listPreference);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
final ListPreference listPreference = (ListPreference) preference;
|
||||
|
||||
listPreference.setValue(String.valueOf(getAccessibilityFloatingMenuSize(mDefaultSize)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
mContentResolver.registerContentObserver(
|
||||
Settings.Secure.getUriFor(
|
||||
Settings.Secure.ACCESSIBILITY_BUTTON_MODE), /* notifyForDescendants= */
|
||||
false, mContentObserver);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
mContentResolver.unregisterContentObserver(mContentObserver);
|
||||
}
|
||||
|
||||
private void updateAvailabilityStatus() {
|
||||
mPreference.setEnabled(AccessibilityUtil.isFloatingMenuEnabled(mContext));
|
||||
}
|
||||
|
||||
private void initValueTitleMap() {
|
||||
if (mValueTitleMap.size() == 0) {
|
||||
final String[] values = mContext.getResources().getStringArray(
|
||||
R.array.accessibility_button_size_selector_values);
|
||||
final String[] titles = mContext.getResources().getStringArray(
|
||||
R.array.accessibility_button_size_selector_titles);
|
||||
final int mapSize = values.length;
|
||||
|
||||
mDefaultSize = Integer.parseInt(values[0]);
|
||||
for (int i = 0; i < mapSize; i++) {
|
||||
mValueTitleMap.put(values[i], titles[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Size
|
||||
private int getAccessibilityFloatingMenuSize(@Size int defaultValue) {
|
||||
return Settings.Secure.getInt(mContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, defaultValue);
|
||||
}
|
||||
|
||||
private void putAccessibilityFloatingMenuSize(@Size int value) {
|
||||
Settings.Secure.putInt(mContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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 android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.FloatRange;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.core.SliderPreferenceController;
|
||||
import com.android.settings.widget.SeekBarPreference;
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
import com.android.settingslib.core.lifecycle.events.OnPause;
|
||||
import com.android.settingslib.core.lifecycle.events.OnResume;
|
||||
|
||||
/** Preference controller that controls the transparency seekbar in accessibility button page. */
|
||||
public class FloatingMenuTransparencyPreferenceController extends SliderPreferenceController
|
||||
implements LifecycleObserver, OnResume, OnPause {
|
||||
|
||||
@VisibleForTesting
|
||||
@FloatRange(from = 0.0, to = 1.0)
|
||||
static final float DEFAULT_TRANSPARENCY = 0.45f;
|
||||
@VisibleForTesting
|
||||
static final float MAXIMUM_TRANSPARENCY = 1.0f;
|
||||
private static final int FADE_ENABLED = 1;
|
||||
private static final float MIN_PROGRESS = 0f;
|
||||
private static final float MAX_PROGRESS = 90f;
|
||||
@VisibleForTesting
|
||||
static final float PRECISION = 100f;
|
||||
|
||||
private final ContentResolver mContentResolver;
|
||||
@VisibleForTesting
|
||||
final ContentObserver mContentObserver;
|
||||
|
||||
@VisibleForTesting
|
||||
SeekBarPreference mPreference;
|
||||
|
||||
public FloatingMenuTransparencyPreferenceController(Context context,
|
||||
String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
mContentResolver = context.getContentResolver();
|
||||
mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
updateAvailabilityStatus();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return AccessibilityUtil.isFloatingMenuEnabled(mContext)
|
||||
? AVAILABLE : DISABLED_DEPENDENT_SETTING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
|
||||
mPreference = screen.findPreference(getPreferenceKey());
|
||||
mPreference.setContinuousUpdates(true);
|
||||
mPreference.setMax(getMax());
|
||||
mPreference.setMin(getMin());
|
||||
mPreference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_ENDS);
|
||||
|
||||
updateState(mPreference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
mContentResolver.registerContentObserver(
|
||||
Settings.Secure.getUriFor(
|
||||
Settings.Secure.ACCESSIBILITY_BUTTON_MODE), /* notifyForDescendants= */
|
||||
false, mContentObserver);
|
||||
mContentResolver.registerContentObserver(
|
||||
Settings.Secure.getUriFor(
|
||||
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED),
|
||||
/* notifyForDescendants= */ false, mContentObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
mContentResolver.unregisterContentObserver(mContentObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSliderPosition() {
|
||||
return convertTransparencyFloatToInt(getTransparency());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setSliderPosition(int position) {
|
||||
final float opacityValue = MAXIMUM_TRANSPARENCY - convertTransparencyIntToFloat(position);
|
||||
return Settings.Secure.putFloat(mContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, opacityValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMax() {
|
||||
return (int) MAX_PROGRESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMin() {
|
||||
return (int) MIN_PROGRESS;
|
||||
}
|
||||
|
||||
private void updateAvailabilityStatus() {
|
||||
final boolean fadeEnabled = Settings.Secure.getInt(mContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, FADE_ENABLED)
|
||||
== FADE_ENABLED;
|
||||
|
||||
mPreference.setEnabled(AccessibilityUtil.isFloatingMenuEnabled(mContext) && fadeEnabled);
|
||||
}
|
||||
|
||||
private int convertTransparencyFloatToInt(float value) {
|
||||
return Math.round(value * PRECISION);
|
||||
}
|
||||
|
||||
private float convertTransparencyIntToFloat(int value) {
|
||||
return (float) value / PRECISION;
|
||||
}
|
||||
|
||||
private float getTransparency() {
|
||||
float transparencyValue = MAXIMUM_TRANSPARENCY - (Settings.Secure.getFloat(mContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, DEFAULT_TRANSPARENCY));
|
||||
final float minValue = MIN_PROGRESS / PRECISION;
|
||||
final float maxValue = MAX_PROGRESS / PRECISION;
|
||||
|
||||
return (transparencyValue < minValue || transparencyValue > maxValue)
|
||||
? DEFAULT_TRANSPARENCY : transparencyValue;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
* Copyright (C) 2020 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.
|
||||
@@ -14,43 +14,37 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.security;
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.UserHandle;
|
||||
import android.graphics.fonts.FontStyle;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.core.TogglePreferenceController;
|
||||
|
||||
public class LockdownButtonPreferenceController extends TogglePreferenceController {
|
||||
/** PreferenceController for displaying all text in bold. */
|
||||
public class FontWeightAdjustmentPreferenceController extends TogglePreferenceController {
|
||||
static final int BOLD_TEXT_ADJUSTMENT =
|
||||
FontStyle.FONT_WEIGHT_BOLD - FontStyle.FONT_WEIGHT_NORMAL;
|
||||
|
||||
private final LockPatternUtils mLockPatternUtils;
|
||||
|
||||
public LockdownButtonPreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
mLockPatternUtils = new LockPatternUtils(context);
|
||||
public FontWeightAdjustmentPreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
if (mLockPatternUtils.isSecure(UserHandle.myUserId())) {
|
||||
return AVAILABLE;
|
||||
} else {
|
||||
return DISABLED_FOR_USER;
|
||||
}
|
||||
return AVAILABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
return Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0) != 0;
|
||||
Settings.Secure.FONT_WEIGHT_ADJUSTMENT, 0) == BOLD_TEXT_ADJUSTMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setChecked(boolean isChecked) {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.LOCKDOWN_IN_POWER_MENU, isChecked ? 1 : 0);
|
||||
return true;
|
||||
return Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.FONT_WEIGHT_ADJUSTMENT, (isChecked ? BOLD_TEXT_ADJUSTMENT : 0));
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ public class InvisibleToggleAccessibilityServicePreferenceFragment extends
|
||||
@Override
|
||||
protected void onInstallSwitchPreferenceToggleSwitch() {
|
||||
super.onInstallSwitchPreferenceToggleSwitch();
|
||||
mToggleServiceDividerSwitchPreference.setVisible(false);
|
||||
mToggleServiceSwitchPreference.setVisible(false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An {@link ArrayAdapter} to fill the information of {@link ItemInfo} in the item view. The item
|
||||
* view must have textview to set the title.
|
||||
*
|
||||
* @param <T> the type of elements in the array, inherited from {@link ItemInfo}.
|
||||
*/
|
||||
public class ItemInfoArrayAdapter<T extends ItemInfoArrayAdapter.ItemInfo> extends ArrayAdapter<T> {
|
||||
|
||||
public ItemInfoArrayAdapter(@NonNull Context context, @NonNull List<T> items) {
|
||||
super(context, R.layout.dialog_single_radio_choice_list_item, R.id.title, items);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
||||
final View root = super.getView(position, convertView, parent);
|
||||
|
||||
final ItemInfo item = getItem(position);
|
||||
final TextView title = root.findViewById(R.id.title);
|
||||
title.setText(item.mTitle);
|
||||
final TextView summary = root.findViewById(R.id.summary);
|
||||
if (!TextUtils.isEmpty(item.mSummary)) {
|
||||
summary.setVisibility(View.VISIBLE);
|
||||
summary.setText(item.mSummary);
|
||||
} else {
|
||||
summary.setVisibility(View.GONE);
|
||||
}
|
||||
final ImageView image = root.findViewById(R.id.image);
|
||||
image.setImageResource(item.mDrawableId);
|
||||
if (getContext().getResources().getConfiguration().getLayoutDirection()
|
||||
== View.LAYOUT_DIRECTION_LTR) {
|
||||
image.setScaleType(ImageView.ScaleType.FIT_START);
|
||||
} else {
|
||||
image.setScaleType(ImageView.ScaleType.FIT_END);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Presents a data structure shown in the item view.
|
||||
*/
|
||||
public static class ItemInfo {
|
||||
@NonNull
|
||||
public final CharSequence mTitle;
|
||||
@Nullable
|
||||
public final CharSequence mSummary;
|
||||
@DrawableRes
|
||||
public final int mDrawableId;
|
||||
|
||||
public ItemInfo(@NonNull CharSequence title, @Nullable CharSequence summary,
|
||||
@DrawableRes int drawableId) {
|
||||
mTitle = title;
|
||||
mSummary = summary;
|
||||
mDrawableId = drawableId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,45 +30,41 @@ import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.SwitchPreference;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Fragment for providing open activity button. */
|
||||
public class LaunchAccessibilityActivityPreferenceFragment extends
|
||||
ToggleFeaturePreferenceFragment {
|
||||
public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeaturePreferenceFragment {
|
||||
private static final String TAG = "LaunchA11yActivity";
|
||||
private static final String EMPTY_STRING = "";
|
||||
protected static final String KEY_LAUNCH_PREFERENCE = "launch_preference";
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
final View view = super.onCreateView(inflater, container, savedInstanceState);
|
||||
|
||||
mToggleServiceDividerSwitchPreference.setSwitchVisibility(View.GONE);
|
||||
}
|
||||
// Init new preference to replace the switch preference instead.
|
||||
initLaunchPreference();
|
||||
removePreference(KEY_USE_SERVICE_PREFERENCE);
|
||||
return view;
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
|
||||
logAccessibilityServiceEnabled(mComponentName, enabled);
|
||||
launchShortcutTargetActivity(getPrefContext().getDisplayId(), mComponentName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInstallSwitchPreferenceToggleSwitch() {
|
||||
super.onInstallSwitchPreferenceToggleSwitch();
|
||||
mToggleServiceDividerSwitchPreference.setOnPreferenceClickListener((preference) -> {
|
||||
final boolean checked = ((DividerSwitchPreference) preference).isChecked();
|
||||
onPreferenceToggled(mPreferenceKey, checked);
|
||||
return false;
|
||||
});
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -82,10 +78,12 @@ public class LaunchAccessibilityActivityPreferenceFragment extends
|
||||
// Settings animated image.
|
||||
final int animatedImageRes = arguments.getInt(
|
||||
AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES);
|
||||
mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
|
||||
.authority(mComponentName.getPackageName())
|
||||
.appendPath(String.valueOf(animatedImageRes))
|
||||
.build();
|
||||
if (animatedImageRes > 0) {
|
||||
mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
|
||||
.authority(mComponentName.getPackageName())
|
||||
.appendPath(String.valueOf(animatedImageRes))
|
||||
.build();
|
||||
}
|
||||
|
||||
// Settings html description.
|
||||
mHtmlDescription = arguments.getCharSequence(AccessibilitySettings.EXTRA_HTML_DESCRIPTION);
|
||||
@@ -97,12 +95,6 @@ public class LaunchAccessibilityActivityPreferenceFragment extends
|
||||
mSettingsTitle = (mSettingsIntent == null) ? null : settingsTitle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettingsClicked(ShortcutPreference preference) {
|
||||
super.onSettingsClicked(preference);
|
||||
showDialog(DialogEnums.EDIT_SHORTCUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
int getUserShortcutTypes() {
|
||||
return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(),
|
||||
@@ -116,16 +108,6 @@ public class LaunchAccessibilityActivityPreferenceFragment extends
|
||||
// accessibility service from this page.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateToggleServiceTitle(SwitchPreference switchPreference) {
|
||||
final AccessibilityShortcutInfo info = getAccessibilityShortcutInfo();
|
||||
final String switchBarText = (info == null) ? EMPTY_STRING : getString(
|
||||
R.string.accessibility_service_master_open_title,
|
||||
info.getActivityInfo().loadLabel(getPackageManager()));
|
||||
|
||||
switchPreference.setTitle(switchBarText);
|
||||
}
|
||||
|
||||
// IMPORTANT: Refresh the info since there are dynamically changing capabilities.
|
||||
private AccessibilityShortcutInfo getAccessibilityShortcutInfo() {
|
||||
final List<AccessibilityShortcutInfo> infos = AccessibilityManager.getInstance(
|
||||
@@ -143,6 +125,34 @@ public class LaunchAccessibilityActivityPreferenceFragment extends
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Customizes the order by preference key. */
|
||||
protected List<String> getPreferenceOrderList() {
|
||||
final List<String> lists = new ArrayList<>();
|
||||
lists.add(KEY_ANIMATED_IMAGE);
|
||||
lists.add(KEY_LAUNCH_PREFERENCE);
|
||||
lists.add(KEY_GENERAL_CATEGORY);
|
||||
lists.add(KEY_HTML_DESCRIPTION_PREFERENCE);
|
||||
return lists;
|
||||
}
|
||||
|
||||
private void initLaunchPreference() {
|
||||
final Preference launchPreference = new Preference(getPrefContext());
|
||||
launchPreference.setKey(KEY_LAUNCH_PREFERENCE);
|
||||
|
||||
final AccessibilityShortcutInfo info = getAccessibilityShortcutInfo();
|
||||
final String switchBarText = (info == null) ? EMPTY_STRING : getString(
|
||||
R.string.accessibility_service_primary_open_title,
|
||||
info.getActivityInfo().loadLabel(getPackageManager()));
|
||||
launchPreference.setTitle(switchBarText);
|
||||
|
||||
launchPreference.setOnPreferenceClickListener(preference -> {
|
||||
logAccessibilityServiceEnabled(mComponentName, /* enabled= */ true);
|
||||
launchShortcutTargetActivity(getPrefContext().getDisplayId(), mComponentName);
|
||||
return true;
|
||||
});
|
||||
getPreferenceScreen().addPreference(launchPreference);
|
||||
}
|
||||
|
||||
private void launchShortcutTargetActivity(int displayId, ComponentName name) {
|
||||
final Intent intent = new Intent();
|
||||
final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle();
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/** Class to provide magnification capabilities. */
|
||||
public final class MagnificationCapabilities {
|
||||
|
||||
private static final String KEY_CAPABILITY =
|
||||
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY;
|
||||
|
||||
/**
|
||||
* Annotation for supported magnification mode.
|
||||
*
|
||||
* @see Settings.Secure#ACCESSIBILITY_MAGNIFICATION_CAPABILITY
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({
|
||||
MagnificationMode.NONE,
|
||||
MagnificationMode.FULLSCREEN,
|
||||
MagnificationMode.WINDOW,
|
||||
MagnificationMode.ALL,
|
||||
})
|
||||
|
||||
public @interface MagnificationMode {
|
||||
int NONE = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
|
||||
int FULLSCREEN = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
|
||||
int WINDOW = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
|
||||
int ALL = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the summary for the given {@code capabilities}.
|
||||
*
|
||||
* @param context A {@link Context}.
|
||||
* @param capabilities Magnification capabilities {@link MagnificationMode}
|
||||
* @return The summary text represents the given capabilities
|
||||
*/
|
||||
public static String getSummary(Context context, @MagnificationMode int capabilities) {
|
||||
final String[] summaries = context.getResources().getStringArray(
|
||||
R.array.magnification_mode_summaries);
|
||||
final int[] values = context.getResources().getIntArray(
|
||||
R.array.magnification_mode_values);
|
||||
|
||||
final int idx = Ints.indexOf(values, capabilities);
|
||||
return summaries[idx == /* no index exist */ -1 ? 0 : idx];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the magnification capabilities {@link MagnificationMode} to settings key. This
|
||||
* overwrites any existing capabilities.
|
||||
*
|
||||
* @param context A {@link Context}.
|
||||
* @param capabilities Magnification capabilities {@link MagnificationMode}
|
||||
*/
|
||||
public static void setCapabilities(Context context, @MagnificationMode int capabilities) {
|
||||
final ContentResolver contentResolver = context.getContentResolver();
|
||||
|
||||
Settings.Secure.putIntForUser(contentResolver, KEY_CAPABILITY, capabilities,
|
||||
contentResolver.getUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the magnification capabilities {@link MagnificationMode} from setting's key. May be
|
||||
* default value {@link MagnificationMode#FULLSCREEN} if not set.
|
||||
*
|
||||
* @param context A {@link Context}.
|
||||
* @return The magnification capabilities {@link MagnificationMode}
|
||||
*/
|
||||
@MagnificationMode
|
||||
public static int getCapabilities(Context context) {
|
||||
final ContentResolver contentResolver = context.getContentResolver();
|
||||
|
||||
return Settings.Secure.getIntForUser(contentResolver, KEY_CAPABILITY,
|
||||
MagnificationMode.FULLSCREEN, contentResolver.getUserId());
|
||||
}
|
||||
|
||||
private MagnificationCapabilities() {}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.UserHandle;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.android.settings.core.TogglePreferenceController;
|
||||
|
||||
/** Controller that shows the magnification enable mode summary. */
|
||||
public class MagnificationEnablePreferenceController extends TogglePreferenceController {
|
||||
|
||||
private static final String KEY_ENABLE = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE;
|
||||
|
||||
public MagnificationEnablePreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
final int enableMode = Settings.Secure.getIntForUser(mContext.getContentResolver(),
|
||||
KEY_ENABLE,
|
||||
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
|
||||
UserHandle.USER_CURRENT);
|
||||
return enableMode == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setChecked(boolean isChecked) {
|
||||
final int value = isChecked ? Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
|
||||
: Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
|
||||
return Settings.Secure.putIntForUser(mContext.getContentResolver(), KEY_ENABLE, value,
|
||||
UserHandle.USER_CURRENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return AVAILABLE;
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import android.content.Context;
|
||||
import android.icu.text.MessageFormat;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
@@ -93,8 +94,12 @@ public class MagnificationGesturesPreferenceController extends TogglePreferenceC
|
||||
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
|
||||
extras.putInt(AccessibilitySettings.EXTRA_TITLE_RES,
|
||||
R.string.accessibility_screen_magnification_gestures_title);
|
||||
extras.putCharSequence(AccessibilitySettings.EXTRA_HTML_DESCRIPTION,
|
||||
context.getText(R.string.accessibility_screen_magnification_summary));
|
||||
|
||||
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);
|
||||
|
||||
extras.putInt(AccessibilitySettings.EXTRA_VIDEO_RAW_RESOURCE_ID,
|
||||
R.raw.accessibility_screen_magnification);
|
||||
}
|
||||
|
||||
@@ -16,15 +16,87 @@
|
||||
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
|
||||
import static com.android.settings.accessibility.AccessibilityDialogUtils.CustomButton;
|
||||
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
|
||||
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.DialogCreatable;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.accessibility.MagnificationCapabilities.MagnificationMode;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
import com.android.settingslib.core.lifecycle.events.OnCreate;
|
||||
import com.android.settingslib.core.lifecycle.events.OnResume;
|
||||
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
|
||||
|
||||
/** Controller that shows the magnification area mode summary. */
|
||||
public class MagnificationModePreferenceController extends BasePreferenceController {
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/** Controller that shows the magnification area mode summary and the preference click behavior. */
|
||||
public class MagnificationModePreferenceController extends BasePreferenceController implements
|
||||
DialogCreatable, LifecycleObserver, OnCreate, OnResume, OnSaveInstanceState {
|
||||
|
||||
static final String PREF_KEY = "screen_magnification_mode";
|
||||
private static final int DIALOG_ID_BASE = 10;
|
||||
@VisibleForTesting
|
||||
static final int DIALOG_MAGNIFICATION_MODE = DIALOG_ID_BASE + 1;
|
||||
@VisibleForTesting
|
||||
static final int DIALOG_MAGNIFICATION_SWITCH_SHORTCUT = DIALOG_ID_BASE + 2;
|
||||
@VisibleForTesting
|
||||
static final String EXTRA_MODE = "mode";
|
||||
|
||||
private static final String TAG = "MagnificationModePreferenceController";
|
||||
private static final char COMPONENT_NAME_SEPARATOR = ':';
|
||||
|
||||
private DialogHelper mDialogHelper;
|
||||
// The magnification mode in the dialog.
|
||||
private int mMode = MagnificationMode.NONE;
|
||||
private Preference mModePreference;
|
||||
|
||||
@VisibleForTesting
|
||||
ListView mMagnificationModesListView;
|
||||
|
||||
private final List<MagnificationModeInfo> mModeInfos = new ArrayList<>();
|
||||
|
||||
public MagnificationModePreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
initModeInfos();
|
||||
}
|
||||
|
||||
private void initModeInfos() {
|
||||
mModeInfos.add(new MagnificationModeInfo(mContext.getText(
|
||||
R.string.accessibility_magnification_mode_dialog_option_full_screen), null,
|
||||
R.drawable.ic_illustration_fullscreen, MagnificationMode.FULLSCREEN));
|
||||
mModeInfos.add(new MagnificationModeInfo(
|
||||
mContext.getText(R.string.accessibility_magnification_mode_dialog_option_window),
|
||||
null, R.drawable.ic_illustration_window, MagnificationMode.WINDOW));
|
||||
mModeInfos.add(new MagnificationModeInfo(
|
||||
mContext.getText(R.string.accessibility_magnification_mode_dialog_option_switch),
|
||||
mContext.getText(
|
||||
R.string.accessibility_magnification_area_settings_mode_switch_summary),
|
||||
R.drawable.ic_illustration_switch, MagnificationMode.ALL));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -32,10 +104,197 @@ public class MagnificationModePreferenceController extends BasePreferenceControl
|
||||
return AVAILABLE;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
return MagnificationSettingsFragment.getMagnificationCapabilitiesSummary(
|
||||
mContext);
|
||||
final int capabilities = MagnificationCapabilities.getCapabilities(mContext);
|
||||
return MagnificationCapabilities.getSummary(mContext, capabilities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
if (savedInstanceState != null) {
|
||||
mMode = savedInstanceState.getInt(EXTRA_MODE, MagnificationMode.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
mModePreference = screen.findPreference(getPreferenceKey());
|
||||
mModePreference.setOnPreferenceClickListener(preference -> {
|
||||
mMode = MagnificationCapabilities.getCapabilities(mContext);
|
||||
mDialogHelper.showDialog(DIALOG_MAGNIFICATION_MODE);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
outState.putInt(EXTRA_MODE, mMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link DialogHelper} used to show the dialog.
|
||||
*/
|
||||
public void setDialogHelper(DialogHelper dialogHelper) {
|
||||
mDialogHelper = dialogHelper;
|
||||
mDialogHelper.setDialogDelegate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(int dialogId) {
|
||||
switch (dialogId) {
|
||||
case DIALOG_MAGNIFICATION_MODE:
|
||||
return createMagnificationModeDialog();
|
||||
|
||||
case DIALOG_MAGNIFICATION_SWITCH_SHORTCUT:
|
||||
return createMagnificationShortCutConfirmDialog();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDialogMetricsCategory(int dialogId) {
|
||||
switch (dialogId) {
|
||||
case DIALOG_MAGNIFICATION_MODE:
|
||||
return SettingsEnums.DIALOG_MAGNIFICATION_CAPABILITY;
|
||||
case DIALOG_MAGNIFICATION_SWITCH_SHORTCUT:
|
||||
return SettingsEnums.DIALOG_MAGNIFICATION_SWITCH_SHORTCUT;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private Dialog createMagnificationModeDialog() {
|
||||
mMagnificationModesListView = AccessibilityDialogUtils.createSingleChoiceListView(
|
||||
mContext, mModeInfos, this::onMagnificationModeSelected);
|
||||
|
||||
final View headerView = LayoutInflater.from(mContext).inflate(
|
||||
R.layout.accessibility_magnification_mode_header, mMagnificationModesListView,
|
||||
false);
|
||||
mMagnificationModesListView.addHeaderView(headerView, /* data= */ null, /* isSelectable= */
|
||||
false);
|
||||
|
||||
mMagnificationModesListView.setItemChecked(computeSelectionIndex(), true);
|
||||
final CharSequence title = mContext.getString(
|
||||
R.string.accessibility_magnification_mode_dialog_title);
|
||||
|
||||
return AccessibilityDialogUtils.createCustomDialog(mContext, title,
|
||||
mMagnificationModesListView, this::onMagnificationModeDialogPositiveButtonClicked);
|
||||
}
|
||||
|
||||
private void onMagnificationModeDialogPositiveButtonClicked(DialogInterface dialogInterface,
|
||||
int which) {
|
||||
final int selectedIndex = mMagnificationModesListView.getCheckedItemPosition();
|
||||
if (selectedIndex != AdapterView.INVALID_POSITION) {
|
||||
final MagnificationModeInfo modeInfo =
|
||||
(MagnificationModeInfo) mMagnificationModesListView.getItemAtPosition(
|
||||
selectedIndex);
|
||||
setMode(modeInfo.mMagnificationMode);
|
||||
} else {
|
||||
Log.w(TAG, "invalid index");
|
||||
}
|
||||
}
|
||||
|
||||
private void setMode(int mode) {
|
||||
mMode = mode;
|
||||
MagnificationCapabilities.setCapabilities(mContext, mMode);
|
||||
mModePreference.setSummary(
|
||||
MagnificationCapabilities.getSummary(mContext, mMode));
|
||||
}
|
||||
|
||||
private void onMagnificationModeSelected(AdapterView<?> parent, View view, int position,
|
||||
long id) {
|
||||
final MagnificationModeInfo modeInfo =
|
||||
(MagnificationModeInfo) mMagnificationModesListView.getItemAtPosition(
|
||||
position);
|
||||
if (modeInfo.mMagnificationMode == mMode) {
|
||||
return;
|
||||
}
|
||||
mMode = modeInfo.mMagnificationMode;
|
||||
if (isTripleTapEnabled(mContext) && mMode != MagnificationMode.FULLSCREEN) {
|
||||
mDialogHelper.showDialog(DIALOG_MAGNIFICATION_SWITCH_SHORTCUT);
|
||||
}
|
||||
}
|
||||
|
||||
private int computeSelectionIndex() {
|
||||
final int modesSize = mModeInfos.size();
|
||||
for (int i = 0; i < modesSize; i++) {
|
||||
if (mModeInfos.get(i).mMagnificationMode == mMode) {
|
||||
return i + mMagnificationModesListView.getHeaderViewsCount();
|
||||
}
|
||||
}
|
||||
Log.w(TAG, "computeSelectionIndex failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static boolean isTripleTapEnabled(Context context) {
|
||||
return Settings.Secure.getInt(context.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, OFF) == ON;
|
||||
}
|
||||
|
||||
private Dialog createMagnificationShortCutConfirmDialog() {
|
||||
return AccessibilityDialogUtils.createMagnificationSwitchShortcutDialog(mContext,
|
||||
this::onSwitchShortcutDialogButtonClicked);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void onSwitchShortcutDialogButtonClicked(@CustomButton int which) {
|
||||
optOutMagnificationFromTripleTap();
|
||||
//TODO(b/147990389): Merge this function into AccessibilityUtils after the format of
|
||||
// magnification target is changed to ComponentName.
|
||||
optInMagnificationToAccessibilityButton();
|
||||
}
|
||||
|
||||
private void optOutMagnificationFromTripleTap() {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, OFF);
|
||||
}
|
||||
|
||||
private void optInMagnificationToAccessibilityButton() {
|
||||
final String targetKey = Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
|
||||
final String targetString = Settings.Secure.getString(mContext.getContentResolver(),
|
||||
targetKey);
|
||||
if (targetString != null && targetString.contains(MAGNIFICATION_CONTROLLER_NAME)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final StringJoiner joiner = new StringJoiner(String.valueOf(COMPONENT_NAME_SEPARATOR));
|
||||
|
||||
if (!TextUtils.isEmpty(targetString)) {
|
||||
joiner.add(targetString);
|
||||
}
|
||||
joiner.add(MAGNIFICATION_CONTROLLER_NAME);
|
||||
|
||||
Settings.Secure.putString(mContext.getContentResolver(), targetKey,
|
||||
joiner.toString());
|
||||
}
|
||||
|
||||
// TODO(b/186731461): Remove it when this controller is used in DashBoardFragment only.
|
||||
@Override
|
||||
public void onResume() {
|
||||
updateState(mModePreference);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An interface to help the delegate to show the dialog. It will be injected to the delegate.
|
||||
*/
|
||||
interface DialogHelper extends DialogCreatable {
|
||||
void showDialog(int dialogId);
|
||||
void setDialogDelegate(DialogCreatable delegate);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static class MagnificationModeInfo extends ItemInfoArrayAdapter.ItemInfo {
|
||||
@MagnificationMode
|
||||
public final int mMagnificationMode;
|
||||
|
||||
MagnificationModeInfo(@NonNull CharSequence title, @Nullable CharSequence summary,
|
||||
@DrawableRes int drawableId, @MagnificationMode int magnificationMode) {
|
||||
super(title, summary, drawableId);
|
||||
mMagnificationMode = magnificationMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,62 +19,22 @@ package com.android.settings.accessibility;
|
||||
import android.app.Dialog;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.DialogCreatable;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/** Settings page for magnification. */
|
||||
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
||||
public class MagnificationSettingsFragment extends DashboardFragment {
|
||||
public class MagnificationSettingsFragment extends DashboardFragment implements
|
||||
MagnificationModePreferenceController.DialogHelper {
|
||||
|
||||
private static final String TAG = "MagnificationSettingsFragment";
|
||||
private static final String PREF_KEY_MODE = "magnification_mode";
|
||||
// TODO(b/146019459): Use magnification_capability.
|
||||
private static final String KEY_CAPABILITY = Settings.System.MASTER_MONO;
|
||||
private static final int DIALOG_MAGNIFICATION_CAPABILITY = 1;
|
||||
private static final String EXTRA_CAPABILITY = "capability";
|
||||
private Preference mModePreference;
|
||||
private int mCapabilities = MagnifyMode.NONE;
|
||||
private CheckBox mMagnifyFullScreenCheckBox;
|
||||
private CheckBox mMagnifyWindowCheckBox;
|
||||
|
||||
static String getMagnificationCapabilitiesSummary(Context context) {
|
||||
final String[] magnificationModeSummaries = context.getResources().getStringArray(
|
||||
R.array.magnification_mode_summaries);
|
||||
final int[] magnificationModeValues = context.getResources().getIntArray(
|
||||
R.array.magnification_mode_values);
|
||||
final int capabilities = MagnificationSettingsFragment.getMagnificationCapabilities(
|
||||
context);
|
||||
private DialogCreatable mDialogDelegate;
|
||||
|
||||
final int idx = Ints.indexOf(magnificationModeValues, capabilities);
|
||||
return magnificationModeSummaries[idx == -1 ? 0 : idx];
|
||||
}
|
||||
|
||||
private static int getMagnificationCapabilities(Context context) {
|
||||
return getSecureIntValue(context, KEY_CAPABILITY, MagnifyMode.FULLSCREEN);
|
||||
}
|
||||
|
||||
private static int getSecureIntValue(Context context, String key, int defaultValue) {
|
||||
return Settings.Secure.getIntForUser(
|
||||
context.getContentResolver(),
|
||||
key, defaultValue, context.getContentResolver().getUserId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
@@ -82,30 +42,27 @@ public class MagnificationSettingsFragment extends DashboardFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
outState.putInt(EXTRA_CAPABILITY, mCapabilities);
|
||||
super.onSaveInstanceState(outState);
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
use(MagnificationModePreferenceController.class).setDialogHelper(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
if (savedInstanceState != null) {
|
||||
mCapabilities = savedInstanceState.getInt(EXTRA_CAPABILITY, MagnifyMode.NONE);
|
||||
}
|
||||
if (mCapabilities == MagnifyMode.NONE) {
|
||||
mCapabilities = getMagnificationCapabilities(getPrefContext());
|
||||
}
|
||||
public void showDialog(int dialogId) {
|
||||
super.showDialog(dialogId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDialogDelegate(DialogCreatable delegate) {
|
||||
mDialogDelegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDialogMetricsCategory(int dialogId) {
|
||||
switch (dialogId) {
|
||||
case DIALOG_MAGNIFICATION_CAPABILITY:
|
||||
return SettingsEnums.DIALOG_MAGNIFICATION_CAPABILITY;
|
||||
default:
|
||||
return 0;
|
||||
if (mDialogDelegate != null) {
|
||||
return mDialogDelegate.getDialogMetricsCategory(dialogId);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -113,17 +70,6 @@ public class MagnificationSettingsFragment extends DashboardFragment {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
mModePreference = findPreference(PREF_KEY_MODE);
|
||||
mModePreference.setOnPreferenceClickListener(preference -> {
|
||||
mCapabilities = getMagnificationCapabilities(getPrefContext());
|
||||
showDialog(DIALOG_MAGNIFICATION_CAPABILITY);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.accessibility_magnification_service_settings;
|
||||
@@ -131,104 +77,15 @@ public class MagnificationSettingsFragment extends DashboardFragment {
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(int dialogId) {
|
||||
if (dialogId == DIALOG_MAGNIFICATION_CAPABILITY) {
|
||||
final String title = getPrefContext().getString(
|
||||
R.string.accessibility_magnification_mode_title);
|
||||
AlertDialog alertDialog = AccessibilityEditDialogUtils
|
||||
.showMagnificationModeDialog(getPrefContext(), title,
|
||||
this::callOnAlertDialogCheckboxClicked);
|
||||
initializeDialogCheckBox(alertDialog);
|
||||
return alertDialog;
|
||||
if (mDialogDelegate != null) {
|
||||
final Dialog dialog = mDialogDelegate.onCreateDialog(dialogId);
|
||||
if (dialog != null) {
|
||||
return dialog;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported dialogId " + dialogId);
|
||||
}
|
||||
|
||||
private void callOnAlertDialogCheckboxClicked(DialogInterface dialog, int which) {
|
||||
updateCapabilities(true);
|
||||
mModePreference.setSummary(
|
||||
getMagnificationCapabilitiesSummary(getPrefContext()));
|
||||
}
|
||||
|
||||
private void initializeDialogCheckBox(AlertDialog dialog) {
|
||||
final View dialogFullScreenView = dialog.findViewById(R.id.magnify_full_screen);
|
||||
mMagnifyFullScreenCheckBox = dialogFullScreenView.findViewById(R.id.checkbox);
|
||||
|
||||
final View dialogWidowView = dialog.findViewById(R.id.magnify_window_screen);
|
||||
mMagnifyWindowCheckBox = dialogWidowView.findViewById(R.id.checkbox);
|
||||
|
||||
updateAlertDialogCheckState();
|
||||
updateAlertDialogEnableState();
|
||||
}
|
||||
|
||||
private void updateAlertDialogCheckState() {
|
||||
updateCheckStatus(mMagnifyWindowCheckBox, MagnifyMode.WINDOW);
|
||||
updateCheckStatus(mMagnifyFullScreenCheckBox, MagnifyMode.FULLSCREEN);
|
||||
|
||||
}
|
||||
|
||||
private void updateCheckStatus(CheckBox checkBox, int mode) {
|
||||
checkBox.setChecked((mode & mCapabilities) != 0);
|
||||
checkBox.setOnClickListener(v -> {
|
||||
updateCapabilities(false);
|
||||
updateAlertDialogEnableState();
|
||||
});
|
||||
}
|
||||
|
||||
private void updateAlertDialogEnableState() {
|
||||
if (mCapabilities != MagnifyMode.ALL) {
|
||||
disableEnabledMagnificationModePreference();
|
||||
} else {
|
||||
enableAllPreference();
|
||||
}
|
||||
}
|
||||
|
||||
private void enableAllPreference() {
|
||||
mMagnifyFullScreenCheckBox.setEnabled(true);
|
||||
mMagnifyWindowCheckBox.setEnabled(true);
|
||||
}
|
||||
|
||||
private void disableEnabledMagnificationModePreference() {
|
||||
if (!mMagnifyFullScreenCheckBox.isChecked()) {
|
||||
mMagnifyWindowCheckBox.setEnabled(false);
|
||||
} else if (!mMagnifyWindowCheckBox.isChecked()) {
|
||||
mMagnifyFullScreenCheckBox.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCapabilities(boolean saveToDB) {
|
||||
int capabilities = 0;
|
||||
capabilities |=
|
||||
mMagnifyFullScreenCheckBox.isChecked() ? MagnifyMode.FULLSCREEN : 0;
|
||||
capabilities |= mMagnifyWindowCheckBox.isChecked() ? MagnifyMode.WINDOW : 0;
|
||||
mCapabilities = capabilities;
|
||||
if (saveToDB) {
|
||||
setMagnificationCapabilities(capabilities);
|
||||
}
|
||||
}
|
||||
|
||||
private void setSecureIntValue(String key, int value) {
|
||||
Settings.Secure.putIntForUser(getPrefContext().getContentResolver(),
|
||||
key, value, getPrefContext().getContentResolver().getUserId());
|
||||
}
|
||||
|
||||
private void setMagnificationCapabilities(int capabilities) {
|
||||
setSecureIntValue(KEY_CAPABILITY, capabilities);
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({
|
||||
MagnifyMode.NONE,
|
||||
MagnifyMode.FULLSCREEN,
|
||||
MagnifyMode.WINDOW,
|
||||
MagnifyMode.ALL,
|
||||
})
|
||||
private @interface MagnifyMode {
|
||||
int NONE = 0;
|
||||
int FULLSCREEN = 1;
|
||||
int WINDOW = 2;
|
||||
int ALL = 3;
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider(R.xml.accessibility_magnification_service_settings);
|
||||
}
|
||||
|
||||
@@ -16,23 +16,58 @@
|
||||
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ListView;
|
||||
import static android.graphics.drawable.GradientDrawable.Orientation;
|
||||
|
||||
import static com.android.settings.accessibility.AccessibilityUtil.getScreenHeightPixels;
|
||||
import static com.android.settings.accessibility.AccessibilityUtil.getScreenWidthPixels;
|
||||
|
||||
import static com.google.common.primitives.Ints.max;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Paint.FontMetrics;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
/** Preference that easier preview by matching name to color. */
|
||||
public class PaletteListPreference extends Preference {
|
||||
import com.google.common.primitives.Floats;
|
||||
import com.google.common.primitives.Ints;
|
||||
|
||||
private ListView mListView;
|
||||
private ViewTreeObserver.OnPreDrawListener mPreDrawListener;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** Preference that easier preview by matching name to color. */
|
||||
public final class PaletteListPreference extends Preference {
|
||||
|
||||
private final List<Integer> mGradientColors = new ArrayList<>();
|
||||
private final List<Float> mGradientOffsets = new ArrayList<>();
|
||||
|
||||
@IntDef({
|
||||
Position.START,
|
||||
Position.CENTER,
|
||||
Position.END,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface Position {
|
||||
int START = 0;
|
||||
int CENTER = 1;
|
||||
int END = 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new PaletteListPreference with the given context's theme and the supplied
|
||||
@@ -61,47 +96,94 @@ public class PaletteListPreference extends Preference {
|
||||
public PaletteListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
setLayoutResource(R.layout.daltonizer_preview);
|
||||
initPreDrawListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||
super.onBindViewHolder(holder);
|
||||
|
||||
final View rootView = holder.itemView;
|
||||
mListView = rootView.findViewById(R.id.palette_listView);
|
||||
if (mPreDrawListener != null) {
|
||||
mListView.getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
|
||||
final ViewGroup paletteView = holder.itemView.findViewById(R.id.palette_view);
|
||||
initPaletteAttributes(getContext());
|
||||
initPaletteView(getContext(), paletteView);
|
||||
}
|
||||
|
||||
private void initPaletteAttributes(Context context) {
|
||||
final int defaultColor = context.getColor(R.color.palette_list_gradient_background);
|
||||
mGradientColors.add(Position.START, defaultColor);
|
||||
mGradientColors.add(Position.CENTER, defaultColor);
|
||||
mGradientColors.add(Position.END, defaultColor);
|
||||
|
||||
mGradientOffsets.add(Position.START, /* element= */ 0.0f);
|
||||
mGradientOffsets.add(Position.CENTER, /* element= */ 0.5f);
|
||||
mGradientOffsets.add(Position.END, /* element= */ 1.0f);
|
||||
}
|
||||
|
||||
private void initPaletteView(Context context, ViewGroup rootView) {
|
||||
if (rootView.getChildCount() > 0) {
|
||||
rootView.removeAllViews();
|
||||
}
|
||||
|
||||
final List<Integer> paletteColors = getPaletteColors(context);
|
||||
final List<String> paletteData = getPaletteData(context);
|
||||
|
||||
final float textPadding =
|
||||
context.getResources().getDimension(R.dimen.accessibility_layout_margin_start_end);
|
||||
final String maxLengthData =
|
||||
Collections.max(paletteData, Comparator.comparing(String::length));
|
||||
final int textWidth = getTextWidth(context, maxLengthData);
|
||||
final float textBound = (textWidth + textPadding) / getScreenWidthPixels(context);
|
||||
mGradientOffsets.set(Position.CENTER, textBound);
|
||||
|
||||
final int screenHalfHeight = getScreenHeightPixels(context) / 2;
|
||||
final int paletteItemHeight =
|
||||
max(screenHalfHeight / paletteData.size(), getTextLineHeight(context));
|
||||
|
||||
for (int i = 0; i < paletteData.size(); ++i) {
|
||||
final TextView textView = new TextView(context);
|
||||
textView.setText(paletteData.get(i));
|
||||
textView.setHeight(paletteItemHeight);
|
||||
textView.setPaddingRelative(Math.round(textPadding), 0, 0, 0);
|
||||
textView.setGravity(Gravity.CENTER_VERTICAL);
|
||||
textView.setBackground(createGradientDrawable(rootView, paletteColors.get(i)));
|
||||
|
||||
rootView.addView(textView);
|
||||
}
|
||||
}
|
||||
|
||||
private void initPreDrawListener() {
|
||||
mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
if (mListView == null) {
|
||||
return false;
|
||||
}
|
||||
private GradientDrawable createGradientDrawable(ViewGroup rootView, @ColorInt int color) {
|
||||
mGradientColors.set(Position.END, color);
|
||||
|
||||
final int listViewHeight = mListView.getMeasuredHeight();
|
||||
final int listViewWidth = mListView.getMeasuredWidth();
|
||||
final GradientDrawable gradientDrawable = new GradientDrawable();
|
||||
final Orientation orientation =
|
||||
rootView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
|
||||
? Orientation.RIGHT_LEFT
|
||||
: Orientation.LEFT_RIGHT;
|
||||
gradientDrawable.setOrientation(orientation);
|
||||
gradientDrawable.setColors(Ints.toArray(mGradientColors), Floats.toArray(mGradientOffsets));
|
||||
|
||||
// Removes the callback after get result of measure view.
|
||||
final ViewTreeObserver viewTreeObserver = mListView.getViewTreeObserver();
|
||||
if (viewTreeObserver.isAlive()) {
|
||||
viewTreeObserver.removeOnPreDrawListener(this);
|
||||
}
|
||||
mPreDrawListener = null;
|
||||
return gradientDrawable;
|
||||
}
|
||||
|
||||
// Resets layout parameters to display whole items from listView.
|
||||
final FrameLayout.LayoutParams layoutParams =
|
||||
(FrameLayout.LayoutParams) mListView.getLayoutParams();
|
||||
layoutParams.height = listViewHeight * mListView.getAdapter().getCount();
|
||||
layoutParams.width = listViewWidth;
|
||||
mListView.setLayoutParams(layoutParams);
|
||||
private List<Integer> getPaletteColors(Context context) {
|
||||
final int[] paletteResources =
|
||||
context.getResources().getIntArray(R.array.setting_palette_colors);
|
||||
return Arrays.stream(paletteResources).boxed().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
private List<String> getPaletteData(Context context) {
|
||||
final String[] paletteResources =
|
||||
context.getResources().getStringArray(R.array.setting_palette_data);
|
||||
return Arrays.asList(paletteResources);
|
||||
}
|
||||
|
||||
private int getTextWidth(Context context, String text) {
|
||||
final TextView tempView = new TextView(context);
|
||||
return Math.round(tempView.getPaint().measureText(text));
|
||||
}
|
||||
|
||||
private int getTextLineHeight(Context context) {
|
||||
final TextView tempView = new TextView(context);
|
||||
final FontMetrics fontMetrics = tempView.getPaint().getFontMetrics();
|
||||
return Math.round(fontMetrics.bottom - fontMetrics.top);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,301 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.graphics.drawable.GradientDrawable.Orientation;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Display;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Custom ListView {@link ListView} which displays palette to deploy the color code preview.
|
||||
*
|
||||
* <p>The preview shows gradient from color white to specific color code on each list view item, in
|
||||
* addition, text view adjusts the attribute of width for adapting the text length.
|
||||
*
|
||||
* <p>The text cannot fills the whole view for ensuring the gradient color preview can purely
|
||||
* display also the view background shows the color beside the text variable end point.
|
||||
*/
|
||||
public class PaletteListView extends ListView {
|
||||
private final Context mContext;
|
||||
private final DisplayAdapter mDisplayAdapter;
|
||||
private final LayoutInflater mLayoutInflater;
|
||||
private final String mDefaultGradientColorCodeString;
|
||||
private final int mDefaultGradientColor;
|
||||
private float mTextBound;
|
||||
private static final float LANDSCAPE_MAX_WIDTH_PERCENTAGE = 100f;
|
||||
|
||||
public PaletteListView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public PaletteListView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public PaletteListView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mContext = context;
|
||||
mDisplayAdapter = new DisplayAdapter();
|
||||
mLayoutInflater = LayoutInflater.from(context);
|
||||
mDefaultGradientColorCodeString =
|
||||
getResources().getString(R.color.palette_list_gradient_background);
|
||||
mDefaultGradientColor =
|
||||
getResources().getColor(R.color.palette_list_gradient_background, null);
|
||||
mTextBound = 0.0f;
|
||||
init();
|
||||
}
|
||||
|
||||
private static int getScreenWidth(WindowManager windowManager) {
|
||||
final Display display = windowManager.getDefaultDisplay();
|
||||
final DisplayMetrics displayMetrics = new DisplayMetrics();
|
||||
display.getMetrics(displayMetrics);
|
||||
return displayMetrics.widthPixels;
|
||||
}
|
||||
|
||||
private void init() {
|
||||
final TypedArray colorNameArray = getResources().obtainTypedArray(
|
||||
R.array.setting_palette_colors);
|
||||
final TypedArray colorCodeArray = getResources().obtainTypedArray(
|
||||
R.array.setting_palette_data);
|
||||
final int colorNameArrayLength = colorNameArray.length();
|
||||
final List<ColorAttributes> colorList = new ArrayList<>();
|
||||
computeTextWidthBounds(colorNameArray);
|
||||
|
||||
for (int index = 0; index < colorNameArrayLength; index++) {
|
||||
colorList.add(
|
||||
new ColorAttributes(
|
||||
/* colorName= */ colorNameArray.getString(index),
|
||||
/* colorCode= */ colorCodeArray.getColor(index, mDefaultGradientColor),
|
||||
/* textBound= */ mTextBound,
|
||||
/* gradientDrawable= */
|
||||
new GradientDrawable(Orientation.LEFT_RIGHT, null)));
|
||||
}
|
||||
|
||||
mDisplayAdapter.setColorList(colorList);
|
||||
setAdapter(mDisplayAdapter);
|
||||
setDividerHeight(/* height= */ 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets string array that required the color name and color code for deploy the new color
|
||||
* preview.
|
||||
*
|
||||
* <p>The parameters not allow null define but two array length inconsistent are acceptable, in
|
||||
* addition, to prevent IndexOutOfBoundsException the algorithm will check array data, and base
|
||||
* on the array size to display data, or fills color code array if length less than other.
|
||||
*
|
||||
* @param colorNames a string array of color name
|
||||
* @param colorCodes a string array of color code
|
||||
* @return true if new array data apply successful
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean setPaletteListColors(@NonNull String[] colorNames, @NonNull String[] colorCodes) {
|
||||
if (colorNames == null || colorCodes == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int colorNameArrayLength = colorNames.length;
|
||||
final int colorCodeArrayLength = colorCodes.length;
|
||||
final List<ColorAttributes> colorList = new ArrayList<>();
|
||||
final String[] colorCodeArray = fillColorCodeArray(colorCodes, colorNameArrayLength,
|
||||
colorCodeArrayLength);
|
||||
computeTextWidthBounds(colorNames);
|
||||
|
||||
for (int index = 0; index < colorNameArrayLength; index++) {
|
||||
colorList.add(
|
||||
new ColorAttributes(
|
||||
/* colorName= */ colorNames[index],
|
||||
/* colorCode= */ Color.parseColor(colorCodeArray[index]),
|
||||
/* textBound= */ mTextBound,
|
||||
/* gradientDrawable= */
|
||||
new GradientDrawable(Orientation.LEFT_RIGHT, null)));
|
||||
}
|
||||
|
||||
mDisplayAdapter.setColorList(colorList);
|
||||
mDisplayAdapter.notifyDataSetChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
private String[] fillColorCodeArray(String[] colorCodes, int colorNameArrayLength,
|
||||
int colorCodeArrayLength) {
|
||||
if (colorNameArrayLength == colorCodeArrayLength
|
||||
|| colorNameArrayLength < colorCodeArrayLength) {
|
||||
return colorCodes;
|
||||
}
|
||||
|
||||
final String[] colorCodeArray = new String[colorNameArrayLength];
|
||||
for (int index = 0; index < colorNameArrayLength; index++) {
|
||||
if (index < colorCodeArrayLength) {
|
||||
colorCodeArray[index] = colorCodes[index];
|
||||
} else {
|
||||
colorCodeArray[index] = mDefaultGradientColorCodeString;
|
||||
}
|
||||
}
|
||||
return colorCodeArray;
|
||||
}
|
||||
|
||||
private void computeTextWidthBounds(TypedArray colorNameTypedArray) {
|
||||
final int colorNameArrayLength = colorNameTypedArray.length();
|
||||
final String[] colorNames = new String[colorNameArrayLength];
|
||||
for (int index = 0; index < colorNameArrayLength; index++) {
|
||||
colorNames[index] = colorNameTypedArray.getString(index);
|
||||
}
|
||||
|
||||
measureBound(colorNames);
|
||||
}
|
||||
|
||||
private void computeTextWidthBounds(String[] colorNameArray) {
|
||||
final int colorNameArrayLength = colorNameArray.length;
|
||||
final String[] colorNames = new String[colorNameArrayLength];
|
||||
for (int index = 0; index < colorNameArrayLength; index++) {
|
||||
colorNames[index] = colorNameArray[index];
|
||||
}
|
||||
|
||||
measureBound(colorNames);
|
||||
}
|
||||
|
||||
private void measureBound(String[] dataArray) {
|
||||
final WindowManager windowManager = (WindowManager) mContext.getSystemService(
|
||||
Context.WINDOW_SERVICE);
|
||||
final View view = mLayoutInflater.inflate(R.layout.palette_listview_item, null);
|
||||
final TextView textView = view.findViewById(R.id.item_textview);
|
||||
final List<String> colorNameList = new ArrayList<>(Arrays.asList(dataArray));
|
||||
Collections.sort(colorNameList, Comparator.comparing(String::length));
|
||||
// Gets the last index of list which sort by text length.
|
||||
textView.setText(Iterables.getLast(colorNameList));
|
||||
|
||||
final float textWidth = textView.getPaint().measureText(textView.getText().toString());
|
||||
// Computes rate of text width compare to screen width, and measures the round the double
|
||||
// to two decimal places manually.
|
||||
final float textBound = Math.round(
|
||||
textWidth / getScreenWidth(windowManager) * LANDSCAPE_MAX_WIDTH_PERCENTAGE)
|
||||
/ LANDSCAPE_MAX_WIDTH_PERCENTAGE;
|
||||
|
||||
// Left padding and right padding with color preview.
|
||||
final float paddingPixel = getResources().getDimension(
|
||||
R.dimen.accessibility_layout_margin_start_end);
|
||||
final float paddingWidth =
|
||||
Math.round(paddingPixel / getScreenWidth(windowManager)
|
||||
* LANDSCAPE_MAX_WIDTH_PERCENTAGE) / LANDSCAPE_MAX_WIDTH_PERCENTAGE;
|
||||
mTextBound = textBound + paddingWidth + paddingWidth;
|
||||
}
|
||||
|
||||
private static class ViewHolder {
|
||||
public TextView textView;
|
||||
}
|
||||
|
||||
/** An adapter that converts color text title and color code to text views. */
|
||||
private final class DisplayAdapter extends BaseAdapter {
|
||||
|
||||
private List<ColorAttributes> mColorList;
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mColorList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return mColorList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
final ViewHolder viewHolder;
|
||||
final ColorAttributes paletteAttribute = mColorList.get(position);
|
||||
final String colorName = paletteAttribute.getColorName();
|
||||
final GradientDrawable gradientDrawable = paletteAttribute.getGradientDrawable();
|
||||
|
||||
if (convertView == null) {
|
||||
convertView = mLayoutInflater.inflate(R.layout.palette_listview_item, null);
|
||||
viewHolder = new ViewHolder();
|
||||
viewHolder.textView = convertView.findViewById(R.id.item_textview);
|
||||
convertView.setTag(viewHolder);
|
||||
} else {
|
||||
viewHolder = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
viewHolder.textView.setText(colorName);
|
||||
viewHolder.textView.setBackground(gradientDrawable);
|
||||
return convertView;
|
||||
}
|
||||
|
||||
protected void setColorList(List<ColorAttributes> colorList) {
|
||||
mColorList = colorList;
|
||||
}
|
||||
}
|
||||
|
||||
private final class ColorAttributes {
|
||||
private final int mColorIndex = 2; // index for inject color.
|
||||
private final int mColorOffsetIndex = 1; // index for offset effect.
|
||||
private final String mColorName;
|
||||
private final GradientDrawable mGradientDrawable;
|
||||
private final int[] mGradientColors =
|
||||
{/* startColor=*/ mDefaultGradientColor, /* centerColor=*/ mDefaultGradientColor,
|
||||
/* endCode= */ 0};
|
||||
private final float[] mGradientOffsets =
|
||||
{/* starOffset= */ 0.0f, /* centerOffset= */ 0.5f, /* endOffset= */ 1.0f};
|
||||
|
||||
ColorAttributes(
|
||||
String colorName, int colorCode, float textBound,
|
||||
GradientDrawable gradientDrawable) {
|
||||
mGradientColors[mColorIndex] = colorCode;
|
||||
mGradientOffsets[mColorOffsetIndex] = textBound;
|
||||
gradientDrawable.setColors(mGradientColors, mGradientOffsets);
|
||||
mColorName = colorName;
|
||||
mGradientDrawable = gradientDrawable;
|
||||
}
|
||||
|
||||
public String getColorName() {
|
||||
return mColorName;
|
||||
}
|
||||
|
||||
public GradientDrawable getGradientDrawable() {
|
||||
return mGradientDrawable;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.text.TextUtils;
|
||||
|
||||
import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
/**
|
||||
* A data class for containing {@link ComponentName#flattenToString()} and
|
||||
* {@link UserShortcutType}. Represents the preferred shortcuts of the service or activity.
|
||||
*/
|
||||
public class PreferredShortcut {
|
||||
|
||||
private static final char COMPONENT_NAME_SEPARATOR = ':';
|
||||
private static final TextUtils.SimpleStringSplitter sStringColonSplitter =
|
||||
new TextUtils.SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
|
||||
|
||||
/**
|
||||
* Creates a {@link PreferredShortcut} from a encoded string described in {@link #toString()}.
|
||||
*
|
||||
* @param preferredShortcutString A string conform to the format described in {@link
|
||||
* #toString()}
|
||||
* @return A {@link PreferredShortcut} with the specified value
|
||||
* @throws IllegalArgumentException If preferredShortcutString does not conform to the format
|
||||
* described in {@link #toString()}
|
||||
*/
|
||||
public static PreferredShortcut fromString(String preferredShortcutString) {
|
||||
sStringColonSplitter.setString(preferredShortcutString);
|
||||
if (sStringColonSplitter.hasNext()) {
|
||||
final String componentName = sStringColonSplitter.next();
|
||||
final int type = Integer.parseInt(sStringColonSplitter.next());
|
||||
return new PreferredShortcut(componentName, type);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid PreferredShortcut string: " + preferredShortcutString);
|
||||
}
|
||||
|
||||
/** The format of {@link ComponentName#flattenToString()} */
|
||||
private String mComponentName;
|
||||
/** The format of {@link UserShortcutType} */
|
||||
private int mType;
|
||||
|
||||
public PreferredShortcut(String componentName, int type) {
|
||||
mComponentName = componentName;
|
||||
mType = type;
|
||||
}
|
||||
|
||||
public String getComponentName() {
|
||||
return mComponentName;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mComponentName + COMPONENT_NAME_SEPARATOR + mType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
PreferredShortcut that = (PreferredShortcut) o;
|
||||
return mType == that.mType && Objects.equal(mComponentName, that.mComponentName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(mComponentName, mType);
|
||||
}
|
||||
}
|
||||
102
src/com/android/settings/accessibility/PreferredShortcuts.java
Normal file
102
src/com/android/settings/accessibility/PreferredShortcuts.java
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/** Static utility methods relating to {@link PreferredShortcut} */
|
||||
public final class PreferredShortcuts {
|
||||
|
||||
private static final String ACCESSIBILITY_PERF = "accessibility_prefs";
|
||||
private static final String USER_SHORTCUT_TYPE = "user_shortcut_type";
|
||||
|
||||
/**
|
||||
* Retrieves {@link UserShortcutType} for the given {@code componentName} from
|
||||
* SharedPreferences.
|
||||
*
|
||||
* @param context {@link Context} to access the {@link SharedPreferences}
|
||||
* @param componentName Name of the service or activity, should be the format of {@link
|
||||
* ComponentName#flattenToString()}.
|
||||
* @param defaultType See {@link UserShortcutType}
|
||||
* @return {@link UserShortcutType}
|
||||
*/
|
||||
public static int retrieveUserShortcutType(Context context, String componentName,
|
||||
int defaultType) {
|
||||
if (componentName == null) {
|
||||
return defaultType;
|
||||
}
|
||||
|
||||
// Create a mutable set to modify
|
||||
final Set<String> info = new HashSet<>(getFromSharedPreferences(context));
|
||||
info.removeIf(str -> !str.contains(componentName));
|
||||
|
||||
if (info.isEmpty()) {
|
||||
return defaultType;
|
||||
}
|
||||
|
||||
final String str = info.stream().findFirst().get();
|
||||
final PreferredShortcut shortcut = PreferredShortcut.fromString(str);
|
||||
return shortcut.getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a {@link PreferredShortcut} which containing {@link ComponentName#flattenToString()}
|
||||
* and {@link UserShortcutType} in SharedPreferences.
|
||||
*
|
||||
* @param context {@link Context} to access the {@link SharedPreferences}
|
||||
* @param shortcut Contains {@link ComponentName#flattenToString()} and {@link UserShortcutType}
|
||||
*/
|
||||
public static void saveUserShortcutType(Context context, PreferredShortcut shortcut) {
|
||||
final String componentName = shortcut.getComponentName();
|
||||
if (componentName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a mutable set to modify
|
||||
final Set<String> info = new HashSet<>(getFromSharedPreferences(context));
|
||||
info.removeIf(str -> str.contains(componentName));
|
||||
info.add(shortcut.toString());
|
||||
saveToSharedPreferences(context, info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a immutable set of {@link PreferredShortcut#toString()} list from
|
||||
* SharedPreferences.
|
||||
*/
|
||||
private static Set<String> getFromSharedPreferences(Context context) {
|
||||
return getSharedPreferences(context).getStringSet(USER_SHORTCUT_TYPE, Set.of());
|
||||
}
|
||||
|
||||
/** Sets a set of {@link PreferredShortcut#toString()} list into SharedPreferences. */
|
||||
private static void saveToSharedPreferences(Context context, Set<String> data) {
|
||||
SharedPreferences.Editor editor = getSharedPreferences(context).edit();
|
||||
editor.putStringSet(USER_SHORTCUT_TYPE, data).apply();
|
||||
}
|
||||
|
||||
private static SharedPreferences getSharedPreferences(Context context) {
|
||||
return context.getSharedPreferences(ACCESSIBILITY_PERF, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
private PreferredShortcuts() {}
|
||||
}
|
||||
@@ -22,9 +22,12 @@ import android.provider.Settings;
|
||||
|
||||
import com.android.settings.core.TogglePreferenceController;
|
||||
|
||||
public class MasterMonoPreferenceController extends TogglePreferenceController {
|
||||
/**
|
||||
* A toggle preference controller for Primary Mono
|
||||
*/
|
||||
public class PrimaryMonoPreferenceController extends TogglePreferenceController {
|
||||
|
||||
public MasterMonoPreferenceController(Context context, String preferenceKey) {
|
||||
public PrimaryMonoPreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
* 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.
|
||||
@@ -20,27 +20,34 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.PersistableBundle;
|
||||
import android.provider.Settings;
|
||||
import android.telecom.TelecomManager;
|
||||
import android.telecom.PhoneAccount;
|
||||
import android.telecom.PhoneAccountHandle;
|
||||
import android.telephony.CarrierConfigManager;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.accessibility.rtt.TelecomUtil;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** A controller to control the status for RTT setting in Accessibility screen.*/
|
||||
/** A controller to control the status for RTT setting in Accessibility screen. */
|
||||
public class RTTSettingPreferenceController extends BasePreferenceController {
|
||||
|
||||
private static final String DIALER_RTT_CONFIGURATION = "dialer_rtt_configuration";
|
||||
private static final String TAG = "RTTSettingsCtr";
|
||||
|
||||
private static final String DIALER_RTT_CONFIGURATION = "dialer_rtt_configuration";
|
||||
private final Context mContext;
|
||||
private final PackageManager mPackageManager;
|
||||
private final TelecomManager mTelecomManager;
|
||||
private final CarrierConfigManager mCarrierConfigManager;
|
||||
private final CharSequence[] mModes;
|
||||
private final String mDialerPackage;
|
||||
|
||||
@@ -52,17 +59,17 @@ public class RTTSettingPreferenceController extends BasePreferenceController {
|
||||
mContext = context;
|
||||
mModes = mContext.getResources().getTextArray(R.array.rtt_setting_mode);
|
||||
mDialerPackage = mContext.getString(R.string.config_rtt_setting_package_name);
|
||||
mPackageManager = context.getPackageManager();
|
||||
mTelecomManager = context.getSystemService(TelecomManager.class);
|
||||
mPackageManager = mContext.getPackageManager();
|
||||
mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
|
||||
mRTTIntent = new Intent(context.getString(R.string.config_rtt_setting_intent_action));
|
||||
|
||||
Log.d(TAG, "init controller");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
final List<ResolveInfo> resolved =
|
||||
mPackageManager.queryIntentActivities(mRTTIntent, 0 /* flags */);
|
||||
return resolved != null && !resolved.isEmpty() && isDialerSupportRTTSetting()
|
||||
return resolved != null && !resolved.isEmpty() && isRttSettingSupported()
|
||||
? AVAILABLE
|
||||
: UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
@@ -77,12 +84,82 @@ public class RTTSettingPreferenceController extends BasePreferenceController {
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
final int option = Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
DIALER_RTT_CONFIGURATION, 1 /* not visible */);
|
||||
DIALER_RTT_CONFIGURATION, 0 /* Invalid value */);
|
||||
Log.d(TAG, "DIALER_RTT_CONFIGURATION value = " + option);
|
||||
return mModes[option];
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean isDialerSupportRTTSetting() {
|
||||
return TextUtils.equals(mTelecomManager.getDefaultDialerPackage(), mDialerPackage);
|
||||
boolean isRttSettingSupported() {
|
||||
Log.d(TAG, "isRttSettingSupported [start]");
|
||||
if (!isDefaultDialerSupportedRTT(mContext)) {
|
||||
Log.d(TAG, "Dialer doesn't support RTT.");
|
||||
return false;
|
||||
}
|
||||
// At least one PhoneAccount must have both isRttSupported and
|
||||
// ignore_rtt_mode_setting_bool being true
|
||||
for (PhoneAccountHandle phoneAccountHandle :
|
||||
TelecomUtil.getCallCapablePhoneAccounts(mContext)) {
|
||||
final int subId =
|
||||
TelecomUtil.getSubIdForPhoneAccountHandle(mContext, phoneAccountHandle);
|
||||
Log.d(TAG, "subscription id for the device: " + subId);
|
||||
|
||||
final boolean isRttCallingSupported = isRttSupportedByTelecom(phoneAccountHandle);
|
||||
Log.d(TAG, "rtt calling supported by telecom:: " + isRttCallingSupported);
|
||||
|
||||
if (isRttCallingSupported) {
|
||||
PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId);
|
||||
// If IGNORE_RTT_MODE_SETTING_BOOL=true, RTT visibility is not supported because
|
||||
// this means we must use the legacy Telecom setting, which does not support RTT
|
||||
// visibility.
|
||||
if (carrierConfig != null
|
||||
&& getBooleanCarrierConfig(
|
||||
CarrierConfigManager.KEY_IGNORE_RTT_MODE_SETTING_BOOL)) {
|
||||
Log.d(TAG, "RTT visibility setting is supported.");
|
||||
return true;
|
||||
}
|
||||
Log.d(TAG, "IGNORE_RTT_MODE_SETTING_BOOL is false.");
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "isRttSettingSupported [Not support]");
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isRttSupportedByTelecom(PhoneAccountHandle phoneAccountHandle) {
|
||||
PhoneAccount phoneAccount =
|
||||
TelecomUtil.getTelecomManager(mContext).getPhoneAccount(phoneAccountHandle);
|
||||
if (phoneAccount != null && phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
|
||||
Log.d(TAG, "Phone account has RTT capability.");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the boolean config from carrier config manager.
|
||||
*
|
||||
* @param key config key defined in CarrierConfigManager.
|
||||
* @return boolean value of corresponding key.
|
||||
*/
|
||||
private boolean getBooleanCarrierConfig(String key) {
|
||||
if (mCarrierConfigManager == null) {
|
||||
// Return static default defined in CarrierConfigManager.
|
||||
return CarrierConfigManager.getDefaultConfig().getBoolean(key);
|
||||
}
|
||||
|
||||
// If an invalid subId is used, this bundle will contain default values.
|
||||
final int subId = SubscriptionManager.getDefaultVoiceSubscriptionId();
|
||||
final PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(subId);
|
||||
|
||||
return bundle != null
|
||||
? bundle.getBoolean(key)
|
||||
: CarrierConfigManager.getDefaultConfig().getBoolean(key);
|
||||
}
|
||||
|
||||
/** Returns whether is a correct default dialer which supports RTT. */
|
||||
private static boolean isDefaultDialerSupportedRTT(Context context) {
|
||||
return TextUtils.equals(
|
||||
context.getString(R.string.config_rtt_setting_package_name),
|
||||
TelecomUtil.getTelecomManager(context).getDefaultDialerPackage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.hardware.display.ColorDisplayManager;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.core.SliderPreferenceController;
|
||||
import com.android.settings.widget.SeekBarPreference;
|
||||
|
||||
/** PreferenceController for feature intensity. */
|
||||
public class ReduceBrightColorsIntensityPreferenceController extends SliderPreferenceController {
|
||||
|
||||
private static final int INVERSE_PERCENTAGE_BASE = 100;
|
||||
private final ColorDisplayManager mColorDisplayManager;
|
||||
|
||||
public ReduceBrightColorsIntensityPreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
mColorDisplayManager = context.getSystemService(ColorDisplayManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
if (!ColorDisplayManager.isReduceBrightColorsAvailable(mContext)) {
|
||||
return UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
if (!mColorDisplayManager.isReduceBrightColorsActivated()) {
|
||||
return DISABLED_DEPENDENT_SETTING;
|
||||
}
|
||||
return AVAILABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
final SeekBarPreference preference = screen.findPreference(getPreferenceKey());
|
||||
preference.setContinuousUpdates(true);
|
||||
preference.setMax(getMax());
|
||||
preference.setMin(getMin());
|
||||
preference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_ENDS);
|
||||
updateState(preference);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
preference.setEnabled(mColorDisplayManager.isReduceBrightColorsActivated());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSliderPosition() {
|
||||
return INVERSE_PERCENTAGE_BASE - mColorDisplayManager.getReduceBrightColorsStrength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setSliderPosition(int position) {
|
||||
return mColorDisplayManager.setReduceBrightColorsStrength(
|
||||
INVERSE_PERCENTAGE_BASE - position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMax() {
|
||||
return INVERSE_PERCENTAGE_BASE
|
||||
- ColorDisplayManager.getMinimumReduceBrightColorsStrength(mContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMin() {
|
||||
return INVERSE_PERCENTAGE_BASE
|
||||
- ColorDisplayManager.getMaximumReduceBrightColorsStrength(mContext);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.hardware.display.ColorDisplayManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.core.TogglePreferenceController;
|
||||
|
||||
/** PreferenceController for persisting feature activation state after a restart. */
|
||||
public class ReduceBrightColorsPersistencePreferenceController extends TogglePreferenceController {
|
||||
private final ColorDisplayManager mColorDisplayManager;
|
||||
|
||||
public ReduceBrightColorsPersistencePreferenceController(
|
||||
Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
mColorDisplayManager = context.getSystemService(ColorDisplayManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
if (!ColorDisplayManager.isReduceBrightColorsAvailable(mContext)) {
|
||||
return UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
if (!mColorDisplayManager.isReduceBrightColorsActivated()) {
|
||||
return DISABLED_DEPENDENT_SETTING;
|
||||
}
|
||||
return AVAILABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
return Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.REDUCE_BRIGHT_COLORS_PERSIST_ACROSS_REBOOTS, 0) == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setChecked(boolean isChecked) {
|
||||
return Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.REDUCE_BRIGHT_COLORS_PERSIST_ACROSS_REBOOTS, (isChecked ? 1 : 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
preference.setEnabled(mColorDisplayManager.isReduceBrightColorsActivated());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.hardware.display.ColorDisplayManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
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.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
|
||||
implements LifecycleObserver, OnStart, OnStop {
|
||||
private ContentObserver mSettingsContentObserver;
|
||||
private PrimarySwitchPreference mPreference;
|
||||
private final Context mContext;
|
||||
private final ColorDisplayManager mColorDisplayManager;
|
||||
|
||||
public ReduceBrightColorsPreferenceController(Context context,
|
||||
String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
mContext = context;
|
||||
mSettingsContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())){
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
final String path = uri == null ? null : uri.getLastPathSegment();
|
||||
if (TextUtils.equals(path, Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED)) {
|
||||
updateState(mPreference);
|
||||
}
|
||||
}
|
||||
};
|
||||
mColorDisplayManager = mContext.getSystemService(ColorDisplayManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
return mColorDisplayManager.isReduceBrightColorsActivated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setChecked(boolean isChecked) {
|
||||
return mColorDisplayManager.setReduceBrightColorsActivated(isChecked);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
return mContext.getText(
|
||||
R.string.reduce_bright_colors_preference_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
refreshSummary(preference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return ColorDisplayManager.isReduceBrightColorsAvailable(mContext) ? AVAILABLE
|
||||
: UNSUPPORTED_ON_DEVICE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
mPreference = screen.findPreference(getPreferenceKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
|
||||
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED),
|
||||
false, mSettingsContentObserver, UserHandle.USER_CURRENT);
|
||||
}
|
||||
@Override
|
||||
public void onStop() {
|
||||
mContext.getContentResolver().unregisterContentObserver(mSettingsContentObserver);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.SharedPreferences;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/** Utility class for SharedPreferences. */
|
||||
public final class SharedPreferenceUtils {
|
||||
|
||||
private static final String ACCESSIBILITY_PERF = "accessibility_prefs";
|
||||
private static final String USER_SHORTCUT_TYPE = "user_shortcut_type";
|
||||
private SharedPreferenceUtils() { }
|
||||
|
||||
private static SharedPreferences getSharedPreferences(Context context, String fileName) {
|
||||
return context.getSharedPreferences(fileName, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
/** Returns a set of user shortcuts list to determine user preferred service shortcut. */
|
||||
public static Set<String> getUserShortcutTypes(Context context) {
|
||||
return getSharedPreferences(context, ACCESSIBILITY_PERF)
|
||||
.getStringSet(USER_SHORTCUT_TYPE, ImmutableSet.of());
|
||||
}
|
||||
|
||||
/** Sets a set of user shortcuts list to determine user preferred service shortcut. */
|
||||
public static void setUserShortcutType(Context context, Set<String> data) {
|
||||
SharedPreferences.Editor editor = getSharedPreferences(context, ACCESSIBILITY_PERF).edit();
|
||||
editor.remove(USER_SHORTCUT_TYPE).apply();
|
||||
editor.putStringSet(USER_SHORTCUT_TYPE, data).apply();
|
||||
}
|
||||
}
|
||||
@@ -62,8 +62,8 @@ public class ShortcutPreference extends Preference {
|
||||
ShortcutPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setLayoutResource(R.layout.accessibility_shortcut_secondary_action);
|
||||
setWidgetLayoutResource(R.layout.preference_widget_master_switch);
|
||||
setIconSpaceReserved(true);
|
||||
setWidgetLayoutResource(R.layout.preference_widget_primary_switch);
|
||||
setIconSpaceReserved(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 android.app.settings.SettingsEnums;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
|
||||
/** Accessibility settings for accessibility shortcuts. */
|
||||
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
||||
public class ShortcutsSettingsFragment extends DashboardFragment {
|
||||
|
||||
private static final String TAG = "ShortcutsSettingsFragment";
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.ACCESSIBILITY_SHORTCUTS_SETTINGS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.accessibility_shortcuts_settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLogTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider(R.xml.accessibility_shortcuts_settings);
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
* 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.
|
||||
@@ -11,10 +11,10 @@
|
||||
* 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
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.gestures;
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
|
||||
@@ -23,14 +23,20 @@ import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
|
||||
@SearchIndexable
|
||||
public class GlobalActionsPanelSettings extends DashboardFragment {
|
||||
/** Accessibility settings for system controls. */
|
||||
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
||||
public class SystemControlsFragment extends DashboardFragment {
|
||||
|
||||
private static final String TAG = "GlobalActionsPanelSettings";
|
||||
private static final String TAG = "SystemControlsFragment";
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.GLOBAL_ACTIONS_PANEL_SETTINGS;
|
||||
return SettingsEnums.ACCESSIBILITY_SYSTEM_CONTROLS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.accessibility_system_controls;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -38,11 +44,7 @@ public class GlobalActionsPanelSettings extends DashboardFragment {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.global_actions_panel_settings;
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider(R.xml.global_actions_panel_settings);
|
||||
new BaseSearchIndexProvider(R.xml.accessibility_system_controls);
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
* 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.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.gestures;
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
|
||||
@@ -23,14 +23,20 @@ import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
|
||||
@SearchIndexable
|
||||
public class DeviceControlsSettings extends DashboardFragment {
|
||||
/** Accessibility settings for tap assistance. */
|
||||
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
||||
public class TapAssistanceFragment extends DashboardFragment {
|
||||
|
||||
private static final String TAG = "QuickControlsSettings";
|
||||
private static final String TAG = "TapAssistanceFragment";
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.DEVICE_CONTROLS_SETTINGS;
|
||||
return SettingsEnums.ACCESSIBILITY_TAP_ASSISTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.accessibility_tap_assistance;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -38,11 +44,7 @@ public class DeviceControlsSettings extends DashboardFragment {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.device_controls_settings;
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider(R.xml.device_controls_settings);
|
||||
new BaseSearchIndexProvider(R.xml.accessibility_tap_assistance);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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 android.app.settings.SettingsEnums;
|
||||
import android.hardware.display.ColorDisplayManager;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
|
||||
/** Accessibility settings for text and display. */
|
||||
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
||||
public class TextAndDisplayFragment extends DashboardFragment {
|
||||
|
||||
private static final String TAG = "TextAndDisplayFragment";
|
||||
|
||||
private static final String CATEGORY_EXPERIMENTAL = "experimental_category";
|
||||
|
||||
// Preferences
|
||||
private static final String DISPLAY_DALTONIZER_PREFERENCE_SCREEN = "daltonizer_preference";
|
||||
private static final String TOGGLE_DISABLE_ANIMATIONS = "toggle_disable_animations";
|
||||
private static final String TOGGLE_LARGE_POINTER_ICON = "toggle_large_pointer_icon";
|
||||
|
||||
private Preference mDisplayDaltonizerPreferenceScreen;
|
||||
private SwitchPreference mToggleDisableAnimationsPreference;
|
||||
private SwitchPreference mToggleLargePointerIconPreference;
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.ACCESSIBILITY_TEXT_AND_DISPLAY;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
initializeAllPreferences();
|
||||
updateSystemPreferences();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.accessibility_text_and_display;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLogTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
private void initializeAllPreferences() {
|
||||
// Display color adjustments.
|
||||
mDisplayDaltonizerPreferenceScreen = findPreference(DISPLAY_DALTONIZER_PREFERENCE_SCREEN);
|
||||
|
||||
// Disable animation.
|
||||
mToggleDisableAnimationsPreference = findPreference(TOGGLE_DISABLE_ANIMATIONS);
|
||||
|
||||
// Large pointer icon.
|
||||
mToggleLargePointerIconPreference = findPreference(TOGGLE_LARGE_POINTER_ICON);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates preferences related to system configurations.
|
||||
*/
|
||||
private void updateSystemPreferences() {
|
||||
final PreferenceCategory experimentalCategory = getPreferenceScreen().findPreference(
|
||||
CATEGORY_EXPERIMENTAL);
|
||||
if (ColorDisplayManager.isColorTransformAccelerated(getContext())) {
|
||||
mDisplayDaltonizerPreferenceScreen.setSummary(AccessibilityUtil.getSummary(
|
||||
getContext(), Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED));
|
||||
getPreferenceScreen().removePreference(experimentalCategory);
|
||||
} else {
|
||||
// Move following preferences to experimental category if device don't supports HWC
|
||||
// hardware-accelerated color transform.
|
||||
getPreferenceScreen().removePreference(mDisplayDaltonizerPreferenceScreen);
|
||||
getPreferenceScreen().removePreference(mToggleDisableAnimationsPreference);
|
||||
getPreferenceScreen().removePreference(mToggleLargePointerIconPreference);
|
||||
experimentalCategory.addPreference(mDisplayDaltonizerPreferenceScreen);
|
||||
experimentalCategory.addPreference(mToggleDisableAnimationsPreference);
|
||||
experimentalCategory.addPreference(mToggleLargePointerIconPreference);
|
||||
}
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider(R.xml.accessibility_text_and_display);
|
||||
}
|
||||
@@ -16,17 +16,23 @@
|
||||
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
|
||||
import static com.android.settings.accessibility.AccessibilityStatsLogUtils.logAccessibilityServiceEnabled;
|
||||
import static com.android.settings.accessibility.PreferredShortcuts.retrieveUserShortcutType;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.net.Uri;
|
||||
@@ -36,18 +42,20 @@ import android.os.UserHandle;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.widget.Switch;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.SwitchPreference;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
|
||||
import com.android.settings.password.ConfirmDeviceCredentialActivity;
|
||||
import com.android.settings.widget.SettingsMainSwitchPreference;
|
||||
import com.android.settingslib.accessibility.AccessibilityUtils;
|
||||
|
||||
import java.util.List;
|
||||
@@ -57,7 +65,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
public class ToggleAccessibilityServicePreferenceFragment extends
|
||||
ToggleFeaturePreferenceFragment {
|
||||
|
||||
public static final int ACTIVITY_REQUEST_CONFIRM_CREDENTIAL_FOR_WEAKER_ENCRYPTION = 1;
|
||||
private static final String TAG = "ToggleAccessibilityServicePreferenceFragment";
|
||||
private static final int ACTIVITY_REQUEST_CONFIRM_CREDENTIAL_FOR_WEAKER_ENCRYPTION = 1;
|
||||
private LockPatternUtils mLockPatternUtils;
|
||||
private AtomicBoolean mIsDialogShown = new AtomicBoolean(/* initialValue= */ false);
|
||||
|
||||
@@ -72,6 +81,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
||||
};
|
||||
|
||||
private Dialog mDialog;
|
||||
private BroadcastReceiver mPackageRemovedReceiver;
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
@@ -91,6 +101,17 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
||||
mLockPatternUtils = new LockPatternUtils(getPrefContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
final AccessibilityServiceInfo serviceInfo = getAccessibilityServiceInfo();
|
||||
if (serviceInfo == null) {
|
||||
getActivity().finishAndRemoveTask();
|
||||
} else if (!AccessibilityUtil.isSystemApp(serviceInfo)) {
|
||||
registerPackageRemoveReceiver();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@@ -109,6 +130,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
||||
// capabilities. For
|
||||
// example, before JellyBean MR2 the user was granting the explore by touch
|
||||
// one.
|
||||
@Nullable
|
||||
AccessibilityServiceInfo getAccessibilityServiceInfo() {
|
||||
final List<AccessibilityServiceInfo> infos = AccessibilityManager.getInstance(
|
||||
getPrefContext()).getInstalledAccessibilityServiceList();
|
||||
@@ -134,7 +156,8 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
||||
}
|
||||
mDialog = AccessibilityServiceWarning
|
||||
.createCapabilitiesDialog(getPrefContext(), info,
|
||||
this::onDialogButtonFromEnableToggleClicked);
|
||||
this::onDialogButtonFromEnableToggleClicked,
|
||||
this::onDialogButtonFromUninstallClicked);
|
||||
break;
|
||||
}
|
||||
case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE: {
|
||||
@@ -144,7 +167,8 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
||||
}
|
||||
mDialog = AccessibilityServiceWarning
|
||||
.createCapabilitiesDialog(getPrefContext(), info,
|
||||
this::onDialogButtonFromShortcutToggleClicked);
|
||||
this::onDialogButtonFromShortcutToggleClicked,
|
||||
this::onDialogButtonFromUninstallClicked);
|
||||
break;
|
||||
}
|
||||
case DialogEnums.ENABLE_WARNING_FROM_SHORTCUT: {
|
||||
@@ -154,7 +178,8 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
||||
}
|
||||
mDialog = AccessibilityServiceWarning
|
||||
.createCapabilitiesDialog(getPrefContext(), info,
|
||||
this::onDialogButtonFromShortcutClicked);
|
||||
this::onDialogButtonFromShortcutClicked,
|
||||
this::onDialogButtonFromUninstallClicked);
|
||||
break;
|
||||
}
|
||||
case DialogEnums.DISABLE_WARNING_FROM_TOGGLE: {
|
||||
@@ -197,21 +222,26 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateToggleServiceTitle(SwitchPreference switchPreference) {
|
||||
protected void updateToggleServiceTitle(SettingsMainSwitchPreference switchPreference) {
|
||||
final AccessibilityServiceInfo info = getAccessibilityServiceInfo();
|
||||
final String switchBarText = (info == null) ? "" :
|
||||
getString(R.string.accessibility_service_master_switch_title,
|
||||
getString(R.string.accessibility_service_primary_switch_title,
|
||||
info.getResolveInfo().loadLabel(getPackageManager()));
|
||||
switchPreference.setTitle(switchBarText);
|
||||
}
|
||||
|
||||
private void updateSwitchBarToggleSwitch() {
|
||||
final boolean checked = AccessibilityUtils.getEnabledServicesFromSettings(getPrefContext())
|
||||
.contains(mComponentName);
|
||||
if (mToggleServiceDividerSwitchPreference.isChecked() == checked) {
|
||||
@Override
|
||||
protected void updateSwitchBarToggleSwitch() {
|
||||
final boolean checked = isAccessibilityServiceEnabled();
|
||||
if (mToggleServiceSwitchPreference.isChecked() == checked) {
|
||||
return;
|
||||
}
|
||||
mToggleServiceDividerSwitchPreference.setChecked(checked);
|
||||
mToggleServiceSwitchPreference.setChecked(checked);
|
||||
}
|
||||
|
||||
private boolean isAccessibilityServiceEnabled() {
|
||||
return AccessibilityUtils.getEnabledServicesFromSettings(getPrefContext())
|
||||
.contains(mComponentName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,6 +273,32 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
||||
}
|
||||
}
|
||||
|
||||
private void registerPackageRemoveReceiver() {
|
||||
if (mPackageRemovedReceiver != null || getContext() == null) {
|
||||
return;
|
||||
}
|
||||
mPackageRemovedReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
final String packageName = intent.getData().getSchemeSpecificPart();
|
||||
if (TextUtils.equals(mComponentName.getPackageName(), packageName)) {
|
||||
getActivity().finishAndRemoveTask();
|
||||
}
|
||||
}
|
||||
};
|
||||
final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
|
||||
filter.addDataScheme("package");
|
||||
getContext().registerReceiver(mPackageRemovedReceiver, filter);
|
||||
}
|
||||
|
||||
private void unregisterPackageRemoveReceiver() {
|
||||
if (mPackageRemovedReceiver == null || getContext() == null) {
|
||||
return;
|
||||
}
|
||||
getContext().unregisterReceiver(mPackageRemovedReceiver);
|
||||
mPackageRemovedReceiver = null;
|
||||
}
|
||||
|
||||
private boolean isServiceSupportAccessibilityButton() {
|
||||
final AccessibilityManager ams = getPrefContext().getSystemService(
|
||||
AccessibilityManager.class);
|
||||
@@ -262,7 +318,6 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
||||
}
|
||||
|
||||
private void handleConfirmServiceEnabled(boolean confirmed) {
|
||||
mToggleServiceDividerSwitchPreference.setChecked(confirmed);
|
||||
getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED, confirmed);
|
||||
onPreferenceToggled(mPreferenceKey, confirmed);
|
||||
}
|
||||
@@ -285,16 +340,18 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInstallSwitchPreferenceToggleSwitch() {
|
||||
super.onInstallSwitchPreferenceToggleSwitch();
|
||||
mToggleServiceDividerSwitchPreference.setOnPreferenceClickListener(this::onPreferenceClick);
|
||||
public void onSwitchChanged(Switch switchView, boolean isChecked) {
|
||||
if (isChecked != isAccessibilityServiceEnabled()) {
|
||||
onPreferenceClick(isChecked);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToggleClicked(ShortcutPreference preference) {
|
||||
final int shortcutTypes = getUserShortcutTypes(getPrefContext(), UserShortcutType.SOFTWARE);
|
||||
final int shortcutTypes = retrieveUserShortcutType(getPrefContext(),
|
||||
mComponentName.flattenToString(), UserShortcutType.SOFTWARE);
|
||||
if (preference.isChecked()) {
|
||||
if (!mToggleServiceDividerSwitchPreference.isChecked()) {
|
||||
if (!mToggleServiceSwitchPreference.isChecked()) {
|
||||
preference.setChecked(false);
|
||||
showPopupDialog(DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE);
|
||||
} else {
|
||||
@@ -311,9 +368,8 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
||||
|
||||
@Override
|
||||
public void onSettingsClicked(ShortcutPreference preference) {
|
||||
super.onSettingsClicked(preference);
|
||||
final boolean isServiceOnOrShortcutAdded = mShortcutPreference.isChecked()
|
||||
|| mToggleServiceDividerSwitchPreference.isChecked();
|
||||
|| mToggleServiceSwitchPreference.isChecked();
|
||||
showPopupDialog(isServiceOnOrShortcutAdded ? DialogEnums.EDIT_SHORTCUT
|
||||
: DialogEnums.ENABLE_WARNING_FROM_SHORTCUT);
|
||||
}
|
||||
@@ -340,10 +396,12 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
||||
// Settings animated image.
|
||||
final int animatedImageRes = arguments.getInt(
|
||||
AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES);
|
||||
mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
|
||||
.authority(mComponentName.getPackageName())
|
||||
.appendPath(String.valueOf(animatedImageRes))
|
||||
.build();
|
||||
if (animatedImageRes > 0) {
|
||||
mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
|
||||
.authority(mComponentName.getPackageName())
|
||||
.appendPath(String.valueOf(animatedImageRes))
|
||||
.build();
|
||||
}
|
||||
|
||||
// Get Accessibility service name.
|
||||
mPackageName = getAccessibilityServiceInfo().getResolveInfo().loadLabel(
|
||||
@@ -374,6 +432,35 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
||||
}
|
||||
}
|
||||
|
||||
private void onDialogButtonFromUninstallClicked() {
|
||||
mDialog.dismiss();
|
||||
final Intent uninstallIntent = createUninstallPackageActivityIntent();
|
||||
if (uninstallIntent == null) {
|
||||
return;
|
||||
}
|
||||
startActivity(uninstallIntent);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Intent createUninstallPackageActivityIntent() {
|
||||
final AccessibilityServiceInfo a11yServiceInfo = getAccessibilityServiceInfo();
|
||||
if (a11yServiceInfo == null) {
|
||||
Log.w(TAG, "createUnInstallIntent -- invalid a11yServiceInfo");
|
||||
return null;
|
||||
}
|
||||
final ApplicationInfo appInfo =
|
||||
a11yServiceInfo.getResolveInfo().serviceInfo.applicationInfo;
|
||||
final Uri packageUri = Uri.parse("package:" + appInfo.packageName);
|
||||
final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri);
|
||||
return uninstallIntent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
unregisterPackageRemoveReceiver();
|
||||
}
|
||||
|
||||
private void onAllowButtonFromEnableToggleClicked() {
|
||||
if (isFullDiskEncrypted()) {
|
||||
final String title = createConfirmCredentialReasonMessage();
|
||||
@@ -411,7 +498,8 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
||||
private void onAllowButtonFromShortcutToggleClicked() {
|
||||
mShortcutPreference.setChecked(true);
|
||||
|
||||
final int shortcutTypes = getUserShortcutTypes(getPrefContext(), UserShortcutType.SOFTWARE);
|
||||
final int shortcutTypes = retrieveUserShortcutType(getPrefContext(),
|
||||
mComponentName.flattenToString(), UserShortcutType.SOFTWARE);
|
||||
AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes, mComponentName);
|
||||
|
||||
mIsDialogShown.set(false);
|
||||
@@ -450,10 +538,9 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
||||
mDialog.dismiss();
|
||||
}
|
||||
|
||||
private boolean onPreferenceClick(Preference preference) {
|
||||
boolean checked = ((DividerSwitchPreference) preference).isChecked();
|
||||
if (checked) {
|
||||
mToggleServiceDividerSwitchPreference.setChecked(false);
|
||||
private boolean onPreferenceClick(boolean isChecked) {
|
||||
if (isChecked) {
|
||||
mToggleServiceSwitchPreference.setChecked(false);
|
||||
getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED,
|
||||
/* disableService */ false);
|
||||
if (!mShortcutPreference.isChecked()) {
|
||||
@@ -465,7 +552,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mToggleServiceDividerSwitchPreference.setChecked(true);
|
||||
mToggleServiceSwitchPreference.setChecked(true);
|
||||
getArguments().putBoolean(AccessibilitySettings.EXTRA_CHECKED,
|
||||
/* enableService */ true);
|
||||
showDialog(DialogEnums.DISABLE_WARNING_FROM_TOGGLE);
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.widget.ImageView;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
@@ -49,7 +50,9 @@ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceContro
|
||||
|
||||
private static final String CONTROL_AUTOCLICK_DELAY_SECURE =
|
||||
Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY;
|
||||
private static final String KEY_CUSTOM_DELAY_VALUE = "custom_delay_value";
|
||||
|
||||
@VisibleForTesting
|
||||
static final String KEY_CUSTOM_DELAY_VALUE = "custom_delay_value";
|
||||
|
||||
// Min allowed autoclick delay value.
|
||||
static final int MIN_AUTOCLICK_DELAY_MS = 200;
|
||||
@@ -59,7 +62,8 @@ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceContro
|
||||
|
||||
// Allowed autoclick delay values are discrete.
|
||||
// This is the difference between two allowed values.
|
||||
private static final int AUTOCLICK_DELAY_STEP = 100;
|
||||
@VisibleForTesting
|
||||
static final int AUTOCLICK_DELAY_STEP = 100;
|
||||
|
||||
private final SharedPreferences mSharedPreferences;
|
||||
private final ContentResolver mContentResolver;
|
||||
@@ -68,7 +72,8 @@ public class ToggleAutoclickCustomSeekbarController extends BasePreferenceContro
|
||||
private SeekBar mSeekBar;
|
||||
private TextView mDelayLabel;
|
||||
|
||||
private final SeekBar.OnSeekBarChangeListener mSeekBarChangeListener =
|
||||
@VisibleForTesting
|
||||
final SeekBar.OnSeekBarChangeListener mSeekBarChangeListener =
|
||||
new SeekBar.OnSeekBarChangeListener() {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
/**
|
||||
* Preference controller for accessibility autoclick footer.
|
||||
*/
|
||||
public class ToggleAutoclickFooterPreferenceController extends
|
||||
AccessibilityFooterPreferenceController {
|
||||
|
||||
public ToggleAutoclickFooterPreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLabelName() {
|
||||
return mContext.getString(R.string.accessibility_autoclick_preference_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getHelpResource() {
|
||||
return R.string.help_url_autoclick;
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import android.content.res.Resources;
|
||||
import android.provider.Settings;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.lifecycle.LifecycleObserver;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
@@ -44,16 +45,23 @@ import java.util.Map;
|
||||
public class ToggleAutoclickPreferenceController extends BasePreferenceController implements
|
||||
LifecycleObserver, RadioButtonPreference.OnClickListener, PreferenceControllerMixin {
|
||||
|
||||
private static final String CONTROL_AUTOCLICK_DELAY_SECURE =
|
||||
@VisibleForTesting
|
||||
static final String CONTROL_AUTOCLICK_DELAY_SECURE =
|
||||
Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY;
|
||||
private static final String KEY_AUTOCLICK_CUSTOM_SEEKBAR = "autoclick_custom_seekbar";
|
||||
|
||||
@VisibleForTesting
|
||||
static final String KEY_AUTOCLICK_CUSTOM_SEEKBAR = "autoclick_custom_seekbar";
|
||||
static final String KEY_DELAY_MODE = "delay_mode";
|
||||
|
||||
private static final int AUTOCLICK_OFF_MODE = 0;
|
||||
private static final int AUTOCLICK_CUSTOM_MODE = 2000;
|
||||
@VisibleForTesting
|
||||
static final int AUTOCLICK_OFF_MODE = 0;
|
||||
|
||||
@VisibleForTesting
|
||||
static final int AUTOCLICK_CUSTOM_MODE = 2000;
|
||||
|
||||
// Pair the preference key and autoclick mode value.
|
||||
private final Map<String, Integer> mAccessibilityAutoclickKeyToValueMap = new ArrayMap<>();
|
||||
@VisibleForTesting
|
||||
Map<String, Integer> mAccessibilityAutoclickKeyToValueMap = new ArrayMap<>();
|
||||
|
||||
private SharedPreferences mSharedPreferences;
|
||||
private final ContentResolver mContentResolver;
|
||||
@@ -70,13 +78,7 @@ public class ToggleAutoclickPreferenceController extends BasePreferenceControlle
|
||||
private int mCurrentUiAutoClickMode;
|
||||
|
||||
public ToggleAutoclickPreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
|
||||
mSharedPreferences = context.getSharedPreferences(context.getPackageName(), MODE_PRIVATE);
|
||||
mContentResolver = context.getContentResolver();
|
||||
mResources = context.getResources();
|
||||
|
||||
setAutoclickModeToKeyMap();
|
||||
this(context, /* lifecycle= */ null, preferenceKey);
|
||||
}
|
||||
|
||||
public ToggleAutoclickPreferenceController(Context context, Lifecycle lifecycle,
|
||||
|
||||
@@ -31,15 +31,15 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.widget.SettingsMainSwitchPreference;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Settings page for color inversion. */
|
||||
public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePreferenceFragment {
|
||||
public class ToggleColorInversionPreferenceFragment extends
|
||||
ToggleFeaturePreferenceFragment {
|
||||
|
||||
private static final String ENABLED = Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED;
|
||||
private final Handler mHandler = new Handler();
|
||||
@@ -64,25 +64,14 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere
|
||||
@Override
|
||||
protected void onRemoveSwitchPreferenceToggleSwitch() {
|
||||
super.onRemoveSwitchPreferenceToggleSwitch();
|
||||
mToggleServiceDividerSwitchPreference.setOnPreferenceClickListener(null);
|
||||
mToggleServiceSwitchPreference.setOnPreferenceClickListener(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateToggleServiceTitle(SwitchPreference switchPreference) {
|
||||
protected void updateToggleServiceTitle(SettingsMainSwitchPreference switchPreference) {
|
||||
switchPreference.setTitle(R.string.accessibility_display_inversion_switch_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInstallSwitchPreferenceToggleSwitch() {
|
||||
super.onInstallSwitchPreferenceToggleSwitch();
|
||||
updateSwitchBarToggleSwitch();
|
||||
mToggleServiceDividerSwitchPreference.setOnPreferenceClickListener((preference) -> {
|
||||
boolean checked = ((SwitchPreference) preference).isChecked();
|
||||
onPreferenceToggled(mPreferenceKey, checked);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -91,7 +80,7 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere
|
||||
mHtmlDescription = getText(R.string.accessibility_display_inversion_preference_subtitle);
|
||||
mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
|
||||
.authority(getPrefContext().getPackageName())
|
||||
.appendPath(String.valueOf(R.drawable.accessibility_color_inversion_banner))
|
||||
.appendPath(String.valueOf(R.raw.accessibility_color_inversion_banner))
|
||||
.build();
|
||||
final List<String> enableServiceFeatureKeys = new ArrayList<>(/* initialCapacity= */ 1);
|
||||
enableServiceFeatureKeys.add(ENABLED);
|
||||
@@ -122,23 +111,18 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere
|
||||
return R.string.help_url_color_inversion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettingsClicked(ShortcutPreference preference) {
|
||||
super.onSettingsClicked(preference);
|
||||
showDialog(DialogEnums.EDIT_SHORTCUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
int getUserShortcutTypes() {
|
||||
return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(),
|
||||
mComponentName);
|
||||
}
|
||||
|
||||
private void updateSwitchBarToggleSwitch() {
|
||||
@Override
|
||||
protected void updateSwitchBarToggleSwitch() {
|
||||
final boolean checked = Settings.Secure.getInt(getContentResolver(), ENABLED, OFF) == ON;
|
||||
if (mToggleServiceDividerSwitchPreference.isChecked() == checked) {
|
||||
if (mToggleServiceSwitchPreference.isChecked() == checked) {
|
||||
return;
|
||||
}
|
||||
mToggleServiceDividerSwitchPreference.setChecked(checked);
|
||||
mToggleServiceSwitchPreference.setChecked(checked);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,11 +33,10 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settings.widget.SettingsMainSwitchPreference;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
@@ -99,34 +98,19 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
updatePreferenceOrder();
|
||||
}
|
||||
|
||||
/** Customizes the order by preference key. */
|
||||
private List<String> getPreferenceOrderList() {
|
||||
List<String> lists = new ArrayList<>();
|
||||
protected List<String> getPreferenceOrderList() {
|
||||
final List<String> lists = new ArrayList<>();
|
||||
lists.add(KEY_PREVIEW);
|
||||
lists.add(KEY_USE_SERVICE_PREFERENCE);
|
||||
lists.add(KEY_CATEGORY_MODE);
|
||||
lists.add(KEY_GENERAL_CATEGORY);
|
||||
lists.add(KEY_INTRODUCTION_CATEGORY);
|
||||
lists.add(KEY_HTML_DESCRIPTION_PREFERENCE);
|
||||
return lists;
|
||||
}
|
||||
|
||||
private void updatePreferenceOrder() {
|
||||
List<String> lists = getPreferenceOrderList();
|
||||
final PreferenceScreen preferenceScreen = getPreferenceScreen();
|
||||
preferenceScreen.setOrderingAsAdded(false);
|
||||
|
||||
final int size = lists.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
final Preference preference = preferenceScreen.findPreference(lists.get(i));
|
||||
if (preference != null) {
|
||||
preference.setOrder(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@@ -175,29 +159,12 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe
|
||||
@Override
|
||||
protected void onRemoveSwitchPreferenceToggleSwitch() {
|
||||
super.onRemoveSwitchPreferenceToggleSwitch();
|
||||
mToggleServiceDividerSwitchPreference.setOnPreferenceClickListener(null);
|
||||
mToggleServiceSwitchPreference.setOnPreferenceClickListener(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateToggleServiceTitle(SwitchPreference switchPreference) {
|
||||
switchPreference.setTitle(R.string.accessibility_daltonizer_master_switch_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInstallSwitchPreferenceToggleSwitch() {
|
||||
super.onInstallSwitchPreferenceToggleSwitch();
|
||||
updateSwitchBarToggleSwitch();
|
||||
mToggleServiceDividerSwitchPreference.setOnPreferenceClickListener((preference) -> {
|
||||
boolean checked = ((SwitchPreference) preference).isChecked();
|
||||
onPreferenceToggled(mPreferenceKey, checked);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettingsClicked(ShortcutPreference preference) {
|
||||
super.onSettingsClicked(preference);
|
||||
showDialog(DialogEnums.EDIT_SHORTCUT);
|
||||
protected void updateToggleServiceTitle(SettingsMainSwitchPreference switchPreference) {
|
||||
switchPreference.setTitle(R.string.accessibility_daltonizer_primary_switch_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -206,12 +173,13 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe
|
||||
mComponentName);
|
||||
}
|
||||
|
||||
private void updateSwitchBarToggleSwitch() {
|
||||
@Override
|
||||
protected void updateSwitchBarToggleSwitch() {
|
||||
final boolean checked = Settings.Secure.getInt(getContentResolver(), ENABLED, OFF) == ON;
|
||||
if (mToggleServiceDividerSwitchPreference.isChecked() == checked) {
|
||||
if (mToggleServiceSwitchPreference.isChecked() == checked) {
|
||||
return;
|
||||
}
|
||||
mToggleServiceDividerSwitchPreference.setChecked(checked);
|
||||
mToggleServiceSwitchPreference.setChecked(checked);
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import static com.android.settings.accessibility.AccessibilityUtil.getScreenHeightPixels;
|
||||
import static com.android.settings.accessibility.AccessibilityUtil.getScreenWidthPixels;
|
||||
import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.settings.SettingsEnums;
|
||||
@@ -43,38 +42,40 @@ import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Switch;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
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.UserShortcutType;
|
||||
import com.android.settings.widget.SwitchBar;
|
||||
import com.android.settings.utils.LocaleUtils;
|
||||
import com.android.settings.widget.SettingsMainSwitchBar;
|
||||
import com.android.settings.widget.SettingsMainSwitchPreference;
|
||||
import com.android.settingslib.HelpUtils;
|
||||
import com.android.settingslib.accessibility.AccessibilityUtils;
|
||||
import com.android.settingslib.widget.FooterPreference;
|
||||
import com.android.settingslib.widget.IllustrationPreference;
|
||||
import com.android.settingslib.widget.OnMainSwitchChangeListener;
|
||||
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Base class for accessibility fragments with toggle, shortcut, some helper functions
|
||||
* and dialog management.
|
||||
*/
|
||||
public abstract class ToggleFeaturePreferenceFragment extends SettingsPreferenceFragment
|
||||
implements ShortcutPreference.OnClickCallback {
|
||||
implements ShortcutPreference.OnClickCallback, OnMainSwitchChangeListener {
|
||||
|
||||
protected DividerSwitchPreference mToggleServiceDividerSwitchPreference;
|
||||
protected SettingsMainSwitchPreference mToggleServiceSwitchPreference;
|
||||
protected ShortcutPreference mShortcutPreference;
|
||||
protected Preference mSettingsPreference;
|
||||
protected String mPreferenceKey;
|
||||
@@ -87,19 +88,24 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
protected Uri mImageUri;
|
||||
private CharSequence mDescription;
|
||||
protected CharSequence mHtmlDescription;
|
||||
// Used to restore the edit dialog status.
|
||||
protected int mUserShortcutTypesCache = UserShortcutType.EMPTY;
|
||||
|
||||
private static final String DRAWABLE_FOLDER = "drawable";
|
||||
protected static final String KEY_USE_SERVICE_PREFERENCE = "use_service";
|
||||
protected static final String KEY_GENERAL_CATEGORY = "general_categories";
|
||||
protected static final String KEY_INTRODUCTION_CATEGORY = "introduction_categories";
|
||||
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";
|
||||
private static final String EXTRA_SHORTCUT_TYPE = "shortcut_type";
|
||||
protected static final String KEY_SAVED_USER_SHORTCUT_TYPE = "shortcut_type";
|
||||
protected static final String KEY_ANIMATED_IMAGE = "animated_image";
|
||||
|
||||
private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
|
||||
private int mUserShortcutTypes = UserShortcutType.EMPTY;
|
||||
private SettingsContentObserver mSettingsContentObserver;
|
||||
|
||||
private CheckBox mSoftwareTypeCheckBox;
|
||||
private CheckBox mHardwareTypeCheckBox;
|
||||
private SettingsContentObserver mSettingsContentObserver;
|
||||
|
||||
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;
|
||||
|
||||
// For html description of accessibility service, must follow the rule, such as
|
||||
// <img src="R.drawable.fileName"/>, a11y settings will get the resources successfully.
|
||||
@@ -121,10 +127,17 @@ 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);
|
||||
}
|
||||
|
||||
setupDefaultShortcutIfNecessary(getPrefContext());
|
||||
final int resId = getPreferenceScreenResId();
|
||||
if (resId <= 0) {
|
||||
PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(
|
||||
final PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(
|
||||
getPrefContext());
|
||||
setPreferenceScreen(preferenceScreen);
|
||||
}
|
||||
@@ -144,6 +157,21 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
// Need to be called as early as possible. Protected variables will be assigned here.
|
||||
onProcessArguments(getArguments());
|
||||
|
||||
initAnimatedImagePreference();
|
||||
initToggleServiceSwitchPreference();
|
||||
initGeneralCategory();
|
||||
initShortcutPreference();
|
||||
initSettingsPreference();
|
||||
initHtmlTextPreference();
|
||||
initFooterPreference();
|
||||
|
||||
installActionBarToggleSwitch();
|
||||
|
||||
updateToggleServiceTitle(mToggleServiceSwitchPreference);
|
||||
|
||||
mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
|
||||
removeDialog(DialogEnums.EDIT_SHORTCUT);
|
||||
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
|
||||
@@ -156,91 +184,16 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
final SettingsActivity activity = (SettingsActivity) getActivity();
|
||||
final SwitchBar switchBar = activity.getSwitchBar();
|
||||
final SettingsMainSwitchBar switchBar = activity.getSwitchBar();
|
||||
switchBar.hide();
|
||||
|
||||
// Need to be called as early as possible. Protected variables will be assigned here.
|
||||
onProcessArguments(getArguments());
|
||||
|
||||
PreferenceScreen preferenceScreen = getPreferenceScreen();
|
||||
if (mImageUri != null) {
|
||||
final int screenHalfHeight = getScreenHeightPixels(getPrefContext()) / /* half */ 2;
|
||||
final AnimatedImagePreference animatedImagePreference = new AnimatedImagePreference(
|
||||
getPrefContext());
|
||||
animatedImagePreference.setImageUri(mImageUri);
|
||||
animatedImagePreference.setSelectable(false);
|
||||
animatedImagePreference.setMaxHeight(screenHalfHeight);
|
||||
preferenceScreen.addPreference(animatedImagePreference);
|
||||
}
|
||||
|
||||
mToggleServiceDividerSwitchPreference = new DividerSwitchPreference(getPrefContext());
|
||||
mToggleServiceDividerSwitchPreference.setKey(KEY_USE_SERVICE_PREFERENCE);
|
||||
if (getArguments().containsKey(AccessibilitySettings.EXTRA_CHECKED)) {
|
||||
final boolean enabled = getArguments().getBoolean(AccessibilitySettings.EXTRA_CHECKED);
|
||||
mToggleServiceDividerSwitchPreference.setChecked(enabled);
|
||||
}
|
||||
|
||||
preferenceScreen.addPreference(mToggleServiceDividerSwitchPreference);
|
||||
|
||||
updateToggleServiceTitle(mToggleServiceDividerSwitchPreference);
|
||||
|
||||
final PreferenceCategory groupCategory = new PreferenceCategory(getPrefContext());
|
||||
groupCategory.setKey(KEY_GENERAL_CATEGORY);
|
||||
groupCategory.setTitle(R.string.accessibility_screen_option);
|
||||
preferenceScreen.addPreference(groupCategory);
|
||||
|
||||
initShortcutPreference(savedInstanceState);
|
||||
groupCategory.addPreference(mShortcutPreference);
|
||||
|
||||
// Show the "Settings" menu as if it were a preference screen.
|
||||
if (mSettingsTitle != null && mSettingsIntent != null) {
|
||||
mSettingsPreference = new Preference(getPrefContext());
|
||||
mSettingsPreference.setTitle(mSettingsTitle);
|
||||
mSettingsPreference.setIconSpaceReserved(true);
|
||||
mSettingsPreference.setIntent(mSettingsIntent);
|
||||
}
|
||||
|
||||
// The downloaded app may not show Settings. The framework app has Settings.
|
||||
if (mSettingsPreference != null) {
|
||||
groupCategory.addPreference(mSettingsPreference);
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(mHtmlDescription)) {
|
||||
final PreferenceCategory introductionCategory = new PreferenceCategory(
|
||||
getPrefContext());
|
||||
final CharSequence title = getString(R.string.accessibility_introduction_title,
|
||||
mPackageName);
|
||||
introductionCategory.setKey(KEY_INTRODUCTION_CATEGORY);
|
||||
introductionCategory.setTitle(title);
|
||||
preferenceScreen.addPreference(introductionCategory);
|
||||
|
||||
final HtmlTextPreference htmlTextPreference = new HtmlTextPreference(getPrefContext());
|
||||
htmlTextPreference.setSummary(mHtmlDescription);
|
||||
htmlTextPreference.setImageGetter(mImageGetter);
|
||||
htmlTextPreference.setSelectable(false);
|
||||
introductionCategory.addPreference(htmlTextPreference);
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(mDescription)) {
|
||||
createFooterPreference(mDescription);
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(mHtmlDescription) && TextUtils.isEmpty(mDescription)) {
|
||||
final CharSequence defaultDescription = getText(
|
||||
R.string.accessibility_service_default_description);
|
||||
createFooterPreference(defaultDescription);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
installActionBarToggleSwitch();
|
||||
updatePreferenceOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
final AccessibilityManager am = getPrefContext().getSystemService(
|
||||
AccessibilityManager.class);
|
||||
am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
|
||||
@@ -260,7 +213,10 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
outState.putInt(EXTRA_SHORTCUT_TYPE, mUserShortcutTypesCache);
|
||||
final int value = getShortcutTypeCheckBoxValue();
|
||||
if (value != NOT_SET) {
|
||||
outState.putInt(KEY_SAVED_USER_SHORTCUT_TYPE, value);
|
||||
}
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
@@ -271,9 +227,12 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
case DialogEnums.EDIT_SHORTCUT:
|
||||
final CharSequence dialogTitle = getPrefContext().getString(
|
||||
R.string.accessibility_shortcut_title, mPackageName);
|
||||
dialog = AccessibilityEditDialogUtils.showEditShortcutDialog(
|
||||
getPrefContext(), dialogTitle, this::callOnAlertDialogCheckboxClicked);
|
||||
initializeDialogCheckBox(dialog);
|
||||
final int dialogType = WizardManagerHelper.isAnySetupWizard(getIntent())
|
||||
? DialogType.EDIT_SHORTCUT_GENERIC_SUW : DialogType.EDIT_SHORTCUT_GENERIC;
|
||||
dialog = AccessibilityDialogUtils.showEditShortcutDialog(
|
||||
getPrefContext(), dialogType, dialogTitle,
|
||||
this::callOnAlertDialogCheckboxClicked);
|
||||
setupEditShortcutDialog(dialog);
|
||||
return dialog;
|
||||
case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
|
||||
dialog = AccessibilityGestureNavigationTutorial
|
||||
@@ -298,92 +257,58 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
}
|
||||
}
|
||||
|
||||
/** Denotes the dialog emuns for show dialog */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
protected @interface DialogEnums {
|
||||
|
||||
/** OPEN: Settings > Accessibility > Any toggle service > Shortcut > Settings. */
|
||||
int EDIT_SHORTCUT = 1;
|
||||
|
||||
/** OPEN: Settings > Accessibility > Magnification > Shortcut > Settings. */
|
||||
int MAGNIFICATION_EDIT_SHORTCUT = 1001;
|
||||
|
||||
/**
|
||||
* OPEN: Settings > Accessibility > Downloaded toggle service > Toggle use service to
|
||||
* enable service.
|
||||
*/
|
||||
int ENABLE_WARNING_FROM_TOGGLE = 1002;
|
||||
|
||||
/** OPEN: Settings > Accessibility > Downloaded toggle service > Shortcut checkbox. */
|
||||
int ENABLE_WARNING_FROM_SHORTCUT = 1003;
|
||||
|
||||
/**
|
||||
* OPEN: Settings > Accessibility > Downloaded toggle service > Shortcut checkbox
|
||||
* toggle.
|
||||
*/
|
||||
int ENABLE_WARNING_FROM_SHORTCUT_TOGGLE = 1004;
|
||||
|
||||
/**
|
||||
* OPEN: Settings > Accessibility > Downloaded toggle service > Toggle use service to
|
||||
* disable service.
|
||||
*/
|
||||
int DISABLE_WARNING_FROM_TOGGLE = 1005;
|
||||
|
||||
/**
|
||||
* OPEN: Settings > Accessibility > Magnification > Toggle user service in button
|
||||
* navigation.
|
||||
*/
|
||||
int ACCESSIBILITY_BUTTON_TUTORIAL = 1006;
|
||||
|
||||
/**
|
||||
* OPEN: Settings > Accessibility > Magnification > Toggle user service in gesture
|
||||
* navigation.
|
||||
*/
|
||||
int GESTURE_NAVIGATION_TUTORIAL = 1007;
|
||||
|
||||
/**
|
||||
* OPEN: Settings > Accessibility > Downloaded toggle service > Toggle user service > Show
|
||||
* launch tutorial.
|
||||
*/
|
||||
int LAUNCH_ACCESSIBILITY_TUTORIAL = 1008;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.ACCESSIBILITY_SERVICE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHelpResource() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
removeActionBarToggleSwitch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwitchChanged(Switch switchView, boolean isChecked) {
|
||||
onPreferenceToggled(mPreferenceKey, isChecked);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shortcut type list which has been checked by user.
|
||||
*/
|
||||
abstract int getUserShortcutTypes();
|
||||
|
||||
protected void updateToggleServiceTitle(SwitchPreference switchPreference) {
|
||||
switchPreference.setTitle(R.string.accessibility_service_master_switch_title);
|
||||
protected void updateToggleServiceTitle(SettingsMainSwitchPreference switchPreference) {
|
||||
switchPreference.setTitle(R.string.accessibility_service_primary_switch_title);
|
||||
}
|
||||
|
||||
protected abstract void onPreferenceToggled(String preferenceKey, boolean enabled);
|
||||
|
||||
protected void onInstallSwitchPreferenceToggleSwitch() {
|
||||
// Implement this to set a checked listener.
|
||||
updateSwitchBarToggleSwitch();
|
||||
mToggleServiceSwitchPreference.addOnSwitchChangeListener(this);
|
||||
}
|
||||
|
||||
protected void onRemoveSwitchPreferenceToggleSwitch() {
|
||||
// Implement this to reset a checked listener.
|
||||
}
|
||||
|
||||
protected void updateSwitchBarToggleSwitch() {
|
||||
// Implement this to update the state of switch.
|
||||
}
|
||||
|
||||
private void installActionBarToggleSwitch() {
|
||||
onInstallSwitchPreferenceToggleSwitch();
|
||||
}
|
||||
|
||||
private void removeActionBarToggleSwitch() {
|
||||
mToggleServiceDividerSwitchPreference.setOnPreferenceClickListener(null);
|
||||
mToggleServiceSwitchPreference.setOnPreferenceClickListener(null);
|
||||
onRemoveSwitchPreferenceToggleSwitch();
|
||||
}
|
||||
|
||||
@@ -415,6 +340,31 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
}
|
||||
}
|
||||
|
||||
/** Customizes the order by preference key. */
|
||||
protected List<String> getPreferenceOrderList() {
|
||||
final List<String> lists = new ArrayList<>();
|
||||
lists.add(KEY_ANIMATED_IMAGE);
|
||||
lists.add(KEY_USE_SERVICE_PREFERENCE);
|
||||
lists.add(KEY_GENERAL_CATEGORY);
|
||||
lists.add(KEY_HTML_DESCRIPTION_PREFERENCE);
|
||||
return lists;
|
||||
}
|
||||
|
||||
private void updatePreferenceOrder() {
|
||||
final List<String> lists = getPreferenceOrderList();
|
||||
|
||||
final PreferenceScreen preferenceScreen = getPreferenceScreen();
|
||||
preferenceScreen.setOrderingAsAdded(false);
|
||||
|
||||
final int size = lists.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
final Preference preference = preferenceScreen.findPreference(lists.get(i));
|
||||
if (preference != null) {
|
||||
preference.setOrder(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Drawable getDrawableFromUri(Uri imageUri) {
|
||||
if (mImageGetterCacheView == null) {
|
||||
mImageGetterCacheView = new ImageView(getPrefContext());
|
||||
@@ -432,8 +382,8 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
mImageGetterCacheView.setImageURI(null);
|
||||
final int imageWidth = drawable.getIntrinsicWidth();
|
||||
final int imageHeight = drawable.getIntrinsicHeight();
|
||||
final int screenHalfHeight = getScreenHeightPixels(getPrefContext()) / /* half */ 2;
|
||||
if ((imageWidth > getScreenWidthPixels(getPrefContext()))
|
||||
final int screenHalfHeight = AccessibilityUtil.getScreenHeightPixels(getPrefContext()) / 2;
|
||||
if ((imageWidth > AccessibilityUtil.getScreenWidthPixels(getPrefContext()))
|
||||
|| (imageHeight > screenHalfHeight)) {
|
||||
return null;
|
||||
}
|
||||
@@ -444,60 +394,161 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
return drawable;
|
||||
}
|
||||
|
||||
static final class AccessibilityUserShortcutType {
|
||||
private static final char COMPONENT_NAME_SEPARATOR = ':';
|
||||
private static final TextUtils.SimpleStringSplitter sStringColonSplitter =
|
||||
new TextUtils.SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
|
||||
|
||||
private String mComponentName;
|
||||
private int mType;
|
||||
|
||||
AccessibilityUserShortcutType(String componentName, int type) {
|
||||
this.mComponentName = componentName;
|
||||
this.mType = type;
|
||||
private void initAnimatedImagePreference() {
|
||||
if (mImageUri == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AccessibilityUserShortcutType(String flattenedString) {
|
||||
sStringColonSplitter.setString(flattenedString);
|
||||
if (sStringColonSplitter.hasNext()) {
|
||||
this.mComponentName = sStringColonSplitter.next();
|
||||
this.mType = Integer.parseInt(sStringColonSplitter.next());
|
||||
}
|
||||
final IllustrationPreference illustrationPreference =
|
||||
new IllustrationPreference(getPrefContext());
|
||||
illustrationPreference.setImageUri(mImageUri);
|
||||
illustrationPreference.setSelectable(false);
|
||||
illustrationPreference.setKey(KEY_ANIMATED_IMAGE);
|
||||
|
||||
getPreferenceScreen().addPreference(illustrationPreference);
|
||||
}
|
||||
|
||||
private void initToggleServiceSwitchPreference() {
|
||||
mToggleServiceSwitchPreference = new SettingsMainSwitchPreference(getPrefContext());
|
||||
mToggleServiceSwitchPreference.setKey(KEY_USE_SERVICE_PREFERENCE);
|
||||
if (getArguments().containsKey(AccessibilitySettings.EXTRA_CHECKED)) {
|
||||
final boolean enabled = getArguments().getBoolean(AccessibilitySettings.EXTRA_CHECKED);
|
||||
mToggleServiceSwitchPreference.setChecked(enabled);
|
||||
}
|
||||
|
||||
String getComponentName() {
|
||||
return mComponentName;
|
||||
getPreferenceScreen().addPreference(mToggleServiceSwitchPreference);
|
||||
}
|
||||
|
||||
private void initGeneralCategory() {
|
||||
final PreferenceCategory generalCategory = new PreferenceCategory(getPrefContext());
|
||||
generalCategory.setKey(KEY_GENERAL_CATEGORY);
|
||||
generalCategory.setTitle(R.string.accessibility_screen_option);
|
||||
|
||||
getPreferenceScreen().addPreference(generalCategory);
|
||||
}
|
||||
|
||||
protected void initShortcutPreference() {
|
||||
// Initial the shortcut preference.
|
||||
mShortcutPreference = new ShortcutPreference(getPrefContext(), /* attrs= */ null);
|
||||
mShortcutPreference.setPersistent(false);
|
||||
mShortcutPreference.setKey(getShortcutPreferenceKey());
|
||||
mShortcutPreference.setOnClickCallback(this);
|
||||
|
||||
final CharSequence title = getString(R.string.accessibility_shortcut_title, mPackageName);
|
||||
mShortcutPreference.setTitle(title);
|
||||
|
||||
final PreferenceCategory generalCategory = findPreference(KEY_GENERAL_CATEGORY);
|
||||
generalCategory.addPreference(mShortcutPreference);
|
||||
}
|
||||
|
||||
protected void initSettingsPreference() {
|
||||
if (mSettingsTitle == null || mSettingsIntent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
void setComponentName(String componentName) {
|
||||
this.mComponentName = componentName;
|
||||
// Show the "Settings" menu as if it were a preference screen.
|
||||
mSettingsPreference = new Preference(getPrefContext());
|
||||
mSettingsPreference.setTitle(mSettingsTitle);
|
||||
mSettingsPreference.setIconSpaceReserved(false);
|
||||
mSettingsPreference.setIntent(mSettingsIntent);
|
||||
|
||||
final PreferenceCategory generalCategory = findPreference(KEY_GENERAL_CATEGORY);
|
||||
generalCategory.addPreference(mSettingsPreference);
|
||||
}
|
||||
|
||||
private void initHtmlTextPreference() {
|
||||
if (TextUtils.isEmpty(mHtmlDescription)) {
|
||||
return;
|
||||
}
|
||||
final PreferenceScreen screen = getPreferenceScreen();
|
||||
final CharSequence htmlDescription = Html.fromHtml(mHtmlDescription.toString(),
|
||||
Html.FROM_HTML_MODE_COMPACT, mImageGetter, /* tagHandler= */ null);
|
||||
final String iconContentDescription =
|
||||
getString(R.string.accessibility_introduction_title, mPackageName);
|
||||
|
||||
final AccessibilityFooterPreference htmlFooterPreference =
|
||||
new AccessibilityFooterPreference(screen.getContext());
|
||||
htmlFooterPreference.setKey(KEY_HTML_DESCRIPTION_PREFERENCE);
|
||||
htmlFooterPreference.setSummary(htmlDescription);
|
||||
htmlFooterPreference.setContentDescription(
|
||||
generateFooterContentDescription(htmlDescription));
|
||||
|
||||
// Only framework tools support help link
|
||||
if (getHelpResource() != 0) {
|
||||
htmlFooterPreference.setLearnMoreAction(view -> {
|
||||
final Intent helpIntent = HelpUtils.getHelpIntent(
|
||||
getContext(), getContext().getString(getHelpResource()),
|
||||
getContext().getClass().getName());
|
||||
view.startActivityForResult(helpIntent, 0);
|
||||
});
|
||||
|
||||
final String learnMoreContentDescription = getPrefContext().getString(
|
||||
R.string.footer_learn_more_content_description, mPackageName);
|
||||
htmlFooterPreference.setLearnMoreContentDescription(learnMoreContentDescription);
|
||||
htmlFooterPreference.setLinkEnabled(true);
|
||||
} else {
|
||||
htmlFooterPreference.setLinkEnabled(false);
|
||||
}
|
||||
screen.addPreference(htmlFooterPreference);
|
||||
}
|
||||
|
||||
private void initFooterPreference() {
|
||||
if (!TextUtils.isEmpty(mDescription)) {
|
||||
createFooterPreference(getPreferenceScreen(), mDescription,
|
||||
getString(R.string.accessibility_introduction_title, mPackageName));
|
||||
}
|
||||
|
||||
int getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
void setType(int type) {
|
||||
this.mType = type;
|
||||
}
|
||||
|
||||
String flattenToString() {
|
||||
final StringJoiner joiner = new StringJoiner(String.valueOf(COMPONENT_NAME_SEPARATOR));
|
||||
joiner.add(mComponentName);
|
||||
joiner.add(String.valueOf(mType));
|
||||
return joiner.toString();
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
private void setDialogTextAreaClickListener(View dialogView, CheckBox checkBox) {
|
||||
final View dialogTextArea = dialogView.findViewById(R.id.container);
|
||||
dialogTextArea.setOnClickListener(v -> {
|
||||
checkBox.toggle();
|
||||
updateUserShortcutType(/* saveChanges= */ false);
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates {@link AccessibilityFooterPreference} and append into {@link PreferenceScreen}
|
||||
*
|
||||
* @param screen The preference screen to add the footer preference
|
||||
* @param summary The summary of the preference summary.
|
||||
* @param iconContentDescription The content description of icon in the footer.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void createFooterPreference(PreferenceScreen screen, CharSequence summary,
|
||||
String iconContentDescription) {
|
||||
final AccessibilityFooterPreference footerPreference =
|
||||
new AccessibilityFooterPreference(screen.getContext());
|
||||
footerPreference.setSummary(summary);
|
||||
footerPreference.setContentDescription(
|
||||
generateFooterContentDescription(summary));
|
||||
|
||||
// Only framework tools support help link
|
||||
if (getHelpResource() != 0) {
|
||||
footerPreference.setLearnMoreAction(view -> {
|
||||
final Intent helpIntent = HelpUtils.getHelpIntent(
|
||||
getContext(), getContext().getString(getHelpResource()),
|
||||
getContext().getClass().getName());
|
||||
view.startActivityForResult(helpIntent, 0);
|
||||
});
|
||||
|
||||
final String learnMoreContentDescription = getPrefContext().getString(
|
||||
R.string.footer_learn_more_content_description, mPackageName);
|
||||
footerPreference.setLearnMoreContentDescription(learnMoreContentDescription);
|
||||
}
|
||||
screen.addPreference(footerPreference);
|
||||
}
|
||||
|
||||
private void initializeDialogCheckBox(Dialog dialog) {
|
||||
private CharSequence generateFooterContentDescription(CharSequence footerContent) {
|
||||
final StringBuffer sb = new StringBuffer();
|
||||
sb.append(getPrefContext().getString(
|
||||
R.string.accessibility_introduction_title, mPackageName))
|
||||
.append("\n\n")
|
||||
.append(footerContent);
|
||||
return sb;
|
||||
}
|
||||
@VisibleForTesting
|
||||
void setupEditShortcutDialog(Dialog dialog) {
|
||||
final View dialogSoftwareView = dialog.findViewById(R.id.software_shortcut);
|
||||
mSoftwareTypeCheckBox = dialogSoftwareView.findViewById(R.id.checkbox);
|
||||
setDialogTextAreaClickListener(dialogSoftwareView, mSoftwareTypeCheckBox);
|
||||
@@ -506,57 +557,58 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
mHardwareTypeCheckBox = dialogHardwareView.findViewById(R.id.checkbox);
|
||||
setDialogTextAreaClickListener(dialogHardwareView, mHardwareTypeCheckBox);
|
||||
|
||||
updateAlertDialogCheckState();
|
||||
updateEditShortcutDialogCheckBox();
|
||||
}
|
||||
|
||||
private void updateAlertDialogCheckState() {
|
||||
if (mUserShortcutTypesCache != UserShortcutType.EMPTY) {
|
||||
updateCheckStatus(mSoftwareTypeCheckBox, UserShortcutType.SOFTWARE);
|
||||
updateCheckStatus(mHardwareTypeCheckBox, UserShortcutType.HARDWARE);
|
||||
private void setDialogTextAreaClickListener(View dialogView, CheckBox checkBox) {
|
||||
final View dialogTextArea = dialogView.findViewById(R.id.container);
|
||||
dialogTextArea.setOnClickListener(v -> checkBox.toggle());
|
||||
}
|
||||
|
||||
private void updateEditShortcutDialogCheckBox() {
|
||||
// If it is during onConfigChanged process then restore the value, or get the saved value
|
||||
// when shortcutPreference is checked.
|
||||
int value = restoreOnConfigChangedValue();
|
||||
if (value == NOT_SET) {
|
||||
final int lastNonEmptyUserShortcutType = PreferredShortcuts.retrieveUserShortcutType(
|
||||
getPrefContext(), mComponentName.flattenToString(), UserShortcutType.SOFTWARE);
|
||||
value = mShortcutPreference.isChecked() ? lastNonEmptyUserShortcutType
|
||||
: UserShortcutType.EMPTY;
|
||||
}
|
||||
|
||||
mSoftwareTypeCheckBox.setChecked(
|
||||
hasShortcutType(value, UserShortcutType.SOFTWARE));
|
||||
mHardwareTypeCheckBox.setChecked(
|
||||
hasShortcutType(value, UserShortcutType.HARDWARE));
|
||||
}
|
||||
|
||||
private void updateCheckStatus(CheckBox checkBox, @UserShortcutType int type) {
|
||||
checkBox.setChecked((mUserShortcutTypesCache & type) == type);
|
||||
private int restoreOnConfigChangedValue() {
|
||||
final int savedValue = mSavedCheckBoxValue;
|
||||
mSavedCheckBoxValue = NOT_SET;
|
||||
return savedValue;
|
||||
}
|
||||
|
||||
private void updateUserShortcutType(boolean saveChanges) {
|
||||
mUserShortcutTypesCache = UserShortcutType.EMPTY;
|
||||
private boolean hasShortcutType(int value, @UserShortcutType int type) {
|
||||
return (value & type) == type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns accumulated {@link UserShortcutType} checkbox value or {@code NOT_SET} if checkboxes
|
||||
* did not exist.
|
||||
*/
|
||||
protected int getShortcutTypeCheckBoxValue() {
|
||||
if (mSoftwareTypeCheckBox == null || mHardwareTypeCheckBox == null) {
|
||||
return NOT_SET;
|
||||
}
|
||||
|
||||
int value = UserShortcutType.EMPTY;
|
||||
if (mSoftwareTypeCheckBox.isChecked()) {
|
||||
mUserShortcutTypesCache |= UserShortcutType.SOFTWARE;
|
||||
value |= UserShortcutType.SOFTWARE;
|
||||
}
|
||||
if (mHardwareTypeCheckBox.isChecked()) {
|
||||
mUserShortcutTypesCache |= UserShortcutType.HARDWARE;
|
||||
value |= UserShortcutType.HARDWARE;
|
||||
}
|
||||
|
||||
if (saveChanges) {
|
||||
final boolean isChanged = (mUserShortcutTypesCache != UserShortcutType.EMPTY);
|
||||
if (isChanged) {
|
||||
setUserShortcutType(getPrefContext(), mUserShortcutTypesCache);
|
||||
}
|
||||
mUserShortcutTypes = mUserShortcutTypesCache;
|
||||
}
|
||||
}
|
||||
|
||||
private void setUserShortcutType(Context context, int type) {
|
||||
if (mComponentName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<String> info = SharedPreferenceUtils.getUserShortcutTypes(context);
|
||||
final String componentName = mComponentName.flattenToString();
|
||||
if (info.isEmpty()) {
|
||||
info = new HashSet<>();
|
||||
} else {
|
||||
final Set<String> filtered = info.stream()
|
||||
.filter(str -> str.contains(componentName))
|
||||
.collect(Collectors.toSet());
|
||||
info.removeAll(filtered);
|
||||
}
|
||||
final AccessibilityUserShortcutType shortcut = new AccessibilityUserShortcutType(
|
||||
componentName, type);
|
||||
info.add(shortcut.flattenToString());
|
||||
SharedPreferenceUtils.setUserShortcutType(context, info);
|
||||
return value;
|
||||
}
|
||||
|
||||
protected CharSequence getShortcutTypeSummary(Context context) {
|
||||
@@ -568,20 +620,17 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
return context.getText(R.string.switch_off_text);
|
||||
}
|
||||
|
||||
final int shortcutTypes = getUserShortcutTypes(context, UserShortcutType.SOFTWARE);
|
||||
int resId = R.string.accessibility_shortcut_edit_summary_software;
|
||||
if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
|
||||
resId = AccessibilityUtil.isTouchExploreEnabled(context)
|
||||
? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
|
||||
: R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
|
||||
}
|
||||
final CharSequence softwareTitle = context.getText(resId);
|
||||
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(context,
|
||||
mComponentName.flattenToString(), UserShortcutType.SOFTWARE);
|
||||
|
||||
List<CharSequence> list = new ArrayList<>();
|
||||
if ((shortcutTypes & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
|
||||
final List<CharSequence> list = new ArrayList<>();
|
||||
final CharSequence softwareTitle = context.getText(
|
||||
R.string.accessibility_shortcut_edit_summary_software);
|
||||
|
||||
if (hasShortcutType(shortcutTypes, UserShortcutType.SOFTWARE)) {
|
||||
list.add(softwareTitle);
|
||||
}
|
||||
if ((shortcutTypes & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE) {
|
||||
if (hasShortcutType(shortcutTypes, UserShortcutType.HARDWARE)) {
|
||||
final CharSequence hardwareTitle = context.getText(
|
||||
R.string.accessibility_shortcut_hardware_keyword);
|
||||
list.add(hardwareTitle);
|
||||
@@ -591,50 +640,29 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
if (list.isEmpty()) {
|
||||
list.add(softwareTitle);
|
||||
}
|
||||
final String joinStrings = TextUtils.join(/* delimiter= */", ", list);
|
||||
|
||||
return CaseMap.toTitle().wholeString().noLowercase().apply(Locale.getDefault(), /* iter= */
|
||||
null, joinStrings);
|
||||
}
|
||||
|
||||
protected int getUserShortcutTypes(Context context, @UserShortcutType int defaultValue) {
|
||||
if (mComponentName == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
final Set<String> info = SharedPreferenceUtils.getUserShortcutTypes(context);
|
||||
final String componentName = mComponentName.flattenToString();
|
||||
final Set<String> filtered = info.stream()
|
||||
.filter(str -> str.contains(componentName))
|
||||
.collect(Collectors.toSet());
|
||||
if (filtered.isEmpty()) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
final String str = (String) filtered.toArray()[0];
|
||||
final AccessibilityUserShortcutType shortcut = new AccessibilityUserShortcutType(str);
|
||||
return shortcut.getType();
|
||||
null, LocaleUtils.getConcatenatedString(list));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be invoked when a button in the edit shortcut dialog is clicked.
|
||||
*
|
||||
* @param dialog The dialog that received the click
|
||||
* @param which The button that was clicked
|
||||
* @param which The button that was clicked
|
||||
*/
|
||||
protected void callOnAlertDialogCheckboxClicked(DialogInterface dialog, int which) {
|
||||
if (mComponentName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateUserShortcutType(/* saveChanges= */ true);
|
||||
AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), mUserShortcutTypes,
|
||||
mComponentName);
|
||||
AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), ~mUserShortcutTypes,
|
||||
mComponentName);
|
||||
mShortcutPreference.setChecked(mUserShortcutTypes != UserShortcutType.EMPTY);
|
||||
mShortcutPreference.setSummary(
|
||||
getShortcutTypeSummary(getPrefContext()));
|
||||
final int value = getShortcutTypeCheckBoxValue();
|
||||
|
||||
saveNonEmptyUserShortcutType(value);
|
||||
AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), value, mComponentName);
|
||||
AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), ~value, mComponentName);
|
||||
mShortcutPreference.setChecked(value != UserShortcutType.EMPTY);
|
||||
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
|
||||
}
|
||||
|
||||
protected void updateShortcutPreferenceData() {
|
||||
@@ -642,47 +670,29 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the user shortcut type from settings provider.
|
||||
mUserShortcutTypes = AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(),
|
||||
mComponentName);
|
||||
if (mUserShortcutTypes != UserShortcutType.EMPTY) {
|
||||
setUserShortcutType(getPrefContext(), mUserShortcutTypes);
|
||||
} else {
|
||||
// Get the user shortcut type from shared_prefs if cannot get from settings provider.
|
||||
mUserShortcutTypes = getUserShortcutTypes(getPrefContext(), UserShortcutType.SOFTWARE);
|
||||
final int shortcutTypes = AccessibilityUtil.getUserShortcutTypesFromSettings(
|
||||
getPrefContext(), mComponentName);
|
||||
if (shortcutTypes != UserShortcutType.EMPTY) {
|
||||
final PreferredShortcut shortcut = new PreferredShortcut(
|
||||
mComponentName.flattenToString(), shortcutTypes);
|
||||
PreferredShortcuts.saveUserShortcutType(getPrefContext(), shortcut);
|
||||
}
|
||||
}
|
||||
|
||||
private void initShortcutPreference(Bundle savedInstanceState) {
|
||||
// Restore the user shortcut type.
|
||||
if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_SHORTCUT_TYPE)) {
|
||||
mUserShortcutTypesCache = savedInstanceState.getInt(EXTRA_SHORTCUT_TYPE,
|
||||
UserShortcutType.EMPTY);
|
||||
}
|
||||
|
||||
// Initial the shortcut preference.
|
||||
mShortcutPreference = new ShortcutPreference(getPrefContext(), null);
|
||||
mShortcutPreference.setPersistent(false);
|
||||
mShortcutPreference.setKey(getShortcutPreferenceKey());
|
||||
mShortcutPreference.setOnClickCallback(this);
|
||||
|
||||
final CharSequence title = getString(R.string.accessibility_shortcut_title, mPackageName);
|
||||
mShortcutPreference.setTitle(title);
|
||||
}
|
||||
|
||||
protected void updateShortcutPreference() {
|
||||
if (mComponentName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int shortcutTypes = getUserShortcutTypes(getPrefContext(), UserShortcutType.SOFTWARE);
|
||||
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(getPrefContext(),
|
||||
mComponentName.flattenToString(), UserShortcutType.SOFTWARE);
|
||||
mShortcutPreference.setChecked(
|
||||
AccessibilityUtil.hasValuesInSettings(getPrefContext(), shortcutTypes,
|
||||
mComponentName));
|
||||
AccessibilityUtil.hasValuesInSettings(getPrefContext(), shortcutTypes,
|
||||
mComponentName));
|
||||
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
|
||||
}
|
||||
|
||||
private String getShortcutPreferenceKey() {
|
||||
protected String getShortcutPreferenceKey() {
|
||||
return KEY_SHORTCUT_PREFERENCE;
|
||||
}
|
||||
|
||||
@@ -692,7 +702,8 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
return;
|
||||
}
|
||||
|
||||
final int shortcutTypes = getUserShortcutTypes(getPrefContext(), UserShortcutType.SOFTWARE);
|
||||
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(getPrefContext(),
|
||||
mComponentName.flattenToString(), UserShortcutType.SOFTWARE);
|
||||
if (preference.isChecked()) {
|
||||
AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes,
|
||||
mComponentName);
|
||||
@@ -706,20 +717,11 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
|
||||
@Override
|
||||
public void onSettingsClicked(ShortcutPreference preference) {
|
||||
// Do not restore shortcut in shortcut chooser dialog when shortcutPreference is turned off.
|
||||
mUserShortcutTypesCache = mShortcutPreference.isChecked()
|
||||
? getUserShortcutTypes(getPrefContext(), UserShortcutType.SOFTWARE)
|
||||
: UserShortcutType.EMPTY;
|
||||
}
|
||||
|
||||
private void createFooterPreference(CharSequence title) {
|
||||
final PreferenceScreen preferenceScreen = getPreferenceScreen();
|
||||
preferenceScreen.addPreference(new FooterPreference.Builder(getActivity()).setTitle(
|
||||
title).build());
|
||||
showDialog(DialogEnums.EDIT_SHORTCUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups a configurable default if the setting has never been set.
|
||||
* Setups a configurable default if the setting has never been set.
|
||||
*/
|
||||
private static void setupDefaultShortcutIfNecessary(Context context) {
|
||||
final String targetKey = Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
|
||||
@@ -745,4 +747,15 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
|
||||
shortcutName.flattenToString());
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void saveNonEmptyUserShortcutType(int type) {
|
||||
if (type == UserShortcutType.EMPTY) {
|
||||
return;
|
||||
}
|
||||
|
||||
final PreferredShortcut shortcut = new PreferredShortcut(
|
||||
mComponentName.flattenToString(), type);
|
||||
PreferredShortcuts.saveUserShortcutType(getPrefContext(), shortcut);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.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;
|
||||
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.search.BaseSearchIndexProvider;
|
||||
import com.android.settings.widget.SeekBarPreference;
|
||||
import com.android.settings.widget.SettingsMainSwitchPreference;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Settings for reducing brightness. */
|
||||
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
|
||||
public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePreferenceFragment {
|
||||
|
||||
private static final String REDUCE_BRIGHT_COLORS_ACTIVATED_KEY =
|
||||
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED;
|
||||
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;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
|
||||
mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
|
||||
.authority(getPrefContext().getPackageName())
|
||||
.appendPath(String.valueOf(R.raw.extra_dim_banner))
|
||||
.build();
|
||||
mComponentName = AccessibilityShortcutController.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);
|
||||
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
|
||||
mToggleServiceSwitchPreference.setTitle(R.string.reduce_bright_colors_switch_title);
|
||||
updateGeneralCategoryOrder();
|
||||
return view;
|
||||
}
|
||||
|
||||
private void updateGeneralCategoryOrder() {
|
||||
final PreferenceCategory generalCategory = findPreference(KEY_GENERAL_CATEGORY);
|
||||
final SeekBarPreference intensity = findPreference(KEY_INTENSITY);
|
||||
getPreferenceScreen().removePreference(intensity);
|
||||
intensity.setOrder(mShortcutPreference.getOrder() - 2);
|
||||
generalCategory.addPreference(intensity);
|
||||
final SwitchPreference persist = findPreference(KEY_PERSIST);
|
||||
getPreferenceScreen().removePreference(persist);
|
||||
persist.setOrder(mShortcutPreference.getOrder() - 1);
|
||||
generalCategory.addPreference(persist);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
updateSwitchBarToggleSwitch();
|
||||
mSettingsContentObserver.register(getContentResolver());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
mSettingsContentObserver.unregister(getContentResolver());
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.REDUCE_BRIGHT_COLORS_SETTINGS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHelpResource() {
|
||||
// TODO(170973645): Link to help support page
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.reduce_bright_colors_settings;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
|
||||
AccessibilityStatsLogUtils.logAccessibilityServiceEnabled(mComponentName, enabled);
|
||||
mColorDisplayManager.setReduceBrightColorsActivated(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRemoveSwitchPreferenceToggleSwitch() {
|
||||
super.onRemoveSwitchPreferenceToggleSwitch();
|
||||
mToggleServiceSwitchPreference.setOnPreferenceClickListener(
|
||||
/* onPreferenceClickListener= */ null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateToggleServiceTitle(SettingsMainSwitchPreference switchPreference) {
|
||||
switchPreference.setTitle(R.string.reduce_bright_colors_preference_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
int getUserShortcutTypes() {
|
||||
return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(),
|
||||
mComponentName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateSwitchBarToggleSwitch() {
|
||||
final boolean checked = mColorDisplayManager.isReduceBrightColorsActivated();
|
||||
mRbcIntensityPreferenceController.updateState(getPreferenceScreen()
|
||||
.findPreference(KEY_INTENSITY));
|
||||
mRbcPersistencePreferenceController.updateState(getPreferenceScreen()
|
||||
.findPreference(KEY_PERSIST));
|
||||
if (mToggleServiceSwitchPreference.isChecked() != checked) {
|
||||
mToggleServiceSwitchPreference.setChecked(checked);
|
||||
}
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider(R.xml.reduce_bright_colors_settings) {
|
||||
@Override
|
||||
protected boolean isPageSearchEnabled(Context context) {
|
||||
return ColorDisplayManager.isReduceBrightColorsAvailable(context);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.settings.accessibility;
|
||||
|
||||
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
|
||||
import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
|
||||
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
|
||||
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
|
||||
|
||||
@@ -38,41 +39,44 @@ import android.view.accessibility.AccessibilityManager.TouchExplorationStateChan
|
||||
import android.widget.CheckBox;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
|
||||
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.UserShortcutType;
|
||||
import com.android.settings.utils.LocaleUtils;
|
||||
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Fragment that shows the actual UI for providing basic magnification accessibility service setup
|
||||
* and does not have toggle bar to turn on service to use.
|
||||
*/
|
||||
public class ToggleScreenMagnificationPreferenceFragment extends
|
||||
ToggleFeaturePreferenceFragment {
|
||||
|
||||
private static final String EXTRA_SHORTCUT_TYPE = "shortcut_type";
|
||||
private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
|
||||
ToggleFeaturePreferenceFragment implements
|
||||
MagnificationModePreferenceController.DialogHelper {
|
||||
// TODO(b/147021230): Move duplicated functions with android/internal/accessibility into util.
|
||||
private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
|
||||
private int mUserShortcutType = UserShortcutType.EMPTY;
|
||||
|
||||
private CheckBox mSoftwareTypeCheckBox;
|
||||
private CheckBox mHardwareTypeCheckBox;
|
||||
private CheckBox mTripleTapTypeCheckBox;
|
||||
|
||||
// TODO(b/147021230): Will move common functions and variables to
|
||||
// android/internal/accessibility folder. For now, magnification need to be treated
|
||||
// individually.
|
||||
private static final char COMPONENT_NAME_SEPARATOR = ':';
|
||||
private static final TextUtils.SimpleStringSplitter sStringColonSplitter =
|
||||
new TextUtils.SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
|
||||
|
||||
private MagnificationModePreferenceController mModePreferenceController;
|
||||
private DialogCreatable mDialogDelegate;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -85,7 +89,7 @@ public class ToggleScreenMagnificationPreferenceFragment extends
|
||||
mPackageName = getString(R.string.accessibility_screen_magnification_title);
|
||||
mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
|
||||
.authority(getPrefContext().getPackageName())
|
||||
.appendPath(String.valueOf(R.drawable.accessibility_magnification_banner))
|
||||
.appendPath(String.valueOf(R.raw.accessibility_magnification_banner))
|
||||
.build();
|
||||
mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
|
||||
removeDialog(DialogEnums.EDIT_SHORTCUT);
|
||||
@@ -94,19 +98,6 @@ public class ToggleScreenMagnificationPreferenceFragment extends
|
||||
return super.onCreateView(inflater, container, savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
initShortcutPreference();
|
||||
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
outState.putInt(EXTRA_SHORTCUT_TYPE, mUserShortcutTypesCache);
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@@ -114,9 +105,6 @@ public class ToggleScreenMagnificationPreferenceFragment extends
|
||||
final AccessibilityManager am = getPrefContext().getSystemService(
|
||||
AccessibilityManager.class);
|
||||
am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
|
||||
|
||||
updateShortcutPreferenceData();
|
||||
updateShortcutPreference();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -130,6 +118,12 @@ public class ToggleScreenMagnificationPreferenceFragment extends
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(int dialogId) {
|
||||
if (mDialogDelegate != null) {
|
||||
final Dialog dialog = mDialogDelegate.onCreateDialog(dialogId);
|
||||
if (dialog != null) {
|
||||
return dialog;
|
||||
}
|
||||
}
|
||||
final AlertDialog dialog;
|
||||
switch (dialogId) {
|
||||
case DialogEnums.GESTURE_NAVIGATION_TUTORIAL:
|
||||
@@ -138,25 +132,71 @@ public class ToggleScreenMagnificationPreferenceFragment extends
|
||||
case DialogEnums.MAGNIFICATION_EDIT_SHORTCUT:
|
||||
final CharSequence dialogTitle = getPrefContext().getString(
|
||||
R.string.accessibility_shortcut_title, mPackageName);
|
||||
dialog = AccessibilityEditDialogUtils.showMagnificationEditShortcutDialog(
|
||||
getPrefContext(), dialogTitle,
|
||||
this::callOnAlertDialogCheckboxClicked);
|
||||
initializeDialogCheckBox(dialog);
|
||||
final int dialogType = WizardManagerHelper.isAnySetupWizard(getIntent())
|
||||
? DialogType.EDIT_SHORTCUT_MAGNIFICATION_SUW
|
||||
: DialogType.EDIT_SHORTCUT_MAGNIFICATION;
|
||||
dialog = AccessibilityDialogUtils.showEditShortcutDialog(getPrefContext(),
|
||||
dialogType, dialogTitle, this::callOnAlertDialogCheckboxClicked);
|
||||
setupMagnificationEditShortcutDialog(dialog);
|
||||
return dialog;
|
||||
default:
|
||||
return super.onCreateDialog(dialogId);
|
||||
}
|
||||
}
|
||||
|
||||
private void setDialogTextAreaClickListener(View dialogView, CheckBox checkBox) {
|
||||
final View dialogTextArea = dialogView.findViewById(R.id.container);
|
||||
dialogTextArea.setOnClickListener(v -> {
|
||||
checkBox.toggle();
|
||||
updateUserShortcutType(/* saveChanges= */ false);
|
||||
});
|
||||
@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)) {
|
||||
return;
|
||||
}
|
||||
mSettingsPreference = new Preference(getPrefContext());
|
||||
mSettingsPreference.setTitle(R.string.accessibility_magnification_mode_title);
|
||||
mSettingsPreference.setKey(MagnificationModePreferenceController.PREF_KEY);
|
||||
mSettingsPreference.setPersistent(false);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
private void initializeDialogCheckBox(AlertDialog dialog) {
|
||||
@Override
|
||||
public void showDialog(int dialogId) {
|
||||
super.showDialog(dialogId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDialogDelegate(DialogCreatable delegate) {
|
||||
mDialogDelegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getShortcutTypeCheckBoxValue() {
|
||||
if (mSoftwareTypeCheckBox == null || mHardwareTypeCheckBox == null) {
|
||||
return NOT_SET;
|
||||
}
|
||||
|
||||
int value = UserShortcutType.EMPTY;
|
||||
if (mSoftwareTypeCheckBox.isChecked()) {
|
||||
value |= UserShortcutType.SOFTWARE;
|
||||
}
|
||||
if (mHardwareTypeCheckBox.isChecked()) {
|
||||
value |= UserShortcutType.HARDWARE;
|
||||
}
|
||||
if (mTripleTapTypeCheckBox.isChecked()) {
|
||||
value |= UserShortcutType.TRIPLETAP;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setupMagnificationEditShortcutDialog(AlertDialog dialog) {
|
||||
final View dialogSoftwareView = dialog.findViewById(R.id.software_shortcut);
|
||||
mSoftwareTypeCheckBox = dialogSoftwareView.findViewById(R.id.checkbox);
|
||||
setDialogTextAreaClickListener(dialogSoftwareView, mSoftwareTypeCheckBox);
|
||||
@@ -170,67 +210,46 @@ public class ToggleScreenMagnificationPreferenceFragment extends
|
||||
setDialogTextAreaClickListener(dialogTripleTapView, mTripleTapTypeCheckBox);
|
||||
|
||||
final View advancedView = dialog.findViewById(R.id.advanced_shortcut);
|
||||
updateAlertDialogCheckState();
|
||||
|
||||
// Window magnification mode doesn't support advancedView.
|
||||
if (isWindowMagnification(getPrefContext())) {
|
||||
advancedView.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
// Shows the triple tap checkbox directly if clicked.
|
||||
if (mTripleTapTypeCheckBox.isChecked()) {
|
||||
advancedView.setVisibility(View.GONE);
|
||||
dialogTripleTapView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
updateMagnificationEditShortcutDialogCheckBox();
|
||||
}
|
||||
|
||||
private void updateAlertDialogCheckState() {
|
||||
if (mUserShortcutTypesCache != UserShortcutType.EMPTY) {
|
||||
updateCheckStatus(mSoftwareTypeCheckBox, UserShortcutType.SOFTWARE);
|
||||
updateCheckStatus(mHardwareTypeCheckBox, UserShortcutType.HARDWARE);
|
||||
updateCheckStatus(mTripleTapTypeCheckBox, UserShortcutType.TRIPLETAP);
|
||||
}
|
||||
private void setDialogTextAreaClickListener(View dialogView, CheckBox checkBox) {
|
||||
final View dialogTextArea = dialogView.findViewById(R.id.container);
|
||||
dialogTextArea.setOnClickListener(v -> checkBox.toggle());
|
||||
}
|
||||
|
||||
private void updateCheckStatus(CheckBox checkBox, @UserShortcutType int type) {
|
||||
checkBox.setChecked((mUserShortcutTypesCache & type) == type);
|
||||
private void updateMagnificationEditShortcutDialogCheckBox() {
|
||||
// If it is during onConfigChanged process then restore the value, or get the saved value
|
||||
// when shortcutPreference is checked.
|
||||
int value = restoreOnConfigChangedValue();
|
||||
if (value == NOT_SET) {
|
||||
final int lastNonEmptyUserShortcutType = PreferredShortcuts.retrieveUserShortcutType(
|
||||
getPrefContext(), MAGNIFICATION_CONTROLLER_NAME, UserShortcutType.SOFTWARE);
|
||||
value = mShortcutPreference.isChecked() ? lastNonEmptyUserShortcutType
|
||||
: UserShortcutType.EMPTY;
|
||||
}
|
||||
|
||||
mSoftwareTypeCheckBox.setChecked(
|
||||
hasShortcutType(value, UserShortcutType.SOFTWARE));
|
||||
mHardwareTypeCheckBox.setChecked(
|
||||
hasShortcutType(value, UserShortcutType.HARDWARE));
|
||||
mTripleTapTypeCheckBox.setChecked(
|
||||
hasShortcutType(value, UserShortcutType.TRIPLETAP));
|
||||
}
|
||||
|
||||
private void updateUserShortcutType(boolean saveChanges) {
|
||||
mUserShortcutTypesCache = UserShortcutType.EMPTY;
|
||||
if (mSoftwareTypeCheckBox.isChecked()) {
|
||||
mUserShortcutTypesCache |= UserShortcutType.SOFTWARE;
|
||||
}
|
||||
if (mHardwareTypeCheckBox.isChecked()) {
|
||||
mUserShortcutTypesCache |= UserShortcutType.HARDWARE;
|
||||
}
|
||||
if (mTripleTapTypeCheckBox.isChecked()) {
|
||||
mUserShortcutTypesCache |= UserShortcutType.TRIPLETAP;
|
||||
}
|
||||
|
||||
if (saveChanges) {
|
||||
final boolean isChanged = (mUserShortcutTypesCache != UserShortcutType.EMPTY);
|
||||
if (isChanged) {
|
||||
setUserShortcutType(getPrefContext(), mUserShortcutTypesCache);
|
||||
}
|
||||
mUserShortcutType = mUserShortcutTypesCache;
|
||||
}
|
||||
private int restoreOnConfigChangedValue() {
|
||||
final int savedValue = mSavedCheckBoxValue;
|
||||
mSavedCheckBoxValue = NOT_SET;
|
||||
return savedValue;
|
||||
}
|
||||
|
||||
private void setUserShortcutType(Context context, int type) {
|
||||
Set<String> info = SharedPreferenceUtils.getUserShortcutTypes(context);
|
||||
if (info.isEmpty()) {
|
||||
info = new HashSet<>();
|
||||
} else {
|
||||
final Set<String> filtered = info.stream().filter(
|
||||
str -> str.contains(MAGNIFICATION_CONTROLLER_NAME)).collect(
|
||||
Collectors.toSet());
|
||||
info.removeAll(filtered);
|
||||
}
|
||||
final AccessibilityUserShortcutType shortcut = new AccessibilityUserShortcutType(
|
||||
MAGNIFICATION_CONTROLLER_NAME, type);
|
||||
info.add(shortcut.flattenToString());
|
||||
SharedPreferenceUtils.setUserShortcutType(context, info);
|
||||
private boolean hasShortcutType(int value, @UserShortcutType int type) {
|
||||
return (value & type) == type;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -239,26 +258,23 @@ public class ToggleScreenMagnificationPreferenceFragment extends
|
||||
return context.getText(R.string.switch_off_text);
|
||||
}
|
||||
|
||||
final int shortcutType = getUserShortcutTypes(context, UserShortcutType.EMPTY);
|
||||
int resId = R.string.accessibility_shortcut_edit_summary_software;
|
||||
if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
|
||||
resId = AccessibilityUtil.isTouchExploreEnabled(context)
|
||||
? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
|
||||
: R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
|
||||
}
|
||||
final CharSequence softwareTitle = context.getText(resId);
|
||||
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(context,
|
||||
MAGNIFICATION_CONTROLLER_NAME, UserShortcutType.SOFTWARE);
|
||||
|
||||
List<CharSequence> list = new ArrayList<>();
|
||||
if ((shortcutType & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
|
||||
final List<CharSequence> list = new ArrayList<>();
|
||||
final CharSequence softwareTitle = context.getText(
|
||||
R.string.accessibility_shortcut_edit_summary_software);
|
||||
|
||||
if (hasShortcutType(shortcutTypes, UserShortcutType.SOFTWARE)) {
|
||||
list.add(softwareTitle);
|
||||
}
|
||||
if ((shortcutType & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE) {
|
||||
if (hasShortcutType(shortcutTypes, UserShortcutType.HARDWARE)) {
|
||||
final CharSequence hardwareTitle = context.getText(
|
||||
R.string.accessibility_shortcut_hardware_keyword);
|
||||
list.add(hardwareTitle);
|
||||
}
|
||||
|
||||
if ((shortcutType & UserShortcutType.TRIPLETAP) == UserShortcutType.TRIPLETAP) {
|
||||
if (hasShortcutType(shortcutTypes, UserShortcutType.TRIPLETAP)) {
|
||||
final CharSequence tripleTapTitle = context.getText(
|
||||
R.string.accessibility_shortcut_triple_tap_keyword);
|
||||
list.add(tripleTapTitle);
|
||||
@@ -268,37 +284,28 @@ public class ToggleScreenMagnificationPreferenceFragment extends
|
||||
if (list.isEmpty()) {
|
||||
list.add(softwareTitle);
|
||||
}
|
||||
final String joinStrings = TextUtils.join(/* delimiter= */", ", list);
|
||||
|
||||
return CaseMap.toTitle().wholeString().noLowercase().apply(Locale.getDefault(), /* iter= */
|
||||
null, joinStrings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getUserShortcutTypes(Context context, @UserShortcutType int defaultValue) {
|
||||
final Set<String> info = SharedPreferenceUtils.getUserShortcutTypes(context);
|
||||
final Set<String> filtered = info.stream().filter(
|
||||
str -> str.contains(MAGNIFICATION_CONTROLLER_NAME)).collect(
|
||||
Collectors.toSet());
|
||||
if (filtered.isEmpty()) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
final String str = (String) filtered.toArray()[0];
|
||||
final AccessibilityUserShortcutType shortcut = new AccessibilityUserShortcutType(str);
|
||||
return shortcut.getType();
|
||||
null, LocaleUtils.getConcatenatedString(list));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void callOnAlertDialogCheckboxClicked(DialogInterface dialog, int which) {
|
||||
updateUserShortcutType(/* saveChanges= */ true);
|
||||
optInAllMagnificationValuesToSettings(getPrefContext(), mUserShortcutType);
|
||||
optOutAllMagnificationValuesFromSettings(getPrefContext(), ~mUserShortcutType);
|
||||
mShortcutPreference.setChecked(mUserShortcutType != UserShortcutType.EMPTY);
|
||||
final int value = getShortcutTypeCheckBoxValue();
|
||||
|
||||
saveNonEmptyUserShortcutType(value);
|
||||
optInAllMagnificationValuesToSettings(getPrefContext(), value);
|
||||
optOutAllMagnificationValuesFromSettings(getPrefContext(), ~value);
|
||||
mShortcutPreference.setChecked(value != UserShortcutType.EMPTY);
|
||||
mShortcutPreference.setSummary(
|
||||
getShortcutTypeSummary(getPrefContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHelpResource() {
|
||||
return R.string.help_url_magnification;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
// TODO: Distinguish between magnification modes
|
||||
@@ -307,6 +314,13 @@ public class ToggleScreenMagnificationPreferenceFragment extends
|
||||
|
||||
@Override
|
||||
public int getDialogMetricsCategory(int dialogId) {
|
||||
if (mDialogDelegate != null) {
|
||||
final int category = mDialogDelegate.getDialogMetricsCategory(dialogId);
|
||||
if (category != 0) {
|
||||
return category;
|
||||
}
|
||||
}
|
||||
|
||||
switch (dialogId) {
|
||||
case DialogEnums.GESTURE_NAVIGATION_TUTORIAL:
|
||||
return SettingsEnums.DIALOG_TOGGLE_SCREEN_MAGNIFICATION_GESTURE_NAVIGATION;
|
||||
@@ -336,13 +350,13 @@ public class ToggleScreenMagnificationPreferenceFragment extends
|
||||
|
||||
@Override
|
||||
protected void onInstallSwitchPreferenceToggleSwitch() {
|
||||
super.onInstallSwitchPreferenceToggleSwitch();
|
||||
mToggleServiceDividerSwitchPreference.setVisible(false);
|
||||
mToggleServiceSwitchPreference.setVisible(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToggleClicked(ShortcutPreference preference) {
|
||||
final int shortcutTypes = getUserShortcutTypes(getPrefContext(), UserShortcutType.SOFTWARE);
|
||||
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(getPrefContext(),
|
||||
MAGNIFICATION_CONTROLLER_NAME, UserShortcutType.SOFTWARE);
|
||||
if (preference.isChecked()) {
|
||||
optInAllMagnificationValuesToSettings(getPrefContext(), shortcutTypes);
|
||||
showDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL);
|
||||
@@ -354,44 +368,54 @@ public class ToggleScreenMagnificationPreferenceFragment extends
|
||||
|
||||
@Override
|
||||
public void onSettingsClicked(ShortcutPreference preference) {
|
||||
// Do not restore shortcut in shortcut chooser dialog when shortcutPreference is turned off.
|
||||
mUserShortcutTypesCache = mShortcutPreference.isChecked()
|
||||
? getUserShortcutTypes(getPrefContext(), UserShortcutType.SOFTWARE)
|
||||
: UserShortcutType.EMPTY;
|
||||
showDialog(DialogEnums.MAGNIFICATION_EDIT_SHORTCUT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateShortcutPreferenceData() {
|
||||
// Get the user shortcut type from settings provider.
|
||||
mUserShortcutType = getUserShortcutTypeFromSettings(getPrefContext());
|
||||
if (mUserShortcutType != UserShortcutType.EMPTY) {
|
||||
setUserShortcutType(getPrefContext(), mUserShortcutType);
|
||||
} else {
|
||||
// Get the user shortcut type from shared_prefs if cannot get from settings provider.
|
||||
mUserShortcutType = getUserShortcutTypes(getPrefContext(), UserShortcutType.SOFTWARE);
|
||||
final int shortcutTypes = getUserShortcutTypeFromSettings(getPrefContext());
|
||||
if (shortcutTypes != UserShortcutType.EMPTY) {
|
||||
final PreferredShortcut shortcut = new PreferredShortcut(
|
||||
MAGNIFICATION_CONTROLLER_NAME, shortcutTypes);
|
||||
PreferredShortcuts.saveUserShortcutType(getPrefContext(), shortcut);
|
||||
}
|
||||
}
|
||||
|
||||
private void initShortcutPreference() {
|
||||
@Override
|
||||
protected void initShortcutPreference() {
|
||||
mShortcutPreference = new ShortcutPreference(getPrefContext(), null);
|
||||
mShortcutPreference.setPersistent(false);
|
||||
mShortcutPreference.setKey(KEY_SHORTCUT_PREFERENCE);
|
||||
mShortcutPreference.setKey(getShortcutPreferenceKey());
|
||||
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
|
||||
mShortcutPreference.setOnClickCallback(this);
|
||||
|
||||
final CharSequence title = getString(R.string.accessibility_shortcut_title, mPackageName);
|
||||
mShortcutPreference.setTitle(title);
|
||||
|
||||
final PreferenceCategory generalCategory = findPreference(KEY_GENERAL_CATEGORY);
|
||||
generalCategory.addPreference(mShortcutPreference);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateShortcutPreference() {
|
||||
final int shortcutTypes = getUserShortcutTypes(getPrefContext(), UserShortcutType.SOFTWARE);
|
||||
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(getPrefContext(),
|
||||
MAGNIFICATION_CONTROLLER_NAME, UserShortcutType.SOFTWARE);
|
||||
mShortcutPreference.setChecked(
|
||||
hasMagnificationValuesInSettings(getPrefContext(), shortcutTypes));
|
||||
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void saveNonEmptyUserShortcutType(int type) {
|
||||
if (type == UserShortcutType.EMPTY) {
|
||||
return;
|
||||
}
|
||||
|
||||
final PreferredShortcut shortcut = new PreferredShortcut(
|
||||
MAGNIFICATION_CONTROLLER_NAME, type);
|
||||
PreferredShortcuts.saveUserShortcutType(getPrefContext(), shortcut);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static void optInAllMagnificationValuesToSettings(Context context, int shortcutTypes) {
|
||||
if ((shortcutTypes & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.settings.accessibility;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
public class ToggleScreenMagnificationPreferenceFragmentForSetupWizard
|
||||
extends ToggleScreenMagnificationPreferenceFragment {
|
||||
@@ -32,14 +33,28 @@ public class ToggleScreenMagnificationPreferenceFragmentForSetupWizard
|
||||
// Log the final choice in value if it's different from the previous value.
|
||||
Bundle args = getArguments();
|
||||
if ((args != null) && args.containsKey(AccessibilitySettings.EXTRA_CHECKED)) {
|
||||
if (mToggleServiceDividerSwitchPreference.isChecked() != args.getBoolean(
|
||||
if (mToggleServiceSwitchPreference.isChecked() != args.getBoolean(
|
||||
AccessibilitySettings.EXTRA_CHECKED)) {
|
||||
// TODO: Distinguish between magnification modes
|
||||
mMetricsFeatureProvider.action(getContext(),
|
||||
SettingsEnums.SUW_ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFICATION,
|
||||
mToggleServiceDividerSwitchPreference.isChecked());
|
||||
mToggleServiceSwitchPreference.isChecked());
|
||||
}
|
||||
}
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHelpResource() {
|
||||
// Hides help center in action bar and footer bar in SuW
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
// Hide the setting from the vision settings.
|
||||
mSettingsPreference.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ public class ToggleScreenReaderPreferenceFragmentForSetupWizard
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
mToggleSwitchWasInitiallyChecked = mToggleServiceDividerSwitchPreference.isChecked();
|
||||
mToggleSwitchWasInitiallyChecked = mToggleServiceSwitchPreference.isChecked();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -39,10 +39,10 @@ public class ToggleScreenReaderPreferenceFragmentForSetupWizard
|
||||
@Override
|
||||
public void onStop() {
|
||||
// Log the final choice in value if it's different from the previous value.
|
||||
if (mToggleServiceDividerSwitchPreference.isChecked() != mToggleSwitchWasInitiallyChecked) {
|
||||
if (mToggleServiceSwitchPreference.isChecked() != mToggleSwitchWasInitiallyChecked) {
|
||||
mMetricsFeatureProvider.action(getContext(),
|
||||
SettingsEnums.SUW_ACCESSIBILITY_TOGGLE_SCREEN_READER,
|
||||
mToggleServiceDividerSwitchPreference.isChecked());
|
||||
mToggleServiceSwitchPreference.isChecked());
|
||||
}
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ public class ToggleSelectToSpeakPreferenceFragmentForSetupWizard
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
mToggleSwitchWasInitiallyChecked = mToggleServiceDividerSwitchPreference.isChecked();
|
||||
mToggleSwitchWasInitiallyChecked = mToggleServiceSwitchPreference.isChecked();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -39,10 +39,10 @@ public class ToggleSelectToSpeakPreferenceFragmentForSetupWizard
|
||||
@Override
|
||||
public void onStop() {
|
||||
// Log the final choice in value if it's different from the previous value.
|
||||
if (mToggleServiceDividerSwitchPreference.isChecked() != mToggleSwitchWasInitiallyChecked) {
|
||||
if (mToggleServiceSwitchPreference.isChecked() != mToggleSwitchWasInitiallyChecked) {
|
||||
mMetricsFeatureProvider.action(getContext(),
|
||||
SettingsEnums.SUW_ACCESSIBILITY_TOGGLE_SELECT_TO_SPEAK,
|
||||
mToggleServiceDividerSwitchPreference.isChecked());
|
||||
mToggleServiceSwitchPreference.isChecked());
|
||||
}
|
||||
|
||||
super.onStop();
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.view.TextureView.SurfaceTextureListener;
|
||||
|
||||
import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.RawRes;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
/**
|
||||
* Plays the video by {@link MediaPlayer} on {@link TextureView}, calls {@link #create(Context, int,
|
||||
@@ -32,19 +33,25 @@ import androidx.annotation.RawRes;
|
||||
* is no longer used, call {@link #release()} so that MediaPlayer object can be released.
|
||||
*/
|
||||
public class VideoPlayer implements SurfaceTextureListener {
|
||||
private final Context context;
|
||||
private final Object mediaPlayerLock = new Object();
|
||||
private final Context mContext;
|
||||
private final Object mMediaPlayerLock = new Object();
|
||||
// Media player object can't be used after it has been released, so it will be set to null. But
|
||||
// VideoPlayer is asynchronized, media player object might be paused or resumed again before
|
||||
// released media player is set to null. Therefore, lock mediaPlayer and mediaPlayerState by
|
||||
// mediaPlayerLock keep their states consistent.
|
||||
@VisibleForTesting
|
||||
@GuardedBy("mediaPlayerLock")
|
||||
private MediaPlayer mediaPlayer;
|
||||
MediaPlayer mMediaPlayer;
|
||||
|
||||
@VisibleForTesting
|
||||
@GuardedBy("mediaPlayerLock")
|
||||
private State mediaPlayerState = State.NONE;
|
||||
State mMediaPlayerState = State.NONE;
|
||||
|
||||
@RawRes
|
||||
private final int videoRes;
|
||||
private Surface animationSurface;
|
||||
private final int mVideoRes;
|
||||
|
||||
@VisibleForTesting
|
||||
Surface mAnimationSurface;
|
||||
|
||||
|
||||
/**
|
||||
@@ -58,54 +65,54 @@ public class VideoPlayer implements SurfaceTextureListener {
|
||||
}
|
||||
|
||||
private VideoPlayer(Context context, @RawRes int videoRes, TextureView textureView) {
|
||||
this.context = context;
|
||||
this.videoRes = videoRes;
|
||||
this.mContext = context;
|
||||
this.mVideoRes = videoRes;
|
||||
textureView.setSurfaceTextureListener(this);
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
synchronized (mediaPlayerLock) {
|
||||
if (mediaPlayerState == State.STARTED) {
|
||||
mediaPlayerState = State.PAUSED;
|
||||
mediaPlayer.pause();
|
||||
synchronized (mMediaPlayerLock) {
|
||||
if (mMediaPlayerState == State.STARTED) {
|
||||
mMediaPlayerState = State.PAUSED;
|
||||
mMediaPlayer.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
synchronized (mediaPlayerLock) {
|
||||
if (mediaPlayerState == State.PAUSED) {
|
||||
mediaPlayer.start();
|
||||
mediaPlayerState = State.STARTED;
|
||||
synchronized (mMediaPlayerLock) {
|
||||
if (mMediaPlayerState == State.PAUSED) {
|
||||
mMediaPlayer.start();
|
||||
mMediaPlayerState = State.STARTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Release media player when it's no longer needed. */
|
||||
public void release() {
|
||||
synchronized (mediaPlayerLock) {
|
||||
if (mediaPlayerState != State.NONE && mediaPlayerState != State.END) {
|
||||
mediaPlayerState = State.END;
|
||||
mediaPlayer.release();
|
||||
mediaPlayer = null;
|
||||
synchronized (mMediaPlayerLock) {
|
||||
if (mMediaPlayerState != State.NONE && mMediaPlayerState != State.END) {
|
||||
mMediaPlayerState = State.END;
|
||||
mMediaPlayer.release();
|
||||
mMediaPlayer = null;
|
||||
}
|
||||
}
|
||||
if (animationSurface != null) {
|
||||
animationSurface.release();
|
||||
animationSurface = null;
|
||||
if (mAnimationSurface != null) {
|
||||
mAnimationSurface.release();
|
||||
mAnimationSurface = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
|
||||
animationSurface = new Surface(surface);
|
||||
synchronized (mediaPlayerLock) {
|
||||
mediaPlayer = MediaPlayer.create(context, videoRes);
|
||||
mediaPlayerState = State.PREPARED;
|
||||
mediaPlayer.setSurface(animationSurface);
|
||||
mediaPlayer.setLooping(true);
|
||||
mediaPlayer.start();
|
||||
mediaPlayerState = State.STARTED;
|
||||
mAnimationSurface = new Surface(surface);
|
||||
synchronized (mMediaPlayerLock) {
|
||||
mMediaPlayer = MediaPlayer.create(mContext, mVideoRes);
|
||||
mMediaPlayerState = State.PREPARED;
|
||||
mMediaPlayer.setSurface(mAnimationSurface);
|
||||
mMediaPlayer.setLooping(true);
|
||||
mMediaPlayer.start();
|
||||
mMediaPlayerState = State.STARTED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,6 @@ import android.view.View;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
* Fragment that only allowed hardware {@link UserShortcutType} for shortcut to open.
|
||||
*
|
||||
@@ -67,10 +65,9 @@ public class VolumeShortcutToggleAccessibilityServicePreferenceFragment extends
|
||||
}
|
||||
|
||||
private void setAllowedPreferredShortcutType(int type) {
|
||||
final AccessibilityUserShortcutType shortcut = new AccessibilityUserShortcutType(
|
||||
mComponentName.flattenToString(), type);
|
||||
final String componentNameString = mComponentName.flattenToString();
|
||||
final PreferredShortcut shortcut = new PreferredShortcut(componentNameString, type);
|
||||
|
||||
SharedPreferenceUtils.setUserShortcutType(getPrefContext(),
|
||||
ImmutableSet.of(shortcut.flattenToString()));
|
||||
PreferredShortcuts.saveUserShortcutType(getPrefContext(), shortcut);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class VolumeShortcutToggleScreenReaderPreferenceFragmentForSetupWizard
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
mToggleSwitchWasInitiallyChecked = mToggleServiceDividerSwitchPreference.isChecked();
|
||||
mToggleSwitchWasInitiallyChecked = mToggleServiceSwitchPreference.isChecked();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -40,10 +40,10 @@ public class VolumeShortcutToggleScreenReaderPreferenceFragmentForSetupWizard
|
||||
@Override
|
||||
public void onStop() {
|
||||
// Log the final choice in value if it's different from the previous value.
|
||||
if (mToggleServiceDividerSwitchPreference.isChecked() != mToggleSwitchWasInitiallyChecked) {
|
||||
if (mToggleServiceSwitchPreference.isChecked() != mToggleSwitchWasInitiallyChecked) {
|
||||
mMetricsFeatureProvider.action(getContext(),
|
||||
SettingsEnums.SUW_ACCESSIBILITY_TOGGLE_SCREEN_READER,
|
||||
mToggleServiceDividerSwitchPreference.isChecked());
|
||||
mToggleServiceSwitchPreference.isChecked());
|
||||
}
|
||||
|
||||
super.onStop();
|
||||
|
||||
@@ -29,7 +29,7 @@ public class VolumeShortcutToggleSelectToSpeakPreferenceFragmentForSetupWizard
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
mToggleSwitchWasInitiallyChecked = mToggleServiceDividerSwitchPreference.isChecked();
|
||||
mToggleSwitchWasInitiallyChecked = mToggleServiceSwitchPreference.isChecked();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -40,10 +40,10 @@ public class VolumeShortcutToggleSelectToSpeakPreferenceFragmentForSetupWizard
|
||||
@Override
|
||||
public void onStop() {
|
||||
// Log the final choice in value if it's different from the previous value.
|
||||
if (mToggleServiceDividerSwitchPreference.isChecked() != mToggleSwitchWasInitiallyChecked) {
|
||||
if (mToggleServiceSwitchPreference.isChecked() != mToggleSwitchWasInitiallyChecked) {
|
||||
mMetricsFeatureProvider.action(getContext(),
|
||||
SettingsEnums.SUW_ACCESSIBILITY_TOGGLE_SELECT_TO_SPEAK,
|
||||
mToggleServiceDividerSwitchPreference.isChecked());
|
||||
mToggleServiceSwitchPreference.isChecked());
|
||||
}
|
||||
|
||||
super.onStop();
|
||||
|
||||
81
src/com/android/settings/accessibility/rtt/TelecomUtil.java
Normal file
81
src/com/android/settings/accessibility/rtt/TelecomUtil.java
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.rtt;
|
||||
|
||||
import android.content.Context;
|
||||
import android.telecom.PhoneAccountHandle;
|
||||
import android.telecom.TelecomManager;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A util class checks some SIM card information and permissions.
|
||||
*/
|
||||
public abstract class TelecomUtil {
|
||||
|
||||
private static final String TAG = "TelecomUtil";
|
||||
|
||||
/** Get a list of phone accounts which are call capable. */
|
||||
public static List<PhoneAccountHandle> getCallCapablePhoneAccounts(Context context) {
|
||||
return Optional.ofNullable(getTelecomManager(context).getCallCapablePhoneAccounts())
|
||||
.orElse(new ArrayList<>());
|
||||
}
|
||||
|
||||
/** Returns a {@link TelecomManager} instance. */
|
||||
public static TelecomManager getTelecomManager(Context context) {
|
||||
return context.getApplicationContext().getSystemService(TelecomManager.class);
|
||||
}
|
||||
|
||||
/** Returns a subscription id of the SIM. */
|
||||
public static int getSubIdForPhoneAccountHandle(
|
||||
Context context, PhoneAccountHandle phoneAccountHandle) {
|
||||
Optional<SubscriptionInfo> info = getSubscriptionInfo(context, phoneAccountHandle);
|
||||
return info.map(SubscriptionInfo::getSubscriptionId)
|
||||
.orElse(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link SubscriptionInfo} of the SIM if {@code phoneAccountHandle} corresponds
|
||||
* to a valid SIM. Absent otherwise.
|
||||
*/
|
||||
private static Optional<SubscriptionInfo> getSubscriptionInfo(
|
||||
Context context, PhoneAccountHandle phoneAccountHandle) {
|
||||
if (TextUtils.isEmpty(phoneAccountHandle.getId())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
SubscriptionManager subscriptionManager = context.getSystemService(
|
||||
SubscriptionManager.class);
|
||||
List<SubscriptionInfo> subscriptionInfos =
|
||||
subscriptionManager.getActiveSubscriptionInfoList();
|
||||
if (subscriptionInfos == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
for (SubscriptionInfo info : subscriptionInfos) {
|
||||
if (phoneAccountHandle.getId().startsWith(info.getIccId())) {
|
||||
return Optional.of(info);
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "Failed to find SubscriptionInfo for phoneAccountHandle");
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,9 @@ import android.os.UserManager;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.applications.autofill.PasswordsPreferenceController;
|
||||
import com.android.settings.applications.defaultapps.DefaultAutofillPreferenceController;
|
||||
import com.android.settings.applications.defaultapps.DefaultWorkAutofillPreferenceController;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
@@ -67,15 +70,29 @@ public class AccountDashboardFragment extends DashboardFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
|
||||
final String[] authorities = getIntent().getStringArrayExtra(EXTRA_AUTHORITIES);
|
||||
return buildPreferenceControllers(context, this /* parent */, authorities);
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
|
||||
}
|
||||
|
||||
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
|
||||
SettingsPreferenceFragment parent, String[] authorities) {
|
||||
@Override
|
||||
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
|
||||
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
||||
buildAutofillPreferenceControllers(context, controllers);
|
||||
final String[] authorities = getIntent().getStringArrayExtra(EXTRA_AUTHORITIES);
|
||||
buildAccountPreferenceControllers(context, this /* parent */, authorities, controllers);
|
||||
return controllers;
|
||||
}
|
||||
|
||||
static void buildAutofillPreferenceControllers(
|
||||
Context context, List<AbstractPreferenceController> controllers) {
|
||||
controllers.add(new DefaultAutofillPreferenceController(context));
|
||||
controllers.add(new DefaultWorkAutofillPreferenceController(context));
|
||||
}
|
||||
|
||||
private static void buildAccountPreferenceControllers(
|
||||
Context context, SettingsPreferenceFragment parent, String[] authorities,
|
||||
List<AbstractPreferenceController> controllers) {
|
||||
final AccountPreferenceController accountPrefController =
|
||||
new AccountPreferenceController(context, parent, authorities,
|
||||
ProfileSelectFragment.ProfileType.ALL);
|
||||
@@ -86,7 +103,6 @@ public class AccountDashboardFragment extends DashboardFragment {
|
||||
controllers.add(new AutoSyncDataPreferenceController(context, parent));
|
||||
controllers.add(new AutoSyncPersonalDataPreferenceController(context, parent));
|
||||
controllers.add(new AutoSyncWorkDataPreferenceController(context, parent));
|
||||
return controllers;
|
||||
}
|
||||
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
@@ -95,8 +111,11 @@ public class AccountDashboardFragment extends DashboardFragment {
|
||||
@Override
|
||||
public List<AbstractPreferenceController> createPreferenceControllers(
|
||||
Context context) {
|
||||
return buildPreferenceControllers(
|
||||
context, null /* parent */, null /* authorities*/);
|
||||
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
||||
buildAccountPreferenceControllers(
|
||||
context, null /* parent */, null /* authorities*/, controllers);
|
||||
buildAutofillPreferenceControllers(context, controllers);
|
||||
return controllers;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,11 +18,14 @@ package com.android.settings.accounts;
|
||||
|
||||
import static android.provider.Settings.EXTRA_AUTHORITIES;
|
||||
|
||||
import static com.android.settings.accounts.AccountDashboardFragment.buildAutofillPreferenceControllers;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.SettingsPreferenceFragment;
|
||||
import com.android.settings.applications.autofill.PasswordsPreferenceController;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
|
||||
import com.android.settings.users.AutoSyncDataPreferenceController;
|
||||
@@ -60,15 +63,23 @@ public class AccountPersonalDashboardFragment extends DashboardFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
|
||||
final String[] authorities = getIntent().getStringArrayExtra(EXTRA_AUTHORITIES);
|
||||
return buildPreferenceControllers(context, this /* parent */, authorities);
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
getSettingsLifecycle().addObserver(use(PasswordsPreferenceController.class));
|
||||
}
|
||||
|
||||
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
|
||||
SettingsPreferenceFragment parent, String[] authorities) {
|
||||
@Override
|
||||
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
|
||||
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
||||
buildAutofillPreferenceControllers(context, controllers);
|
||||
final String[] authorities = getIntent().getStringArrayExtra(EXTRA_AUTHORITIES);
|
||||
buildAccountPreferenceControllers(context, this /* parent */, authorities, controllers);
|
||||
return controllers;
|
||||
}
|
||||
|
||||
private static void buildAccountPreferenceControllers(
|
||||
Context context, SettingsPreferenceFragment parent, String[] authorities,
|
||||
List<AbstractPreferenceController> controllers) {
|
||||
final AccountPreferenceController accountPrefController =
|
||||
new AccountPreferenceController(context, parent, authorities,
|
||||
ProfileSelectFragment.ProfileType.PERSONAL);
|
||||
@@ -78,7 +89,6 @@ public class AccountPersonalDashboardFragment extends DashboardFragment {
|
||||
controllers.add(accountPrefController);
|
||||
controllers.add(new AutoSyncDataPreferenceController(context, parent));
|
||||
controllers.add(new AutoSyncPersonalDataPreferenceController(context, parent));
|
||||
return controllers;
|
||||
}
|
||||
|
||||
// TODO: b/141601408. After featureFlag settings_work_profile is launched, unmark this
|
||||
@@ -88,6 +98,7 @@ public class AccountPersonalDashboardFragment extends DashboardFragment {
|
||||
// @Override
|
||||
// public List<AbstractPreferenceController> createPreferenceControllers(
|
||||
// Context context) {
|
||||
// ..Add autofill here too..
|
||||
// return buildPreferenceControllers(
|
||||
// context, null /* parent */, null /* authorities*/);
|
||||
// }
|
||||
|
||||
@@ -75,7 +75,7 @@ public class AccountPreferenceController extends AbstractPreferenceController
|
||||
|
||||
private static final String TAG = "AccountPrefController";
|
||||
|
||||
private static final int ORDER_ACCOUNT_PROFILES = 1;
|
||||
private static final int ORDER_ACCOUNT_PROFILES = 101;
|
||||
private static final int ORDER_LAST = 1002;
|
||||
private static final int ORDER_NEXT_TO_LAST = 1001;
|
||||
private static final int ORDER_NEXT_TO_NEXT_TO_LAST = 1000;
|
||||
@@ -323,11 +323,12 @@ public class AccountPreferenceController extends AbstractPreferenceController
|
||||
mHelper.createAccessiblePreferenceCategory(
|
||||
mFragment.getPreferenceManager().getContext());
|
||||
preferenceGroup.setOrder(mAccountProfileOrder++);
|
||||
preferenceGroup.setTitle(R.string.account_settings); // default title; may be modified below
|
||||
if (isSingleProfile()) {
|
||||
preferenceGroup.setTitle(context.getString(R.string.account_for_section_header,
|
||||
BidiFormatter.getInstance().unicodeWrap(userInfo.name)));
|
||||
preferenceGroup.setContentDescription(
|
||||
mContext.getString(R.string.account_settings));
|
||||
final String title = context.getString(R.string.account_for_section_header,
|
||||
BidiFormatter.getInstance().unicodeWrap(userInfo.name));
|
||||
preferenceGroup.setTitle(title);
|
||||
preferenceGroup.setContentDescription(title);
|
||||
} else if (userInfo.isManagedProfile()) {
|
||||
if (mType == ProfileSelectFragment.ProfileType.ALL) {
|
||||
preferenceGroup.setTitle(R.string.category_work);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user