Accessibility Service & Shortcut Redesign - Remove “volume key shortcut” setting (2/n)

Bug: 142529032
Test: make RunSettingsRoboTests ROBOTEST_FILTER=AccessibilityShortcutPreferenceControllerTest
Change-Id: Ic55d4fd8db0678b8fccbbb6ef1d217b47a5094bd
This commit is contained in:
menghanli
2020-02-13 14:24:04 +08:00
parent c359bdf884
commit a8b9f30d11
7 changed files with 160 additions and 342 deletions

View File

@@ -20,12 +20,6 @@
android:persistent="false"
android:title="@string/accessibility_settings">
<Preference
android:fragment="com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment"
android:key="accessibility_shortcut_preference"
android:persistent="false"
android:title="@string/accessibility_global_gesture_preference_title" />
<PreferenceCategory
android:key="user_installed_services_category"
android:persistent="false"
@@ -199,6 +193,13 @@
android:persistent="false"
android:title="@string/experimental_category_title">
<SwitchPreference
android:key="accessibility_shortcut_preference"
android:persistent="false"
android:title="@string/accessibility_shortcut_service_on_lock_screen_title"
android:summary="@string/accessibility_shortcut_description"
settings:controller="com.android.settings.accessibility.AccessibilityShortcutPreferenceController"/>
<SwitchPreference
android:key="toggle_high_text_contrast_preference"
android:persistent="false"

View File

@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/accessibility_global_gesture_preference_title">
<SwitchPreference
android:key="accessibility_shortcut_on_lock_screen"
android:title="@string/accessibility_shortcut_service_on_lock_screen_title" />
<com.android.settingslib.widget.FooterPreference
android:key="accessibility_shortcut_footer"
android:selectable="false"
android:title="@string/accessibility_shortcut_description"
settings:searchable="false" />
</PreferenceScreen>

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import android.content.ContentResolver;
import android.content.Context;
import android.os.UserHandle;
import android.provider.Settings;
import com.android.settings.core.TogglePreferenceController;
/**
* Settings page for accessibility shortcut
*/
public class AccessibilityShortcutPreferenceController extends TogglePreferenceController {
public AccessibilityShortcutPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public boolean isChecked() {
final ContentResolver cr = mContext.getContentResolver();
// The shortcut is enabled by default on the lock screen as long as the user has
// enabled the shortcut with the warning dialog
final int dialogShown = Settings.Secure.getInt(
cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, OFF);
final boolean enabledFromLockScreen = Settings.Secure.getInt(
cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, dialogShown) == ON;
return enabledFromLockScreen;
}
@Override
public boolean setChecked(boolean isChecked) {
return Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, isChecked ? ON : OFF,
UserHandle.USER_CURRENT);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
}

View File

@@ -1,182 +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.accessibility;
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.Nullable;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.settings.R;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.accessibility.AccessibilityUtils;
import com.android.settingslib.search.Indexable;
import com.android.settingslib.search.SearchIndexable;
/**
* Settings page for accessibility shortcut
*/
@SearchIndexable
public class AccessibilityShortcutPreferenceFragment extends ToggleFeaturePreferenceFragment
implements Indexable {
public static final String ON_LOCK_SCREEN_KEY = "accessibility_shortcut_on_lock_screen";
private SwitchPreference mOnLockScreenSwitchPreference;
private final ContentObserver mContentObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
updatePreferences();
}
};
@Override
public int getMetricsCategory() {
return SettingsEnums.ACCESSIBILITY_TOGGLE_GLOBAL_GESTURE;
}
@Override
public int getHelpResource() {
return R.string.help_url_accessibility_shortcut;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mOnLockScreenSwitchPreference = (SwitchPreference) findPreference(ON_LOCK_SCREEN_KEY);
mOnLockScreenSwitchPreference.setOnPreferenceChangeListener((Preference p, Object o) -> {
Settings.Secure.putInt(getContentResolver(),
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
((Boolean) o) ? ON : OFF);
return true;
});
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final PreferenceScreen preferenceScreen = getPreferenceScreen();
preferenceScreen.findPreference(KEY_GENERAL_CATEGORY).setVisible(false);
preferenceScreen.setOrderingAsAdded(false);
mToggleServiceDividerSwitchPreference.setVisible(false);
}
@Override
public void onResume() {
super.onResume();
updatePreferences();
getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN),
false, mContentObserver);
}
@Override
public void onPause() {
getContentResolver().unregisterContentObserver(mContentObserver);
super.onPause();
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.accessibility_shortcut_settings;
}
@Override
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
Settings.Secure.putInt(getContentResolver(), preferenceKey, enabled ? ON : OFF);
updatePreferences();
}
private void updatePreferences() {
ContentResolver cr = getContentResolver();
// The shortcut is enabled by default on the lock screen as long as the user has
// enabled the shortcut with the warning dialog
final int dialogShown = Settings.Secure.getInt(
cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, OFF);
final boolean enabledFromLockScreen = Settings.Secure.getInt(
cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, dialogShown) == ON;
mOnLockScreenSwitchPreference.setChecked(enabledFromLockScreen);
}
/**
* Get the user-visible name of the service currently selected for the shortcut.
*
* @param context The current context
* @return The name of the service or a string saying that none is selected.
*/
public static CharSequence getServiceName(Context context) {
if (!shortcutFeatureAvailable(context)) {
return context.getString(R.string.accessibility_no_service_selected);
}
AccessibilityServiceInfo shortcutServiceInfo = getServiceInfo(context);
if (shortcutServiceInfo != null) {
return shortcutServiceInfo.getResolveInfo().loadLabel(context.getPackageManager());
}
return AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
.get(getShortcutComponent(context)).getLabel(context);
}
private static AccessibilityServiceInfo getServiceInfo(Context context) {
return AccessibilityManager.getInstance(context)
.getInstalledServiceInfoWithComponentName(getShortcutComponent(context));
}
private static boolean shortcutFeatureAvailable(Context context) {
ComponentName shortcutFeature = getShortcutComponent(context);
if (shortcutFeature == null) return false;
if (AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
.containsKey(shortcutFeature)) {
return true;
}
return getServiceInfo(context) != null;
}
private static @Nullable ComponentName getShortcutComponent(Context context) {
String componentNameString = AccessibilityUtils.getShortcutTargetServiceComponentNameString(
context, UserHandle.myUserId());
if (componentNameString == null) return null;
return ComponentName.unflattenFromString(componentNameString);
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
// This fragment is for details of the shortcut. Only the shortcut itself needs
// to be indexed.
protected boolean isPageSearchEnabled(Context context) {
return false;
}
};
}

View File

@@ -98,6 +98,7 @@ final class AccessibilityUtil {
/** Denotes the accessibility enabled status */
@Retention(RetentionPolicy.SOURCE)
public @interface State {
int UNKNOWN = -1;
int OFF = 0;
int ON = 1;
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import static com.android.settings.accessibility.AccessibilityUtil.State.UNKNOWN;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.os.UserHandle;
import android.provider.Settings;
import androidx.preference.SwitchPreference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class AccessibilityShortcutPreferenceControllerTest {
private Context mContext;
private SwitchPreference mPreference;
private AccessibilityShortcutPreferenceController mController;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mPreference = new SwitchPreference(mContext);
mController = new AccessibilityShortcutPreferenceController(mContext,
"accessibility_shortcut_preference");
}
@Test
public void isChecked_enabledShortcutOnLockScreen_shouldReturnTrue() {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, ON, UserHandle.USER_CURRENT);
mController.updateState(mPreference);
assertThat(mController.isChecked()).isTrue();
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void isChecked_disabledShortcutOnLockScreen_shouldReturnFalse() {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, OFF,
UserHandle.USER_CURRENT);
mController.updateState(mPreference);
assertThat(mController.isChecked()).isFalse();
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void setChecked_setTrue_shouldEnableShortcutOnLockScreen() {
mController.setChecked(true);
assertThat(Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, UNKNOWN,
UserHandle.USER_CURRENT)).isEqualTo(ON);
}
@Test
public void setChecked_setFalse_shouldDisableShortcutOnLockScreen() {
mController.setChecked(false);
assertThat(Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, UNKNOWN,
UserHandle.USER_CURRENT)).isEqualTo(OFF);
}
}

View File

@@ -1,124 +0,0 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.accessibility;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
import static androidx.test.espresso.matcher.ViewMatchers.isNotChecked;
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.Matchers.allOf;
import android.app.Instrumentation;
import android.os.Bundle;
import android.provider.Settings;
import android.widget.CompoundButton;
import androidx.test.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import com.android.settings.R;
import com.android.settings.Settings.AccessibilitySettingsActivity;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.core.SubSettingLauncher;
import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class AccessibilityShortcutPreferenceFragmentTest {
@Rule
public final ActivityTestRule<AccessibilitySettingsActivity> mActivityRule =
new ActivityTestRule<>(AccessibilitySettingsActivity.class, true);
private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private AccessibilityShortcutPreferenceFragment mAccessibilityShortcutPreferenceFragment;
private AccessibilitySettingsActivity mActivity;
@Before
public void setUp() {
mActivity = mActivityRule.getActivity();
}
@Test
public void lockScreenPreference_setOnBeforeDialogShown_isOn() {
setDialogShown(false);
setOnLockscreen(true);
startFragment();
assertLockscreenSwitchIsCheckedIs(true);
}
@Test
public void lockScreenPreference_defaultAfterDialogShown_isOn() {
setDialogShown(true);
setOnLockscreen(null);
startFragment();
assertLockscreenSwitchIsCheckedIs(true);
}
private void startFragment() {
mInstrumentation.runOnMainSync(() -> {
new SubSettingLauncher(mActivity)
.setDestination(AccessibilityShortcutPreferenceFragment.class.getName())
.setArguments(new Bundle())
.setSourceMetricsCategory(
InstrumentedPreferenceFragment.METRICS_CATEGORY_UNKNOWN)
.launch();
});
}
private void setDialogShown(boolean shown) {
Settings.Secure.putInt(mActivity.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, shown ? 1 : 0);
}
private void setOnLockscreen(Boolean onLockscreen) {
if (onLockscreen == null) {
Settings.Secure.putString(mActivity.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, null);
} else {
Settings.Secure.putInt(mActivity.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, onLockscreen ? 1 : 0);
}
}
private void assertLockscreenSwitchIsCheckedIs(boolean isChecked) {
// Identify the switch by looking for a grandparent that has a descendent with the
// switch label. To disambiguate, make sure that grandparent doesn't also have a descendant
// with the title of the main switch
final String lockScreenSwitchTitle =
mActivity.getString(R.string.accessibility_shortcut_service_on_lock_screen_title);
final String mainSwitchTitle =
mActivity.getString(R.string.accessibility_service_master_switch_title);
Matcher isCheckedMatcher = (isChecked) ? isChecked() : isNotChecked();
Matcher hasLockScreenTitleDescendant = hasDescendant(withText(lockScreenSwitchTitle));
Matcher noMainSwitchTitleDescendant = not(hasDescendant(withText(mainSwitchTitle)));
onView(allOf(withParent(withParent(allOf(
hasLockScreenTitleDescendant, noMainSwitchTitleDescendant))),
instanceOf(CompoundButton.class))).check(matches(isCheckedMatcher));
}
}