Merge sc-v2-dev-plus-aosp-without-vendor@8084891

Bug: 214455710
Merged-In: I962c318f41adcf180b885f2052ce0ec4952edfb6
Change-Id: I77764eaf895ac3c13c7440adb5b3f597a516d690
This commit is contained in:
Xin Li
2022-02-11 07:29:32 +00:00
704 changed files with 54456 additions and 61773 deletions

View File

@@ -83,13 +83,14 @@ public class Settings extends SettingsActivity {
}
public static class VpnSettingsActivity extends SettingsActivity { /* empty */ }
public static class DataSaverSummaryActivity extends SettingsActivity{ /* empty */ }
/** Activity for Data saver settings. */
public static class DataSaverSummaryActivity extends SettingsActivity { /* empty */ }
public static class DateTimeSettingsActivity extends SettingsActivity { /* empty */ }
public static class PrivateVolumeForgetActivity 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 NetworkSelectActivity extends SettingsActivity { /* empty */ }
/** Activity for the Wi-Fi network details settings. */
public static class WifiDetailsSettingsActivity extends SettingsActivity { /* empty */ }
public static class WifiP2pSettingsActivity extends SettingsActivity { /* empty */ }
@@ -121,13 +122,11 @@ 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 */ }
/**
* Activity for lockscreen settings.
*/
/** Activity for lockscreen settings. */
public static class LockScreenSettingsActivity extends SettingsActivity { /* empty */ }
/**
* Activity for Reduce Bright Colors.
*/
/** Activity for bluetooth pairing settings. */
public static class BlueToothPairingActivity 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 {

View File

@@ -16,6 +16,10 @@
package com.android.settings;
import static android.provider.Settings.ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY;
import static android.provider.Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY;
import static android.provider.Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI;
import static com.android.settings.applications.appinfo.AppButtonsPreferenceController.KEY_REMOVE_TASK_WHEN_FINISHING;
import android.app.ActionBar;
@@ -29,6 +33,7 @@ import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.graphics.drawable.Icon;
import android.os.AsyncTask;
@@ -52,12 +57,15 @@ import androidx.preference.PreferenceManager;
import com.android.internal.util.ArrayUtils;
import com.android.settings.Settings.WifiSettingsActivity;
import com.android.settings.activityembedding.ActivityEmbeddingUtils;
import com.android.settings.applications.manageapplications.ManageApplications;
import com.android.settings.core.OnActivityResultListener;
import com.android.settings.core.SettingsBaseActivity;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.core.gateway.SettingsGateway;
import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.homepage.SettingsHomepageActivity;
import com.android.settings.homepage.SliceDeepLinkHomepageActivity;
import com.android.settings.homepage.TopLevelSettings;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.wfd.WifiDisplaySettings;
@@ -136,6 +144,12 @@ public class SettingsActivity extends SettingsBaseActivity
public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
":settings:show_fragment_as_subsetting";
/**
* Additional extra of Settings#ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK.
* Set true when the deep link intent is from a slice
*/
public static final String EXTRA_IS_FROM_SLICE = "is_from_slice";
/**
* Personal or Work profile tab of {@link ProfileSelectFragment}
* <p>0: Personal tab.
@@ -147,9 +161,13 @@ public class SettingsActivity extends SettingsBaseActivity
public static final String META_DATA_KEY_FRAGMENT_CLASS =
"com.android.settings.FRAGMENT_CLASS";
public static final String META_DATA_KEY_HIGHLIGHT_MENU_KEY =
"com.android.settings.HIGHLIGHT_MENU_KEY";
private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
private String mFragmentClass;
private String mHighlightMenuKey;
private CharSequence mInitialTitle;
private int mInitialTitleResId;
@@ -229,18 +247,25 @@ public class SettingsActivity extends SettingsBaseActivity
@Override
protected void onCreate(Bundle savedState) {
// Should happen before any call to getIntent()
getMetaData();
final Intent intent = getIntent();
if (shouldShowTwoPaneDeepLink(intent)) {
launchHomepageForTwoPaneDeepLink(intent);
finishAndRemoveTask();
super.onCreate(savedState);
return;
}
super.onCreate(savedState);
Log.d(LOG_TAG, "Starting onCreate");
long startTime = System.currentTimeMillis();
final FeatureFactory factory = FeatureFactory.getFactory(this);
mDashboardFeatureProvider = factory.getDashboardFeatureProvider(this);
// Should happen before any call to getIntent()
getMetaData();
final Intent intent = getIntent();
if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
}
@@ -248,17 +273,11 @@ public class SettingsActivity extends SettingsBaseActivity
// Getting Intent properties can only be done after the super.onCreate(...)
final String initialFragmentName = getInitialFragmentName(intent);
// This is a "Sub Settings" when:
// - this is a real SubSettings
// - or :settings:show_fragment_as_subsetting is passed to the Intent
final boolean isSubSettings = this instanceof SubSettings ||
intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
// If this is a sub settings, then apply the SubSettings Theme for the ActionBar content
// insets.
// If this is in setup flow, don't apply theme. Because light theme needs to be applied
// in SettingsBaseActivity#onCreate().
if (isSubSettings && !WizardManagerHelper.isAnySetupWizard(getIntent())) {
if (isSubSettings(intent) && !WizardManagerHelper.isAnySetupWizard(getIntent())) {
setTheme(R.style.Theme_SubSettings);
}
@@ -347,6 +366,99 @@ public class SettingsActivity extends SettingsBaseActivity
}
}
private boolean isSubSettings(Intent intent) {
return this instanceof SubSettings ||
intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
}
/**
* Returns the deep link trampoline intent for large screen devices.
*/
public static Intent getTrampolineIntent(Intent intent, String highlightMenuKey) {
final Intent detailIntent = new Intent(intent);
// It's a deep link intent, SettingsHomepageActivity will set SplitPairRule and start it.
final Intent trampolineIntent = new Intent(ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY)
.setPackage(Utils.SETTINGS_PACKAGE_NAME)
.replaceExtras(detailIntent);
// Relay detail intent data to prevent failure of Intent#ParseUri.
// If Intent#getData() is not null, Intent#toUri will return an Uri which has the scheme of
// Intent#getData() and it may not be the scheme of an Intent.
trampolineIntent.putExtra(
SettingsHomepageActivity.EXTRA_SETTINGS_LARGE_SCREEN_DEEP_LINK_INTENT_DATA,
detailIntent.getData());
detailIntent.setData(null);
trampolineIntent.putExtra(EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI,
detailIntent.toUri(Intent.URI_INTENT_SCHEME));
trampolineIntent.putExtra(EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY,
highlightMenuKey);
trampolineIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
return trampolineIntent;
}
private void launchHomepageForTwoPaneDeepLink(Intent intent) {
final Intent trampolineIntent;
if (intent.getBooleanExtra(EXTRA_IS_FROM_SLICE, false)) {
// Get menu key for slice deep link case.
final String highlightMenuKey = intent.getStringExtra(
EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY);
if (!TextUtils.isEmpty(highlightMenuKey)) {
mHighlightMenuKey = highlightMenuKey;
}
trampolineIntent = getTrampolineIntent(intent, mHighlightMenuKey);
trampolineIntent.setClass(this, SliceDeepLinkHomepageActivity.class);
} else {
trampolineIntent = getTrampolineIntent(intent, mHighlightMenuKey);
}
startActivity(trampolineIntent);
}
private boolean shouldShowTwoPaneDeepLink(Intent intent) {
if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this)) {
return false;
}
// If the activity is not the task root, it should not start trampoline for deep links.
if (!isTaskRoot()) {
return false;
}
// Only starts trampoline for deep links. Should return false for all the cases that
// Settings app starts SettingsActivity or SubSetting by itself.
if (intent.getAction() == null) {
// Other apps should send deep link intent which matches intent filter of the Activity.
return false;
}
if (intent.getBooleanExtra(EXTRA_IS_FROM_SLICE, false)) {
// Slice deep link starts the Intent using SubSettingLauncher. Returns true to show
// 2-pane deep link.
return true;
}
if (isSubSettings(intent)) {
return false;
}
if (intent.getBooleanExtra(SettingsHomepageActivity.EXTRA_IS_FROM_SETTINGS_HOMEPAGE,
/* defaultValue */ false)) {
return false;
}
if (TextUtils.equals(intent.getAction(), Intent.ACTION_CREATE_SHORTCUT)) {
// Returns false to show full screen for Intent.ACTION_CREATE_SHORTCUT because
// - Launcher startActivityForResult for Intent.ACTION_CREATE_SHORTCUT and activity
// stack starts from launcher, CreateShortcutActivity will not follows SplitPaitRule
// registered by Settings.
// - There is no CreateShortcutActivity entry point from Settings app UI.
return false;
}
return true;
}
/** Returns the initial fragment name that the activity will launch. */
@VisibleForTesting
public String getInitialFragmentName(Intent intent) {
@@ -407,6 +519,9 @@ public class SettingsActivity extends SettingsBaseActivity
return;
} catch (NameNotFoundException e) {
Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName);
} catch (Resources.NotFoundException resourceNotFound) {
Log.w(LOG_TAG,
"Could not find title resource in " + initialTitleResPackageName);
}
} else {
setTitle(mInitialTitleResId);
@@ -716,6 +831,7 @@ public class SettingsActivity extends SettingsBaseActivity
PackageManager.GET_META_DATA);
if (ai == null || ai.metaData == null) return;
mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
mHighlightMenuKey = ai.metaData.getString(META_DATA_KEY_HIGHLIGHT_MENU_KEY);
} catch (NameNotFoundException nnfe) {
// No recovery
Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());

View File

@@ -0,0 +1,47 @@
/*
* 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.app.Application;
import com.android.settings.activityembedding.ActivityEmbeddingRulesController;
import com.android.settings.homepage.SettingsHomepageActivity;
import java.lang.ref.WeakReference;
/** Settings application which sets up activity embedding rules for the large screen device. */
public class SettingsApplication extends Application {
private WeakReference<SettingsHomepageActivity> mHomeActivity = new WeakReference<>(null);
@Override
public void onCreate() {
super.onCreate();
final ActivityEmbeddingRulesController controller =
new ActivityEmbeddingRulesController(this);
controller.initRules();
}
public void setHomeActivity(SettingsHomepageActivity homeActivity) {
mHomeActivity = new WeakReference<>(homeActivity);
}
public SettingsHomepageActivity getHomeActivity() {
return mHomeActivity.get();
}
}

View File

@@ -37,8 +37,11 @@ import android.os.UserManager;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.window.embedding.SplitController;
import com.android.settings.Settings.CreateShortcutActivity;
import com.android.settings.homepage.DeepLinkHomepageActivity;
import com.android.settings.search.SearchStateReceiver;
import com.android.settingslib.utils.ThreadUtils;
import java.util.ArrayList;
@@ -48,7 +51,8 @@ import java.util.List;
* Listens to {@link Intent.ACTION_PRE_BOOT_COMPLETED} and {@link Intent.ACTION_USER_INITIALIZED}
* performs setup steps for a managed profile (disables the launcher icon of the Settings app,
* adds cross-profile intent filters for the appropriate Settings activities), disables the
* webview setting for non-admin users, and updates the intent flags for any existing shortcuts.
* webview setting for non-admin users, updates the intent flags for any existing shortcuts and
* enables DeepLinkHomepageActivity for large screen devices.
*/
public class SettingsInitialize extends BroadcastReceiver {
private static final String TAG = "Settings";
@@ -64,6 +68,7 @@ public class SettingsInitialize extends BroadcastReceiver {
managedProfileSetup(context, pm, broadcast, userInfo);
webviewSettingSetup(context, pm, userInfo);
ThreadUtils.postOnBackgroundThread(() -> refreshExistingShortcuts(context));
enableTwoPaneDeepLinkActivityIfNecessary(pm, context);
}
private void managedProfileSetup(Context context, final PackageManager pm, Intent broadcast,
@@ -143,4 +148,17 @@ public class SettingsInitialize extends BroadcastReceiver {
}
shortcutManager.updateShortcuts(updates);
}
private void enableTwoPaneDeepLinkActivityIfNecessary(PackageManager pm, Context context) {
final ComponentName deepLinkHome = new ComponentName(context,
DeepLinkHomepageActivity.class);
final ComponentName searchStateReceiver = new ComponentName(context,
SearchStateReceiver.class);
final int enableState = SplitController.getInstance().isSplitSupported()
? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
pm.setComponentEnabledSetting(deepLinkHome, enableState, PackageManager.DONT_KILL_APP);
pm.setComponentEnabledSetting(searchStateReceiver, enableState,
PackageManager.DONT_KILL_APP);
}
}

View File

@@ -119,8 +119,7 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
@VisibleForTesting
public HighlightablePreferenceGroupAdapter mAdapter;
@VisibleForTesting
public boolean mPreferenceHighlighted = false;
private boolean mPreferenceHighlighted = false;
@Override
public void onCreate(Bundle icicle) {

View File

@@ -1,42 +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;
import android.app.Activity;
import android.os.Bundle;
import com.android.settings.accessibility.AccessibilityGestureNavigationTutorial;
import com.android.settings.R;
/**
* This activity is to create the tutorial dialog in gesture navigation settings since we couldn't
* use the dialog utils because SystemNavigationGestureSettings extends RadioButtonPickerFragment,
* not SettingsPreferenceFragment.
*/
public class SettingsTutorialDialogWrapperActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showDialog();
}
private void showDialog() {
AccessibilityGestureNavigationTutorial
.showGestureNavigationSettingsTutorialDialog(this, dialog -> finish());
}
}

View File

@@ -1013,7 +1013,8 @@ public final class Utils extends com.android.settingslib.Utils {
Drawable safeIcon = icon;
if ((icon != null) && !(icon instanceof VectorDrawable)) {
safeIcon = getSafeDrawable(icon, 500, 500);
safeIcon = getSafeDrawable(icon,
/* MAX_DRAWABLE_SIZE */ 600, /* MAX_DRAWABLE_SIZE */ 600);
}
return safeIcon;
@@ -1222,7 +1223,11 @@ public final class Utils extends com.android.settingslib.Utils {
return getColorAttrDefaultColor(context, android.R.attr.textColorSecondary);
}
public static boolean isProviderModelEnabled(Context context) {
return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
/**
* Returns the highlight color of homepage preference icons.
*/
@ColorInt
public static int getHomepageIconColorHighlight(Context context) {
return getColorAttrDefaultColor(context, android.R.attr.textColorSecondaryInverse);
}
}

View File

@@ -33,21 +33,26 @@ public class AccessibilityButtonFooterPreferenceController extends
}
@Override
protected String getLabelName() {
return mContext.getString(R.string.accessibility_button_title);
protected String getLearnMoreContentDescription() {
return mContext.getString(
R.string.accessibility_button_gesture_footer_learn_more_content_description);
}
@Override
protected String getIntroductionTitle() {
return mContext.getString(R.string.accessibility_button_about_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));
}
final int titleResource = AccessibilityUtil.isGestureNavigateEnabled(mContext)
? R.string.accessibility_button_gesture_description
: R.string.accessibility_button_description;
final AccessibilityFooterPreference footerPreference =
screen.findPreference(getPreferenceKey());
footerPreference.setTitle(titleResource);
super.displayPreference(screen);
}
}

View File

@@ -17,6 +17,7 @@
package com.android.settings.accessibility;
import android.app.settings.SettingsEnums;
import android.os.Bundle;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
@@ -29,6 +30,14 @@ public class AccessibilityButtonFragment extends DashboardFragment {
private static final String TAG = "AccessibilityButtonFragment";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final int titleResource = AccessibilityUtil.isGestureNavigateEnabled(getPrefContext())
? R.string.accessibility_button_gesture_title : R.string.accessibility_button_title;
getActivity().setTitle(titleResource);
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.accessibility_button_settings;

View File

@@ -0,0 +1,82 @@
/*
* 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 androidx.preference.ListPreference;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.google.common.primitives.Ints;
import java.util.Optional;
/** Preference controller that controls the button or gesture in accessibility button page. */
public class AccessibilityButtonGesturePreferenceController extends BasePreferenceController
implements Preference.OnPreferenceChangeListener {
private Optional<Integer> mDefaultGesture = Optional.empty();
public AccessibilityButtonGesturePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return AccessibilityUtil.isGestureNavigateEnabled(mContext)
? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
}
@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, getDefaultGestureValue());
return String.valueOf(mode);
}
private int getDefaultGestureValue() {
if (!mDefaultGesture.isPresent()) {
final String[] valuesList = mContext.getResources().getStringArray(
R.array.accessibility_button_gesture_selector_values);
mDefaultGesture = Optional.of(Integer.parseInt(valuesList[0]));
}
return mDefaultGesture.get();
}
}

View File

@@ -18,7 +18,6 @@ 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;
@@ -28,16 +27,16 @@ import com.android.settings.core.BasePreferenceController;
import com.google.common.primitives.Ints;
import java.util.Optional;
/** 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;
private Optional<Integer> mDefaultLocation = Optional.empty();
public AccessibilityButtonLocationPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
initValueTitleMap();
}
@Override
@@ -68,22 +67,16 @@ public class AccessibilityButtonLocationPreferenceController extends BasePrefere
private String getCurrentAccessibilityButtonMode() {
final int mode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, mDefaultLocation);
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, getDefaultLocationValue());
return String.valueOf(mode);
}
private void initValueTitleMap() {
if (mValueTitleMap.size() == 0) {
final String[] values = mContext.getResources().getStringArray(
private int getDefaultLocationValue() {
if (!mDefaultLocation.isPresent()) {
final String[] valuesList = 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]);
}
mDefaultLocation = Optional.of(Integer.parseInt(valuesList[0]));
}
return mDefaultLocation.get();
}
}

View File

@@ -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.content.Context;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
/**
* Preference controller for accessibility button preference.
*/
public class AccessibilityButtonPreferenceController extends BasePreferenceController {
public AccessibilityButtonPreferenceController(Context context, String key) {
super(context, key);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
final int titleResource = AccessibilityUtil.isGestureNavigateEnabled(mContext)
? R.string.accessibility_button_gesture_title : R.string.accessibility_button_title;
final Preference preference = screen.findPreference(getPreferenceKey());
preference.setTitle(titleResource);
}
}

View File

@@ -23,7 +23,7 @@ import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.widget.ImageView;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceScreen;
@@ -33,7 +33,7 @@ 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;
import com.android.settingslib.widget.IllustrationPreference;
/** Preference controller that controls the preview effect in accessibility button page. */
public class AccessibilityButtonPreviewPreferenceController extends BasePreferenceController
@@ -46,10 +46,12 @@ public class AccessibilityButtonPreviewPreferenceController extends BasePreferen
private final ContentResolver mContentResolver;
@VisibleForTesting
final ContentObserver mContentObserver;
private FloatingMenuLayerDrawable mFloatingMenuPreviewDrawable;
private AccessibilityLayerDrawable mAccessibilityPreviewDrawable;
@VisibleForTesting
ImageView mPreview;
IllustrationPreference mIllustrationPreference;
private AccessibilityManager.TouchExplorationStateChangeListener
mTouchExplorationStateChangeListener;
public AccessibilityButtonPreviewPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
@@ -60,6 +62,9 @@ public class AccessibilityButtonPreviewPreferenceController extends BasePreferen
updatePreviewPreference();
}
};
mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
updatePreviewPreference();
};
}
@Override
@@ -70,14 +75,16 @@ public class AccessibilityButtonPreviewPreferenceController extends BasePreferen
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
final LayoutPreference preference = screen.findPreference(getPreferenceKey());
mPreview = preference.findViewById(R.id.preview_image);
mIllustrationPreference = screen.findPreference(getPreferenceKey());
updatePreviewPreference();
}
@Override
public void onResume() {
final AccessibilityManager am = mContext.getSystemService(AccessibilityManager.class);
am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE),
/* notifyForDescendants= */ false, mContentObserver);
@@ -91,6 +98,9 @@ public class AccessibilityButtonPreviewPreferenceController extends BasePreferen
@Override
public void onPause() {
final AccessibilityManager am = mContext.getSystemService(AccessibilityManager.class);
am.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
mContentResolver.unregisterContentObserver(mContentObserver);
}
@@ -103,24 +113,29 @@ public class AccessibilityButtonPreviewPreferenceController extends BasePreferen
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();
mIllustrationPreference.setImageDrawable(
getAccessibilityPreviewDrawable(floatingMenuIconId, opacity));
} else if (AccessibilityUtil.isGestureNavigateEnabled(mContext)) {
mIllustrationPreference.setImageDrawable(mContext.getDrawable(
AccessibilityUtil.isTouchExploreEnabled(mContext)
? R.drawable.accessibility_button_preview_three_finger
: R.drawable.accessibility_button_preview_two_finger));
} else {
mPreview.setImageDrawable(
mIllustrationPreference.setImageDrawable(
mContext.getDrawable(R.drawable.accessibility_button_navigation));
}
}
private Drawable getFloatingMenuPreviewDrawable(int resId, int opacity) {
if (mFloatingMenuPreviewDrawable == null) {
mFloatingMenuPreviewDrawable = FloatingMenuLayerDrawable.createLayerDrawable(
private Drawable getAccessibilityPreviewDrawable(int resId, int opacity) {
if (mAccessibilityPreviewDrawable == null) {
mAccessibilityPreviewDrawable = AccessibilityLayerDrawable.createLayerDrawable(
mContext, resId, opacity);
} else {
mFloatingMenuPreviewDrawable.updateLayerDrawable(mContext, resId, opacity);
mAccessibilityPreviewDrawable.updateLayerDrawable(mContext, resId, opacity);
// Only change alpha (opacity) value did not change drawable id. It needs to force to
// redraw.
mAccessibilityPreviewDrawable.invalidateSelf();
}
return mFloatingMenuPreviewDrawable;
return mAccessibilityPreviewDrawable;
}
}

View File

@@ -31,8 +31,14 @@ public class AccessibilityControlTimeoutFooterPreferenceController extends
}
@Override
protected String getLabelName() {
return mContext.getString(R.string.accessibility_setting_item_control_timeout_title);
protected String getLearnMoreContentDescription() {
return mContext.getString(
R.string.accessibility_control_timeout_footer_learn_more_content_description);
}
@Override
protected String getIntroductionTitle() {
return mContext.getString(R.string.accessibility_control_timeout_about_title);
}
@Override

View File

@@ -69,7 +69,7 @@ public final class AccessibilityControlTimeoutPreferenceFragment extends Dashboa
@Override
public int getMetricsCategory() {
return SettingsEnums.ACCESSIBILITY;
return SettingsEnums.ACCESSIBILITY_TIMEOUT;
}
@Override

View File

@@ -31,6 +31,7 @@ import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.style.ImageSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AbsListView;
@@ -44,9 +45,11 @@ import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RawRes;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
@@ -54,6 +57,9 @@ import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.utils.AnnotationSpan;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieDrawable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
@@ -63,6 +69,7 @@ import java.util.List;
* Utility class for creating the edit dialog.
*/
public class AccessibilityDialogUtils {
private static final String TAG = "AccessibilityDialogUtils";
/** Denotes the dialog emuns for show dialog. */
@Retention(RetentionPolicy.SOURCE)
@@ -173,6 +180,23 @@ public class AccessibilityDialogUtils {
return alertDialog;
}
/**
* Updates the software shortcut in edit shortcut dialog.
*
* @param context A valid context
* @param editShortcutDialog Need to be a type of edit shortcut dialog
* @return True if the update is successful
*/
public static boolean updateSoftwareShortcutInDialog(Context context,
Dialog editShortcutDialog) {
final View container = editShortcutDialog.findViewById(R.id.container_layout);
if (container != null) {
initSoftwareShortcut(context, container);
return true;
}
return false;
}
private static AlertDialog createDialog(Context context, int dialogType,
CharSequence dialogTitle, DialogInterface.OnClickListener listener) {
@@ -315,9 +339,22 @@ public class AccessibilityDialogUtils {
}
private static void setupShortcutWidget(View view, CharSequence titleText,
CharSequence summaryText, int imageResId) {
CharSequence summaryText, @DrawableRes int imageResId) {
setupShortcutWidgetWithTitleAndSummary(view, titleText, summaryText);
setupShortcutWidgetWithImageResource(view, imageResId);
}
private static void setupShortcutWidgetWithImageRawResource(View view, CharSequence titleText,
CharSequence summaryText, @RawRes int imageRawResId) {
setupShortcutWidgetWithTitleAndSummary(view, titleText, summaryText);
setupShortcutWidgetWithImageRawResource(view, imageRawResId);
}
private static void setupShortcutWidgetWithTitleAndSummary(View view, CharSequence titleText,
CharSequence summaryText) {
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);
@@ -326,8 +363,23 @@ public class AccessibilityDialogUtils {
summary.setMovementMethod(LinkMovementMethod.getInstance());
summary.setFocusable(false);
}
final ImageView image = view.findViewById(R.id.image);
image.setImageResource(imageResId);
}
private static void setupShortcutWidgetWithImageResource(View view,
@DrawableRes int imageResId) {
final ImageView imageView = view.findViewById(R.id.image);
imageView.setImageResource(imageResId);
}
private static void setupShortcutWidgetWithImageRawResource(View view,
@RawRes int imageRawResId) {
final LottieAnimationView lottieView = view.findViewById(R.id.image);
lottieView.setFailureListener(
result -> Log.w(TAG, "Invalid image raw resource id: " + imageRawResId,
result));
lottieView.setAnimation(imageRawResId);
lottieView.setRepeatCount(LottieDrawable.INFINITE);
lottieView.playAnimation();
}
private static void initSoftwareShortcutForSUW(Context context, View view) {
@@ -344,12 +396,11 @@ public class AccessibilityDialogUtils {
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,
setupShortcutWidget(dialogView,
retrieveTitle(context),
retrieveSoftwareShortcutSummary(context, lineHeight),
retrieveSoftwareShortcutImageResId(context));
}
@@ -362,7 +413,6 @@ public class AccessibilityDialogUtils {
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) {
@@ -375,9 +425,8 @@ public class AccessibilityDialogUtils {
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.
setupShortcutWidgetWithImageRawResource(dialogView, title, summary,
R.raw.accessibility_shortcut_type_triple_tap);
}
private static void initAdvancedWidget(View view) {
@@ -398,20 +447,49 @@ public class AccessibilityDialogUtils {
return sb;
}
private static CharSequence retrieveTitle(Context context) {
int resId;
if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
resId = R.string.accessibility_shortcut_edit_dialog_title_software;
} else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
resId = R.string.accessibility_shortcut_edit_dialog_title_software_by_gesture;
} else {
resId = R.string.accessibility_shortcut_edit_dialog_title_software;
}
return context.getText(resId);
}
private static CharSequence retrieveSoftwareShortcutSummary(Context context, int lineHeight) {
final SpannableStringBuilder sb = new SpannableStringBuilder();
if (!AccessibilityUtil.isFloatingMenuEnabled(context)) {
if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
sb.append(getCustomizeAccessibilityButtonLink(context));
} else 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;
sb.append(context.getText(resId));
sb.append("\n\n");
sb.append(getCustomizeAccessibilityButtonLink(context));
} else {
sb.append(getSummaryStringWithIcon(context, lineHeight));
sb.append("\n\n");
sb.append(getCustomizeAccessibilityButtonLink(context));
}
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;
int resId;
if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
resId = R.drawable.accessibility_shortcut_type_software_floating;
} else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
resId = AccessibilityUtil.isTouchExploreEnabled(context)
? R.drawable.accessibility_shortcut_type_software_gesture_talkback
: R.drawable.accessibility_shortcut_type_software_gesture;
} else {
resId = R.drawable.accessibility_shortcut_type_software;
}
return resId;
}
private static CharSequence getCustomizeAccessibilityButtonLink(Context context) {
@@ -422,7 +500,6 @@ public class AccessibilityDialogUtils {
.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);
}

View File

@@ -21,14 +21,18 @@ 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.
* Preference controller that controls the help link and customizes the preference title in {@link
* AccessibilityFooterPreference}.
*/
public abstract class AccessibilityFooterPreferenceController extends BasePreferenceController {
public class AccessibilityFooterPreferenceController extends BasePreferenceController {
private int mHelpResource;
private String mLearnMoreContentDescription;
private String mIntroductionTitle;
public AccessibilityFooterPreferenceController(Context context, String key) {
super(context, key);
@@ -49,36 +53,74 @@ public abstract class AccessibilityFooterPreferenceController extends BasePrefer
}
/**
* Override this if showing a help item in the footer bar, by returning the resource id.
* Setups a help item in the {@link AccessibilityFooterPreference} with specific content
* description.
*/
public void setupHelpLink(int helpResource, String learnMoreContentDescription) {
mHelpResource = helpResource;
mLearnMoreContentDescription = learnMoreContentDescription;
}
/**
* Overrides this if showing a help item in the {@link AccessibilityFooterPreference}, by
* returning the resource id.
*
* @return the resource id for the help url
*/
protected int getHelpResource() {
return 0;
return mHelpResource;
}
/** Returns the accessibility feature name. */
protected abstract String getLabelName();
/**
* Overrides this if showing a help item in the {@link AccessibilityFooterPreference} with
* specific content description.
*
* @return the content description for the help url
*/
protected String getLearnMoreContentDescription() {
return mLearnMoreContentDescription;
}
/**
* Sets the announcement the specific features introduction in the {@link
* AccessibilityFooterPreference}.
*/
public void setIntroductionTitle(String introductionTitle) {
mIntroductionTitle = introductionTitle;
}
/**
* Overrides this if announcement the specific features introduction in the {@link
* AccessibilityFooterPreference}.
*
* @return the extended content description for specific features introduction
*/
protected String getIntroductionTitle() {
return mIntroductionTitle;
}
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());
sb.append(getIntroductionTitle()).append("\n\n").append(footerPreference.getTitle());
footerPreference.setContentDescription(sb);
final Intent helpIntent;
if (getHelpResource() != 0) {
// Returns may be null if content is wrong or empty.
helpIntent = HelpUtils.getHelpIntent(mContext, mContext.getString(getHelpResource()),
mContext.getClass().getName());
} else {
helpIntent = null;
}
if (helpIntent != null) {
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);
footerPreference.setLearnMoreContentDescription(getLearnMoreContentDescription());
footerPreference.setLinkEnabled(true);
} else {
footerPreference.setLinkEnabled(false);
}
}
}

View File

@@ -27,13 +27,15 @@ import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.style.ImageSpan;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextSwitcher;
@@ -41,8 +43,10 @@ import android.widget.TextView;
import androidx.annotation.AnimRes;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.RawRes;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
@@ -53,6 +57,9 @@ import androidx.viewpager.widget.ViewPager;
import com.android.settings.R;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieDrawable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -63,17 +70,19 @@ import java.util.List;
* accessibility services.
*/
public final class AccessibilityGestureNavigationTutorial {
private static final String TAG = "AccessibilityGestureNavigationTutorial";
/** IntDef enum for dialog type. */
@Retention(RetentionPolicy.SOURCE)
@IntDef({
DialogType.LAUNCH_SERVICE_BY_ACCESSIBILITY_BUTTON,
DialogType.LAUNCH_SERVICE_BY_GESTURE_NAVIGATION,
DialogType.LAUNCH_SERVICE_BY_ACCESSIBILITY_GESTURE,
DialogType.GESTURE_NAVIGATION_SETTINGS,
})
private @interface DialogType {
int LAUNCH_SERVICE_BY_ACCESSIBILITY_BUTTON = 0;
int LAUNCH_SERVICE_BY_GESTURE_NAVIGATION = 1;
int LAUNCH_SERVICE_BY_ACCESSIBILITY_GESTURE = 1;
int GESTURE_NAVIGATION_SETTINGS = 2;
}
@@ -82,13 +91,17 @@ public final class AccessibilityGestureNavigationTutorial {
private static final DialogInterface.OnClickListener mOnClickListener =
(DialogInterface dialog, int which) -> dialog.dismiss();
public static void showGestureNavigationSettingsTutorialDialog(Context context,
DialogInterface.OnDismissListener dismissListener) {
/**
* Displays a dialog that guides users to use accessibility features with accessibility
* gestures under system gesture navigation mode.
*/
public static void showGestureNavigationTutorialDialog(Context context,
DialogInterface.OnDismissListener onDismissListener) {
final AlertDialog alertDialog = new AlertDialog.Builder(context)
.setView(createTutorialDialogContentView(context,
DialogType.GESTURE_NAVIGATION_SETTINGS))
.setNegativeButton(R.string.accessibility_tutorial_dialog_button, mOnClickListener)
.setOnDismissListener(dismissListener)
.setOnDismissListener(onDismissListener)
.create();
alertDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
@@ -107,8 +120,8 @@ public final class AccessibilityGestureNavigationTutorial {
return alertDialog;
}
static AlertDialog showGestureNavigationTutorialDialog(Context context) {
return createDialog(context, DialogType.LAUNCH_SERVICE_BY_GESTURE_NAVIGATION);
static AlertDialog showAccessibilityGestureTutorialDialog(Context context) {
return createDialog(context, DialogType.LAUNCH_SERVICE_BY_ACCESSIBILITY_GESTURE);
}
static AlertDialog createAccessibilityTutorialDialog(Context context, int shortcutTypes) {
@@ -119,7 +132,7 @@ public final class AccessibilityGestureNavigationTutorial {
}
/**
* Get a content View for a dialog to confirm that they want to enable a service.
* Gets a content View for a dialog to confirm that they want to enable a service.
*
* @param context A valid context
* @param dialogType The type of tutorial dialog
@@ -136,42 +149,36 @@ public final class AccessibilityGestureNavigationTutorial {
content = inflater.inflate(
R.layout.tutorial_dialog_launch_service_by_accessibility_button, null);
break;
case DialogType.LAUNCH_SERVICE_BY_GESTURE_NAVIGATION:
case DialogType.LAUNCH_SERVICE_BY_ACCESSIBILITY_GESTURE:
content = inflater.inflate(
R.layout.tutorial_dialog_launch_service_by_gesture_navigation, null);
final TextureView gestureTutorialVideo = content.findViewById(
R.id.gesture_tutorial_video);
final TextView gestureTutorialMessage = content.findViewById(
R.id.gesture_tutorial_message);
VideoPlayer.create(context, AccessibilityUtil.isTouchExploreEnabled(context)
? R.raw.illustration_accessibility_gesture_three_finger
: R.raw.illustration_accessibility_gesture_two_finger,
gestureTutorialVideo);
gestureTutorialMessage.setText(AccessibilityUtil.isTouchExploreEnabled(context)
? R.string.accessibility_tutorial_dialog_message_gesture_talkback
: R.string.accessibility_tutorial_dialog_message_gesture);
setupGestureNavigationTextWithImage(context, content);
break;
case DialogType.GESTURE_NAVIGATION_SETTINGS:
content = inflater.inflate(
R.layout.tutorial_dialog_launch_by_gesture_navigation_settings, null);
final TextureView gestureSettingsTutorialVideo = content.findViewById(
R.id.gesture_tutorial_video);
final TextView gestureSettingsTutorialMessage = content.findViewById(
R.id.gesture_tutorial_message);
VideoPlayer.create(context, AccessibilityUtil.isTouchExploreEnabled(context)
? R.raw.illustration_accessibility_gesture_three_finger
: R.raw.illustration_accessibility_gesture_two_finger,
gestureSettingsTutorialVideo);
final int stringResId = AccessibilityUtil.isTouchExploreEnabled(context)
? R.string.accessibility_tutorial_dialog_message_gesture_settings_talkback
: R.string.accessibility_tutorial_dialog_message_gesture_settings;
gestureSettingsTutorialMessage.setText(stringResId);
setupGestureNavigationTextWithImage(context, content);
break;
}
return content;
}
private static void setupGestureNavigationTextWithImage(Context context, View view) {
final boolean isTouchExploreEnabled = AccessibilityUtil.isTouchExploreEnabled(context);
final ImageView imageView = view.findViewById(R.id.image);
final int gestureSettingsImageResId =
isTouchExploreEnabled ? R.drawable.illustration_accessibility_gesture_three_finger
: R.drawable.illustration_accessibility_gesture_two_finger;
imageView.setImageResource(gestureSettingsImageResId);
final TextView textView = view.findViewById(R.id.gesture_tutorial_message);
textView.setText(isTouchExploreEnabled
? R.string.accessibility_tutorial_dialog_message_gesture_settings_talkback
: R.string.accessibility_tutorial_dialog_message_gesture_settings);
}
private static AlertDialog createDialog(Context context, int dialogType) {
final AlertDialog alertDialog = new AlertDialog.Builder(context)
.setView(createTutorialDialogContentView(context, dialogType))
@@ -238,7 +245,7 @@ public final class AccessibilityGestureNavigationTutorial {
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
final View itemView = mTutorialPages.get(position).getImageView();
final View itemView = mTutorialPages.get(position).getIllustrationView();
container.addView(itemView);
return itemView;
}
@@ -256,7 +263,7 @@ public final class AccessibilityGestureNavigationTutorial {
@Override
public void destroyItem(@NonNull ViewGroup container, int position,
@NonNull Object object) {
final View itemView = mTutorialPages.get(position).getImageView();
final View itemView = mTutorialPages.get(position).getIllustrationView();
container.removeView(itemView);
}
}
@@ -269,6 +276,34 @@ public final class AccessibilityGestureNavigationTutorial {
return imageView;
}
private static View createIllustrationView(Context context, @DrawableRes int imageRes) {
final View illustrationFrame = inflateAndInitIllustrationFrame(context);
final LottieAnimationView lottieView = illustrationFrame.findViewById(R.id.image);
lottieView.setImageResource(imageRes);
return illustrationFrame;
}
private static View createIllustrationViewWithImageRawResource(Context context,
@RawRes int imageRawRes) {
final View illustrationFrame = inflateAndInitIllustrationFrame(context);
final LottieAnimationView lottieView = illustrationFrame.findViewById(R.id.image);
lottieView.setFailureListener(
result -> Log.w(TAG, "Invalid image raw resource id: " + imageRawRes,
result));
lottieView.setAnimation(imageRawRes);
lottieView.setRepeatCount(LottieDrawable.INFINITE);
lottieView.playAnimation();
return illustrationFrame;
}
private static View inflateAndInitIllustrationFrame(Context context) {
final LayoutInflater inflater = context.getSystemService(LayoutInflater.class);
return inflater.inflate(R.layout.accessibility_lottie_animation_view, /* root= */ null);
}
private static View createShortcutNavigationContentView(Context context, int shortcutTypes) {
final LayoutInflater inflater = context.getSystemService(LayoutInflater.class);
final View contentView = inflater.inflate(
@@ -323,9 +358,8 @@ public final class AccessibilityGestureNavigationTutorial {
}
private static TutorialPage createSoftwareTutorialPage(@NonNull Context context) {
final CharSequence title = context.getText(
R.string.accessibility_tutorial_dialog_title_button);
final ImageView image = createSoftwareImage(context);
final CharSequence title = getSoftwareTitle(context);
final View image = createSoftwareImage(context);
final CharSequence instruction = getSoftwareInstruction(context);
final ImageView indicatorIcon =
createImageView(context, R.drawable.ic_accessibility_page_indicator);
@@ -337,8 +371,8 @@ public final class AccessibilityGestureNavigationTutorial {
private static TutorialPage createHardwareTutorialPage(@NonNull Context context) {
final CharSequence title =
context.getText(R.string.accessibility_tutorial_dialog_title_volume);
final ImageView image =
createImageView(context, R.drawable.accessibility_shortcut_type_hardware);
final View image =
createIllustrationView(context, R.drawable.accessibility_shortcut_type_hardware);
final ImageView indicatorIcon =
createImageView(context, R.drawable.ic_accessibility_page_indicator);
final CharSequence instruction =
@@ -351,8 +385,9 @@ public final class AccessibilityGestureNavigationTutorial {
private static TutorialPage createTripleTapTutorialPage(@NonNull Context context) {
final CharSequence title =
context.getText(R.string.accessibility_tutorial_dialog_title_triple);
final ImageView image =
createImageView(context, R.drawable.accessibility_shortcut_type_triple_tap);
final View image =
createIllustrationViewWithImageRawResource(context,
R.raw.accessibility_shortcut_type_triple_tap);
final CharSequence instruction =
context.getText(R.string.accessibility_tutorial_dialog_message_triple);
final ImageView indicatorIcon =
@@ -381,19 +416,47 @@ public final class AccessibilityGestureNavigationTutorial {
return tutorialPages;
}
private static ImageView createSoftwareImage(Context context) {
final int resId = AccessibilityUtil.isFloatingMenuEnabled(context)
? R.drawable.accessibility_shortcut_type_software_floating
: R.drawable.accessibility_shortcut_type_software;
private static View createSoftwareImage(Context context) {
int resId;
if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
resId = R.drawable.accessibility_shortcut_type_software_floating;
} else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
resId = AccessibilityUtil.isTouchExploreEnabled(context)
? R.drawable.accessibility_shortcut_type_software_gesture_talkback
: R.drawable.accessibility_shortcut_type_software_gesture;
} else {
resId = R.drawable.accessibility_shortcut_type_software;
}
return createIllustrationView(context, resId);
}
return createImageView(context, resId);
private static CharSequence getSoftwareTitle(Context context) {
int resId;
if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
resId = R.string.accessibility_tutorial_dialog_title_button;
} else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
resId = R.string.accessibility_tutorial_dialog_title_gesture;
} else {
resId = R.string.accessibility_tutorial_dialog_title_button;
}
return context.getText(resId);
}
private static CharSequence getSoftwareInstruction(Context context) {
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));
final SpannableStringBuilder sb = new SpannableStringBuilder();
if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
final int resId = R.string.accessibility_tutorial_dialog_message_floating_button;
sb.append(context.getText(resId));
} else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
final int resId = AccessibilityUtil.isTouchExploreEnabled(context)
? R.string.accessibility_tutorial_dialog_message_gesture_talkback
: R.string.accessibility_tutorial_dialog_message_gesture;
sb.append(context.getText(resId));
} else {
final int resId = R.string.accessibility_tutorial_dialog_message_button;
sb.append(getSoftwareInstructionWithIcon(context, context.getText(resId)));
}
return sb;
}
private static CharSequence getSoftwareInstructionWithIcon(Context context, CharSequence text) {
@@ -416,24 +479,26 @@ public final class AccessibilityGestureNavigationTutorial {
private static class TutorialPage {
private final CharSequence mTitle;
private final ImageView mImageView;
private final View mIllustrationView;
private final ImageView mIndicatorIcon;
private final CharSequence mInstruction;
TutorialPage(CharSequence title, ImageView imageView, ImageView indicatorIcon,
TutorialPage(CharSequence title, View illustrationView, ImageView indicatorIcon,
CharSequence instruction) {
this.mTitle = title;
this.mImageView = imageView;
this.mIllustrationView = illustrationView;
this.mIndicatorIcon = indicatorIcon;
this.mInstruction = instruction;
setupIllustrationChildViewsGravity();
}
public CharSequence getTitle() {
return mTitle;
}
public ImageView getImageView() {
return mImageView;
public View getIllustrationView() {
return mIllustrationView;
}
public ImageView getIndicatorIcon() {
@@ -443,6 +508,23 @@ public final class AccessibilityGestureNavigationTutorial {
public CharSequence getInstruction() {
return mInstruction;
}
private void setupIllustrationChildViewsGravity() {
final View backgroundView = mIllustrationView.findViewById(R.id.image_background);
initViewGravity(backgroundView);
final View lottieView = mIllustrationView.findViewById(R.id.image);
initViewGravity(lottieView);
}
private void initViewGravity(@NonNull View view) {
final FrameLayout.LayoutParams layoutParams =
new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.CENTER;
view.setLayoutParams(layoutParams);
}
}
private static class TutorialPageChangeListener implements ViewPager.OnPageChangeListener {

View File

@@ -27,10 +27,10 @@ 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 {
/** LayerDrawable that contains device icon as background and given icon as foreground. */
public class AccessibilityLayerDrawable extends LayerDrawable {
private FloatingMenuLayerDrawableState mState;
private AccessibilityLayerDrawableState mState;
/**
* Creates a new layer drawable with the list of specified layers.
@@ -38,23 +38,23 @@ public class FloatingMenuLayerDrawable extends LayerDrawable {
* @param layers a list of drawables to use as layers in this new drawable,
* must be non-null
*/
private FloatingMenuLayerDrawable(@NonNull Drawable[] layers) {
private AccessibilityLayerDrawable(@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.
* Create the {@link LayerDrawable} that contains device icon as background and given 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 resId the resource ID of the given icon
* @param opacity the opacity to apply to the given icon
* @return the drawable that combines the device icon and the floating menu icon
* @return the drawable that combines the device icon and the given icon
*/
public static FloatingMenuLayerDrawable createLayerDrawable(Context context, int resId,
public static AccessibilityLayerDrawable createLayerDrawable(Context context, int resId,
int opacity) {
final Drawable bg = context.getDrawable(R.drawable.accessibility_button_preview_base);
final FloatingMenuLayerDrawable basicDrawable = new FloatingMenuLayerDrawable(
final AccessibilityLayerDrawable basicDrawable = new AccessibilityLayerDrawable(
new Drawable[]{bg, null});
basicDrawable.updateLayerDrawable(context, resId, opacity);
@@ -66,7 +66,7 @@ public class FloatingMenuLayerDrawable extends LayerDrawable {
* 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 resId the resource ID of the given icon
* @param opacity the opacity to apply to the given icon
*/
public void updateLayerDrawable(Context context, int resId, int opacity) {
@@ -83,18 +83,18 @@ public class FloatingMenuLayerDrawable extends LayerDrawable {
/** 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);
mState = new AccessibilityLayerDrawableState(context, resId, opacity);
}
/** {@link ConstantState} to store the data of {@link FloatingMenuLayerDrawable}. */
/** {@link ConstantState} to store the data of {@link AccessibilityLayerDrawable}. */
@VisibleForTesting
static class FloatingMenuLayerDrawableState extends ConstantState {
static class AccessibilityLayerDrawableState extends ConstantState {
private final Context mContext;
private final int mResId;
private final int mOpacity;
FloatingMenuLayerDrawableState(Context context, int resId, int opacity) {
AccessibilityLayerDrawableState(Context context, int resId, int opacity) {
mContext = context;
mResId = resId;
mOpacity = opacity;
@@ -119,7 +119,7 @@ public class FloatingMenuLayerDrawable extends LayerDrawable {
if (o == null || getClass() != o.getClass()) {
return false;
}
final FloatingMenuLayerDrawableState that = (FloatingMenuLayerDrawableState) o;
final AccessibilityLayerDrawableState that = (AccessibilityLayerDrawableState) o;
return mResId == that.mResId
&& mOpacity == that.mOpacity
&& Objects.equals(mContext, that.mContext);

View File

@@ -0,0 +1,36 @@
/*
* 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.ComponentName;
import androidx.annotation.Nullable;
/**
* Provider for Accessibility metrics related features.
*/
public interface AccessibilityMetricsFeatureProvider {
/**
* Returns {@link android.app.settings.SettingsEnums} value according to the {@code
* componentName}.
*
* @param componentName the component name of the downloaded service or activity
* @return value in {@link android.app.settings.SettingsEnums}
*/
int getDownloadedFeatureMetricsCategory(@Nullable ComponentName componentName);
}

View File

@@ -0,0 +1,32 @@
/*
* 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.content.ComponentName;
/**
* Provider implementation for Accessibility metrics related features.
*/
public class AccessibilityMetricsFeatureProviderImpl implements
AccessibilityMetricsFeatureProvider {
@Override
public int getDownloadedFeatureMetricsCategory(ComponentName componentName) {
return SettingsEnums.ACCESSIBILITY_SERVICE;
}
}

View File

@@ -0,0 +1,174 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.android.settings.core.SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE;
import android.app.settings.SettingsEnums;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceFragmentCompat;
import com.android.settings.R;
import com.android.settings.core.InstrumentedActivity;
import com.android.settings.display.FontSizePreferenceFragmentForSetupWizard;
import com.android.settings.display.ScreenZoomPreferenceFragmentForSetupWizard;
import com.android.settingslib.transition.SettingsTransitionHelper.TransitionType;
import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupdesign.GlifLayout;
import com.google.android.setupdesign.util.ThemeHelper;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/** Settings font/display size activity for SUW. */
public class AccessibilityScreenSizeForSetupWizardActivity extends InstrumentedActivity {
private static final String TAG = "ScreenSizeForSetup";
// A parameter decides which fragment ({@link FontSizePreferenceFragmentForSetupWizard} or
// {@link ScreenZoomPreferenceFragmentForSetupWizard}) will be visioned.
static final String VISION_FRAGMENT_NO = "vision_fragment_no";
/**
* Flags indicating the type of the fragment.
*/
@IntDef({
FragmentType.FONT_SIZE,
FragmentType.SCREEN_SIZE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface FragmentType {
int FONT_SIZE = 1;
int SCREEN_SIZE = 2;
}
// Keep the last height of the scroll view in the {@link GlifLayout}
private int mLastScrollViewHeight;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final int appliedTheme = ThemeHelper.trySetDynamicColor(this)
? R.style.SudDynamicColorThemeGlifV3_DayNight : R.style.SudThemeGlifV3_DayNight;
setTheme(appliedTheme);
setContentView(R.layout.accessibility_screen_size_setup_wizard);
updateHeaderLayout();
scrollToBottom();
initFooterButton();
if (savedInstanceState == null) {
final PreferenceFragmentCompat fragment =
getFragmentType(getIntent()) == FragmentType.FONT_SIZE
? new FontSizePreferenceFragmentForSetupWizard()
: new ScreenZoomPreferenceFragmentForSetupWizard();
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content_frame, fragment)
.commit();
}
}
@Override
protected void onPause() {
// For accessibility activities launched from setup wizard.
if (getTransitionType(getIntent()) == TransitionType.TRANSITION_FADE) {
overridePendingTransition(R.anim.sud_stay, android.R.anim.fade_out);
}
super.onPause();
}
@Override
public int getMetricsCategory() {
return getFragmentType(getIntent()) == FragmentType.FONT_SIZE
? SettingsEnums.SUW_ACCESSIBILITY_FONT_SIZE
: SettingsEnums.SUW_ACCESSIBILITY_DISPLAY_SIZE;
}
@VisibleForTesting
void updateHeaderLayout() {
if (ThemeHelper.shouldApplyExtendedPartnerConfig(this) && isSuwSupportedTwoPanes()) {
final GlifLayout layout = findViewById(R.id.setup_wizard_layout);
final LinearLayout headerLayout = layout.findManagedViewById(R.id.sud_layout_header);
if (headerLayout != null) {
headerLayout.setPadding(0, layout.getPaddingTop(), 0,
layout.getPaddingBottom());
}
}
((TextView) findViewById(R.id.suc_layout_title)).setText(
getFragmentType(getIntent()) == FragmentType.FONT_SIZE
? R.string.title_font_size
: R.string.screen_zoom_title);
((TextView) findViewById(R.id.sud_layout_subtitle)).setText(
getFragmentType(getIntent()) == FragmentType.FONT_SIZE
? R.string.font_size_summary
: R.string.screen_zoom_summary);
}
private boolean isSuwSupportedTwoPanes() {
return getResources().getBoolean(R.bool.config_suw_supported_two_panes);
}
private void initFooterButton() {
final GlifLayout layout = findViewById(R.id.setup_wizard_layout);
final FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
final View.OnClickListener nextButtonListener = v -> onBackPressed();
final FooterButton primaryButton =
new FooterButton.Builder(this)
.setText(R.string.done)
.setListener(nextButtonListener)
.setButtonType(FooterButton.ButtonType.NEXT)
.setTheme(R.style.SudGlifButton_Primary)
.build();
mixin.setPrimaryButton(primaryButton);
}
/**
* Scrolls to bottom while {@link ScrollView} layout changed.
*/
private void scrollToBottom() {
mLastScrollViewHeight = 0;
final GlifLayout layout = findViewById(R.id.setup_wizard_layout);
final ScrollView scrollView = layout.getScrollView();
scrollView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
final int scrollViewHeight = scrollView.getHeight();
if (scrollViewHeight > 0 && scrollViewHeight != mLastScrollViewHeight) {
mLastScrollViewHeight = scrollViewHeight;
scrollView.post(() -> {
// Here is no need to show the scrolling animation. So disabled first and
// then enabled it after scrolling finished.
scrollView.setSmoothScrollingEnabled(false);
scrollView.fullScroll(View.FOCUS_DOWN);
scrollView.setSmoothScrollingEnabled(true);
});
}
});
}
private int getTransitionType(Intent intent) {
return intent.getIntExtra(EXTRA_PAGE_TRANSITION_TYPE, TransitionType.TRANSITION_NONE);
}
private int getFragmentType(Intent intent) {
return intent.getIntExtra(VISION_FRAGMENT_NO, FragmentType.FONT_SIZE);
}
}

View File

@@ -28,29 +28,29 @@ import android.content.pm.ServiceInfo;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.TextUtils;
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.settings.dashboard.DashboardFragment;
import com.android.settingslib.RestrictedPreference;
import com.google.android.setupdesign.GlifPreferenceLayout;
import com.google.android.setupdesign.util.ThemeHelper;
import java.util.List;
/**
* Activity with the accessibility settings specific to Setup Wizard.
*/
public class AccessibilitySettingsForSetupWizard extends SettingsPreferenceFragment
public class AccessibilitySettingsForSetupWizard extends DashboardFragment
implements Preference.OnPreferenceChangeListener {
private static final String TAG = "AccessibilitySettingsForSetupWizard";
// Preferences.
private static final String DISPLAY_MAGNIFICATION_PREFERENCE =
@@ -81,16 +81,11 @@ public class AccessibilitySettingsForSetupWizard extends SettingsPreferenceFragm
super.onViewCreated(view, savedInstanceState);
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());
}
final String title = getContext().getString(R.string.vision_settings_title);
final String description = getContext().getString(R.string.vision_settings_description);
final Drawable icon = getContext().getDrawable(R.drawable.ic_accessibility_visibility);
AccessibilitySetupWizardUtils.updateGlifPreferenceLayout(getContext(), layout, title,
description, icon);
}
@Override
@@ -103,8 +98,6 @@ public class AccessibilitySettingsForSetupWizard extends SettingsPreferenceFragm
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.accessibility_settings_for_setup_wizard);
mDisplayMagnificationPreference = findPreference(DISPLAY_MAGNIFICATION_PREFERENCE);
mScreenReaderPreference = findPreference(SCREEN_READER_PREFERENCE);
mSelectToSpeakPreference = findPreference(SELECT_TO_SPEAK_PREFERENCE);
@@ -143,6 +136,23 @@ public class AccessibilitySettingsForSetupWizard extends SettingsPreferenceFragm
return super.onPreferenceTreeClick(preference);
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.accessibility_settings_for_setup_wizard;
}
@Override
protected String getLogTag() {
return TAG;
}
/**
* Returns accessibility service info by given package name and service name.
*
* @param packageName Package of accessibility service
* @param serviceName Class of accessibility service
* @return {@link AccessibilityServiceInfo} instance if available, null otherwise.
*/
private AccessibilityServiceInfo findService(String packageName, String serviceName) {
final AccessibilityManager manager =
getActivity().getSystemService(AccessibilityManager.class);
@@ -150,8 +160,8 @@ public class AccessibilitySettingsForSetupWizard extends SettingsPreferenceFragm
manager.getInstalledAccessibilityServiceList();
for (AccessibilityServiceInfo info : accessibilityServices) {
ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo;
if (packageName.equals(serviceInfo.packageName)
&& serviceName.equals(serviceInfo.name)) {
if (TextUtils.equals(packageName, serviceInfo.packageName)
&& TextUtils.equals(serviceName, serviceInfo.name)) {
return info;
}
}

View File

@@ -16,7 +16,10 @@
package com.android.settings.accessibility;
import static com.android.settings.accessibility.AccessibilityScreenSizeForSetupWizardActivity.VISION_FRAGMENT_NO;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
@@ -29,8 +32,8 @@ import androidx.preference.PreferenceFragmentCompat;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SetupWizardUtils;
import com.android.settings.accessibility.AccessibilityScreenSizeForSetupWizardActivity.FragmentType;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.display.FontSizePreferenceFragmentForSetupWizard;
import com.android.settings.search.actionbar.SearchMenuController;
import com.android.settings.support.actionbar.HelpResourceProvider;
import com.android.settingslib.core.instrumentation.Instrumentable;
@@ -123,19 +126,11 @@ public class AccessibilitySettingsForSetupWizardActivity extends SettingsActivit
&& new ComponentName(getPackageName(),
CLASS_NAME_FONT_SIZE_SETTINGS_FOR_SUW).equals(
getIntent().getComponent())) {
final Bundle args = new Bundle();
args.putInt(HelpResourceProvider.HELP_URI_RESOURCE_KEY, 0);
args.putBoolean(SearchMenuController.NEED_SEARCH_ICON_IN_ACTION_BAR, false);
final SubSettingLauncher subSettingLauncher = new SubSettingLauncher(this)
.setDestination(FontSizePreferenceFragmentForSetupWizard.class.getName())
.setArguments(args)
.setSourceMetricsCategory(Instrumentable.METRICS_CATEGORY_UNKNOWN)
.setExtras(SetupWizardUtils.copyLifecycleExtra(getIntent().getExtras(),
new Bundle()))
.setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_FADE);
final Intent intent = new Intent(this,
AccessibilityScreenSizeForSetupWizardActivity.class);
intent.putExtra(VISION_FRAGMENT_NO, FragmentType.FONT_SIZE);
startActivity(intent);
Log.d(LOG_TAG, "Launch font size settings");
subSettingLauncher.launch();
finish();
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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.widget.LinearLayout;
import com.android.settings.R;
import com.google.android.setupdesign.GlifPreferenceLayout;
import com.google.android.setupdesign.util.ThemeHelper;
/** Provides utility methods to accessibility settings for Setup Wizard only. */
class AccessibilitySetupWizardUtils {
private AccessibilitySetupWizardUtils(){}
/**
* Update the {@link GlifPreferenceLayout} attributes if they have previously been initialized.
* When the SetupWizard supports the extended partner configs, it means the material layout
* would be applied. It should set a different padding/margin in views to align Settings style
* for accessibility feature pages.
*
* @param layout The layout instance
* @param title The text to be set as title
* @param description The text to be set as description
* @param icon The icon to be set
*/
public static void updateGlifPreferenceLayout(Context context, GlifPreferenceLayout layout,
CharSequence title, CharSequence description, Drawable icon) {
layout.setHeaderText(title);
layout.setDescriptionText(description);
layout.setIcon(icon);
layout.setDividerInsets(Integer.MAX_VALUE, 0);
if (ThemeHelper.shouldApplyExtendedPartnerConfig(context)) {
final LinearLayout headerLayout = layout.findManagedViewById(R.id.sud_layout_header);
if (headerLayout != null) {
headerLayout.setPadding(0, layout.getPaddingTop(), 0,
layout.getPaddingBottom());
}
}
}
}

View File

@@ -23,6 +23,7 @@ import android.content.Context;
import android.os.UserHandle;
import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
/**
@@ -57,4 +58,9 @@ public class AccessibilityShortcutPreferenceController extends TogglePreferenceC
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
}

View File

@@ -115,8 +115,7 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF
mShortcutPreference.setKey(getShortcutPreferenceKey());
mShortcutPreference.setOnClickCallback(this);
final CharSequence title = getString(R.string.accessibility_shortcut_title, getLabelName());
mShortcutPreference.setTitle(title);
updateShortcutTitle(mShortcutPreference);
getPreferenceScreen().addPreference(mShortcutPreference);
mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
@@ -182,6 +181,11 @@ public abstract class AccessibilityShortcutPreferenceFragment extends DashboardF
}
}
protected void updateShortcutTitle(ShortcutPreference shortcutPreference) {
final CharSequence title = getString(R.string.accessibility_shortcut_title, getLabelName());
shortcutPreference.setTitle(title);
}
@Override
public int getDialogMetricsCategory(int dialogId) {
switch (dialogId) {

View File

@@ -26,6 +26,7 @@ import android.content.Context;
import android.provider.Settings;
import android.view.accessibility.AccessibilityManager;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settingslib.accessibility.AccessibilityUtils;
@@ -98,6 +99,11 @@ public class AccessibilitySlicePreferenceController extends TogglePreferenceCont
return true;
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
private AccessibilityServiceInfo getAccessibilityServiceInfo() {
final AccessibilityManager accessibilityManager = mContext.getSystemService(
AccessibilityManager.class);

View File

@@ -17,16 +17,22 @@
package com.android.settings.accessibility;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.WindowManager;
import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.IntDef;
@@ -390,6 +396,25 @@ final class AccessibilityUtil {
resources.getDisplayMetrics()));
}
/**
* Gets the bounds of the display window excluding the insets of the system bar and display
* cut out.
*
* @param context the current context.
* @return the bounds of the display window.
*/
public static Rect getDisplayBounds(Context context) {
final WindowManager windowManager = context.getSystemService(WindowManager.class);
final WindowMetrics metrics = windowManager.getCurrentWindowMetrics();
final Rect displayBounds = metrics.getBounds();
final Insets displayInsets = metrics.getWindowInsets().getInsetsIgnoringVisibility(
systemBars() | displayCutout());
displayBounds.inset(displayInsets);
return displayBounds;
}
/**
* Indicates if the accessibility service belongs to a system App.
* @param info AccessibilityServiceInfo

View File

@@ -17,6 +17,7 @@
package com.android.settings.accessibility;
import static android.view.HapticFeedbackConstants.CLOCK_TICK;
import static com.android.settings.Utils.isNightMode;
import android.content.Context;
@@ -154,7 +155,7 @@ public class BalanceSeekBar extends SeekBar {
// Draw a vertical line at 50% that represents centred balance
int seekBarCenter = (canvas.getHeight() - getPaddingBottom()) / 2;
canvas.save();
canvas.translate((canvas.getWidth() - mCenterMarkerRect.right) / 2,
canvas.translate((canvas.getWidth() - mCenterMarkerRect.right - getPaddingEnd()) / 2,
seekBarCenter - (mCenterMarkerRect.bottom / 2));
canvas.drawRect(mCenterMarkerRect, mCenterMarkerPaint);
canvas.restore();

View File

@@ -30,12 +30,18 @@ public class CaptionFooterPreferenceController extends AccessibilityFooterPrefer
}
@Override
protected String getLabelName() {
return mContext.getString(R.string.accessibility_captioning_title);
protected String getLearnMoreContentDescription() {
return mContext.getString(
R.string.accessibility_captioning_footer_learn_more_content_description);
}
@Override
protected int getHelpResource() {
protected String getIntroductionTitle() {
return mContext.getString(R.string.accessibility_captioning_about_title);
}
@Override
protected int getHelpResource() {
return R.string.help_url_caption;
}
}

View File

@@ -22,6 +22,7 @@ import android.text.TextUtils;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
public class DisableAnimationsPreferenceController extends TogglePreferenceController {
@@ -71,4 +72,9 @@ public class DisableAnimationsPreferenceController extends TogglePreferenceContr
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.android.settings.accessibility.AccessibilityScreenSizeForSetupWizardActivity.VISION_FRAGMENT_NO;
import static com.android.settings.core.SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE;
import android.content.Context;
import android.content.Intent;
import androidx.preference.Preference;
import com.android.settings.accessibility.AccessibilityScreenSizeForSetupWizardActivity.FragmentType;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.transition.SettingsTransitionHelper.TransitionType;
/** PreferenceController for displaying font size page. */
public class FontSizePreferenceController extends BasePreferenceController {
public FontSizePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (!mPreferenceKey.equals(preference.getKey())) {
return false;
}
final Intent intent = new Intent(mContext,
AccessibilityScreenSizeForSetupWizardActivity.class);
intent.putExtra(VISION_FRAGMENT_NO, FragmentType.FONT_SIZE);
intent.putExtra(EXTRA_PAGE_TRANSITION_TYPE, TransitionType.TRANSITION_FADE);
mContext.startActivity(intent);
return true;
}
}

View File

@@ -20,6 +20,7 @@ import android.content.Context;
import android.graphics.fonts.FontStyle;
import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
/** PreferenceController for displaying all text in bold. */
@@ -47,4 +48,9 @@ public class FontWeightAdjustmentPreferenceController extends TogglePreferenceCo
return Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.FONT_WEIGHT_ADJUSTMENT, (isChecked ? BOLD_TEXT_ADJUSTMENT : 0));
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
}

View File

@@ -19,6 +19,7 @@ package com.android.settings.accessibility;
import android.content.Context;
import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
public class HighTextContrastPreferenceController extends TogglePreferenceController {
@@ -43,4 +44,9 @@ public class HighTextContrastPreferenceController extends TogglePreferenceContro
return Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, (isChecked ? 1 : 0));
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
}

View File

@@ -21,6 +21,7 @@ import android.provider.Settings;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
public class LargePointerIconPreferenceController extends TogglePreferenceController {
@@ -50,4 +51,9 @@ public class LargePointerIconPreferenceController extends TogglePreferenceContro
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
}

View File

@@ -41,6 +41,7 @@ import androidx.annotation.Nullable;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory;
import java.util.ArrayList;
import java.util.List;
@@ -51,6 +52,19 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature
private static final String EMPTY_STRING = "";
protected static final String KEY_LAUNCH_PREFERENCE = "launch_preference";
@Override
public int getMetricsCategory() {
// Retrieve from getArguments() directly because this function will be executed from
// onAttach(), but variable mComponentName only available after onProcessArguments()
// which comes from onCreateView().
final ComponentName componentName = getArguments().getParcelable(
AccessibilitySettings.EXTRA_COMPONENT_NAME);
return FeatureFactory.getFactory(getActivity().getApplicationContext())
.getAccessibilityMetricsFeatureProvider()
.getDownloadedFeatureMetricsCategory(componentName);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@@ -60,7 +74,7 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature
initLaunchPreference();
removePreference(KEY_USE_SERVICE_PREFERENCE);
return view;
};
}
@Override
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
@@ -70,7 +84,6 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature
@Override
protected void onProcessArguments(Bundle arguments) {
super.onProcessArguments(arguments);
mComponentName = arguments.getParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME);
final ActivityInfo info = getAccessibilityShortcutInfo().getActivityInfo();
mPackageName = info.loadLabel(getPackageManager()).toString();
@@ -137,6 +150,7 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature
private void initLaunchPreference() {
final Preference launchPreference = new Preference(getPrefContext());
launchPreference.setLayoutResource(R.layout.accessibility_launch_activity_preference);
launchPreference.setKey(KEY_LAUNCH_PREFERENCE);
final AccessibilityShortcutInfo info = getAccessibilityShortcutInfo();

View File

@@ -23,6 +23,7 @@ import androidx.preference.PreferenceScreen;
import com.android.internal.view.RotationPolicy;
import com.android.internal.view.RotationPolicy.RotationPolicyListener;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
@@ -61,6 +62,11 @@ public class LockScreenRotationPreferenceController extends TogglePreferenceCont
return RotationPolicy.isRotationSupported(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
@Override
public void onStop() {
if (mRotationPolicyListener != null) {

View File

@@ -76,6 +76,11 @@ public class MagnificationGesturesPreferenceController extends TogglePreferenceC
return true;
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
@Override
public CharSequence getSummary() {
int resId = 0;

View File

@@ -82,6 +82,11 @@ public class MagnificationNavbarPreferenceController extends TogglePreferenceCon
return true;
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
@Override
public CharSequence getSummary() {
int resId = 0;

View File

@@ -21,6 +21,7 @@ import android.provider.Settings;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.TogglePreferenceController;
@@ -51,4 +52,9 @@ public class PowerButtonEndsCallPreferenceController extends TogglePreferenceCon
return !KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POWER)
|| !Utils.isVoiceCapable(mContext) ? UNSUPPORTED_ON_DEVICE : AVAILABLE;
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
}

View File

@@ -20,6 +20,7 @@ import android.content.Context;
import android.os.UserHandle;
import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
/**
@@ -47,4 +48,9 @@ public class PrimaryMonoPreferenceController extends TogglePreferenceController
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
}

View File

@@ -22,6 +22,7 @@ import android.provider.Settings;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
/** PreferenceController for persisting feature activation state after a restart. */
@@ -62,4 +63,9 @@ public class ReduceBrightColorsPersistencePreferenceController extends TogglePre
super.updateState(preference);
preference.setEnabled(mColorDisplayManager.isReduceBrightColorsActivated());
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
}

View File

@@ -94,6 +94,11 @@ public class ReduceBrightColorsPreferenceController extends TogglePreferenceCont
mPreference = screen.findPreference(getPreferenceKey());
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
@Override
public void onStart() {
mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.android.settings.accessibility.AccessibilityScreenSizeForSetupWizardActivity.VISION_FRAGMENT_NO;
import static com.android.settings.core.SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE;
import android.content.Context;
import android.content.Intent;
import androidx.preference.Preference;
import com.android.settings.accessibility.AccessibilityScreenSizeForSetupWizardActivity.FragmentType;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.transition.SettingsTransitionHelper.TransitionType;
/** PreferenceController for displaying screen size page. */
public class ScreenSizePreferenceController extends BasePreferenceController {
public ScreenSizePreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (!mPreferenceKey.equals(preference.getKey())) {
return false;
}
final Intent intent = new Intent(mContext,
AccessibilityScreenSizeForSetupWizardActivity.class);
intent.putExtra(VISION_FRAGMENT_NO, FragmentType.SCREEN_SIZE);
intent.putExtra(EXTRA_PAGE_TRANSITION_TYPE, TransitionType.TRANSITION_FADE);
mContext.startActivity(intent);
return true;
}
}

View File

@@ -54,8 +54,8 @@ 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.overlay.FeatureFactory;
import com.android.settings.password.ConfirmDeviceCredentialActivity;
import com.android.settings.widget.SettingsMainSwitchPreference;
import com.android.settingslib.accessibility.AccessibilityUtils;
import java.util.List;
@@ -85,7 +85,15 @@ public class ToggleAccessibilityServicePreferenceFragment extends
@Override
public int getMetricsCategory() {
return SettingsEnums.ACCESSIBILITY_SERVICE;
// Retrieve from getArguments() directly because this function will be executed from
// onAttach(), but variable mComponentName only available after onProcessArguments()
// which comes from onCreateView().
final ComponentName componentName = getArguments().getParcelable(
AccessibilitySettings.EXTRA_COMPONENT_NAME);
return FeatureFactory.getFactory(getActivity().getApplicationContext())
.getAccessibilityMetricsFeatureProvider()
.getDownloadedFeatureMetricsCategory(componentName);
}
@Override
@@ -221,15 +229,6 @@ public class ToggleAccessibilityServicePreferenceFragment extends
mComponentName);
}
@Override
protected void updateToggleServiceTitle(SettingsMainSwitchPreference switchPreference) {
final AccessibilityServiceInfo info = getAccessibilityServiceInfo();
final String switchBarText = (info == null) ? "" :
getString(R.string.accessibility_service_primary_switch_title,
info.getResolveInfo().loadLabel(getPackageManager()));
switchPreference.setTitle(switchBarText);
}
@Override
protected void updateSwitchBarToggleSwitch() {
final boolean checked = isAccessibilityServiceEnabled();

View File

@@ -31,8 +31,14 @@ public class ToggleAutoclickFooterPreferenceController extends
}
@Override
protected String getLabelName() {
return mContext.getString(R.string.accessibility_autoclick_preference_title);
protected String getLearnMoreContentDescription() {
return mContext.getString(
R.string.accessibility_autoclick_footer_learn_more_content_description);
}
@Override
protected String getIntroductionTitle() {
return mContext.getString(R.string.accessibility_autoclick_about_title);
}
@Override

View File

@@ -38,8 +38,7 @@ 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();
@@ -72,6 +71,11 @@ public class ToggleColorInversionPreferenceFragment extends
switchPreference.setTitle(R.string.accessibility_display_inversion_switch_title);
}
@Override
protected void updateShortcutTitle(ShortcutPreference shortcutPreference) {
shortcutPreference.setTitle(R.string.accessibility_display_inversion_shortcut_title);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@@ -90,7 +94,20 @@ public class ToggleColorInversionPreferenceFragment extends
updateSwitchBarToggleSwitch();
}
};
return super.onCreateView(inflater, container, savedInstanceState);
final View view = super.onCreateView(inflater, container, savedInstanceState);
updateFooterPreference();
return view;
}
private void updateFooterPreference() {
final String title = getPrefContext().getString(
R.string.accessibility_color_inversion_about_title);
final String learnMoreContentDescription = getPrefContext().getString(
R.string.accessibility_color_inversion_footer_learn_more_content_description);
mFooterPreferenceController.setIntroductionTitle(title);
mFooterPreferenceController.setupHelpLink(getHelpResource(), learnMoreContentDescription);
mFooterPreferenceController.displayPreference(getPreferenceScreen());
}
@Override

View File

@@ -92,12 +92,20 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe
updateSwitchBarToggleSwitch();
}
};
return super.onCreateView(inflater, container, savedInstanceState);
final View view = super.onCreateView(inflater, container, savedInstanceState);
updateFooterPreference();
return view;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
private void updateFooterPreference() {
final String title = getPrefContext()
.getString(R.string.accessibility_daltonizer_about_title);
final String learnMoreContentDescription = getPrefContext()
.getString(R.string.accessibility_daltonizer_footer_learn_more_content_description);
mFooterPreferenceController.setIntroductionTitle(title);
mFooterPreferenceController.setupHelpLink(getHelpResource(), learnMoreContentDescription);
mFooterPreferenceController.displayPreference(getPreferenceScreen());
}
/** Customizes the order by preference key. */
@@ -167,6 +175,11 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe
switchPreference.setTitle(R.string.accessibility_daltonizer_primary_switch_title);
}
@Override
protected void updateShortcutTitle(ShortcutPreference shortcutPreference) {
shortcutPreference.setTitle(R.string.accessibility_daltonizer_shortcut_title);
}
@Override
int getUserShortcutTypes() {
return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(),

View File

@@ -57,7 +57,6 @@ import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
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.IllustrationPreference;
import com.android.settingslib.widget.OnMainSwitchChangeListener;
@@ -78,7 +77,9 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
protected SettingsMainSwitchPreference mToggleServiceSwitchPreference;
protected ShortcutPreference mShortcutPreference;
protected Preference mSettingsPreference;
protected AccessibilityFooterPreferenceController mFooterPreferenceController;
protected String mPreferenceKey;
protected Dialog mDialog;
protected CharSequence mSettingsTitle;
protected Intent mSettingsIntent;
@@ -106,6 +107,7 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
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;
private boolean mSavedAccessibilityFloatingMenuEnabled;
// For html description of accessibility service, must follow the rule, such as
// <img src="R.drawable.fileName"/>, a11y settings will get the resources successfully.
@@ -127,7 +129,6 @@ 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)) {
@@ -142,9 +143,7 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
setPreferenceScreen(preferenceScreen);
}
final List<String> shortcutFeatureKeys = new ArrayList<>();
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
final List<String> shortcutFeatureKeys = getFeatureSettingsKeys();
mSettingsContentObserver = new SettingsContentObserver(new Handler(), shortcutFeatureKeys) {
@Override
public void onChange(boolean selfChange, Uri uri) {
@@ -154,6 +153,13 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
};
}
protected List<String> getFeatureSettingsKeys() {
final List<String> shortcutFeatureKeys = new ArrayList<>();
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
return shortcutFeatureKeys;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@@ -200,6 +206,8 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
mSettingsContentObserver.register(getContentResolver());
updateShortcutPreferenceData();
updateShortcutPreference();
updateEditShortcutDialogIfNeeded();
}
@Override
@@ -208,6 +216,8 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
AccessibilityManager.class);
am.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
mSettingsContentObserver.unregister(getContentResolver());
mSavedAccessibilityFloatingMenuEnabled = AccessibilityUtil.isFloatingMenuEnabled(
getContext());
super.onPause();
}
@@ -222,24 +232,23 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
@Override
public Dialog onCreateDialog(int dialogId) {
Dialog dialog;
switch (dialogId) {
case DialogEnums.EDIT_SHORTCUT:
final CharSequence dialogTitle = getPrefContext().getString(
R.string.accessibility_shortcut_title, mPackageName);
final int dialogType = WizardManagerHelper.isAnySetupWizard(getIntent())
? DialogType.EDIT_SHORTCUT_GENERIC_SUW : DialogType.EDIT_SHORTCUT_GENERIC;
dialog = AccessibilityDialogUtils.showEditShortcutDialog(
mDialog = AccessibilityDialogUtils.showEditShortcutDialog(
getPrefContext(), dialogType, dialogTitle,
this::callOnAlertDialogCheckboxClicked);
setupEditShortcutDialog(dialog);
return dialog;
setupEditShortcutDialog(mDialog);
return mDialog;
case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
dialog = AccessibilityGestureNavigationTutorial
mDialog = AccessibilityGestureNavigationTutorial
.createAccessibilityTutorialDialog(getPrefContext(),
getUserShortcutTypes());
dialog.setCanceledOnTouchOutside(false);
return dialog;
mDialog.setCanceledOnTouchOutside(false);
return mDialog;
default:
throw new IllegalArgumentException("Unsupported dialogId " + dialogId);
}
@@ -284,7 +293,14 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
abstract int getUserShortcutTypes();
protected void updateToggleServiceTitle(SettingsMainSwitchPreference switchPreference) {
switchPreference.setTitle(R.string.accessibility_service_primary_switch_title);
final CharSequence title =
getString(R.string.accessibility_service_primary_switch_title, mPackageName);
switchPreference.setTitle(title);
}
protected void updateShortcutTitle(ShortcutPreference shortcutPreference) {
final CharSequence title = getString(R.string.accessibility_shortcut_title, mPackageName);
shortcutPreference.setTitle(title);
}
protected abstract void onPreferenceToggled(String preferenceKey, boolean enabled);
@@ -399,10 +415,13 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
return;
}
final int displayHalfHeight =
AccessibilityUtil.getDisplayBounds(getPrefContext()).height() / 2;
final IllustrationPreference illustrationPreference =
new IllustrationPreference(getPrefContext());
illustrationPreference.setImageUri(mImageUri);
illustrationPreference.setSelectable(false);
illustrationPreference.setMaxHeight(displayHalfHeight);
illustrationPreference.setKey(KEY_ANIMATED_IMAGE);
getPreferenceScreen().addPreference(illustrationPreference);
@@ -434,8 +453,7 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
mShortcutPreference.setKey(getShortcutPreferenceKey());
mShortcutPreference.setOnClickCallback(this);
final CharSequence title = getString(R.string.accessibility_shortcut_title, mPackageName);
mShortcutPreference.setTitle(title);
updateShortcutTitle(mShortcutPreference);
final PreferenceCategory generalCategory = findPreference(KEY_GENERAL_CATEGORY);
generalCategory.addPreference(mShortcutPreference);
@@ -463,33 +481,19 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
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);
// TODO(b/171272809): Migrate to DashboardFragment.
final String title = getString(R.string.accessibility_introduction_title, mPackageName);
mFooterPreferenceController = new AccessibilityFooterPreferenceController(
screen.getContext(), htmlFooterPreference.getKey());
mFooterPreferenceController.setIntroductionTitle(title);
mFooterPreferenceController.displayPreference(screen);
}
private void initFooterPreference() {
@@ -512,41 +516,22 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
*
* @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.
* @param introductionTitle The title of introduction in the footer.
*/
@VisibleForTesting
void createFooterPreference(PreferenceScreen screen, CharSequence summary,
String iconContentDescription) {
String introductionTitle) {
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);
mFooterPreferenceController = new AccessibilityFooterPreferenceController(
screen.getContext(), footerPreference.getKey());
mFooterPreferenceController.setIntroductionTitle(introductionTitle);
mFooterPreferenceController.displayPreference(screen);
}
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);
@@ -611,6 +596,18 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
return value;
}
private static CharSequence getSoftwareShortcutTypeSummary(Context context) {
int resId;
if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
resId = R.string.accessibility_shortcut_edit_summary_software;
} else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
resId = R.string.accessibility_shortcut_edit_summary_software_gesture;
} else {
resId = R.string.accessibility_shortcut_edit_summary_software;
}
return context.getText(resId);
}
protected CharSequence getShortcutTypeSummary(Context context) {
if (!mShortcutPreference.isSettingsEditable()) {
return context.getText(R.string.accessibility_shortcut_edit_dialog_title_hardware);
@@ -624,11 +621,8 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
mComponentName.flattenToString(), 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);
list.add(getSoftwareShortcutTypeSummary(context));
}
if (hasShortcutType(shortcutTypes, UserShortcutType.HARDWARE)) {
final CharSequence hardwareTitle = context.getText(
@@ -638,7 +632,7 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
// Show software shortcut if first time to use.
if (list.isEmpty()) {
list.add(softwareTitle);
list.add(getSoftwareShortcutTypeSummary(context));
}
return CaseMap.toTitle().wholeString().noLowercase().apply(Locale.getDefault(), /* iter= */
@@ -748,6 +742,20 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
}
}
private void updateEditShortcutDialogIfNeeded() {
if (mDialog == null || !mDialog.isShowing()) {
return;
}
// Content in software shortcut need to be adjusted depend on the accessibility button
// mode status which can be changed in background.
final boolean valueChanged = mSavedAccessibilityFloatingMenuEnabled
!= AccessibilityUtil.isFloatingMenuEnabled(getContext());
if (valueChanged) {
AccessibilityDialogUtils.updateSoftwareShortcutInDialog(getContext(), mDialog);
}
}
@VisibleForTesting
void saveNonEmptyUserShortcutType(int type) {
if (type == UserShortcutType.EMPTY) {

View File

@@ -86,6 +86,7 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
// Parent sets the title when creating the view, so set it after calling super
mToggleServiceSwitchPreference.setTitle(R.string.reduce_bright_colors_switch_title);
updateGeneralCategoryOrder();
updateFooterPreference();
return view;
}
@@ -101,6 +102,12 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
generalCategory.addPreference(persist);
}
private void updateFooterPreference() {
final String title = getPrefContext().getString(R.string.reduce_bright_colors_about_title);
mFooterPreferenceController.setIntroductionTitle(title);
mFooterPreferenceController.displayPreference(getPreferenceScreen());
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
@@ -135,7 +142,6 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
return R.xml.reduce_bright_colors_settings;
}
@Override
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
AccessibilityStatsLogUtils.logAccessibilityServiceEnabled(mComponentName, enabled);
@@ -154,6 +160,11 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
switchPreference.setTitle(R.string.reduce_bright_colors_preference_title);
}
@Override
protected void updateShortcutTitle(ShortcutPreference shortcutPreference) {
shortcutPreference.setTitle(R.string.reduce_bright_colors_shortcut_title);
}
@Override
int getUserShortcutTypes() {
return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(),

View File

@@ -38,7 +38,6 @@ import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
import android.widget.CheckBox;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
@@ -95,7 +94,20 @@ public class ToggleScreenMagnificationPreferenceFragment extends
removeDialog(DialogEnums.EDIT_SHORTCUT);
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
};
return super.onCreateView(inflater, container, savedInstanceState);
final View view = super.onCreateView(inflater, container, savedInstanceState);
updateFooterPreference();
return view;
}
private void updateFooterPreference() {
final String title = getPrefContext().getString(
R.string.accessibility_screen_magnification_about_title);
final String learnMoreContentDescription = getPrefContext().getString(
R.string.accessibility_screen_magnification_footer_learn_more_content_description);
mFooterPreferenceController.setIntroductionTitle(title);
mFooterPreferenceController.setupHelpLink(getHelpResource(), learnMoreContentDescription);
mFooterPreferenceController.displayPreference(getPreferenceScreen());
}
@Override
@@ -119,26 +131,25 @@ public class ToggleScreenMagnificationPreferenceFragment extends
@Override
public Dialog onCreateDialog(int dialogId) {
if (mDialogDelegate != null) {
final Dialog dialog = mDialogDelegate.onCreateDialog(dialogId);
if (dialog != null) {
return dialog;
mDialog = mDialogDelegate.onCreateDialog(dialogId);
if (mDialog != null) {
return mDialog;
}
}
final AlertDialog dialog;
switch (dialogId) {
case DialogEnums.GESTURE_NAVIGATION_TUTORIAL:
return AccessibilityGestureNavigationTutorial
.showGestureNavigationTutorialDialog(getPrefContext());
.showAccessibilityGestureTutorialDialog(getPrefContext());
case DialogEnums.MAGNIFICATION_EDIT_SHORTCUT:
final CharSequence dialogTitle = getPrefContext().getString(
R.string.accessibility_shortcut_title, mPackageName);
final int dialogType = WizardManagerHelper.isAnySetupWizard(getIntent())
? DialogType.EDIT_SHORTCUT_MAGNIFICATION_SUW
: DialogType.EDIT_SHORTCUT_MAGNIFICATION;
dialog = AccessibilityDialogUtils.showEditShortcutDialog(getPrefContext(),
mDialog = AccessibilityDialogUtils.showEditShortcutDialog(getPrefContext(),
dialogType, dialogTitle, this::callOnAlertDialogCheckboxClicked);
setupMagnificationEditShortcutDialog(dialog);
return dialog;
setupMagnificationEditShortcutDialog(mDialog);
return mDialog;
default:
return super.onCreateDialog(dialogId);
}
@@ -196,7 +207,7 @@ public class ToggleScreenMagnificationPreferenceFragment extends
}
@VisibleForTesting
void setupMagnificationEditShortcutDialog(AlertDialog dialog) {
void setupMagnificationEditShortcutDialog(Dialog dialog) {
final View dialogSoftwareView = dialog.findViewById(R.id.software_shortcut);
mSoftwareTypeCheckBox = dialogSoftwareView.findViewById(R.id.checkbox);
setDialogTextAreaClickListener(dialogSoftwareView, mSoftwareTypeCheckBox);
@@ -252,6 +263,25 @@ public class ToggleScreenMagnificationPreferenceFragment extends
return (value & type) == type;
}
private static CharSequence getSoftwareShortcutTypeSummary(Context context) {
int resId;
if (AccessibilityUtil.isFloatingMenuEnabled(context)) {
resId = R.string.accessibility_shortcut_edit_summary_software;
} else if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
resId = R.string.accessibility_shortcut_edit_summary_software_gesture;
} else {
resId = R.string.accessibility_shortcut_edit_summary_software;
}
return context.getText(resId);
}
@Override
protected List<String> getFeatureSettingsKeys() {
final List<String> shortcutKeys = super.getFeatureSettingsKeys();
shortcutKeys.add(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
return shortcutKeys;
}
@Override
protected CharSequence getShortcutTypeSummary(Context context) {
if (!mShortcutPreference.isChecked()) {
@@ -262,18 +292,14 @@ public class ToggleScreenMagnificationPreferenceFragment extends
MAGNIFICATION_CONTROLLER_NAME, 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);
list.add(getSoftwareShortcutTypeSummary(context));
}
if (hasShortcutType(shortcutTypes, UserShortcutType.HARDWARE)) {
final CharSequence hardwareTitle = context.getText(
R.string.accessibility_shortcut_hardware_keyword);
list.add(hardwareTitle);
}
if (hasShortcutType(shortcutTypes, UserShortcutType.TRIPLETAP)) {
final CharSequence tripleTapTitle = context.getText(
R.string.accessibility_shortcut_triple_tap_keyword);
@@ -282,7 +308,7 @@ public class ToggleScreenMagnificationPreferenceFragment extends
// Show software shortcut if first time to use.
if (list.isEmpty()) {
list.add(softwareTitle);
list.add(getSoftwareShortcutTypeSummary(context));
}
return CaseMap.toTitle().wholeString().noLowercase().apply(Locale.getDefault(), /* iter= */
@@ -396,6 +422,11 @@ public class ToggleScreenMagnificationPreferenceFragment extends
generalCategory.addPreference(mShortcutPreference);
}
@Override
protected void updateShortcutTitle(ShortcutPreference shortcutPreference) {
shortcutPreference.setTitle(R.string.accessibility_screen_magnification_shortcut_title);
}
@Override
protected void updateShortcutPreference() {
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(getPrefContext(),

View File

@@ -17,12 +17,45 @@
package com.android.settings.accessibility;
import android.app.settings.SettingsEnums;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.google.android.setupdesign.GlifPreferenceLayout;
public class ToggleScreenMagnificationPreferenceFragmentForSetupWizard
extends ToggleScreenMagnificationPreferenceFragment {
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final GlifPreferenceLayout layout = (GlifPreferenceLayout) view;
final String title = getContext().getString(
R.string.accessibility_screen_magnification_title);
final String description = getContext().getString(
R.string.accessibility_preference_magnification_summary);
final Drawable icon = getContext().getDrawable(R.drawable.ic_accessibility_visibility);
AccessibilitySetupWizardUtils.updateGlifPreferenceLayout(getContext(), layout, title,
description, icon);
// Hide the setting from the vision settings.
mSettingsPreference.setVisible(false);
}
@Override
public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
final GlifPreferenceLayout layout = (GlifPreferenceLayout) parent;
return layout.onCreateRecyclerView(inflater, parent, savedInstanceState);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.SUW_ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFICATION;
@@ -49,12 +82,4 @@ public class ToggleScreenMagnificationPreferenceFragmentForSetupWizard
// 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);
}
}

View File

@@ -17,8 +17,17 @@
package com.android.settings.accessibility;
import android.app.settings.SettingsEnums;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.google.android.setupdesign.GlifPreferenceLayout;
public class ToggleScreenReaderPreferenceFragmentForSetupWizard
extends ToggleAccessibilityServicePreferenceFragment {
@@ -28,9 +37,24 @@ public class ToggleScreenReaderPreferenceFragmentForSetupWizard
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final GlifPreferenceLayout layout = (GlifPreferenceLayout) view;
final String title = getArguments().getString(AccessibilitySettings.EXTRA_TITLE);
final String description = getContext().getString(R.string.talkback_summary);
final Drawable icon = getContext().getDrawable(R.drawable.ic_accessibility_visibility);
AccessibilitySetupWizardUtils.updateGlifPreferenceLayout(getContext(), layout, title,
description, icon);
mToggleSwitchWasInitiallyChecked = mToggleServiceSwitchPreference.isChecked();
}
@Override
public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
final GlifPreferenceLayout layout = (GlifPreferenceLayout) parent;
return layout.onCreateRecyclerView(inflater, parent, savedInstanceState);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.SUW_ACCESSIBILITY_TOGGLE_SCREEN_READER;

View File

@@ -17,8 +17,17 @@
package com.android.settings.accessibility;
import android.app.settings.SettingsEnums;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.google.android.setupdesign.GlifPreferenceLayout;
public class ToggleSelectToSpeakPreferenceFragmentForSetupWizard
extends InvisibleToggleAccessibilityServicePreferenceFragment {
@@ -28,12 +37,27 @@ public class ToggleSelectToSpeakPreferenceFragmentForSetupWizard
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final GlifPreferenceLayout layout = (GlifPreferenceLayout) view;
final String title = getArguments().getString(AccessibilitySettings.EXTRA_TITLE);
final String description = getContext().getString(R.string.select_to_speak_summary);
final Drawable icon = getContext().getDrawable(R.drawable.ic_accessibility_visibility);
AccessibilitySetupWizardUtils.updateGlifPreferenceLayout(getContext(), layout, title,
description, icon);
mToggleSwitchWasInitiallyChecked = mToggleServiceSwitchPreference.isChecked();
}
@Override
public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
final GlifPreferenceLayout layout = (GlifPreferenceLayout) parent;
return layout.onCreateRecyclerView(inflater, parent, savedInstanceState);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.SUW_ACCESSIBILITY_TOGGLE_SCREEN_READER;
return SettingsEnums.SUW_ACCESSIBILITY_TOGGLE_SELECT_TO_SPEAK;
}
@Override

View File

@@ -37,6 +37,7 @@ import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.OnLifecycleEvent;
import com.android.settings.R;
import com.android.settings.activityembedding.ActivityEmbeddingRulesController;
import com.android.settings.homepage.SettingsHomepageActivity;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.utils.ThreadUtils;
@@ -105,6 +106,17 @@ public class AvatarViewMixin implements LifecycleObserver {
return;
}
// Set a component name since activity embedding requires a component name for
// registering a rule.
intent.setComponent(matchedIntents.get(0).getComponentInfo().getComponentName());
ActivityEmbeddingRulesController.registerTwoPanePairRuleForSettingsHome(
mContext,
intent.getComponent(),
intent.getAction(),
false /* finishPrimaryWithSecondary */,
true /* finishSecondaryWithPrimary */,
false /* clearTop */);
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider()
.logSettingsTileClick(KEY_AVATAR_ICON, SettingsEnums.SETTINGS_HOMEPAGE);

View File

@@ -22,6 +22,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import java.util.Set;
@@ -71,6 +72,11 @@ public class CrossProfileCalendarPreferenceController extends TogglePreferenceCo
CROSS_PROFILE_CALENDAR_ENABLED, value, mManagedUser.getIdentifier());
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accounts;
}
static boolean isCrossProfileCalendarDisallowedByAdmin(Context context, int userId) {
final Context managedProfileContext = createPackageContextAsUser(context, userId);
final DevicePolicyManager dpm = managedProfileContext.getSystemService(

View File

@@ -16,33 +16,25 @@
package com.android.settings.accounts;
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.SearchIndexableResource;
import android.util.Log;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceManager;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.Indexable;
import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* Setting page for managed profile.

View File

@@ -53,6 +53,7 @@ import java.io.IOException;
public class RemoveAccountPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, OnClickListener {
private static final String TAG = "RemoveAccountPrefController";
private static final String KEY_REMOVE_ACCOUNT = "remove_account";
private final MetricsFeatureProvider mMetricsFeatureProvider;
@@ -175,10 +176,11 @@ public class RemoveAccountPreferenceController extends AbstractPreferenceControl
| IOException
| AuthenticatorException e) {
// handled below
}
if (failed) {
Log.w(TAG, "Remove account error: " + e);
RemoveAccountFailureDialog.show(getTargetFragment());
} else {
}
Log.i(TAG, "failed: " + failed);
if (!failed) {
targetActivity.finish();
}
}, null, mUserHandle);
@@ -210,7 +212,7 @@ public class RemoveAccountPreferenceController extends AbstractPreferenceControl
final Context context = getActivity();
return new AlertDialog.Builder(context)
.setTitle(R.string.really_remove_account_title)
.setTitle(R.string.remove_account_label)
.setMessage(R.string.remove_account_failed)
.setPositiveButton(android.R.string.ok, null)
.create();

View File

@@ -0,0 +1,219 @@
/*
* 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.activityembedding;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.LayoutDirection;
import android.util.Log;
import androidx.window.embedding.ActivityFilter;
import androidx.window.embedding.ActivityRule;
import androidx.window.embedding.SplitController;
import androidx.window.embedding.SplitPairFilter;
import androidx.window.embedding.SplitPairRule;
import androidx.window.embedding.SplitPlaceholderRule;
import androidx.window.embedding.SplitRule;
import com.android.settings.Settings;
import com.android.settings.SubSettings;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction;
import com.android.settings.homepage.DeepLinkHomepageActivity;
import com.android.settings.homepage.SettingsHomepageActivity;
import com.android.settings.homepage.SliceDeepLinkHomepageActivity;
import java.util.HashSet;
import java.util.Set;
/** A class to initialize split rules for activity embedding. */
public class ActivityEmbeddingRulesController {
private static final String TAG = "ActivityEmbeddingCtrl";
private final Context mContext;
private final SplitController mSplitController;
public ActivityEmbeddingRulesController(Context context) {
mContext = context;
mSplitController = SplitController.getInstance();
}
/**
* Set up embedding rules to place activities to the right pane.
*/
public void initRules() {
if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(mContext)) {
Log.d(TAG, "Not support this feature now");
return;
}
mSplitController.clearRegisteredRules();
// Set a placeholder for home page.
registerHomepagePlaceholderRule();
registerAlwaysExpandRule();
}
/** Register a SplitPairRule for 2-pane. */
public static void registerTwoPanePairRule(Context context,
ComponentName primaryComponent,
ComponentName secondaryComponent,
String secondaryIntentAction,
int finishPrimaryWithSecondary,
int finishSecondaryWithPrimary,
boolean clearTop) {
if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(context)) {
return;
}
final Set<SplitPairFilter> filters = new HashSet<>();
filters.add(new SplitPairFilter(primaryComponent, secondaryComponent,
secondaryIntentAction));
SplitController.getInstance().registerRule(new SplitPairRule(filters,
finishPrimaryWithSecondary,
finishSecondaryWithPrimary,
clearTop,
ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthPx(context),
ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthPx(context),
ActivityEmbeddingUtils.SPLIT_RATIO,
LayoutDirection.LOCALE));
}
/**
* Register a new SplitPairRule for Settings home. Because homepage is able to be opened by
* {@link Settings} or {@link SettingsHomepageActivity} or
* {@link SliceDeepLinkHomepageActivity}, we register split rule for above cases.
*/
public static void registerTwoPanePairRuleForSettingsHome(Context context,
ComponentName secondaryComponent,
String secondaryIntentAction,
boolean finishPrimaryWithSecondary,
boolean finishSecondaryWithPrimary,
boolean clearTop) {
if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(context)) {
return;
}
registerTwoPanePairRule(
context,
new ComponentName(context, Settings.class),
secondaryComponent,
secondaryIntentAction,
finishPrimaryWithSecondary ? SplitRule.FINISH_ADJACENT : SplitRule.FINISH_NEVER,
finishSecondaryWithPrimary ? SplitRule.FINISH_ADJACENT : SplitRule.FINISH_NEVER,
clearTop);
registerTwoPanePairRule(
context,
new ComponentName(context, SettingsHomepageActivity.class),
secondaryComponent,
secondaryIntentAction,
finishPrimaryWithSecondary ? SplitRule.FINISH_ADJACENT : SplitRule.FINISH_NEVER,
finishSecondaryWithPrimary ? SplitRule.FINISH_ADJACENT : SplitRule.FINISH_NEVER,
clearTop);
// We should finish HomePageActivity altogether even if it shows in single pane for all deep
// link cases.
registerTwoPanePairRule(
context,
new ComponentName(context, DeepLinkHomepageActivity.class),
secondaryComponent,
secondaryIntentAction,
finishPrimaryWithSecondary ? SplitRule.FINISH_ALWAYS : SplitRule.FINISH_NEVER,
finishSecondaryWithPrimary ? SplitRule.FINISH_ALWAYS : SplitRule.FINISH_NEVER,
clearTop);
registerTwoPanePairRule(
context,
new ComponentName(context, SliceDeepLinkHomepageActivity.class),
secondaryComponent,
secondaryIntentAction,
finishPrimaryWithSecondary ? SplitRule.FINISH_ALWAYS : SplitRule.FINISH_NEVER,
finishSecondaryWithPrimary ? SplitRule.FINISH_ALWAYS : SplitRule.FINISH_NEVER,
clearTop);
}
/**
* Register a new SplitPairRule for Settings home.
*/
public static void registerTwoPanePairRuleForSettingsHome(Context context,
ComponentName secondaryComponent,
String secondaryIntentAction,
boolean clearTop) {
if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(context)) {
return;
}
registerTwoPanePairRuleForSettingsHome(
context,
secondaryComponent,
secondaryIntentAction,
true /* finishPrimaryWithSecondary */,
true /* finishSecondaryWithPrimary */,
clearTop);
}
/** Register a SplitPairRule for SubSettings if the device supports 2-pane. */
public static void registerSubSettingsPairRule(Context context, boolean clearTop) {
if (!ActivityEmbeddingUtils.isEmbeddingActivityEnabled(context)) {
return;
}
registerTwoPanePairRuleForSettingsHome(
context,
new ComponentName(context, SubSettings.class),
null /* secondaryIntentAction */,
clearTop);
}
private void registerHomepagePlaceholderRule() {
final Set<ActivityFilter> activityFilters = new HashSet<>();
addActivityFilter(activityFilters, SettingsHomepageActivity.class);
addActivityFilter(activityFilters, DeepLinkHomepageActivity.class);
addActivityFilter(activityFilters, SliceDeepLinkHomepageActivity.class);
addActivityFilter(activityFilters, Settings.class);
final Intent intent = new Intent(mContext, Settings.NetworkDashboardActivity.class);
final SplitPlaceholderRule placeholderRule = new SplitPlaceholderRule(
activityFilters,
intent,
true /* stickyPlaceholder */,
SplitRule.FINISH_ADJACENT,
ActivityEmbeddingUtils.getMinCurrentScreenSplitWidthPx(mContext),
ActivityEmbeddingUtils.getMinSmallestScreenSplitWidthPx(mContext),
ActivityEmbeddingUtils.SPLIT_RATIO,
LayoutDirection.LOCALE);
mSplitController.registerRule(placeholderRule);
}
private void registerAlwaysExpandRule() {
final Set<ActivityFilter> activityFilters = new HashSet<>();
addActivityFilter(activityFilters, FingerprintEnrollIntroduction.class);
addActivityFilter(activityFilters, FingerprintEnrollEnrolling.class);
mSplitController.registerRule(new ActivityRule(activityFilters, true /* alwaysExpand */));
}
private void addActivityFilter(Set<ActivityFilter> activityFilters,
Class<? extends Activity> activityClass) {
activityFilters.add(new ActivityFilter(new ComponentName(mContext, activityClass),
null /* intentAction */));
}
}

View File

@@ -0,0 +1,77 @@
/*
* 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.activityembedding;
import android.app.Activity;
import android.app.ActivityTaskManager;
import android.content.Context;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.TypedValue;
import androidx.window.embedding.SplitController;
/** An util class collecting all common methods for the embedding activity features. */
public class ActivityEmbeddingUtils {
public static final float SPLIT_RATIO = 0.5f;
// The smallest value of current width of the window when the split should be used.
private static final float MIN_CURRENT_SCREEN_SPLIT_WIDTH_DP = 720f;
// The smallest value of the smallest-width (sw) of the window in any rotation when
// the split should be used.
private static final float MIN_SMALLEST_SCREEN_SPLIT_WIDTH_DP = 600f;
private static final String TAG = "ActivityEmbeddingUtils";
/** Get the smallest pixel value of width of the window when the split should be used. */
public static int getMinCurrentScreenSplitWidthPx(Context context) {
final DisplayMetrics dm = context.getResources().getDisplayMetrics();
return (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, MIN_CURRENT_SCREEN_SPLIT_WIDTH_DP, dm);
}
/**
* Get the smallest pixel value of the smallest-width (sw) of the window in any rotation when
* the split should be used.
*/
public static int getMinSmallestScreenSplitWidthPx(Context context) {
final DisplayMetrics dm = context.getResources().getDisplayMetrics();
return (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, MIN_SMALLEST_SCREEN_SPLIT_WIDTH_DP, dm);
}
/** Whether to support embedding activity feature. */
public static boolean isEmbeddingActivityEnabled(Context context) {
final boolean isFlagEnabled = FeatureFlagUtils.isEnabled(context,
FeatureFlagUtils.SETTINGS_SUPPORT_LARGE_SCREEN);
final boolean isSplitSupported = SplitController.getInstance().isSplitSupported();
Log.d(TAG, "isFlagEnabled = " + isFlagEnabled);
Log.d(TAG, "isSplitSupported = " + isSplitSupported);
return isFlagEnabled && isSplitSupported;
}
/** Whether the screen meets two-pane resolution. */
public static boolean isTwoPaneResolution(Activity activity) {
final Rect currentTaskBounds =
ActivityTaskManager.getInstance().getTaskBounds(activity.getTaskId());
return currentTaskBounds.width() >= getMinCurrentScreenSplitWidthPx(activity)
&& currentTaskBounds.height() >= getMinSmallestScreenSplitWidthPx(activity);
}
}

View File

@@ -26,6 +26,7 @@ import static com.android.settings.Utils.PROPERTY_APP_HIBERNATION_ENABLED;
import static com.android.settings.Utils.PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS;
import android.app.AppOpsManager;
import android.apphibernation.AppHibernationManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.provider.DeviceConfig;
@@ -134,8 +135,15 @@ public final class HibernationSwitchPreferenceController extends AppInfoPreferen
@Override
public boolean onPreferenceChange(Preference preference, Object isChecked) {
try {
final boolean checked = (boolean) isChecked;
mAppOpsManager.setUidMode(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, mPackageUid,
(boolean) isChecked ? MODE_ALLOWED : MODE_IGNORED);
checked ? MODE_ALLOWED : MODE_IGNORED);
if (!checked) {
final AppHibernationManager ahm =
mContext.getSystemService(AppHibernationManager.class);
ahm.setHibernatingForUser(mPackageName, false);
ahm.setHibernatingGlobally(mPackageName, false);
}
} catch (RuntimeException e) {
return false;
}

View File

@@ -16,6 +16,7 @@
package com.android.settings.applications.autofill;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.service.autofill.AutofillService.EXTRA_RESULT;
import static androidx.lifecycle.Lifecycle.Event.ON_CREATE;
@@ -133,7 +134,8 @@ public class PasswordsPreferenceController extends BasePreferenceController
new Intent(Intent.ACTION_MAIN)
.setClassName(
serviceInfo.packageName,
service.getPasswordsActivity());
service.getPasswordsActivity())
.setFlags(FLAG_ACTIVITY_NEW_TASK);
prefContext.startActivityAsUser(intent, UserHandle.of(user));
return true;
});

View File

@@ -105,6 +105,11 @@ public class AppLaunchSettings extends AppInfoBase implements
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mAppEntry == null) {
Log.w(TAG, "onCreate: mAppEntry is null, please check the reason!!!");
getActivity().finish();
return;
}
addPreferencesFromResource(R.xml.installed_app_launch_settings);
mDomainVerificationManager = mContext.getSystemService(DomainVerificationManager.class);
initUIComponents();

View File

@@ -272,8 +272,8 @@ public class ManageApplications extends InstrumentedFragment
Intent intent = activity.getIntent();
Bundle args = getArguments();
int screenTitle = intent.getIntExtra(
SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, R.string.all_apps);
final int screenTitle = getTitleResId(intent, args);
String className = args != null ? args.getString(EXTRA_CLASSNAME) : null;
if (className == null) {
className = intent.getComponent().getClassName();
@@ -290,49 +290,36 @@ public class ManageApplications extends InstrumentedFragment
mSortOrder = R.id.sort_order_size;
} else if (className.equals(UsageAccessSettingsActivity.class.getName())) {
mListType = LIST_TYPE_USAGE_ACCESS;
screenTitle = R.string.usage_access;
} else if (className.equals(HighPowerApplicationsActivity.class.getName())) {
mListType = LIST_TYPE_HIGH_POWER;
// Default to showing system.
mShowSystem = true;
screenTitle = R.string.high_power_apps;
} else if (className.equals(OverlaySettingsActivity.class.getName())) {
mListType = LIST_TYPE_OVERLAY;
screenTitle = R.string.system_alert_window_settings;
reportIfRestrictedSawIntent(intent);
} else if (className.equals(WriteSettingsActivity.class.getName())) {
mListType = LIST_TYPE_WRITE_SETTINGS;
screenTitle = R.string.write_settings;
} else if (className.equals(ManageExternalSourcesActivity.class.getName())) {
mListType = LIST_TYPE_MANAGE_SOURCES;
screenTitle = R.string.install_other_apps;
} else if (className.equals(GamesStorageActivity.class.getName())) {
mListType = LIST_TYPE_GAMES;
mSortOrder = R.id.sort_order_size;
} else if (className.equals(Settings.ChangeWifiStateActivity.class.getName())) {
mListType = LIST_TYPE_WIFI_ACCESS;
screenTitle = R.string.change_wifi_state_title;
} else if (className.equals(Settings.ManageExternalStorageActivity.class.getName())) {
mListType = LIST_MANAGE_EXTERNAL_STORAGE;
screenTitle = R.string.manage_external_storage_title;
} else if (className.equals(Settings.MediaManagementAppsActivity.class.getName())) {
mListType = LIST_TYPE_MEDIA_MANAGEMENT_APPS;
screenTitle = R.string.media_management_apps_title;
} else if (className.equals(Settings.AlarmsAndRemindersActivity.class.getName())) {
mListType = LIST_TYPE_ALARMS_AND_REMINDERS;
screenTitle = R.string.alarms_and_reminders_title;
} else if (className.equals(Settings.NotificationAppListActivity.class.getName())) {
mListType = LIST_TYPE_NOTIFICATION;
mUsageStatsManager = IUsageStatsManager.Stub.asInterface(
ServiceManager.getService(Context.USAGE_STATS_SERVICE));
mNotificationBackend = new NotificationBackend();
mSortOrder = R.id.sort_order_recent_notification;
screenTitle = R.string.app_notifications_title;
} else {
if (screenTitle == -1) {
screenTitle = R.string.all_apps;
}
mListType = LIST_TYPE_MAIN;
}
final AppFilterRegistry appFilterRegistry = AppFilterRegistry.getInstance();
@@ -881,6 +868,46 @@ public class ManageApplications extends InstrumentedFragment
params.setBehavior(behavior);
}
/**
* Returns a resource ID of title based on what type of app list is
* @param intent the intent of the activity that might include a specified title
* @param args the args that includes a class name of app list
*/
public static int getTitleResId(@NonNull Intent intent, Bundle args) {
int screenTitle = intent.getIntExtra(
SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, R.string.all_apps);
String className = args != null ? args.getString(EXTRA_CLASSNAME) : null;
if (className == null) {
className = intent.getComponent().getClassName();
}
if (className.equals(Settings.UsageAccessSettingsActivity.class.getName())) {
screenTitle = R.string.usage_access;
} else if (className.equals(Settings.HighPowerApplicationsActivity.class.getName())) {
screenTitle = R.string.high_power_apps;
} else if (className.equals(Settings.OverlaySettingsActivity.class.getName())) {
screenTitle = R.string.system_alert_window_settings;
} else if (className.equals(Settings.WriteSettingsActivity.class.getName())) {
screenTitle = R.string.write_settings;
} else if (className.equals(Settings.ManageExternalSourcesActivity.class.getName())) {
screenTitle = R.string.install_other_apps;
} else if (className.equals(Settings.ChangeWifiStateActivity.class.getName())) {
screenTitle = R.string.change_wifi_state_title;
} else if (className.equals(Settings.ManageExternalStorageActivity.class.getName())) {
screenTitle = R.string.manage_external_storage_title;
} else if (className.equals(Settings.MediaManagementAppsActivity.class.getName())) {
screenTitle = R.string.media_management_apps_title;
} else if (className.equals(Settings.AlarmsAndRemindersActivity.class.getName())) {
screenTitle = R.string.alarms_and_reminders_title;
} else if (className.equals(Settings.NotificationAppListActivity.class.getName())) {
screenTitle = R.string.app_notifications_title;
} else {
if (screenTitle == -1) {
screenTitle = R.string.all_apps;
}
}
return screenTitle;
}
static class FilterSpinnerAdapter extends SettingsSpinnerAdapter<CharSequence> {
private final ManageApplications mManageApplications;

View File

@@ -17,27 +17,26 @@
package com.android.settings.applications.managedomainurls;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.util.ArraySet;
import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.verify.domain.DomainVerificationUserState;
import android.util.IconDrawableFactory;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.applications.intentpicker.IntentPickerUtils;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.widget.AppPreference;
public class DomainAppPreference extends AppPreference {
private final AppEntry mEntry;
private final PackageManager mPm;
private final DomainVerificationManager mDomainVerificationManager;
private final IconDrawableFactory mIconDrawableFactory;
public DomainAppPreference(final Context context, IconDrawableFactory iconFactory,
AppEntry entry) {
super(context);
mIconDrawableFactory = iconFactory;
mPm = context.getPackageManager();
mDomainVerificationManager = context.getSystemService(DomainVerificationManager.class);
mEntry = entry;
mEntry.ensureLabel(getContext());
@@ -60,22 +59,14 @@ public class DomainAppPreference extends AppPreference {
}
private CharSequence getDomainsSummary(String packageName) {
// If the user has explicitly said "no" for this package, that's the
// string we should show.
int domainStatus =
mPm.getIntentVerificationStatusAsUser(packageName, UserHandle.myUserId());
if (domainStatus == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
return getContext().getText(R.string.domain_urls_summary_none);
}
// Otherwise, ask package manager for the domains for this package,
// and show the first one (or none if there aren't any).
final ArraySet<String> result = Utils.getHandledDomains(mPm, packageName);
if (result.isEmpty()) {
return getContext().getText(R.string.domain_urls_summary_none);
} else if (result.size() == 1) {
return getContext().getString(R.string.domain_urls_summary_one, result.valueAt(0));
} else {
return getContext().getString(R.string.domain_urls_summary_some, result.valueAt(0));
}
return getContext().getText(isLinkHandlingAllowed(packageName)
? R.string.app_link_open_always : R.string.app_link_open_never);
}
private boolean isLinkHandlingAllowed(String packageName) {
final DomainVerificationUserState userState =
IntentPickerUtils.getDomainVerificationUserState(mDomainVerificationManager,
packageName);
return userState == null ? false : userState.isLinkHandlingAllowed();
}
}

View File

@@ -19,6 +19,7 @@ package com.android.settings.applications.managedomainurls;
import android.content.Context;
import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
public class InstantAppWebActionPreferenceController extends TogglePreferenceController {
@@ -43,4 +44,9 @@ public class InstantAppWebActionPreferenceController extends TogglePreferenceCon
return Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.INSTANT_APPS_ENABLED, isChecked ? 1 : 0);
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_apps;
}
}

View File

@@ -67,7 +67,9 @@ public class PictureInPictureDetailPreferenceController extends AppInfoPreferenc
try {
packageInfoWithActivities = mPackageManager.getPackageInfoAsUser(mPackageName,
PackageManager.GET_ACTIVITIES, UserHandle.myUserId());
} catch (PackageManager.NameNotFoundException e) {
} catch (Exception e) {
// Catch Exception to avoid DeadObjectException thrown with binder transaction
// failures, since the explicit request of DeadObjectException has compiler errors.
Log.e(TAG, "Exception while retrieving the package info of " + mPackageName, e);
}

View File

@@ -27,6 +27,7 @@ import android.util.Log;
import androidx.preference.Preference;
import androidx.preference.SwitchPreference;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
public class AutoRestorePreferenceController extends TogglePreferenceController {
@@ -84,4 +85,9 @@ public class AutoRestorePreferenceController extends TogglePreferenceController
return result;
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_system;
}
}

View File

@@ -22,6 +22,7 @@ import android.app.backup.BackupDataOutput;
import android.app.backup.BackupHelper;
import android.os.ParcelFileDescriptor;
import com.android.settings.fuelgauge.BatteryBackupHelper;
import com.android.settings.shortcut.CreateShortcutPreferenceController;
import java.io.FileInputStream;
@@ -37,6 +38,7 @@ public class SettingsBackupHelper extends BackupAgentHelper {
public void onCreate() {
super.onCreate();
addHelper("no-op", new NoOpHelper());
addHelper(BatteryBackupHelper.TAG, new BatteryBackupHelper(this));
}
@Override

View File

@@ -32,6 +32,7 @@ import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.BiometricManager.BiometricError;
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintManager;
@@ -175,39 +176,64 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
// Default behavior is to enroll BIOMETRIC_WEAK or above. See ACTION_BIOMETRIC_ENROLL.
final int authenticators = getIntent().getIntExtra(
EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, Authenticators.BIOMETRIC_WEAK);
Log.d(TAG, "Authenticators: " + authenticators);
mParentalOptionsRequired = intent.getBooleanExtra(EXTRA_REQUIRE_PARENTAL_CONSENT, false);
mSkipReturnToParent = intent.getBooleanExtra(EXTRA_SKIP_RETURN_TO_PARENT, false);
// determine what can be enrolled
final boolean isSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
final boolean isMultiSensor = mHasFeatureFace && mHasFeatureFingerprint;
Log.d(TAG, "parentalOptionsRequired: " + mParentalOptionsRequired
+ ", skipReturnToParent: " + mSkipReturnToParent
+ ", isSetupWizard: " + isSetupWizard
+ ", isMultiSensor: " + isMultiSensor);
if (mHasFeatureFace) {
final FaceManager faceManager = getSystemService(FaceManager.class);
final List<FaceSensorPropertiesInternal> faceProperties =
faceManager.getSensorPropertiesInternal();
final int maxFacesEnrollableIfSUW = getApplicationContext().getResources()
.getInteger(R.integer.suw_max_faces_enrollable);
if (!faceProperties.isEmpty()) {
final FaceSensorPropertiesInternal props = faceProperties.get(0);
final int maxEnrolls =
isSetupWizard ? 1 : faceProperties.get(0).maxEnrollmentsPerUser;
isSetupWizard ? maxFacesEnrollableIfSUW : props.maxEnrollmentsPerUser;
mIsFaceEnrollable =
faceManager.getEnrolledFaces(mUserId).size() < maxEnrolls;
// exclude face enrollment from setup wizard if configured as a convenience
// isSetupWizard is always false for unicorn enrollment, so if consent is
// required check if setup has completed instead.
final boolean isSettingUp = isSetupWizard || (mParentalOptionsRequired
&& !WizardManagerHelper.isUserSetupComplete(this));
if (isSettingUp && isMultiSensor && mIsFaceEnrollable) {
if (props.sensorStrength == SensorProperties.STRENGTH_CONVENIENCE) {
Log.i(TAG, "Excluding face from SuW enrollment (STRENGTH_CONVENIENCE)");
mIsFaceEnrollable = false;
}
}
}
}
if (mHasFeatureFingerprint) {
final FingerprintManager fpManager = getSystemService(FingerprintManager.class);
final List<FingerprintSensorPropertiesInternal> fpProperties =
fpManager.getSensorPropertiesInternal();
final int maxFingerprintsEnrollableIfSUW = getApplicationContext().getResources()
.getInteger(R.integer.suw_max_fingerprints_enrollable);
if (!fpProperties.isEmpty()) {
final int maxEnrolls =
isSetupWizard ? 1 : fpProperties.get(0).maxEnrollmentsPerUser;
isSetupWizard ? maxFingerprintsEnrollableIfSUW
: fpProperties.get(0).maxEnrollmentsPerUser;
mIsFingerprintEnrollable =
fpManager.getEnrolledFingerprints(mUserId).size() < maxEnrolls;
}
}
mParentalOptionsRequired = intent.getBooleanExtra(EXTRA_REQUIRE_PARENTAL_CONSENT, false);
mSkipReturnToParent = intent.getBooleanExtra(EXTRA_SKIP_RETURN_TO_PARENT, false);
Log.d(TAG, "parentalOptionsRequired: " + mParentalOptionsRequired
+ ", skipReturnToParent: " + mSkipReturnToParent
+ ", isSetupWizard: " + isSetupWizard);
// TODO(b/195128094): remove this restriction
// Consent can only be recorded when this activity is launched directly from the kids
// module. This can be removed when there is a way to notify consent status out of band.
@@ -236,28 +262,21 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
}
}
// start enrollment process if we haven't bailed out yet
if (mParentalOptionsRequired && mParentalOptions == null) {
mParentalConsentHelper = new ParentalConsentHelper(
mIsFaceEnrollable, mIsFingerprintEnrollable, mGkPwHandle);
mParentalConsentHelper = new ParentalConsentHelper(mGkPwHandle);
setOrConfirmCredentialsNow();
} else {
startEnroll();
// Start enrollment process if we haven't bailed out yet
startEnrollWith(authenticators, isSetupWizard);
}
}
private void startEnroll() {
// Default behavior is to enroll BIOMETRIC_WEAK or above. See ACTION_BIOMETRIC_ENROLL.
final int authenticators = getIntent().getIntExtra(
EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, Authenticators.BIOMETRIC_WEAK);
Log.d(TAG, "Authenticators: " + authenticators);
startEnrollWith(authenticators, WizardManagerHelper.isAnySetupWizard(getIntent()));
}
private void startEnrollWith(@Authenticators.Types int authenticators, boolean setupWizard) {
// If the caller is not setup wizard, and the user has something enrolled, finish.
if (!setupWizard) {
// Allow parental consent flow to skip this check, since one modality could be consented
// and another non-consented. This can also happen if the restriction is applied when
// enrollments already exists.
if (!setupWizard && !mParentalOptionsRequired) {
final BiometricManager bm = getSystemService(BiometricManager.class);
final @BiometricError int result = bm.canAuthenticate(authenticators);
if (result != BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED) {
@@ -324,6 +343,27 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
// single enrollment is handled entirely by the launched activity
// this handles multi enroll or if parental consent is required
if (mParentalConsentHelper != null) {
// Lazily retrieve the values from ParentalControlUtils, since the value may not be
// ready in onCreate.
final boolean faceConsentRequired = ParentalControlsUtils
.parentConsentRequired(this, BiometricAuthenticator.TYPE_FACE) != null;
final boolean fpConsentRequired = ParentalControlsUtils
.parentConsentRequired(this, BiometricAuthenticator.TYPE_FINGERPRINT) != null;
final boolean requestFaceConsent = faceConsentRequired
&& mHasFeatureFace
&& mIsFaceEnrollable;
final boolean requestFpConsent = fpConsentRequired && mHasFeatureFingerprint;
Log.d(TAG, "faceConsentRequired: " + faceConsentRequired
+ ", fpConsentRequired: " + fpConsentRequired
+ ", hasFeatureFace: " + mHasFeatureFace
+ ", hasFeatureFingerprint: " + mHasFeatureFingerprint
+ ", faceEnrollable: " + mIsFaceEnrollable
+ ", fpEnrollable: " + mIsFingerprintEnrollable);
mParentalConsentHelper.setConsentRequirement(requestFaceConsent, requestFpConsent);
handleOnActivityResultWhileConsenting(requestCode, resultCode, data);
} else {
handleOnActivityResultWhileEnrolling(requestCode, resultCode, data);
@@ -356,10 +396,18 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
final boolean isStillPrompting = mParentalConsentHelper.launchNext(
this, REQUEST_CHOOSE_OPTIONS, resultCode, data);
if (!isStillPrompting) {
Log.d(TAG, "Enrollment consent options set, starting enrollment");
mParentalOptions = mParentalConsentHelper.getConsentResult();
mParentalConsentHelper = null;
startEnroll();
Log.d(TAG, "Enrollment consent options set, starting enrollment: "
+ mParentalOptions);
// Note that we start enrollment with CONVENIENCE instead of the default
// of WEAK in startEnroll(), since we want to allow enrollment for any
// sensor as long as it has been consented for. We should eventually
// clean up this logic and do something like pass in the parental consent
// result, so that we can request enrollment for specific sensors, but
// that's quite a large and risky change to the startEnrollWith() logic.
startEnrollWith(Authenticators.BIOMETRIC_CONVENIENCE,
WizardManagerHelper.isAnySetupWizard(getIntent()));
}
} else {
Log.d(TAG, "Unknown or cancelled parental consent");

View File

@@ -203,11 +203,15 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
getMoreButtonTextRes(), this::onNextButtonClick);
requireScrollMixin.setOnRequireScrollStateChangedListener(
scrollNeeded -> {
// Update text of primary button from "More" to "Agree".
final int primaryButtonTextRes = scrollNeeded
? getMoreButtonTextRes()
: getAgreeButtonTextRes();
getPrimaryFooterButton().setText(this, primaryButtonTextRes);
boolean enrollmentCompleted = checkMaxEnrolled() != 0;
if (!enrollmentCompleted) {
// Update text of primary button from "More" to "Agree".
final int primaryButtonTextRes = scrollNeeded
? getMoreButtonTextRes()
: getAgreeButtonTextRes();
getPrimaryFooterButton().setText(this, primaryButtonTextRes);
}
// Show secondary button once scroll is completed.
if (!scrollNeeded) {
@@ -257,8 +261,12 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
// Lock thingy is already set up, launch directly to the next page
launchNextEnrollingActivity(mToken);
} else {
setResult(RESULT_FINISHED);
finish();
boolean couldStartNextBiometric = BiometricUtils.tryStartingNextBiometricEnroll(this,
ENROLL_NEXT_BIOMETRIC_REQUEST, "enrollIntroduction#onNextButtonClicked");
if (!couldStartNextBiometric) {
setResult(RESULT_FINISHED);
finish();
}
}
}

View File

@@ -22,6 +22,9 @@ import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.os.storage.StorageManager;
import android.util.Log;
import android.view.Surface;
@@ -273,4 +276,17 @@ public class BiometricUtils {
public static boolean isReverseLandscape(@NonNull Context context) {
return context.getDisplay().getRotation() == Surface.ROTATION_270;
}
/**
* @param faceManager
* @return True if at least one sensor is set as a convenience.
*/
public static boolean isConvenience(@NonNull FaceManager faceManager) {
for (FaceSensorPropertiesInternal props : faceManager.getSensorPropertiesInternal()) {
if (props.sensorStrength == SensorProperties.STRENGTH_CONVENIENCE) {
return true;
}
}
return false;
}
}

View File

@@ -52,8 +52,8 @@ public class ParentalConsentHelper {
private static final String KEY_FINGERPRINT_CONSENT_STRINGS = "fingerprint_strings";
private static final String KEY_IRIS_CONSENT_STRINGS = "iris_strings";
private final boolean mRequireFace;
private final boolean mRequireFingerprint;
private boolean mRequireFace;
private boolean mRequireFingerprint;
private long mGkPwHandle;
@Nullable
@@ -64,15 +64,19 @@ public class ParentalConsentHelper {
/**
* Helper for aggregating user consent.
*
* @param requireFace if face consent should be shown
* @param requireFingerprint if fingerprint consent should be shown
* @param gkPwHandle for launched intents
*/
public ParentalConsentHelper(boolean requireFace, boolean requireFingerprint,
@Nullable Long gkPwHandle) {
public ParentalConsentHelper(@Nullable Long gkPwHandle) {
mGkPwHandle = gkPwHandle != null ? gkPwHandle : 0L;
}
/**
* @param requireFace if face consent should be shown
* @param requireFingerprint if fingerprint consent should be shown
*/
public void setConsentRequirement(boolean requireFace, boolean requireFingerprint) {
mRequireFace = requireFace;
mRequireFingerprint = requireFingerprint;
mGkPwHandle = gkPwHandle != null ? gkPwHandle : 0L;
}
/**

View File

@@ -91,4 +91,10 @@ public class BiometricSettingsAppPreferenceController extends TogglePreferenceCo
public final boolean isSliceable() {
return false;
}
@Override
public int getSliceHighlightMenuRes() {
// not needed since it's not sliceable
return NO_RES;
}
}

View File

@@ -73,4 +73,10 @@ public class BiometricSettingsKeyguardPreferenceController extends TogglePrefere
public final boolean isSliceable() {
return false;
}
@Override
public int getSliceHighlightMenuRes() {
// not needed since it's not sliceable
return NO_RES;
}
}

View File

@@ -21,13 +21,18 @@ import static com.android.settings.password.ChooseLockPattern.RESULT_FINISHED;
import android.content.Context;
import android.content.Intent;
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.preference.Preference;
import com.android.settings.R;
@@ -90,6 +95,16 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
mConfirmCredential = true;
launchChooseOrConfirmLock();
}
final Preference unlockPhonePreference = findPreference(getUnlockPhonePreferenceKey());
if (unlockPhonePreference != null) {
unlockPhonePreference.setSummary(getUseAnyBiometricSummary());
}
final Preference useInAppsPreference = findPreference(getUseInAppsPreferenceKey());
if (useInAppsPreference != null) {
useInAppsPreference.setSummary(getUseClass2BiometricSummary());
}
}
@Override
@@ -185,6 +200,16 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
*/
public abstract String getFingerprintPreferenceKey();
/**
* @return The preference key of the "Unlock your phone" setting toggle.
*/
public abstract String getUnlockPhonePreferenceKey();
/**
* @return The preference key of the "Verify it's you in apps" setting toggle.
*/
public abstract String getUseInAppsPreferenceKey();
private void launchChooseOrConfirmLock() {
final ChooseLockSettingsHelper.Builder builder =
new ChooseLockSettingsHelper.Builder(getActivity(), this)
@@ -214,4 +239,59 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
startActivityForResult(intent, CHOOSE_LOCK_REQUEST);
}
}
@NonNull
private String getUseAnyBiometricSummary() {
boolean isFaceAllowed = mFaceManager != null && mFaceManager.isHardwareDetected();
boolean isFingerprintAllowed =
mFingerprintManager != null && mFingerprintManager.isHardwareDetected();
@StringRes final int resId = getUseBiometricSummaryRes(isFaceAllowed, isFingerprintAllowed);
return resId == 0 ? "" : getString(resId);
}
@NonNull
private String getUseClass2BiometricSummary() {
boolean isFaceAllowed = false;
if (mFaceManager != null) {
for (final FaceSensorPropertiesInternal sensorProps
: mFaceManager.getSensorPropertiesInternal()) {
if (sensorProps.sensorStrength == SensorProperties.STRENGTH_WEAK
|| sensorProps.sensorStrength == SensorProperties.STRENGTH_STRONG) {
isFaceAllowed = true;
break;
}
}
}
boolean isFingerprintAllowed = false;
if (mFingerprintManager != null) {
for (final FingerprintSensorPropertiesInternal sensorProps
: mFingerprintManager.getSensorPropertiesInternal()) {
if (sensorProps.sensorStrength == SensorProperties.STRENGTH_WEAK
|| sensorProps.sensorStrength == SensorProperties.STRENGTH_STRONG) {
isFingerprintAllowed = true;
break;
}
}
}
@StringRes final int resId = getUseBiometricSummaryRes(isFaceAllowed, isFingerprintAllowed);
return resId == 0 ? "" : getString(resId);
}
@StringRes
private static int getUseBiometricSummaryRes(boolean isFaceAllowed,
boolean isFingerprintAllowed) {
if (isFaceAllowed && isFingerprintAllowed) {
return R.string.biometric_settings_use_face_or_fingerprint_preference_summary;
} else if (isFaceAllowed) {
return R.string.biometric_settings_use_face_preference_summary;
} else if (isFingerprintAllowed) {
return R.string.biometric_settings_use_fingerprint_preference_summary;
} else {
return 0;
}
}
}

View File

@@ -28,6 +28,8 @@ public class CombinedBiometricProfileSettings extends BiometricsSettingsBase {
private static final String TAG = "BiometricProfileSetting";
private static final String KEY_FACE_SETTINGS = "biometric_face_settings_profile";
private static final String KEY_FINGERPRINT_SETTINGS = "biometric_fingerprint_settings_profile";
private static final String KEY_UNLOCK_PHONE = "biometric_settings_biometric_keyguard_profile";
private static final String KEY_USE_IN_APPS = "biometric_settings_biometric_app_profile";
@Override
public void onAttach(Context context) {
@@ -50,6 +52,16 @@ public class CombinedBiometricProfileSettings extends BiometricsSettingsBase {
return KEY_FINGERPRINT_SETTINGS;
}
@Override
public String getUnlockPhonePreferenceKey() {
return KEY_UNLOCK_PHONE;
}
@Override
public String getUseInAppsPreferenceKey() {
return KEY_USE_IN_APPS;
}
@Override
protected String getLogTag() {
return TAG;

View File

@@ -30,6 +30,8 @@ public class CombinedBiometricSettings extends BiometricsSettingsBase {
private static final String TAG = "BiometricSettings";
private static final String KEY_FACE_SETTINGS = "biometric_face_settings";
private static final String KEY_FINGERPRINT_SETTINGS = "biometric_fingerprint_settings";
private static final String KEY_UNLOCK_PHONE = "biometric_settings_biometric_keyguard";
private static final String KEY_USE_IN_APPS = "biometric_settings_biometric_app";
@Override
public void onAttach(Context context) {
@@ -53,6 +55,16 @@ public class CombinedBiometricSettings extends BiometricsSettingsBase {
return KEY_FINGERPRINT_SETTINGS;
}
@Override
public String getUnlockPhonePreferenceKey() {
return KEY_UNLOCK_PHONE;
}
@Override
public String getUseInAppsPreferenceKey() {
return KEY_USE_IN_APPS;
}
@Override
protected String getLogTag() {
return TAG;

View File

@@ -104,12 +104,34 @@ public class CombinedBiometricStatusPreferenceController extends
private void updateStateInternal() {
// This controller currently is shown if fingerprint&face exist on the device. If this
// changes in the future, the modalities passed into the below will need to be updated.
updateStateInternal(ParentalControlsUtils.parentConsentRequired(mContext,
BiometricAuthenticator.TYPE_FACE | BiometricAuthenticator.TYPE_FINGERPRINT));
final RestrictedLockUtils.EnforcedAdmin faceAdmin = ParentalControlsUtils
.parentConsentRequired(mContext, BiometricAuthenticator.TYPE_FACE);
final RestrictedLockUtils.EnforcedAdmin fpAdmin = ParentalControlsUtils
.parentConsentRequired(mContext, BiometricAuthenticator.TYPE_FINGERPRINT);
// If the admins are non-null, they are actually always the same. Just the helper class
// we create above always return the admin, instead of a boolean.
final boolean faceConsentRequired = faceAdmin != null;
final boolean fpConsentRequired = fpAdmin != null;
final RestrictedLockUtils.EnforcedAdmin admin = faceAdmin != null ? faceAdmin : fpAdmin;
updateStateInternal(admin, faceConsentRequired, fpConsentRequired);
}
@VisibleForTesting
void updateStateInternal(@Nullable RestrictedLockUtils.EnforcedAdmin enforcedAdmin) {
void updateStateInternal(@Nullable RestrictedLockUtils.EnforcedAdmin enforcedAdmin,
boolean faceConsentRequired, boolean fpConsentRequired) {
// Disable the preference (and show the consent flow) only if consent is required for all
// modalities. Otherwise, users will not be able to enter and modify settings for modalities
// which have already been consented. In any case, the controllers for the modalities which
// have not yet been consented will be disabled in the combined page anyway - users can
// go through the consent+enrollment flow from there.
final boolean disablePreference = faceConsentRequired && fpConsentRequired;
if (!disablePreference) {
enforcedAdmin = null;
}
if (mPreference != null) {
mPreference.setDisabledByAdmin(enforcedAdmin);
}

View File

@@ -19,10 +19,12 @@ package com.android.settings.biometrics.face;
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.Intent;
import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -34,10 +36,12 @@ import androidx.annotation.StringRes;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollActivity;
import com.android.settings.biometrics.BiometricEnrollIntroduction;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.utils.SensorPrivacyManagerHelper;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.google.android.setupcompat.template.FooterButton;
@@ -57,6 +61,7 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
private FaceFeatureProvider mFaceFeatureProvider;
@Nullable private FooterButton mPrimaryFooterButton;
@Nullable private FooterButton mSecondaryFooterButton;
@Nullable private SensorPrivacyManager mSensorPrivacyManager;
@Override
protected void onCancelButtonClick(View view) {
@@ -112,6 +117,14 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
howMessage.setText(getHowMessage());
inControlMessage.setText(getInControlMessage());
// Set up and show the "less secure" info section if necessary.
if (getResources().getBoolean(R.bool.config_face_intro_show_less_secure)) {
final LinearLayout infoRowLessSecure = findViewById(R.id.info_row_less_secure);
final ImageView iconLessSecure = findViewById(R.id.icon_less_secure);
infoRowLessSecure.setVisibility(View.VISIBLE);
iconLessSecure.getBackground().setColorFilter(getIconColorFilter());
}
// Set up and show the "require eyes" info section if necessary.
if (getResources().getBoolean(R.bool.config_face_intro_show_require_eyes)) {
final LinearLayout infoRowRequireEyes = findViewById(R.id.info_row_require_eyes);
@@ -142,6 +155,14 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
});
}
}
mSensorPrivacyManager = getApplicationContext()
.getSystemService(SensorPrivacyManager.class);
final SensorPrivacyManagerHelper helper = SensorPrivacyManagerHelper
.getInstance(getApplicationContext());
final boolean cameraPrivacyEnabled = helper
.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA, mUserId);
Log.v(TAG, "cameraPrivacyEnabled : " + cameraPrivacyEnabled);
}
protected boolean generateChallengeOnCreate() {
@@ -226,13 +247,20 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
}
private boolean maxFacesEnrolled() {
final boolean isSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
if (mFaceManager != null) {
final List<FaceSensorPropertiesInternal> props =
mFaceManager.getSensorPropertiesInternal();
// This will need to be updated for devices with multiple face sensors.
final int max = props.get(0).maxEnrollmentsPerUser;
final int numEnrolledFaces = mFaceManager.getEnrolledFaces(mUserId).size();
return numEnrolledFaces >= max;
final int maxFacesEnrollableIfSUW = getApplicationContext().getResources()
.getInteger(R.integer.suw_max_faces_enrollable);
if (isSetupWizard) {
return numEnrolledFaces >= maxFacesEnrollableIfSUW;
} else {
return numEnrolledFaces >= max;
}
} else {
return false;
}
@@ -293,6 +321,28 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
return BiometricAuthenticator.TYPE_FACE;
}
@Override
protected void onNextButtonClick(View view) {
final boolean parentelConsentRequired =
getIntent()
.getBooleanExtra(BiometricEnrollActivity.EXTRA_REQUIRE_PARENTAL_CONSENT, false);
final boolean cameraPrivacyEnabled = SensorPrivacyManagerHelper
.getInstance(getApplicationContext())
.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA, mUserId);
final boolean isSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
final boolean isSettingUp = isSetupWizard || (parentelConsentRequired
&& !WizardManagerHelper.isUserSetupComplete(this));
if (cameraPrivacyEnabled && !isSettingUp) {
if (mSensorPrivacyManager == null) {
mSensorPrivacyManager = getApplicationContext()
.getSystemService(SensorPrivacyManager.class);
}
mSensorPrivacyManager.showSensorUseDialog(SensorPrivacyManager.Sensors.CAMERA);
} else {
super.onNextButtonClick(view);
}
}
@Override
@NonNull
protected FooterButton getPrimaryFooterButton() {

View File

@@ -19,13 +19,17 @@ package com.android.settings.biometrics.face;
import static android.provider.Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION;
import android.content.Context;
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorProperties;
import android.provider.Settings;
import androidx.preference.Preference;
import com.android.settings.Utils;
import java.util.List;
/**
* Preference controller giving the user an option to always require confirmation.
*/
@@ -75,6 +79,14 @@ public class FaceSettingsConfirmPreferenceController extends FaceSettingsPrefere
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
List<FaceSensorProperties> properties = mFaceManager.getSensorProperties();
// If a sensor is convenience, it is possible that it becomes weak or strong with
// an update. For this reason, the sensor is conditionally unavailable.
if (!properties.isEmpty()
&& properties.get(0).getSensorStrength() == SensorProperties.STRENGTH_CONVENIENCE) {
return CONDITIONALLY_UNAVAILABLE;
} else {
return AVAILABLE;
}
}
}

View File

@@ -51,4 +51,10 @@ public abstract class FaceSettingsPreferenceController extends TogglePreferenceC
public final boolean isSliceable() {
return false;
}
@Override
public int getSliceHighlightMenuRes() {
// not needed since it's not sliceable
return NO_RES;
}
}

View File

@@ -33,6 +33,7 @@ import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.overlay.FeatureFactory;
@@ -56,6 +57,7 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference
public static class ConfirmRemoveDialog extends InstrumentedDialogFragment {
private boolean mIsConvenience;
private DialogInterface.OnClickListener mOnClickListener;
@Override
@@ -68,7 +70,9 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.security_settings_face_settings_remove_dialog_title)
.setMessage(R.string.security_settings_face_settings_remove_dialog_details)
.setMessage(mIsConvenience
? R.string.security_settings_face_settings_remove_dialog_details_convenience
: R.string.security_settings_face_settings_remove_dialog_details)
.setPositiveButton(R.string.delete, mOnClickListener)
.setNegativeButton(R.string.cancel, mOnClickListener);
AlertDialog dialog = builder.create();
@@ -76,6 +80,10 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference
return dialog;
}
public void setIsConvenience(boolean isConvenience) {
mIsConvenience = isConvenience;
}
public void setOnClickListener(DialogInterface.OnClickListener listener) {
mOnClickListener = listener;
}
@@ -197,6 +205,7 @@ public class FaceSettingsRemoveButtonPreferenceController extends BasePreference
mRemoving = true;
ConfirmRemoveDialog dialog = new ConfirmRemoveDialog();
dialog.setOnClickListener(mOnClickListener);
dialog.setIsConvenience(BiometricUtils.isConvenience(mFaceManager));
dialog.show(mActivity.getSupportFragmentManager(), ConfirmRemoveDialog.class.getName());
}
}

View File

@@ -18,6 +18,7 @@ package com.android.settings.biometrics.fingerprint;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
@@ -46,7 +47,6 @@ import android.view.animation.Interpolator;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.IntDef;
import androidx.appcompat.app.AlertDialog;
import com.android.settings.R;
@@ -158,7 +158,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
mIsSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
if (mCanAssumeUdfps) {
updateTitleAndDescriptionForUdfps();
updateTitleAndDescription();
} else {
setHeaderText(R.string.security_settings_fingerprint_enroll_repeat_title);
}
@@ -567,6 +567,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
private final Animator.AnimatorListener mProgressAnimationListener =
new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) { }

View File

@@ -21,7 +21,11 @@ import android.content.Intent;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Bundle;
import android.view.OrientationEventListener;
import android.view.Surface;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.Nullable;
@@ -32,6 +36,7 @@ import com.android.settings.biometrics.BiometricEnrollSidecar;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.airbnb.lottie.LottieAnimationView;
import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupcompat.template.FooterButton;
@@ -49,6 +54,10 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements
private FingerprintEnrollSidecar mSidecar;
private boolean mNextClicked;
private boolean mCanAssumeUdfps;
private boolean mCanAssumeSidefps;
private OrientationEventListener mOrientationEventListener;
private int mPreviousRotation = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -58,6 +67,7 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements
final List<FingerprintSensorPropertiesInternal> props =
fingerprintManager.getSensorPropertiesInternal();
mCanAssumeUdfps = props != null && props.size() == 1 && props.get(0).isAnyUdfpsType();
mCanAssumeSidefps = props != null && props.size() == 1 && props.get(0).isAnySidefpsType();
setContentView(getContentView());
mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
mFooterBarMixin.setSecondaryButton(
@@ -69,6 +79,8 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements
.build()
);
listenOrientationEvent();
if (mCanAssumeUdfps) {
setHeaderText(R.string.security_settings_udfps_enroll_find_sensor_title);
setDescriptionText(R.string.security_settings_udfps_enroll_find_sensor_message);
@@ -80,6 +92,35 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements
.setTheme(R.style.SudGlifButton_Primary)
.build()
);
LottieAnimationView lottieAnimationView = findViewById(R.id.illustration_lottie);
AccessibilityManager am = getSystemService(AccessibilityManager.class);
if (am.isEnabled()) {
lottieAnimationView.setAnimation(R.raw.udfps_edu_a11y_lottie);
}
} else if (mCanAssumeSidefps) {
setHeaderText(R.string.security_settings_fingerprint_enroll_find_sensor_title);
setDescriptionText(R.string.security_settings_fingerprint_enroll_find_sensor_message);
final LottieAnimationView lottieAnimationView = findViewById(R.id.illustration_lottie);
final LottieAnimationView lottieAnimationViewPortrait =
findViewById(R.id.illustration_lottie_portrait);
final int rotation = getApplicationContext().getDisplay().getRotation();
switch(rotation) {
case Surface.ROTATION_90:
lottieAnimationView.setVisibility(View.GONE);
lottieAnimationViewPortrait.setVisibility(View.VISIBLE);
break;
case Surface.ROTATION_270:
lottieAnimationView.setVisibility(View.GONE);
lottieAnimationViewPortrait.setVisibility(View.VISIBLE);
lottieAnimationViewPortrait.setRotation(180);
break;
default:
lottieAnimationView.setVisibility(View.VISIBLE);
lottieAnimationViewPortrait.setVisibility(View.GONE);
break;
}
} else {
setHeaderText(R.string.security_settings_fingerprint_enroll_find_sensor_title);
setDescriptionText(R.string.security_settings_fingerprint_enroll_find_sensor_message);
@@ -110,7 +151,15 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements
}
mAnimation = null;
if (!mCanAssumeUdfps) {
if (mCanAssumeUdfps) {
LottieAnimationView lottieAnimationView = findViewById(R.id.illustration_lottie);
lottieAnimationView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onStartButtonClick(v);
}
});
} else {
View animationView = findViewById(R.id.fingerprint_sensor_location_animation);
if (animationView instanceof FingerprintFindSensorAnimation) {
mAnimation = (FingerprintFindSensorAnimation) animationView;
@@ -127,6 +176,8 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements
protected int getContentView() {
if (mCanAssumeUdfps) {
return R.layout.udfps_enroll_find_sensor_layout;
} else if (mCanAssumeSidefps) {
return R.layout.sfps_enroll_find_sensor_layout;
}
return R.layout.fingerprint_enroll_find_sensor;
}
@@ -202,6 +253,7 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements
@Override
protected void onDestroy() {
stopListenOrientationEvent();
super.onDestroy();
if (mAnimation != null) {
mAnimation.stopAnimation();
@@ -279,4 +331,37 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements
public int getMetricsCategory() {
return SettingsEnums.FINGERPRINT_FIND_SENSOR;
}
private void listenOrientationEvent() {
if (!mCanAssumeSidefps) {
// Do nothing if the device doesn't support SideFPS.
return;
}
mOrientationEventListener = new OrientationEventListener(this) {
@Override
public void onOrientationChanged(int orientation) {
final int currentRotation = getDisplay().getRotation();
if ((mPreviousRotation == Surface.ROTATION_90
&& currentRotation == Surface.ROTATION_270) || (
mPreviousRotation == Surface.ROTATION_270
&& currentRotation == Surface.ROTATION_90)) {
mPreviousRotation = currentRotation;
recreate();
}
}
};
mOrientationEventListener.enable();
mPreviousRotation = getDisplay().getRotation();
}
private void stopListenOrientationEvent() {
if (!mCanAssumeSidefps) {
// Do nothing if the device doesn't support SideFPS.
return;
}
if (mOrientationEventListener != null) {
mOrientationEventListener.disable();
}
mOrientationEventListener = null;
}
}

View File

@@ -44,6 +44,7 @@ import com.android.settingslib.HelpUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.android.setupdesign.span.LinkSpan;
import java.util.List;
@@ -203,6 +204,7 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
@Override
protected int checkMaxEnrolled() {
final boolean isSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
if (mFingerprintManager != null) {
final List<FingerprintSensorPropertiesInternal> props =
mFingerprintManager.getSensorPropertiesInternal();
@@ -210,13 +212,22 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
final int max = props.get(0).maxEnrollmentsPerUser;
final int numEnrolledFingerprints =
mFingerprintManager.getEnrolledFingerprints(mUserId).size();
if (numEnrolledFingerprints >= max) {
final int maxFingerprintsEnrollableIfSUW = getApplicationContext().getResources()
.getInteger(R.integer.suw_max_fingerprints_enrollable);
if (isSetupWizard) {
if (numEnrolledFingerprints >= maxFingerprintsEnrollableIfSUW) {
return R.string.fingerprint_intro_error_max;
} else {
return 0;
}
} else if (numEnrolledFingerprints >= max) {
return R.string.fingerprint_intro_error_max;
} else {
return 0;
}
} else {
return R.string.fingerprint_intro_error_unknown;
}
return 0;
}
@Override

View File

@@ -31,12 +31,18 @@ import com.android.settings.biometrics.BiometricErrorDialog;
*/
public class FingerprintErrorDialog extends BiometricErrorDialog {
public static void showErrorDialog(BiometricEnrollBase host, int errMsgId) {
if (host.isFinishing()) {
return;
}
final FragmentManager fragmentManager = host.getSupportFragmentManager();
if (fragmentManager.isDestroyed()) {
return;
}
final CharSequence errMsg = host.getText(getErrorMessage(errMsgId));
final FingerprintErrorDialog dialog = newInstance(errMsg, errMsgId);
final FragmentManager fragmentManager = host.getSupportFragmentManager();
if (!fragmentManager.isDestroyed()) {
dialog.show(fragmentManager, FingerprintErrorDialog.class.getName());
}
dialog.show(fragmentManager, FingerprintErrorDialog.class.getName());
}
private static int getErrorMessage(int errMsgId) {

View File

@@ -1,85 +0,0 @@
/*
* Copyright (C) 2017 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.bluetooth;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
/**
* Controller that shows received files
*/
public class BluetoothFilesPreferenceController extends BasePreferenceController
implements PreferenceControllerMixin {
private static final String TAG = "BluetoothFilesPrefCtrl";
public static final String KEY_RECEIVED_FILES = "bt_received_files";
/* Private intent to show the list of received files */
@VisibleForTesting
static final String ACTION_OPEN_FILES = "com.android.bluetooth.action.TransferHistory";
@VisibleForTesting
static final String EXTRA_SHOW_ALL_FILES = "android.btopp.intent.extra.SHOW_ALL";
@VisibleForTesting
static final String EXTRA_DIRECTION = "direction";
private MetricsFeatureProvider mMetricsFeatureProvider;
public BluetoothFilesPreferenceController(Context context) {
super(context, KEY_RECEIVED_FILES);
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
}
@Override
public int getAvailabilityStatus() {
return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
? AVAILABLE
: UNSUPPORTED_ON_DEVICE;
}
@Override
public String getPreferenceKey() {
return KEY_RECEIVED_FILES;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (KEY_RECEIVED_FILES.equals(preference.getKey())) {
mMetricsFeatureProvider.action(mContext,
SettingsEnums.ACTION_BLUETOOTH_FILES);
Intent intent = new Intent(ACTION_OPEN_FILES);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(EXTRA_DIRECTION, 1 /* DIRECTION_INBOUND */);
intent.putExtra(EXTRA_SHOW_ALL_FILES, true);
mContext.startActivity(intent);
return true;
}
return false;
}
}

View File

@@ -188,7 +188,8 @@ public final class BluetoothPairingService extends Service {
}
PendingIntent pairIntent = PendingIntent.getService(this, 0, pairingDialogIntent,
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT
| PendingIntent.FLAG_IMMUTABLE);
Intent serviceIntent = new Intent(ACTION_DISMISS_PAIRING);
serviceIntent.setClass(this, BluetoothPairingService.class);

View File

@@ -98,7 +98,8 @@ public class BluetoothSliceBuilder {
SettingsSlicesContract.KEY_BLUETOOTH).build();
return SliceBuilderUtils.buildSearchResultPageIntent(context,
BluetoothDashboardFragment.class.getName(), null /* key */, screenTitle,
SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY)
SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY,
R.string.menu_key_connected_devices)
.setClassName(context.getPackageName(), SubSettings.class.getName())
.setData(contentUri);
}

View File

@@ -29,7 +29,6 @@ import androidx.appcompat.app.AlertDialog;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -73,8 +72,6 @@ public class ForgetDeviceDialogFragment extends InstrumentedDialogFragment {
};
Context context = getContext();
mDevice = getDevice(context);
final boolean untetheredHeadset = BluetoothUtils.getBooleanMetaData(
mDevice.getDevice(), BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET);
AlertDialog dialog = new AlertDialog.Builder(context)
.setPositiveButton(R.string.bluetooth_unpair_dialog_forget_confirm_button,
@@ -82,9 +79,7 @@ public class ForgetDeviceDialogFragment extends InstrumentedDialogFragment {
.setNegativeButton(android.R.string.cancel, null)
.create();
dialog.setTitle(R.string.bluetooth_unpair_dialog_title);
dialog.setMessage(context.getString(untetheredHeadset
? R.string.bluetooth_untethered_unpair_dialog_body
: R.string.bluetooth_unpair_dialog_body,
dialog.setMessage(context.getString(R.string.bluetooth_unpair_dialog_body,
mDevice.getName()));
return dialog;
}

View File

@@ -21,11 +21,11 @@ import android.content.pm.PackageManager;
import android.provider.SearchIndexableResource;
import com.android.settings.R;
import com.android.settings.bluetooth.BluetoothFilesPreferenceController;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.nfc.AndroidBeamPreferenceController;
import com.android.settings.print.PrintSettingPreferenceController;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.uwb.UwbPreferenceController;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable;
@@ -43,6 +43,7 @@ public class AdvancedConnectedDeviceDashboardFragment extends DashboardFragment
private static final String TAG = "AdvancedConnectedDeviceFrag";
static final String KEY_BLUETOOTH = "bluetooth_settings";
static final String KEY_UWB = "uwb_settings";
@Override
public int getMetricsCategory() {
@@ -64,6 +65,15 @@ public class AdvancedConnectedDeviceDashboardFragment extends DashboardFragment
return R.xml.connected_devices_advanced;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
UwbPreferenceController uwbPreferenceController = use(UwbPreferenceController.class);
if (uwbPreferenceController != null && getSettingsLifecycle() != null) {
getSettingsLifecycle().addObserver(uwbPreferenceController);
}
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return buildControllers(context, getSettingsLifecycle());
@@ -73,8 +83,6 @@ public class AdvancedConnectedDeviceDashboardFragment extends DashboardFragment
Lifecycle lifecycle) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new BluetoothFilesPreferenceController(context));
final PrintSettingPreferenceController printerController =
new PrintSettingPreferenceController(context);

View File

@@ -158,8 +158,7 @@ public abstract class InstrumentedPreferenceFragment extends ObservablePreferenc
switch (newState) {
case RecyclerView.SCROLL_STATE_DRAGGING:
final Configuration.Builder builder =
new Configuration.Builder(CUJ_SETTINGS_PAGE_SCROLL)
.setView(recyclerView)
Configuration.Builder.withView(CUJ_SETTINGS_PAGE_SCROLL, recyclerView)
.setTag(mClassName);
mMonitor.begin(builder);
break;

View File

@@ -73,7 +73,8 @@ public class PreferenceXmlParserUtils {
MetadataFlag.FLAG_NEED_PREF_ICON,
MetadataFlag.FLAG_NEED_SEARCHABLE,
MetadataFlag.FLAG_UNAVAILABLE_SLICE_SUBTITLE,
MetadataFlag.FLAG_FOR_WORK})
MetadataFlag.FLAG_FOR_WORK,
MetadataFlag.FLAG_NEED_HIGHLIGHTABLE_MENU_KEY})
@Retention(RetentionPolicy.SOURCE)
public @interface MetadataFlag {
@@ -89,6 +90,7 @@ public class PreferenceXmlParserUtils {
int FLAG_NEED_PREF_APPEND = 1 << 10;
int FLAG_UNAVAILABLE_SLICE_SUBTITLE = 1 << 11;
int FLAG_FOR_WORK = 1 << 12;
int FLAG_NEED_HIGHLIGHTABLE_MENU_KEY = 1 << 13;
}
public static final String METADATA_PREF_TYPE = "type";
@@ -102,6 +104,7 @@ public class PreferenceXmlParserUtils {
public static final String METADATA_APPEND = "staticPreferenceLocation";
public static final String METADATA_UNAVAILABLE_SLICE_SUBTITLE = "unavailable_slice_subtitle";
public static final String METADATA_FOR_WORK = "for_work";
public static final String METADATA_HIGHLIGHTABLE_MENU_KEY = "highlightable_menu_key";
private static final String ENTRIES_SEPARATOR = "|";
@@ -250,6 +253,10 @@ public class PreferenceXmlParserUtils {
preferenceMetadata.putBoolean(METADATA_FOR_WORK,
isForWork(preferenceAttributes));
}
if (hasFlag(flags, MetadataFlag.FLAG_NEED_HIGHLIGHTABLE_MENU_KEY)) {
preferenceMetadata.putString(METADATA_HIGHLIGHTABLE_MENU_KEY,
getHighlightableMenuKey(preferenceAttributes));
}
metadata.add(preferenceMetadata);
preferenceAttributes.recycle();
@@ -314,6 +321,10 @@ public class PreferenceXmlParserUtils {
return styledAttributes.getString(R.styleable.Preference_controller);
}
private static String getHighlightableMenuKey(TypedArray styledAttributes) {
return styledAttributes.getString(R.styleable.Preference_highlightableMenuKey);
}
private static int getIcon(TypedArray styledAttributes) {
return styledAttributes.getResourceId(com.android.internal.R.styleable.Icon_icon, 0);
}

View File

@@ -36,6 +36,7 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.fragment.app.FragmentActivity;
import com.android.settings.R;
import com.android.settings.SetupWizardUtils;
import com.android.settings.SubSettings;
import com.android.settings.core.CategoryMixin.CategoryHandler;
import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin;
@@ -73,6 +74,9 @@ public class SettingsBaseActivity extends FragmentActivity implements CategoryHa
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (isFinishing()) {
return;
}
if (isLockTaskModePinned() && !isSettingsRunOnTop()) {
Log.w(TAG, "Devices lock task mode pinned.");
finish();
@@ -91,17 +95,14 @@ public class SettingsBaseActivity extends FragmentActivity implements CategoryHa
// Apply SetupWizard light theme during setup flow. This is for SubSettings pages.
final boolean isAnySetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
if (isAnySetupWizard && this instanceof SubSettings) {
int appliedTheme;
if (ThemeHelper.trySetDynamicColor(this)) {
appliedTheme = ThemeHelper.isSetupWizardDayNightEnabled(this)
final int appliedTheme = ThemeHelper.isSetupWizardDayNightEnabled(this)
? R.style.SudDynamicColorThemeSettings_SetupWizard_DayNight
: R.style.SudDynamicColorThemeSettings_SetupWizard;
setTheme(appliedTheme);
} else {
appliedTheme = ThemeHelper.isSetupWizardDayNightEnabled(this)
? R.style.SubSettings_SetupWizard
: R.style.SudThemeGlifV3_Light;
setTheme(SetupWizardUtils.getTheme(this, getIntent()));
}
setTheme(appliedTheme);
}
if (isToolbarEnabled() && !isAnySetupWizard) {
@@ -186,19 +187,17 @@ public class SettingsBaseActivity extends FragmentActivity implements CategoryHa
@Override
public void setTitle(CharSequence title) {
super.setTitle(title);
if (mCollapsingToolbarLayout != null) {
mCollapsingToolbarLayout.setTitle(title);
} else {
super.setTitle(title);
}
}
@Override
public void setTitle(int titleId) {
super.setTitle(getText(titleId));
if (mCollapsingToolbarLayout != null) {
mCollapsingToolbarLayout.setTitle(getText(titleId));
} else {
super.setTitle(titleId);
}
}

View File

@@ -89,4 +89,7 @@ public abstract class TogglePreferenceController extends BasePreferenceControlle
public boolean isPublicSlice() {
return false;
}
@Override
public abstract int getSliceHighlightMenuRes();
}

View File

@@ -29,6 +29,7 @@ import com.android.settings.accessibility.AccessibilityDetailsSettingsFragment;
import com.android.settings.accessibility.AccessibilitySettings;
import com.android.settings.accessibility.AccessibilitySettingsForSetupWizard;
import com.android.settings.accessibility.CaptionPropertiesFragment;
import com.android.settings.accessibility.ToggleColorInversionPreferenceFragment;
import com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment;
import com.android.settings.accessibility.ToggleReduceBrightColorsPreferenceFragment;
import com.android.settings.accounts.AccountDashboardFragment;
@@ -67,6 +68,7 @@ import com.android.settings.biometrics.combination.CombinedBiometricSettings;
import com.android.settings.biometrics.face.FaceSettings;
import com.android.settings.biometrics.fingerprint.FingerprintSettings;
import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;
import com.android.settings.bluetooth.BluetoothPairingDetail;
import com.android.settings.bugreporthandler.BugReportHandlerPicker;
import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
@@ -119,6 +121,7 @@ import com.android.settings.network.NetworkDashboardFragment;
import com.android.settings.network.NetworkProviderSettings;
import com.android.settings.network.apn.ApnEditor;
import com.android.settings.network.apn.ApnSettings;
import com.android.settings.network.telephony.NetworkSelectSettings;
import com.android.settings.nfc.AndroidBeam;
import com.android.settings.nfc.PaymentSettings;
import com.android.settings.notification.ConfigureNotificationSettings;
@@ -175,6 +178,7 @@ public class SettingsGateway {
public static final String[] ENTRY_FRAGMENTS = {
AdvancedConnectedDeviceDashboardFragment.class.getName(),
CreateShortcut.class.getName(),
BluetoothPairingDetail.class.getName(),
WifiSettings.class.getName(),
WifiNetworkDetailsFragment.class.getName(),
ConfigureWifiSettings.class.getName(),
@@ -215,6 +219,7 @@ public class SettingsGateway {
AccessibilitySettingsForSetupWizard.class.getName(),
CaptionPropertiesFragment.class.getName(),
ToggleDaltonizerPreferenceFragment.class.getName(),
ToggleColorInversionPreferenceFragment.class.getName(),
ToggleReduceBrightColorsPreferenceFragment.class.getName(),
TextToSpeechSettings.class.getName(),
PrivateVolumeForget.class.getName(),
@@ -317,6 +322,7 @@ public class SettingsGateway {
InteractAcrossProfilesDetails.class.getName(),
MediaControlsSettings.class.getName(),
NetworkProviderSettings.class.getName(),
NetworkSelectSettings.class.getName(),
AlarmsAndRemindersDetails.class.getName(),
MediaManagementAppsDetails.class.getName(),
AutoBrightnessSettings.class.getName()
@@ -341,6 +347,7 @@ public class SettingsGateway {
Settings.WifiSettingsActivity.class.getName(),
Settings.DataUsageSummaryActivity.class.getName(),
Settings.NetworkProviderSettingsActivity.class.getName(),
Settings.NetworkSelectActivity.class.getName(),
// Home page > Connected devices
Settings.BluetoothSettingsActivity.class.getName(),
Settings.WifiDisplaySettingsActivity.class.getName(),

View File

@@ -17,6 +17,7 @@ package com.android.settings.dashboard;
import android.content.ComponentName;
import android.content.Context;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -24,6 +25,7 @@ import android.util.Pair;
import androidx.annotation.VisibleForTesting;
import com.android.settings.homepage.HighlightableMenu;
import com.android.settingslib.applications.InterestingConfigChanges;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.DashboardCategory;
@@ -153,6 +155,20 @@ public class CategoryManager {
filterDuplicateTiles(mCategoryByKeyMap);
if (firstLoading) {
logTiles(context);
final DashboardCategory homepageCategory = mCategoryByKeyMap.get(
CategoryKey.CATEGORY_HOMEPAGE);
if (homepageCategory == null) {
return;
}
for (Tile tile : homepageCategory.getTiles()) {
final String key = tile.getKey(context);
if (TextUtils.isEmpty(key)) {
Log.w(TAG, "Key hint missing for homepage tile: " + tile.getTitle(context));
continue;
}
HighlightableMenu.addMenuKey(key);
}
}
}
}

View File

@@ -47,9 +47,9 @@ public interface DashboardFeatureProvider {
* Binds preference to data provided by tile and gets dynamic data observers.
*
* @param activity If tile contains intent to launch, it will be launched from this activity
* @param fragment The fragment that the preference will be bound to
* @param forceRoundedIcon Whether or not injected tiles from other packages should be forced to
* rounded icon.
* @param sourceMetricsCategory The context (source) from which an action is performed
* @param pref The preference to bind data
* @param tile The binding data
* @param key They key for preference. If null, we will generate one from tile data
@@ -58,7 +58,7 @@ public interface DashboardFeatureProvider {
* @return The list of dynamic data observers
*/
List<DynamicDataObserver> bindPreferenceToTileAndGetObservers(FragmentActivity activity,
boolean forceRoundedIcon, int sourceMetricsCategory, Preference pref, Tile tile,
DashboardFragment fragment, boolean forceRoundedIcon, Preference pref, Tile tile,
String key, int baseOrder);
/**

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