Convert a11y shortcut edit screen to a full screen.
Bug: 300302098 Flag: adb shell device_config put accessibility com.android.settings.accessibility.edit_shortcuts_in_full_screen true Test: manual Test: atest com.android.settings.accessibility.shortcuts Change-Id: I0b5e367fed962ff01036122ce7ff32a8ae47fbc3
This commit is contained in:
@@ -2,6 +2,14 @@ package: "com.android.settings.accessibility"
|
|||||||
|
|
||||||
# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
|
# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
|
||||||
|
|
||||||
|
flag {
|
||||||
|
name: "edit_shortcuts_in_full_screen"
|
||||||
|
namespace: "accessibility"
|
||||||
|
description: "Show the edit shorcuts screen in full screen, since the shortcut options are increasing."
|
||||||
|
bug: "300302098"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
flag {
|
flag {
|
||||||
name: "remove_qs_tooltip_in_suw"
|
name: "remove_qs_tooltip_in_suw"
|
||||||
namespace: "accessibility"
|
namespace: "accessibility"
|
||||||
|
84
res/layout/accessibility_shortcut_option_checkable.xml
Normal file
84
res/layout/accessibility_shortcut_option_checkable.xml
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2023 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/a11y_shortcut_option_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||||
|
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||||
|
android:paddingBottom="?android:attr/listPreferredItemPaddingEnd">
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@android:id/checkbox"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||||
|
android:background="@null"
|
||||||
|
android:clickable="false"
|
||||||
|
android:focusable="false"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@android:id/title"
|
||||||
|
app:layout_constraintEnd_toStartOf="@android:id/title"
|
||||||
|
app:layout_constraintHeight="true"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintWidth="true" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@android:id/title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@android:id/checkbox"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="Shortcut option title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@android:id/summary"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="@android:id/title"
|
||||||
|
app:layout_constraintTop_toBottomOf="@android:id/title"
|
||||||
|
tools:text="Shortcut option summary" />
|
||||||
|
|
||||||
|
<com.airbnb.lottie.LottieAnimationView
|
||||||
|
android:id="@+id/image"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/settingslib_illustration_padding"
|
||||||
|
android:layout_marginBottom="@dimen/settingslib_illustration_padding"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:background="@drawable/protection_background"
|
||||||
|
android:maxHeight="@dimen/accessibility_imageview_size"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0"
|
||||||
|
app:layout_constraintStart_toStartOf="@android:id/title"
|
||||||
|
app:layout_constraintTop_toBottomOf="@android:id/summary"
|
||||||
|
app:layout_constraintWidth_max="@dimen/settingslib_illustration_width" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
26
res/values/accessibility_shortcut_keys.xml
Normal file
26
res/values/accessibility_shortcut_keys.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2023 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<string name="accessibility_shortcut_volume_keys_pref" translatable="false">shortcut_volume_keys_pref</string>
|
||||||
|
<string name="accessibility_shortcut_gesture_pref" translatable="false">shortcut_gesture_pref</string>
|
||||||
|
<string name="accessibility_shortcut_nav_button_pref" translatable="false">shortcut_nav_button_pref</string>
|
||||||
|
<string name="accessibility_shortcut_fab_pref" translatable="false">shortcut_fab_pref</string>
|
||||||
|
<string name="accessibility_shortcut_triple_tap_pref" translatable="false">shortcut_triple_tap_pref</string>
|
||||||
|
<string name="accessibility_shortcut_two_fingers_double_tap_pref" translatable="false">shortcut_two_fingers_double_tap_pref</string>
|
||||||
|
<string name="accessibility_shortcuts_advanced_collapsed" translatable="false">advanced_shortcuts_collapsed</string>
|
||||||
|
</resources>
|
79
res/xml/accessibility_edit_shortcuts.xml
Normal file
79
res/xml/accessibility_edit_shortcuts.xml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2023 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">
|
||||||
|
|
||||||
|
<com.android.settings.accessibility.shortcuts.ShortcutOptionPreference
|
||||||
|
android:key="@string/accessibility_shortcut_fab_pref"
|
||||||
|
android:persistent="false"
|
||||||
|
android:selectable="true"
|
||||||
|
settings:allowDividerAbove="false"
|
||||||
|
settings:allowDividerBelow="false"
|
||||||
|
settings:controller="com.android.settings.accessibility.shortcuts.FloatingButtonShortcutOptionController" />
|
||||||
|
|
||||||
|
<com.android.settings.accessibility.shortcuts.ShortcutOptionPreference
|
||||||
|
android:key="@string/accessibility_shortcut_gesture_pref"
|
||||||
|
android:persistent="false"
|
||||||
|
android:selectable="true"
|
||||||
|
settings:allowDividerAbove="false"
|
||||||
|
settings:allowDividerBelow="false"
|
||||||
|
settings:controller="com.android.settings.accessibility.shortcuts.GestureShortcutOptionController" />
|
||||||
|
|
||||||
|
<com.android.settings.accessibility.shortcuts.ShortcutOptionPreference
|
||||||
|
android:key="@string/accessibility_shortcut_nav_button_pref"
|
||||||
|
android:persistent="false"
|
||||||
|
android:selectable="true"
|
||||||
|
settings:allowDividerAbove="false"
|
||||||
|
settings:allowDividerBelow="false"
|
||||||
|
settings:controller="com.android.settings.accessibility.shortcuts.NavButtonShortcutOptionController" />
|
||||||
|
|
||||||
|
<com.android.settings.accessibility.shortcuts.ShortcutOptionPreference
|
||||||
|
android:key="@string/accessibility_shortcut_volume_keys_pref"
|
||||||
|
android:persistent="false"
|
||||||
|
android:selectable="true"
|
||||||
|
settings:allowDividerAbove="false"
|
||||||
|
settings:allowDividerBelow="false"
|
||||||
|
settings:controller="com.android.settings.accessibility.shortcuts.VolumeKeysShortcutOptionController" />
|
||||||
|
|
||||||
|
<com.android.settings.accessibility.shortcuts.ShortcutOptionPreference
|
||||||
|
android:key="@string/accessibility_shortcut_two_fingers_double_tap_pref"
|
||||||
|
android:persistent="false"
|
||||||
|
android:selectable="true"
|
||||||
|
settings:allowDividerAbove="false"
|
||||||
|
settings:allowDividerBelow="false"
|
||||||
|
settings:controller="com.android.settings.accessibility.shortcuts.TwoFingersDoubleTapShortcutOptionController" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:icon="@drawable/ic_keyboard_arrow_down"
|
||||||
|
android:key="@string/accessibility_shortcuts_advanced_collapsed"
|
||||||
|
android:persistent="false"
|
||||||
|
android:selectable="true"
|
||||||
|
android:title="@string/accessibility_shortcut_edit_dialog_title_advance"
|
||||||
|
settings:allowDividerAbove="false"
|
||||||
|
settings:allowDividerBelow="false"
|
||||||
|
settings:controller="com.android.settings.accessibility.shortcuts.AdvancedShortcutsPreferenceController" />
|
||||||
|
|
||||||
|
<com.android.settings.accessibility.shortcuts.ShortcutOptionPreference
|
||||||
|
android:key="@string/accessibility_shortcut_triple_tap_pref"
|
||||||
|
android:persistent="false"
|
||||||
|
android:selectable="true"
|
||||||
|
settings:allowDividerAbove="false"
|
||||||
|
settings:allowDividerBelow="false"
|
||||||
|
settings:controller="com.android.settings.accessibility.shortcuts.TripleTapShortcutOptionController" />
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
@@ -44,6 +44,7 @@ import androidx.preference.PreferenceScreen;
|
|||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
|
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
|
||||||
|
import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment;
|
||||||
import com.android.settings.dashboard.RestrictedDashboardFragment;
|
import com.android.settings.dashboard.RestrictedDashboardFragment;
|
||||||
import com.android.settings.utils.LocaleUtils;
|
import com.android.settings.utils.LocaleUtils;
|
||||||
|
|
||||||
@@ -251,7 +252,17 @@ public abstract class AccessibilityShortcutPreferenceFragment extends Restricted
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSettingsClicked(ShortcutPreference preference) {
|
public void onSettingsClicked(ShortcutPreference preference) {
|
||||||
showDialog(DialogEnums.EDIT_SHORTCUT);
|
if (Flags.editShortcutsInFullScreen()) {
|
||||||
|
EditShortcutsPreferenceFragment.showEditShortcutScreen(
|
||||||
|
getContext(),
|
||||||
|
getMetricsCategory(),
|
||||||
|
getShortcutTitle(),
|
||||||
|
getComponentName(),
|
||||||
|
getIntent()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
showDialog(DialogEnums.EDIT_SHORTCUT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface denoting a preference is expandable
|
||||||
|
*/
|
||||||
|
public interface ExpandablePreference {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the expandable state of the preference
|
||||||
|
*/
|
||||||
|
void setExpanded(boolean expanded);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if the preference is currently expanded.
|
||||||
|
*/
|
||||||
|
boolean isExpanded();
|
||||||
|
}
|
@@ -48,6 +48,7 @@ import androidx.annotation.Nullable;
|
|||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
|
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
|
||||||
import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
|
import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
|
||||||
|
import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment;
|
||||||
import com.android.settingslib.accessibility.AccessibilityUtils;
|
import com.android.settingslib.accessibility.AccessibilityUtils;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -539,7 +540,17 @@ public class ToggleAccessibilityServicePreferenceFragment extends
|
|||||||
|
|
||||||
private void onAllowButtonFromShortcutClicked() {
|
private void onAllowButtonFromShortcutClicked() {
|
||||||
mIsDialogShown.set(false);
|
mIsDialogShown.set(false);
|
||||||
showPopupDialog(DialogEnums.EDIT_SHORTCUT);
|
if (Flags.editShortcutsInFullScreen()) {
|
||||||
|
EditShortcutsPreferenceFragment.showEditShortcutScreen(
|
||||||
|
getContext(),
|
||||||
|
getMetricsCategory(),
|
||||||
|
getShortcutTitle(),
|
||||||
|
mComponentName,
|
||||||
|
getIntent()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
showPopupDialog(DialogEnums.EDIT_SHORTCUT);
|
||||||
|
}
|
||||||
|
|
||||||
if (mWarningDialog != null) {
|
if (mWarningDialog != null) {
|
||||||
mWarningDialog.dismiss();
|
mWarningDialog.dismiss();
|
||||||
|
@@ -59,6 +59,7 @@ import com.android.settings.SettingsActivity;
|
|||||||
import com.android.settings.accessibility.AccessibilityDialogUtils.DialogType;
|
import com.android.settings.accessibility.AccessibilityDialogUtils.DialogType;
|
||||||
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
|
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
|
||||||
import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
|
import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
|
||||||
|
import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment;
|
||||||
import com.android.settings.dashboard.DashboardFragment;
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
import com.android.settings.flags.Flags;
|
import com.android.settings.flags.Flags;
|
||||||
import com.android.settings.utils.LocaleUtils;
|
import com.android.settings.utils.LocaleUtils;
|
||||||
@@ -832,7 +833,13 @@ public abstract class ToggleFeaturePreferenceFragment extends DashboardFragment
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSettingsClicked(ShortcutPreference preference) {
|
public void onSettingsClicked(ShortcutPreference preference) {
|
||||||
showDialog(DialogEnums.EDIT_SHORTCUT);
|
if (com.android.settings.accessibility.Flags.editShortcutsInFullScreen()) {
|
||||||
|
EditShortcutsPreferenceFragment.showEditShortcutScreen(
|
||||||
|
requireContext(), getMetricsCategory(), getShortcutTitle(),
|
||||||
|
mComponentName, getIntent());
|
||||||
|
} else {
|
||||||
|
showDialog(DialogEnums.EDIT_SHORTCUT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.settings.accessibility;
|
package com.android.settings.accessibility;
|
||||||
|
|
||||||
|
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
|
||||||
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
|
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
|
||||||
import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
|
import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
|
||||||
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
|
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
|
||||||
@@ -55,6 +56,7 @@ import com.android.settings.R;
|
|||||||
import com.android.settings.accessibility.AccessibilityDialogUtils.DialogType;
|
import com.android.settings.accessibility.AccessibilityDialogUtils.DialogType;
|
||||||
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
|
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
|
||||||
import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
|
import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
|
||||||
|
import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment;
|
||||||
import com.android.settings.utils.LocaleUtils;
|
import com.android.settings.utils.LocaleUtils;
|
||||||
import com.android.settingslib.core.AbstractPreferenceController;
|
import com.android.settingslib.core.AbstractPreferenceController;
|
||||||
|
|
||||||
@@ -584,7 +586,16 @@ public class ToggleScreenMagnificationPreferenceFragment extends
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSettingsClicked(ShortcutPreference preference) {
|
public void onSettingsClicked(ShortcutPreference preference) {
|
||||||
showDialog(DialogEnums.MAGNIFICATION_EDIT_SHORTCUT);
|
if (com.android.settings.accessibility.Flags.editShortcutsInFullScreen()) {
|
||||||
|
EditShortcutsPreferenceFragment.showEditShortcutScreen(
|
||||||
|
requireContext(),
|
||||||
|
getMetricsCategory(),
|
||||||
|
getShortcutTitle(),
|
||||||
|
MAGNIFICATION_COMPONENT_NAME,
|
||||||
|
getIntent());
|
||||||
|
} else {
|
||||||
|
showDialog(DialogEnums.MAGNIFICATION_EDIT_SHORTCUT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.android.settings.accessibility.ExpandablePreference;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A preference controller that controls an expandable preference that wraps
|
||||||
|
* the advanced shortcut options.
|
||||||
|
*/
|
||||||
|
public class AdvancedShortcutsPreferenceController extends ShortcutOptionPreferenceController
|
||||||
|
implements ExpandablePreference {
|
||||||
|
|
||||||
|
private boolean mIsExpanded = false;
|
||||||
|
|
||||||
|
public AdvancedShortcutsPreferenceController(Context context, String preferenceKey) {
|
||||||
|
super(context, preferenceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isChecked() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void enableShortcutForTargets(boolean enable) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAvailabilityStatus() {
|
||||||
|
if (!isExpanded() && isShortcutAvailable()) {
|
||||||
|
// "Advanced" is available when the user hasn't clicked on it
|
||||||
|
return AVAILABLE_UNSEARCHABLE;
|
||||||
|
}
|
||||||
|
return CONDITIONALLY_UNAVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setExpanded(boolean expanded) {
|
||||||
|
mIsExpanded = expanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isExpanded() {
|
||||||
|
return mIsExpanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isShortcutAvailable() {
|
||||||
|
// Only Magnification has advanced shortcut options.
|
||||||
|
Set<String> shortcutTargets = getShortcutTargets();
|
||||||
|
return shortcutTargets.size() == 1
|
||||||
|
&& shortcutTargets.contains(MAGNIFICATION_CONTROLLER_NAME);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,307 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE;
|
||||||
|
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
|
||||||
|
import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED;
|
||||||
|
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED;
|
||||||
|
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
|
||||||
|
|
||||||
|
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
|
||||||
|
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
|
||||||
|
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.database.ContentObserver;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.accessibility.AccessibilityManager;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.SetupWizardUtils;
|
||||||
|
import com.android.settings.core.SubSettingLauncher;
|
||||||
|
import com.android.settings.dashboard.DashboardFragment;
|
||||||
|
import com.android.settingslib.core.AbstractPreferenceController;
|
||||||
|
|
||||||
|
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A screen show various accessibility shortcut options for the given a11y feature
|
||||||
|
*/
|
||||||
|
public class EditShortcutsPreferenceFragment extends DashboardFragment {
|
||||||
|
private static final String TAG = "EditShortcutsPreferenceFragment";
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static final String ARG_KEY_SHORTCUT_TARGETS = "targets";
|
||||||
|
@VisibleForTesting
|
||||||
|
static final String SAVED_STATE_IS_EXPANDED = "isExpanded";
|
||||||
|
private ContentObserver mSettingsObserver;
|
||||||
|
|
||||||
|
private static final Uri VOLUME_KEYS_SHORTCUT_SETTING =
|
||||||
|
Settings.Secure.getUriFor(ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
|
||||||
|
private static final Uri BUTTON_SHORTCUT_MODE_SETTING =
|
||||||
|
Settings.Secure.getUriFor(ACCESSIBILITY_BUTTON_MODE);
|
||||||
|
private static final Uri BUTTON_SHORTCUT_SETTING =
|
||||||
|
Settings.Secure.getUriFor(ACCESSIBILITY_BUTTON_TARGETS);
|
||||||
|
|
||||||
|
private static final Uri TRIPLE_TAP_SHORTCUT_SETTING =
|
||||||
|
Settings.Secure.getUriFor(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
|
||||||
|
private static final Uri TWO_FINGERS_DOUBLE_TAP_SHORTCUT_SETTING =
|
||||||
|
Settings.Secure.getUriFor(ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED);
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static final Uri[] SHORTCUT_SETTINGS = {
|
||||||
|
VOLUME_KEYS_SHORTCUT_SETTING,
|
||||||
|
BUTTON_SHORTCUT_MODE_SETTING,
|
||||||
|
BUTTON_SHORTCUT_SETTING,
|
||||||
|
TRIPLE_TAP_SHORTCUT_SETTING,
|
||||||
|
TWO_FINGERS_DOUBLE_TAP_SHORTCUT_SETTING,
|
||||||
|
};
|
||||||
|
|
||||||
|
private Set<String> mShortcutTargets;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private AccessibilityManager.TouchExplorationStateChangeListener
|
||||||
|
mTouchExplorationStateChangeListener;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to show the edit shortcut screen
|
||||||
|
*/
|
||||||
|
public static void showEditShortcutScreen(
|
||||||
|
Context context, int metricsCategory, CharSequence screenTitle,
|
||||||
|
ComponentName target, Intent fromIntent) {
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
|
||||||
|
if (MAGNIFICATION_COMPONENT_NAME.equals(target)) {
|
||||||
|
// We can remove this branch once b/147990389 is completed
|
||||||
|
args.putStringArray(
|
||||||
|
ARG_KEY_SHORTCUT_TARGETS, new String[]{MAGNIFICATION_CONTROLLER_NAME});
|
||||||
|
} else {
|
||||||
|
args.putStringArray(
|
||||||
|
ARG_KEY_SHORTCUT_TARGETS, new String[]{target.flattenToString()});
|
||||||
|
}
|
||||||
|
Intent toIntent = new Intent();
|
||||||
|
if (fromIntent != null) {
|
||||||
|
SetupWizardUtils.copySetupExtras(fromIntent, toIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
new SubSettingLauncher(context)
|
||||||
|
.setDestination(EditShortcutsPreferenceFragment.class.getName())
|
||||||
|
.setExtras(toIntent.getExtras())
|
||||||
|
.setArguments(args)
|
||||||
|
.setSourceMetricsCategory(metricsCategory)
|
||||||
|
.setTitleText(screenTitle)
|
||||||
|
.launch();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
initializeArguments();
|
||||||
|
initializePreferenceControllerArguments();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
boolean isExpanded = savedInstanceState.getBoolean(SAVED_STATE_IS_EXPANDED);
|
||||||
|
if (isExpanded) {
|
||||||
|
onExpanded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mSettingsObserver = new ContentObserver(new Handler()) {
|
||||||
|
@Override
|
||||||
|
public void onChange(boolean selfChange, Uri uri) {
|
||||||
|
if (VOLUME_KEYS_SHORTCUT_SETTING.equals(uri)) {
|
||||||
|
refreshPreferenceController(VolumeKeysShortcutOptionController.class);
|
||||||
|
} else if (BUTTON_SHORTCUT_MODE_SETTING.equals(uri)
|
||||||
|
|| BUTTON_SHORTCUT_SETTING.equals(uri)) {
|
||||||
|
refreshSoftwareShortcutControllers();
|
||||||
|
} else if (TRIPLE_TAP_SHORTCUT_SETTING.equals(uri)) {
|
||||||
|
refreshPreferenceController(TripleTapShortcutOptionController.class);
|
||||||
|
} else if (TWO_FINGERS_DOUBLE_TAP_SHORTCUT_SETTING.equals(uri)) {
|
||||||
|
refreshPreferenceController(TwoFingersDoubleTapShortcutOptionController.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
registerSettingsObserver();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
mTouchExplorationStateChangeListener = isTouchExplorationEnabled ->
|
||||||
|
refreshPreferenceController(GestureShortcutOptionController.class);
|
||||||
|
|
||||||
|
final AccessibilityManager am = getSystemService(
|
||||||
|
AccessibilityManager.class);
|
||||||
|
am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
|
||||||
|
if (mTouchExplorationStateChangeListener != null) {
|
||||||
|
final AccessibilityManager am = getSystemService(
|
||||||
|
AccessibilityManager.class);
|
||||||
|
am.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
outState.putBoolean(
|
||||||
|
SAVED_STATE_IS_EXPANDED,
|
||||||
|
use(AdvancedShortcutsPreferenceController.class).isExpanded());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
unregisterSettingsObserver();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerSettingsObserver() {
|
||||||
|
if (mSettingsObserver != null) {
|
||||||
|
for (Uri uri : SHORTCUT_SETTINGS) {
|
||||||
|
getContentResolver().registerContentObserver(
|
||||||
|
uri, /* notifyForDescendants= */ false, mSettingsObserver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unregisterSettingsObserver() {
|
||||||
|
if (mSettingsObserver != null) {
|
||||||
|
getContentResolver().unregisterContentObserver(mSettingsObserver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeArguments() {
|
||||||
|
Bundle args = getArguments();
|
||||||
|
if (args == null || args.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
EditShortcutsPreferenceFragment.class.getSimpleName()
|
||||||
|
+ " requires non-empty shortcut targets");
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] targets = args.getStringArray(ARG_KEY_SHORTCUT_TARGETS);
|
||||||
|
if (targets == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
EditShortcutsPreferenceFragment.class.getSimpleName()
|
||||||
|
+ " requires non-empty shortcut targets");
|
||||||
|
}
|
||||||
|
|
||||||
|
mShortcutTargets = Set.of(targets);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMetricsCategory() {
|
||||||
|
return SettingsEnums.DIALOG_ACCESSIBILITY_SERVICE_EDIT_SHORTCUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getPreferenceScreenResId() {
|
||||||
|
return R.xml.accessibility_edit_shortcuts;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getLogTag() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceTreeClick(Preference preference) {
|
||||||
|
if (getString(R.string.accessibility_shortcuts_advanced_collapsed)
|
||||||
|
.equals(preference.getKey())) {
|
||||||
|
onExpanded();
|
||||||
|
// log here since calling super.onPreferenceTreeClick will be skipped
|
||||||
|
writePreferenceClickMetric(preference);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onPreferenceTreeClick(preference);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void initializePreferenceControllerArguments() {
|
||||||
|
boolean isInSuw = WizardManagerHelper.isAnySetupWizard(getIntent());
|
||||||
|
|
||||||
|
getPreferenceControllers()
|
||||||
|
.stream()
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.filter(
|
||||||
|
controller -> controller instanceof ShortcutOptionPreferenceController)
|
||||||
|
.forEach(controller -> {
|
||||||
|
ShortcutOptionPreferenceController shortcutOptionPreferenceController =
|
||||||
|
(ShortcutOptionPreferenceController) controller;
|
||||||
|
shortcutOptionPreferenceController.setShortcutTargets(mShortcutTargets);
|
||||||
|
shortcutOptionPreferenceController.setInSetupWizard(isInSuw);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onExpanded() {
|
||||||
|
AdvancedShortcutsPreferenceController advanced =
|
||||||
|
use(AdvancedShortcutsPreferenceController.class);
|
||||||
|
advanced.setExpanded(true);
|
||||||
|
|
||||||
|
TripleTapShortcutOptionController tripleTapShortcutOptionController =
|
||||||
|
use(TripleTapShortcutOptionController.class);
|
||||||
|
tripleTapShortcutOptionController.setExpanded(true);
|
||||||
|
|
||||||
|
refreshPreferenceController(AdvancedShortcutsPreferenceController.class);
|
||||||
|
refreshPreferenceController(TripleTapShortcutOptionController.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshPreferenceController(
|
||||||
|
Class<? extends AbstractPreferenceController> controllerClass) {
|
||||||
|
AbstractPreferenceController controller = use(controllerClass);
|
||||||
|
if (controller != null) {
|
||||||
|
controller.displayPreference(getPreferenceScreen());
|
||||||
|
if (!TextUtils.isEmpty(controller.getPreferenceKey())) {
|
||||||
|
controller.updateState(findPreference(controller.getPreferenceKey()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshSoftwareShortcutControllers() {
|
||||||
|
// Gesture
|
||||||
|
refreshPreferenceController(GestureShortcutOptionController.class);
|
||||||
|
|
||||||
|
// FAB
|
||||||
|
refreshPreferenceController(FloatingButtonShortcutOptionController.class);
|
||||||
|
|
||||||
|
// A11y Nav Button
|
||||||
|
refreshPreferenceController(NavButtonShortcutOptionController.class);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.accessibility.AccessibilityUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A controller handles displaying the floating action button shortcut option preference and
|
||||||
|
* configuring the shortcut.
|
||||||
|
*/
|
||||||
|
public class FloatingButtonShortcutOptionController
|
||||||
|
extends SoftwareShortcutOptionPreferenceController {
|
||||||
|
|
||||||
|
public FloatingButtonShortcutOptionController(Context context, String preferenceKey) {
|
||||||
|
super(context, preferenceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void displayPreference(PreferenceScreen screen) {
|
||||||
|
super.displayPreference(screen);
|
||||||
|
final Preference preference = screen.findPreference(getPreferenceKey());
|
||||||
|
if (preference instanceof ShortcutOptionPreference shortcutOptionPreference) {
|
||||||
|
shortcutOptionPreference.setTitle(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_title_software);
|
||||||
|
shortcutOptionPreference.setIntroImageResId(
|
||||||
|
R.drawable.a11y_shortcut_type_software_floating);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isShortcutAvailable() {
|
||||||
|
return AccessibilityUtil.isFloatingMenuEnabled(mContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public CharSequence getSummary() {
|
||||||
|
if (isInSetupWizard()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new SpannableStringBuilder().append(getCustomizeAccessibilityButtonLink());
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.accessibility.AccessibilityUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A controller handles displaying the gesture shortcut option preference and
|
||||||
|
* configuring the shortcut.
|
||||||
|
*/
|
||||||
|
public class GestureShortcutOptionController extends SoftwareShortcutOptionPreferenceController {
|
||||||
|
|
||||||
|
public GestureShortcutOptionController(Context context, String preferenceKey) {
|
||||||
|
super(context, preferenceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void displayPreference(PreferenceScreen screen) {
|
||||||
|
super.displayPreference(screen);
|
||||||
|
final Preference preference = screen.findPreference(getPreferenceKey());
|
||||||
|
if (preference instanceof ShortcutOptionPreference shortcutOptionPreference) {
|
||||||
|
shortcutOptionPreference.setTitle(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_title_software_by_gesture);
|
||||||
|
|
||||||
|
int resId = AccessibilityUtil.isTouchExploreEnabled(mContext)
|
||||||
|
? R.drawable.a11y_shortcut_type_software_gesture_talkback
|
||||||
|
: R.drawable.a11y_shortcut_type_software_gesture;
|
||||||
|
shortcutOptionPreference.setIntroImageResId(resId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isShortcutAvailable() {
|
||||||
|
return !isInSetupWizard()
|
||||||
|
&& !AccessibilityUtil.isFloatingMenuEnabled(mContext)
|
||||||
|
&& AccessibilityUtil.isGestureNavigateEnabled(mContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getSummary() {
|
||||||
|
final SpannableStringBuilder sb = new SpannableStringBuilder();
|
||||||
|
final int resId = AccessibilityUtil.isTouchExploreEnabled(mContext)
|
||||||
|
? R.string.accessibility_shortcut_edit_dialog_summary_software_gesture_talkback
|
||||||
|
: R.string.accessibility_shortcut_edit_dialog_summary_software_gesture;
|
||||||
|
sb.append(mContext.getText(resId));
|
||||||
|
sb.append("\n\n");
|
||||||
|
sb.append(getCustomizeAccessibilityButtonLink());
|
||||||
|
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.SpannableString;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.style.ImageSpan;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.accessibility.AccessibilityUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A controller handles displaying the nav button shortcut option preference and
|
||||||
|
* configuring the shortcut.
|
||||||
|
*/
|
||||||
|
public class NavButtonShortcutOptionController extends SoftwareShortcutOptionPreferenceController {
|
||||||
|
|
||||||
|
public NavButtonShortcutOptionController(Context context, String preferenceKey) {
|
||||||
|
super(context, preferenceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void displayPreference(PreferenceScreen screen) {
|
||||||
|
super.displayPreference(screen);
|
||||||
|
final Preference preference = screen.findPreference(getPreferenceKey());
|
||||||
|
if (preference instanceof ShortcutOptionPreference shortcutOptionPreference) {
|
||||||
|
shortcutOptionPreference.setTitle(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_title_software);
|
||||||
|
shortcutOptionPreference.setIntroImageResId(R.drawable.a11y_shortcut_type_software);
|
||||||
|
shortcutOptionPreference.setSummaryProvider(
|
||||||
|
new Preference.SummaryProvider<ShortcutOptionPreference>() {
|
||||||
|
@Override
|
||||||
|
public CharSequence provideSummary(
|
||||||
|
@NonNull ShortcutOptionPreference preference) {
|
||||||
|
return getSummary(preference.getSummaryTextLineHeight());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isShortcutAvailable() {
|
||||||
|
return !AccessibilityUtil.isFloatingMenuEnabled(mContext)
|
||||||
|
&& !AccessibilityUtil.isGestureNavigateEnabled(mContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CharSequence getSummary(int lineHeight) {
|
||||||
|
final SpannableStringBuilder sb = new SpannableStringBuilder();
|
||||||
|
sb.append(getSummaryStringWithIcon(lineHeight));
|
||||||
|
|
||||||
|
if (!isInSetupWizard()) {
|
||||||
|
sb.append("\n\n");
|
||||||
|
sb.append(getCustomizeAccessibilityButtonLink());
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SpannableString getSummaryStringWithIcon(int lineHeight) {
|
||||||
|
final String summary = mContext
|
||||||
|
.getString(R.string.accessibility_shortcut_edit_dialog_summary_software);
|
||||||
|
final SpannableString spannableMessage = SpannableString.valueOf(summary);
|
||||||
|
|
||||||
|
// Icon
|
||||||
|
final int indexIconStart = summary.indexOf("%s");
|
||||||
|
final int indexIconEnd = indexIconStart + 2;
|
||||||
|
final Drawable icon = mContext.getDrawable(R.drawable.ic_accessibility_new);
|
||||||
|
final ImageSpan imageSpan = new ImageSpan(icon);
|
||||||
|
imageSpan.setContentDescription("");
|
||||||
|
icon.setBounds(
|
||||||
|
/* left= */ 0, /* top= */ 0, /* right= */ lineHeight, /* bottom= */ lineHeight);
|
||||||
|
spannableMessage.setSpan(
|
||||||
|
imageSpan, indexIconStart, indexIconEnd,
|
||||||
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
return spannableMessage;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.text.method.LinkMovementMethod;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.RawRes;
|
||||||
|
import androidx.preference.CheckBoxPreference;
|
||||||
|
import androidx.preference.PreferenceViewHolder;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settingslib.widget.LottieColorUtils;
|
||||||
|
|
||||||
|
import com.airbnb.lottie.LottieAnimationView;
|
||||||
|
import com.airbnb.lottie.LottieDrawable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A preference represents an accessibility shortcut option with a checkbox and a tutorial image
|
||||||
|
*/
|
||||||
|
public class ShortcutOptionPreference extends CheckBoxPreference {
|
||||||
|
|
||||||
|
private static final String TAG = "ShortcutOptionPreference";
|
||||||
|
|
||||||
|
@DrawableRes
|
||||||
|
private int mIntroImageResId = Resources.ID_NULL;
|
||||||
|
@RawRes
|
||||||
|
private int mIntroImageRawResId = Resources.ID_NULL;
|
||||||
|
|
||||||
|
private int mSummaryTextLineHeight;
|
||||||
|
|
||||||
|
public ShortcutOptionPreference(
|
||||||
|
@NonNull Context context, @Nullable AttributeSet attrs,
|
||||||
|
int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShortcutOptionPreference(@NonNull Context context, @Nullable AttributeSet attrs,
|
||||||
|
int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShortcutOptionPreference(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShortcutOptionPreference(@NonNull Context context) {
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
setLayoutResource(R.layout.accessibility_shortcut_option_checkable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
|
||||||
|
super.onBindViewHolder(holder);
|
||||||
|
|
||||||
|
if (mIntroImageResId == Resources.ID_NULL && mIntroImageRawResId == Resources.ID_NULL) {
|
||||||
|
holder.findViewById(R.id.image).setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
holder.findViewById(R.id.image).setVisibility(View.VISIBLE);
|
||||||
|
LottieAnimationView imageView = holder.itemView.findViewById(R.id.image);
|
||||||
|
|
||||||
|
if (mIntroImageRawResId != Resources.ID_NULL) {
|
||||||
|
imageView.setFailureListener(result ->
|
||||||
|
Log.w(TAG,
|
||||||
|
"Invalid image raw resource id: "
|
||||||
|
+ getContext().getResources()
|
||||||
|
.getResourceEntryName(mIntroImageRawResId),
|
||||||
|
result));
|
||||||
|
imageView.setAnimation(mIntroImageRawResId);
|
||||||
|
imageView.setRepeatCount(LottieDrawable.INFINITE);
|
||||||
|
LottieColorUtils.applyDynamicColors(getContext(), imageView);
|
||||||
|
imageView.playAnimation();
|
||||||
|
} else {
|
||||||
|
imageView.setImageResource(mIntroImageResId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
|
||||||
|
if (summaryView != null) {
|
||||||
|
mSummaryTextLineHeight = summaryView.getLineHeight();
|
||||||
|
summaryView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
syncSummaryView(holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the introduction image for this preference with a drawable resource ID.
|
||||||
|
*/
|
||||||
|
public void setIntroImageResId(@DrawableRes int introImageResId) {
|
||||||
|
if (introImageResId != mIntroImageResId) {
|
||||||
|
mIntroImageResId = introImageResId;
|
||||||
|
mIntroImageRawResId = Resources.ID_NULL;
|
||||||
|
notifyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the introduction image for this preference with a raw resource ID for an animated image.
|
||||||
|
*/
|
||||||
|
public void setIntroImageRawResId(@RawRes int introImageRawResId) {
|
||||||
|
if (introImageRawResId != mIntroImageRawResId) {
|
||||||
|
mIntroImageRawResId = introImageRawResId;
|
||||||
|
mIntroImageResId = Resources.ID_NULL;
|
||||||
|
notifyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSummaryTextLineHeight() {
|
||||||
|
return mSummaryTextLineHeight;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
|
import com.android.internal.accessibility.common.ShortcutConstants;
|
||||||
|
import com.android.internal.accessibility.util.ShortcutUtils;
|
||||||
|
import com.android.internal.util.Preconditions;
|
||||||
|
import com.android.settings.core.BasePreferenceController;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A base preference controller for {@link ShortcutOptionPreference}
|
||||||
|
*/
|
||||||
|
public abstract class ShortcutOptionPreferenceController extends BasePreferenceController
|
||||||
|
implements Preference.OnPreferenceChangeListener {
|
||||||
|
private Set<String> mShortcutTargets = Collections.emptySet();
|
||||||
|
private boolean mIsInSetupWizard;
|
||||||
|
|
||||||
|
public ShortcutOptionPreferenceController(Context context, String preferenceKey) {
|
||||||
|
super(context, preferenceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateState(Preference preference) {
|
||||||
|
super.updateState(preference);
|
||||||
|
if (getPreferenceKey().equals(preference.getKey())
|
||||||
|
&& preference instanceof ShortcutOptionPreference) {
|
||||||
|
((ShortcutOptionPreference) preference).setChecked(isChecked());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAvailabilityStatus() {
|
||||||
|
if (isShortcutAvailable()) {
|
||||||
|
return AVAILABLE_UNSEARCHABLE;
|
||||||
|
}
|
||||||
|
return CONDITIONALLY_UNAVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the targets (i.e. a11y features) to be configured with the a11y shortcut option.
|
||||||
|
* <p>
|
||||||
|
* Note: the shortcutTargets cannot be empty, since the edit a11y shortcut option
|
||||||
|
* is meant to configure the shortcut options for an a11y feature.
|
||||||
|
* </>
|
||||||
|
*
|
||||||
|
* @param shortcutTargets the a11y features, like color correction, Talkback, etc.
|
||||||
|
* @throws NullPointerException if the {@code shortcutTargets} was {@code null}
|
||||||
|
* @throws IllegalArgumentException if the {@code shortcutTargets} was empty
|
||||||
|
*/
|
||||||
|
public void setShortcutTargets(Set<String> shortcutTargets) {
|
||||||
|
Preconditions.checkCollectionNotEmpty(shortcutTargets, /* valueName= */ "a11y targets");
|
||||||
|
|
||||||
|
this.mShortcutTargets = shortcutTargets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInSetupWizard(boolean isInSetupWizard) {
|
||||||
|
this.mIsInSetupWizard = isInSetupWizard;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Set<String> getShortcutTargets() {
|
||||||
|
return mShortcutTargets;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isInSetupWizard() {
|
||||||
|
return mIsInSetupWizard;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean onPreferenceChange(@NonNull Preference preference, Object newValue) {
|
||||||
|
enableShortcutForTargets((Boolean) newValue);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ShortcutConstants.UserShortcutType
|
||||||
|
protected int getShortcutType() {
|
||||||
|
return ShortcutConstants.UserShortcutType.DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the shortcut is associated to the targets
|
||||||
|
*/
|
||||||
|
protected boolean isChecked() {
|
||||||
|
Set<String> targets = ShortcutUtils.getShortcutTargetsFromSettings(
|
||||||
|
mContext, getShortcutType(), UserHandle.myUserId());
|
||||||
|
|
||||||
|
return !targets.isEmpty() && targets.containsAll(getShortcutTargets());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable or disable the shortcut for the given accessibility features.
|
||||||
|
*/
|
||||||
|
protected void enableShortcutForTargets(boolean enable) {
|
||||||
|
Set<String> shortcutTargets = getShortcutTargets();
|
||||||
|
@ShortcutConstants.UserShortcutType int shortcutType = getShortcutType();
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
for (String target : shortcutTargets) {
|
||||||
|
ShortcutUtils.optInValueToSettings(mContext, shortcutType, target);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (String target : shortcutTargets) {
|
||||||
|
ShortcutUtils.optOutValueFromSettings(mContext, shortcutType, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
|
||||||
|
mContext, shortcutTargets, UserHandle.myUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when the user can associate a shortcut to the targets
|
||||||
|
*/
|
||||||
|
protected abstract boolean isShortcutAvailable();
|
||||||
|
}
|
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.android.internal.accessibility.common.ShortcutConstants;
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.accessibility.AccessibilityButtonFragment;
|
||||||
|
import com.android.settings.accessibility.FloatingMenuSizePreferenceController;
|
||||||
|
import com.android.settings.core.SubSettingLauncher;
|
||||||
|
import com.android.settings.utils.AnnotationSpan;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A base controller for the preference controller of software shortcuts.
|
||||||
|
*/
|
||||||
|
public abstract class SoftwareShortcutOptionPreferenceController
|
||||||
|
extends ShortcutOptionPreferenceController {
|
||||||
|
|
||||||
|
public SoftwareShortcutOptionPreferenceController(Context context, String preferenceKey) {
|
||||||
|
super(context, preferenceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ShortcutConstants.UserShortcutType
|
||||||
|
@Override
|
||||||
|
protected int getShortcutType() {
|
||||||
|
return ShortcutConstants.UserShortcutType.SOFTWARE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isMagnificationInTargets() {
|
||||||
|
return getShortcutTargets().contains(MAGNIFICATION_CONTROLLER_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CharSequence getCustomizeAccessibilityButtonLink() {
|
||||||
|
final View.OnClickListener linkListener = v -> new SubSettingLauncher(mContext)
|
||||||
|
.setDestination(AccessibilityButtonFragment.class.getName())
|
||||||
|
.setSourceMetricsCategory(getMetricsCategory())
|
||||||
|
.launch();
|
||||||
|
final AnnotationSpan.LinkInfo linkInfo = new AnnotationSpan.LinkInfo(
|
||||||
|
AnnotationSpan.LinkInfo.DEFAULT_ANNOTATION, linkListener);
|
||||||
|
return AnnotationSpan.linkify(
|
||||||
|
mContext.getText(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_software_floating),
|
||||||
|
linkInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void enableShortcutForTargets(boolean enable) {
|
||||||
|
super.enableShortcutForTargets(enable);
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
// Update the A11y FAB size to large when the Magnification shortcut is enabled
|
||||||
|
// and the user hasn't changed the floating button size
|
||||||
|
if (isMagnificationInTargets()
|
||||||
|
&& Settings.Secure.getInt(mContext.getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
|
||||||
|
FloatingMenuSizePreferenceController.Size.UNKNOWN)
|
||||||
|
== FloatingMenuSizePreferenceController.Size.UNKNOWN) {
|
||||||
|
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
|
||||||
|
FloatingMenuSizePreferenceController.Size.LARGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.icu.text.MessageFormat;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
|
||||||
|
import com.android.internal.accessibility.common.ShortcutConstants;
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.accessibility.AccessibilityUtil;
|
||||||
|
import com.android.settings.accessibility.ExpandablePreference;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A controller handles displaying the triple tap shortcut option preference and
|
||||||
|
* configuring the shortcut.
|
||||||
|
*/
|
||||||
|
public class TripleTapShortcutOptionController extends ShortcutOptionPreferenceController
|
||||||
|
implements ExpandablePreference {
|
||||||
|
|
||||||
|
private boolean mIsExpanded = false;
|
||||||
|
|
||||||
|
public TripleTapShortcutOptionController(Context context, String preferenceKey) {
|
||||||
|
super(context, preferenceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void displayPreference(PreferenceScreen screen) {
|
||||||
|
super.displayPreference(screen);
|
||||||
|
final Preference preference = screen.findPreference(getPreferenceKey());
|
||||||
|
if (preference instanceof ShortcutOptionPreference shortcutOptionPreference) {
|
||||||
|
shortcutOptionPreference.setTitle(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_title_triple_tap);
|
||||||
|
String summary = mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_triple_tap);
|
||||||
|
// Format the number '3' in the summary.
|
||||||
|
final Object[] arguments = {3};
|
||||||
|
summary = MessageFormat.format(summary, arguments);
|
||||||
|
|
||||||
|
shortcutOptionPreference.setSummary(summary);
|
||||||
|
shortcutOptionPreference.setIntroImageRawResId(
|
||||||
|
R.raw.a11y_shortcut_type_triple_tap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAvailabilityStatus() {
|
||||||
|
if (isExpanded() && isShortcutAvailable()) {
|
||||||
|
return AVAILABLE_UNSEARCHABLE;
|
||||||
|
}
|
||||||
|
return CONDITIONALLY_UNAVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ShortcutConstants.UserShortcutType
|
||||||
|
@Override
|
||||||
|
protected int getShortcutType() {
|
||||||
|
return ShortcutConstants.UserShortcutType.TRIPLETAP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setExpanded(boolean expanded) {
|
||||||
|
mIsExpanded = expanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isExpanded() {
|
||||||
|
return mIsExpanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isShortcutAvailable() {
|
||||||
|
Set<String> shortcutTargets = getShortcutTargets();
|
||||||
|
return shortcutTargets.size() == 1
|
||||||
|
&& shortcutTargets.contains(MAGNIFICATION_CONTROLLER_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isChecked() {
|
||||||
|
return Settings.Secure.getInt(
|
||||||
|
mContext.getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
|
||||||
|
AccessibilityUtil.State.OFF) == AccessibilityUtil.State.ON;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void enableShortcutForTargets(boolean enable) {
|
||||||
|
Settings.Secure.putInt(
|
||||||
|
mContext.getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
|
||||||
|
enable ? AccessibilityUtil.State.ON : AccessibilityUtil.State.OFF);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.icu.text.MessageFormat;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
|
||||||
|
import com.android.internal.accessibility.common.ShortcutConstants;
|
||||||
|
import com.android.server.accessibility.Flags;
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.accessibility.AccessibilityUtil;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A controller handles displaying the two fingers double tap shortcut option preference and
|
||||||
|
* configuring the shortcut.
|
||||||
|
*/
|
||||||
|
public class TwoFingersDoubleTapShortcutOptionController
|
||||||
|
extends ShortcutOptionPreferenceController {
|
||||||
|
|
||||||
|
public TwoFingersDoubleTapShortcutOptionController(Context context, String preferenceKey) {
|
||||||
|
super(context, preferenceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ShortcutConstants.UserShortcutType
|
||||||
|
@Override
|
||||||
|
protected int getShortcutType() {
|
||||||
|
return ShortcutConstants.UserShortcutType.TRIPLETAP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void displayPreference(PreferenceScreen screen) {
|
||||||
|
super.displayPreference(screen);
|
||||||
|
final Preference preference = screen.findPreference(getPreferenceKey());
|
||||||
|
if (preference instanceof ShortcutOptionPreference shortcutOptionPreference) {
|
||||||
|
// TODO (b/306153204): Update shortcut string and image when UX provides them
|
||||||
|
shortcutOptionPreference.setTitle(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_title_two_finger_double_tap);
|
||||||
|
String summary = mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_two_finger_double_tap);
|
||||||
|
// Format the number '2' in the summary.
|
||||||
|
final Object[] arguments = {2};
|
||||||
|
summary = MessageFormat.format(summary, arguments);
|
||||||
|
|
||||||
|
shortcutOptionPreference.setSummary(summary);
|
||||||
|
shortcutOptionPreference.setIntroImageRawResId(
|
||||||
|
R.raw.a11y_shortcut_type_triple_tap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isShortcutAvailable() {
|
||||||
|
if (!Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Only Magnification has two fingers triple tap shortcut option.
|
||||||
|
Set<String> shortcutTargets = getShortcutTargets();
|
||||||
|
return shortcutTargets.size() == 1
|
||||||
|
&& shortcutTargets.contains(MAGNIFICATION_CONTROLLER_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isChecked() {
|
||||||
|
return Settings.Secure.getInt(
|
||||||
|
mContext.getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
|
||||||
|
AccessibilityUtil.State.OFF) == AccessibilityUtil.State.ON;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void enableShortcutForTargets(boolean enable) {
|
||||||
|
Settings.Secure.putInt(
|
||||||
|
mContext.getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
|
||||||
|
enable ? AccessibilityUtil.State.ON : AccessibilityUtil.State.OFF);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
|
||||||
|
import com.android.internal.accessibility.common.ShortcutConstants;
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.accessibility.AccessibilityUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A controller handles displaying the volume keys shortcut option preference and
|
||||||
|
* configuring the shortcut.
|
||||||
|
*/
|
||||||
|
public class VolumeKeysShortcutOptionController extends ShortcutOptionPreferenceController {
|
||||||
|
|
||||||
|
public VolumeKeysShortcutOptionController(
|
||||||
|
Context context, String preferenceKey) {
|
||||||
|
super(context, preferenceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ShortcutConstants.UserShortcutType
|
||||||
|
@Override
|
||||||
|
protected int getShortcutType() {
|
||||||
|
return ShortcutConstants.UserShortcutType.HARDWARE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void displayPreference(PreferenceScreen screen) {
|
||||||
|
super.displayPreference(screen);
|
||||||
|
final Preference preference = screen.findPreference(getPreferenceKey());
|
||||||
|
if (preference instanceof ShortcutOptionPreference shortcutOptionPreference) {
|
||||||
|
shortcutOptionPreference.setTitle(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_title_hardware);
|
||||||
|
shortcutOptionPreference.setSummary(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_hardware);
|
||||||
|
shortcutOptionPreference.setIntroImageResId(
|
||||||
|
R.drawable.a11y_shortcut_type_hardware);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isShortcutAvailable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void enableShortcutForTargets(boolean enable) {
|
||||||
|
super.enableShortcutForTargets(enable);
|
||||||
|
if (enable) {
|
||||||
|
AccessibilityUtil.skipVolumeShortcutDialogTimeoutRestriction(mContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
|
||||||
|
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link AdvancedShortcutsPreferenceController}
|
||||||
|
*/
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class AdvancedShortcutsPreferenceControllerTest {
|
||||||
|
private static final String PREF_KEY = "prefKey";
|
||||||
|
private static final String TARGET_MAGNIFICATION =
|
||||||
|
"com.android.server.accessibility.MagnificationController";
|
||||||
|
private static final String TARGET_FAKE =
|
||||||
|
new ComponentName("FakePackage", "FakeClass").flattenToString();
|
||||||
|
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
private AdvancedShortcutsPreferenceController mController;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mController = new AdvancedShortcutsPreferenceController(mContext, PREF_KEY);
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_MAGNIFICATION));
|
||||||
|
Preference preference = new Preference(mContext);
|
||||||
|
preference.setKey(PREF_KEY);
|
||||||
|
PreferenceScreen preferenceScreen =
|
||||||
|
new PreferenceManager(mContext).createPreferenceScreen(mContext);
|
||||||
|
preferenceScreen.addPreference(preference);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_targetIsMagnificationAndIsExpanded_returnsConditionallyUnavailable() {
|
||||||
|
mController.setExpanded(true);
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_MAGNIFICATION));
|
||||||
|
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_targetIsMagnificationAndIsNotExpanded_returnsAvailableUnsearchable() {
|
||||||
|
mController.setExpanded(false);
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_MAGNIFICATION));
|
||||||
|
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_targetIsNotMagnificationAndIsNotExpanded_returnsConditionallyUnavailable() {
|
||||||
|
mController.setExpanded(false);
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_FAKE));
|
||||||
|
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_targetIsNotMagnificationAndIsExpanded_returnsConditionallyUnavailable() {
|
||||||
|
mController.setExpanded(true);
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_FAKE));
|
||||||
|
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setExpanded_expand_updateExpandedValue() {
|
||||||
|
mController.setExpanded(true);
|
||||||
|
|
||||||
|
assertThat(mController.isExpanded()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setExpanded_collapse_updateExpandedValue() {
|
||||||
|
mController.setExpanded(false);
|
||||||
|
|
||||||
|
assertThat(mController.isExpanded()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_multipleTargets_returnFalse() {
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_FAKE, TARGET_MAGNIFICATION));
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_magnificationTargetOnly_returnTrue() {
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_MAGNIFICATION));
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_nonMagnificationTarget_returnFalse() {
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_FAKE));
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isChecked_returnFalse() {
|
||||||
|
assertThat(mController.isChecked()).isFalse();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,438 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
|
||||||
|
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
|
||||||
|
|
||||||
|
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
|
||||||
|
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
|
||||||
|
import static com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment.SHORTCUT_SETTINGS;
|
||||||
|
|
||||||
|
import static com.google.android.setupcompat.util.WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP;
|
||||||
|
import static com.google.android.setupcompat.util.WizardManagerHelper.EXTRA_IS_FIRST_RUN;
|
||||||
|
import static com.google.android.setupcompat.util.WizardManagerHelper.EXTRA_IS_PRE_DEFERRED_SETUP;
|
||||||
|
import static com.google.android.setupcompat.util.WizardManagerHelper.EXTRA_IS_SETUP_FLOW;
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.robolectric.Shadows.shadowOf;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.view.accessibility.AccessibilityManager;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import androidx.fragment.app.testing.FragmentScenario;
|
||||||
|
import androidx.lifecycle.Lifecycle;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.TwoStatePreference;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.internal.accessibility.common.ShortcutConstants;
|
||||||
|
import com.android.internal.accessibility.util.ShortcutUtils;
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.SettingsActivity;
|
||||||
|
import com.android.settings.SubSettings;
|
||||||
|
import com.android.settings.accessibility.AccessibilityUtil;
|
||||||
|
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||||
|
import com.android.settingslib.core.AbstractPreferenceController;
|
||||||
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
|
|
||||||
|
import com.google.android.setupcompat.util.WizardManagerHelper;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.Robolectric;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
import org.robolectric.shadows.ShadowAccessibilityManager;
|
||||||
|
import org.robolectric.shadows.ShadowContentResolver;
|
||||||
|
import org.robolectric.shadows.ShadowLooper;
|
||||||
|
import org.robolectric.util.ReflectionHelpers;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link EditShortcutsPreferenceFragment}
|
||||||
|
*/
|
||||||
|
@Config(shadows = SettingsShadowResources.class)
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class EditShortcutsPreferenceFragmentTest {
|
||||||
|
private static final int METRICS_CATEGORY = 123;
|
||||||
|
private static final CharSequence SCREEN_TITLE = "Fake shortcut title";
|
||||||
|
private static final ComponentName TARGET_FAKE_COMPONENT =
|
||||||
|
new ComponentName("FakePackage", "FakeClass");
|
||||||
|
private static final String TARGET = MAGNIFICATION_CONTROLLER_NAME;
|
||||||
|
private static final Set<String> TARGETS = Set.of(TARGET);
|
||||||
|
|
||||||
|
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
private FragmentActivity mActivity;
|
||||||
|
private FragmentScenario<EditShortcutsPreferenceFragment> mFragmentScenario;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
SettingsShadowResources.overrideResource(
|
||||||
|
com.android.internal.R.integer.config_navBarInteractionMode,
|
||||||
|
NAV_BAR_MODE_GESTURAL);
|
||||||
|
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
|
||||||
|
Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE);
|
||||||
|
|
||||||
|
mActivity = Robolectric.buildActivity(FragmentActivity.class).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanUp() {
|
||||||
|
if (mFragmentScenario != null) {
|
||||||
|
mFragmentScenario.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void showEditShortcutScreen_targetIsMagnification_launchSubSetting() {
|
||||||
|
EditShortcutsPreferenceFragment.showEditShortcutScreen(
|
||||||
|
mActivity, METRICS_CATEGORY, SCREEN_TITLE,
|
||||||
|
MAGNIFICATION_COMPONENT_NAME, /* fromIntent= */ null);
|
||||||
|
|
||||||
|
assertLaunchSubSettingWithCurrentTargetComponents(
|
||||||
|
MAGNIFICATION_CONTROLLER_NAME, /* isInSuw= */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void showEditShortcutScreen_launchSubSetting() {
|
||||||
|
EditShortcutsPreferenceFragment.showEditShortcutScreen(
|
||||||
|
mActivity, METRICS_CATEGORY, SCREEN_TITLE,
|
||||||
|
TARGET_FAKE_COMPONENT, /* fromIntent= */ null);
|
||||||
|
|
||||||
|
assertLaunchSubSettingWithCurrentTargetComponents(
|
||||||
|
TARGET_FAKE_COMPONENT.flattenToString(), /* isInSuw= */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void showEditShortcutScreen_inSuw_launchSubSettingWithSuw() {
|
||||||
|
EditShortcutsPreferenceFragment.showEditShortcutScreen(
|
||||||
|
mActivity, METRICS_CATEGORY, SCREEN_TITLE,
|
||||||
|
TARGET_FAKE_COMPONENT, createSuwIntent(new Intent(), /* isInSuw= */ true));
|
||||||
|
|
||||||
|
assertLaunchSubSettingWithCurrentTargetComponents(
|
||||||
|
TARGET_FAKE_COMPONENT.flattenToString(), /* isInSuw= */ true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fragmentCreated_inSuw_controllersTargetsSet() {
|
||||||
|
mFragmentScenario = createFragScenario(/* isInSuw= */ true);
|
||||||
|
mFragmentScenario.moveToState(Lifecycle.State.CREATED);
|
||||||
|
|
||||||
|
mFragmentScenario.onFragment(fragment -> {
|
||||||
|
List<ShortcutOptionPreferenceController> controllers =
|
||||||
|
getShortcutOptionPreferenceControllers(fragment);
|
||||||
|
|
||||||
|
for (ShortcutOptionPreferenceController controller : controllers) {
|
||||||
|
assertThat(controller.getShortcutTargets()).containsExactlyElementsIn(TARGETS);
|
||||||
|
assertThat(controller.isInSetupWizard()).isTrue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fragmentCreated_notInSuw_controllersTargetsSet() {
|
||||||
|
mFragmentScenario = createFragScenario(/* isInSuw= */ false);
|
||||||
|
mFragmentScenario.moveToState(Lifecycle.State.CREATED);
|
||||||
|
|
||||||
|
mFragmentScenario.onFragment(fragment -> {
|
||||||
|
List<ShortcutOptionPreferenceController> controllers =
|
||||||
|
getShortcutOptionPreferenceControllers(fragment);
|
||||||
|
|
||||||
|
for (ShortcutOptionPreferenceController controller : controllers) {
|
||||||
|
assertThat(controller.getShortcutTargets()).containsExactlyElementsIn(TARGETS);
|
||||||
|
assertThat(controller.isInSetupWizard()).isFalse();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fragmentCreated_settingsObserversAreRegistered() {
|
||||||
|
ShadowContentResolver contentResolver = shadowOf(mContext.getContentResolver());
|
||||||
|
for (Uri uri : SHORTCUT_SETTINGS) {
|
||||||
|
assertThat(contentResolver.getContentObservers(uri)).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
mFragmentScenario = createFragScenario(/* isInSuw= */ false);
|
||||||
|
mFragmentScenario.moveToState(Lifecycle.State.CREATED);
|
||||||
|
|
||||||
|
for (Uri uri : SHORTCUT_SETTINGS) {
|
||||||
|
assertThat(contentResolver.getContentObservers(uri)).isNotEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fragmentDestroyed_unregisterSettingsObserver() {
|
||||||
|
ShadowContentResolver contentResolver = shadowOf(mContext.getContentResolver());
|
||||||
|
|
||||||
|
mFragmentScenario = createFragScenario(/* isInSuw= */ false)
|
||||||
|
.moveToState(Lifecycle.State.CREATED);
|
||||||
|
mFragmentScenario.onFragment(EditShortcutsPreferenceFragment::onDestroy);
|
||||||
|
|
||||||
|
for (Uri uri : SHORTCUT_SETTINGS) {
|
||||||
|
assertThat(contentResolver.getContentObservers(uri)).isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onVolumeKeysShortcutSettingChanged_volumeKeyControllerUpdated() {
|
||||||
|
mFragmentScenario = createFragScenario(/* isInSuw= */ false);
|
||||||
|
mFragmentScenario.moveToState(Lifecycle.State.CREATED);
|
||||||
|
|
||||||
|
ShortcutUtils.optInValueToSettings(
|
||||||
|
mContext, ShortcutConstants.UserShortcutType.HARDWARE, TARGET);
|
||||||
|
|
||||||
|
mFragmentScenario.onFragment(fragment -> {
|
||||||
|
TwoStatePreference preference = fragment.findPreference(
|
||||||
|
mContext.getString(R.string.accessibility_shortcut_volume_keys_pref));
|
||||||
|
assertThat(preference.isChecked()).isTrue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onSoftwareShortcutSettingChanged_softwareControllersUpdated() {
|
||||||
|
mFragmentScenario = createFragScenario(/* isInSuw= */ false);
|
||||||
|
mFragmentScenario.moveToState(Lifecycle.State.CREATED);
|
||||||
|
|
||||||
|
ShortcutUtils.optInValueToSettings(
|
||||||
|
mContext, ShortcutConstants.UserShortcutType.SOFTWARE, TARGET);
|
||||||
|
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
|
||||||
|
|
||||||
|
mFragmentScenario.onFragment(fragment -> {
|
||||||
|
TwoStatePreference preference = fragment.findPreference(
|
||||||
|
mContext.getString(R.string.accessibility_shortcut_gesture_pref));
|
||||||
|
assertThat(preference.isChecked()).isTrue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onSoftwareShortcutModeChanged_softwareControllersUpdated() {
|
||||||
|
mFragmentScenario = createFragScenario(/* isInSuw= */ false);
|
||||||
|
mFragmentScenario.moveToState(Lifecycle.State.CREATED);
|
||||||
|
|
||||||
|
ShortcutUtils.optInValueToSettings(
|
||||||
|
mContext, ShortcutConstants.UserShortcutType.SOFTWARE, TARGET);
|
||||||
|
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
|
||||||
|
|
||||||
|
mFragmentScenario.onFragment(fragment -> {
|
||||||
|
TwoStatePreference preference = fragment.findPreference(
|
||||||
|
mContext.getString(R.string.accessibility_shortcut_gesture_pref));
|
||||||
|
assertThat(preference.isChecked()).isTrue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onTripleTapShortcutSettingChanged_tripleTapShortcutControllerUpdated() {
|
||||||
|
mFragmentScenario = createFragScenario(/* isInSuw= */ false);
|
||||||
|
mFragmentScenario.moveToState(Lifecycle.State.CREATED);
|
||||||
|
|
||||||
|
Settings.Secure.putInt(
|
||||||
|
mContext.getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
|
||||||
|
AccessibilityUtil.State.ON);
|
||||||
|
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
|
||||||
|
|
||||||
|
mFragmentScenario.onFragment(fragment -> {
|
||||||
|
TwoStatePreference preference = fragment.findPreference(
|
||||||
|
mContext.getString(R.string.accessibility_shortcut_triple_tap_pref));
|
||||||
|
assertThat(preference.isChecked()).isTrue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onTwoFingersShortcutSettingChanged_twoFingersDoubleTapShortcutControllerUpdated() {
|
||||||
|
mFragmentScenario = createFragScenario(/* isInSuw= */ false);
|
||||||
|
mFragmentScenario.moveToState(Lifecycle.State.CREATED);
|
||||||
|
|
||||||
|
Settings.Secure.putInt(
|
||||||
|
mContext.getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
|
||||||
|
AccessibilityUtil.State.ON);
|
||||||
|
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
|
||||||
|
|
||||||
|
mFragmentScenario.onFragment(fragment -> {
|
||||||
|
TwoStatePreference preference = fragment.findPreference(
|
||||||
|
mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_two_fingers_double_tap_pref));
|
||||||
|
assertThat(preference.isChecked()).isTrue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fragmentResumed_enableTouchExploration_gestureShortcutOptionSummaryUpdated() {
|
||||||
|
String expectedSummary = mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_software_gesture_talkback)
|
||||||
|
+ "\n\n"
|
||||||
|
+ mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_software_floating);
|
||||||
|
mFragmentScenario = createFragScenario(/* isInSuw= */ false);
|
||||||
|
mFragmentScenario.moveToState(Lifecycle.State.RESUMED);
|
||||||
|
|
||||||
|
ShadowAccessibilityManager am = shadowOf(
|
||||||
|
mContext.getSystemService(AccessibilityManager.class));
|
||||||
|
am.setTouchExplorationEnabled(true);
|
||||||
|
|
||||||
|
mFragmentScenario.onFragment(fragment -> {
|
||||||
|
Preference preference = fragment.findPreference(
|
||||||
|
mContext.getString(R.string.accessibility_shortcut_gesture_pref));
|
||||||
|
assertThat(preference.getSummary().toString()).isEqualTo(expectedSummary);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fragmentPaused_enableTouchExploration_gestureShortcutOptionSummaryNotUpdated() {
|
||||||
|
String expectedSummary = mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_software_gesture)
|
||||||
|
+ "\n\n"
|
||||||
|
+ mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_software_floating);
|
||||||
|
mFragmentScenario = createFragScenario(/* isInSuw= */ false);
|
||||||
|
mFragmentScenario.moveToState(Lifecycle.State.RESUMED).moveToState(Lifecycle.State.STARTED);
|
||||||
|
|
||||||
|
ShadowAccessibilityManager am = shadowOf(
|
||||||
|
mContext.getSystemService(AccessibilityManager.class));
|
||||||
|
am.setTouchExplorationEnabled(true);
|
||||||
|
|
||||||
|
mFragmentScenario.onFragment(fragment -> {
|
||||||
|
Preference preference = fragment.findPreference(
|
||||||
|
mContext.getString(R.string.accessibility_shortcut_gesture_pref));
|
||||||
|
assertThat(preference.getSummary().toString()).isEqualTo(expectedSummary);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onAdvancedPreferenceClicked_advancedShouldBecomeInvisible() {
|
||||||
|
mFragmentScenario = createFragScenario(/* isInSuw= */ false);
|
||||||
|
mFragmentScenario.moveToState(Lifecycle.State.RESUMED);
|
||||||
|
mFragmentScenario.onFragment(fragment -> {
|
||||||
|
Preference advanced = fragment.findPreference(
|
||||||
|
mContext.getString(R.string.accessibility_shortcuts_advanced_collapsed));
|
||||||
|
assertThat(advanced.isVisible()).isTrue();
|
||||||
|
|
||||||
|
fragment.onPreferenceTreeClick(advanced);
|
||||||
|
|
||||||
|
assertThat(advanced.isVisible()).isFalse();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fragmentRecreated_expanded_advancedRemainInvisible() {
|
||||||
|
onAdvancedPreferenceClicked_advancedShouldBecomeInvisible();
|
||||||
|
|
||||||
|
mFragmentScenario.recreate();
|
||||||
|
|
||||||
|
mFragmentScenario.onFragment(fragment -> {
|
||||||
|
Preference advanced = fragment.findPreference(
|
||||||
|
mContext.getString(R.string.accessibility_shortcuts_advanced_collapsed));
|
||||||
|
assertThat(advanced.isVisible()).isFalse();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fragmentRecreated_collapsed_advancedRemainVisible() {
|
||||||
|
mFragmentScenario = createFragScenario(/* isInSuw= */ false);
|
||||||
|
mFragmentScenario.moveToState(Lifecycle.State.RESUMED);
|
||||||
|
|
||||||
|
mFragmentScenario.recreate();
|
||||||
|
|
||||||
|
mFragmentScenario.onFragment(fragment -> {
|
||||||
|
Preference advanced = fragment.findPreference(
|
||||||
|
mContext.getString(R.string.accessibility_shortcuts_advanced_collapsed));
|
||||||
|
assertThat(advanced.isVisible()).isTrue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertLaunchSubSettingWithCurrentTargetComponents(
|
||||||
|
String componentName, boolean isInSuw) {
|
||||||
|
Intent intent = shadowOf(mActivity.getApplication()).getNextStartedActivity();
|
||||||
|
|
||||||
|
assertThat(intent).isNotNull();
|
||||||
|
assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MAIN);
|
||||||
|
assertThat(intent.getComponent()).isEqualTo(
|
||||||
|
new ComponentName(mActivity, SubSettings.class));
|
||||||
|
assertThat(intent.getExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
|
||||||
|
.isEqualTo(EditShortcutsPreferenceFragment.class.getName());
|
||||||
|
assertThat(intent.getExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE))
|
||||||
|
.isEqualTo(SCREEN_TITLE.toString());
|
||||||
|
assertThat(intent.getExtra(
|
||||||
|
MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY)).isEqualTo(METRICS_CATEGORY);
|
||||||
|
Bundle args = (Bundle) intent.getExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
|
||||||
|
assertThat(args).isNotNull();
|
||||||
|
assertThat(Arrays.stream(args.getStringArray(
|
||||||
|
EditShortcutsPreferenceFragment.ARG_KEY_SHORTCUT_TARGETS)).toList())
|
||||||
|
.containsExactly(componentName);
|
||||||
|
assertThat(WizardManagerHelper.isAnySetupWizard(intent)).isEqualTo(isInSuw);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ShortcutOptionPreferenceController> getShortcutOptionPreferenceControllers(
|
||||||
|
EditShortcutsPreferenceFragment fragment) {
|
||||||
|
|
||||||
|
Collection<List<AbstractPreferenceController>> controllers =
|
||||||
|
ReflectionHelpers.callInstanceMethod(fragment, "getPreferenceControllers");
|
||||||
|
List<ShortcutOptionPreferenceController> retControllers = new ArrayList<>();
|
||||||
|
controllers.stream().flatMap(Collection::stream)
|
||||||
|
.filter(controller -> controller instanceof ShortcutOptionPreferenceController)
|
||||||
|
.forEach(controller ->
|
||||||
|
retControllers.add((ShortcutOptionPreferenceController) controller));
|
||||||
|
|
||||||
|
return retControllers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FragmentScenario<EditShortcutsPreferenceFragment> createFragScenario(boolean isInSuw) {
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putStringArray(
|
||||||
|
EditShortcutsPreferenceFragment.ARG_KEY_SHORTCUT_TARGETS, new String[]{TARGET});
|
||||||
|
FragmentScenario<EditShortcutsPreferenceFragment> scenario =
|
||||||
|
FragmentScenario.launch(
|
||||||
|
EditShortcutsPreferenceFragment.class, args,
|
||||||
|
/* themeResId= */ 0, Lifecycle.State.INITIALIZED);
|
||||||
|
scenario.onFragment(fragment -> {
|
||||||
|
Intent intent = fragment.requireActivity().getIntent();
|
||||||
|
fragment.requireActivity().setIntent(createSuwIntent(intent, isInSuw));
|
||||||
|
// Since the fragment is attached before we have a chance
|
||||||
|
// to modify the activity's intent; initialize controllers again
|
||||||
|
fragment.initializePreferenceControllerArguments();
|
||||||
|
});
|
||||||
|
return scenario;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent createSuwIntent(Intent intent, boolean isInSuw) {
|
||||||
|
|
||||||
|
if (intent == null) {
|
||||||
|
intent = new Intent();
|
||||||
|
}
|
||||||
|
intent.putExtra(EXTRA_IS_SETUP_FLOW, isInSuw);
|
||||||
|
intent.putExtra(EXTRA_IS_FIRST_RUN, isInSuw);
|
||||||
|
intent.putExtra(EXTRA_IS_PRE_DEFERRED_SETUP, isInSuw);
|
||||||
|
intent.putExtra(EXTRA_IS_DEFERRED_SETUP, isInSuw);
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
|
||||||
|
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link FloatingButtonShortcutOptionController}
|
||||||
|
*/
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class FloatingButtonShortcutOptionControllerTest {
|
||||||
|
private static final String PREF_KEY = "prefKey";
|
||||||
|
private static final String TARGET =
|
||||||
|
new ComponentName("FakePackage", "FakeClass").flattenToString();
|
||||||
|
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
private FloatingButtonShortcutOptionController mController;
|
||||||
|
private ShortcutOptionPreference mShortcutOptionPreference;
|
||||||
|
|
||||||
|
private PreferenceScreen mPreferenceScreen;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mController = new FloatingButtonShortcutOptionController(
|
||||||
|
mContext, PREF_KEY);
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET));
|
||||||
|
mShortcutOptionPreference = new ShortcutOptionPreference(mContext);
|
||||||
|
mShortcutOptionPreference.setKey(PREF_KEY);
|
||||||
|
mPreferenceScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
|
||||||
|
mPreferenceScreen.addPreference(mShortcutOptionPreference);
|
||||||
|
setFloatingButtonEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void displayPreference_verifyTitle() {
|
||||||
|
mController.displayPreference(mPreferenceScreen);
|
||||||
|
|
||||||
|
assertThat(mShortcutOptionPreference.getTitle().toString()).isEqualTo(
|
||||||
|
mContext.getString(R.string.accessibility_shortcut_edit_dialog_title_software));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getSummary_inSuw_verifySummaryEmpty() {
|
||||||
|
mController.setInSetupWizard(true);
|
||||||
|
|
||||||
|
assertThat(TextUtils.isEmpty(mController.getSummary())).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getSummary_notInSuw_verifySummary() {
|
||||||
|
mController.setInSetupWizard(false);
|
||||||
|
|
||||||
|
assertThat(mController.getSummary().toString()).isEqualTo(
|
||||||
|
mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_software_floating));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_floatingMenuEnabled_returnTrue() {
|
||||||
|
setFloatingButtonEnabled(true);
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_floatingMenuDisabled_returnFalse() {
|
||||||
|
setFloatingButtonEnabled(false);
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFloatingButtonEnabled(boolean enable) {
|
||||||
|
int mode = enable
|
||||||
|
? ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU : ACCESSIBILITY_BUTTON_MODE_GESTURE;
|
||||||
|
|
||||||
|
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, mode);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.accessibility.AccessibilityManager;
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.testutils.AccessibilityTestUtils;
|
||||||
|
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link GestureShortcutOptionController}
|
||||||
|
*/
|
||||||
|
@Config(shadows = SettingsShadowResources.class)
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class GestureShortcutOptionControllerTest {
|
||||||
|
private static final String PREF_KEY = "prefKey";
|
||||||
|
private static final String TARGET =
|
||||||
|
new ComponentName("FakePackage", "FakeClass").flattenToString();
|
||||||
|
private final Context mContext = spy(ApplicationProvider.getApplicationContext());
|
||||||
|
private GestureShortcutOptionController mController;
|
||||||
|
private ShortcutOptionPreference mShortcutOptionPreference;
|
||||||
|
|
||||||
|
private PreferenceScreen mPreferenceScreen;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mController = new GestureShortcutOptionController(
|
||||||
|
mContext, PREF_KEY);
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET));
|
||||||
|
mShortcutOptionPreference = new ShortcutOptionPreference(mContext);
|
||||||
|
mShortcutOptionPreference.setKey(PREF_KEY);
|
||||||
|
mPreferenceScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
|
||||||
|
mPreferenceScreen.addPreference(mShortcutOptionPreference);
|
||||||
|
AccessibilityTestUtils.setSoftwareShortcutMode(
|
||||||
|
mContext, /* gestureNavEnabled= */ true, /* floatingButtonEnabled= */ false);
|
||||||
|
enableTouchExploration(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void displayPreference_verifyTitle() {
|
||||||
|
mController.displayPreference(mPreferenceScreen);
|
||||||
|
|
||||||
|
assertThat(mShortcutOptionPreference.getTitle().toString()).isEqualTo(
|
||||||
|
mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_title_software_by_gesture));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getSummary_touchExplorationDisabled_verifySummary() {
|
||||||
|
enableTouchExploration(false);
|
||||||
|
String expected = mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_software_gesture)
|
||||||
|
+ "\n\n"
|
||||||
|
+ mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_software_floating);
|
||||||
|
|
||||||
|
assertThat(mController.getSummary().toString()).isEqualTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getSummary_touchExplorationEnabled_verifySummary() {
|
||||||
|
enableTouchExploration(true);
|
||||||
|
String expected = mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_software_gesture_talkback)
|
||||||
|
+ "\n\n"
|
||||||
|
+ mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_software_floating);
|
||||||
|
|
||||||
|
assertThat(mController.getSummary().toString()).isEqualTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_inSuw_returnFalse() {
|
||||||
|
mController.setInSetupWizard(true);
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_notInSuwUseGestureNavSystemUseFab_returnFalse() {
|
||||||
|
mController.setInSetupWizard(false);
|
||||||
|
AccessibilityTestUtils.setSoftwareShortcutMode(
|
||||||
|
mContext, /* gestureNavEnabled= */ true, /* floatingButtonEnabled= */ true);
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_notInSuwUseGestureNavSystemNotUseFab_returnTrue() {
|
||||||
|
mController.setInSetupWizard(false);
|
||||||
|
AccessibilityTestUtils.setSoftwareShortcutMode(
|
||||||
|
mContext, /* gestureNavEnabled= */ true, /* floatingButtonEnabled= */ false);
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_notInSuwUseButtonNavSystemUseFab_returnFalse() {
|
||||||
|
mController.setInSetupWizard(false);
|
||||||
|
AccessibilityTestUtils.setSoftwareShortcutMode(
|
||||||
|
mContext, /* gestureNavEnabled= */ false, /* floatingButtonEnabled= */ true);
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_notInSuwUseButtonNavSystemNotUseFab_returnFalse() {
|
||||||
|
mController.setInSetupWizard(false);
|
||||||
|
AccessibilityTestUtils.setSoftwareShortcutMode(
|
||||||
|
mContext, /* gestureNavEnabled= */ false, /* floatingButtonEnabled= */ false);
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enableTouchExploration(boolean enable) {
|
||||||
|
AccessibilityManager am = mock(AccessibilityManager.class);
|
||||||
|
when(mContext.getSystemService(AccessibilityManager.class)).thenReturn(am);
|
||||||
|
when(am.isTouchExplorationEnabled()).thenReturn(enable);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.testutils.AccessibilityTestUtils;
|
||||||
|
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link NavButtonShortcutOptionController}
|
||||||
|
*/
|
||||||
|
@Config(shadows = SettingsShadowResources.class)
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class NavButtonShortcutOptionControllerTest {
|
||||||
|
private static final String PREF_KEY = "prefKey";
|
||||||
|
private static final String TARGET =
|
||||||
|
new ComponentName("FakePackage", "FakeClass").flattenToString();
|
||||||
|
private final Context mContext = spy(ApplicationProvider.getApplicationContext());
|
||||||
|
private NavButtonShortcutOptionController mController;
|
||||||
|
private ShortcutOptionPreference mShortcutOptionPreference;
|
||||||
|
|
||||||
|
private PreferenceScreen mPreferenceScreen;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mController = new NavButtonShortcutOptionController(
|
||||||
|
mContext, PREF_KEY);
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET));
|
||||||
|
mShortcutOptionPreference = new ShortcutOptionPreference(mContext);
|
||||||
|
mShortcutOptionPreference.setKey(PREF_KEY);
|
||||||
|
mPreferenceScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
|
||||||
|
mPreferenceScreen.addPreference(mShortcutOptionPreference);
|
||||||
|
AccessibilityTestUtils.setSoftwareShortcutMode(
|
||||||
|
mContext, /* gestureNavEnabled= */ false, /* floatingButtonEnabled= */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void displayPreference_verifyTitle() {
|
||||||
|
mController.displayPreference(mPreferenceScreen);
|
||||||
|
|
||||||
|
assertThat(mShortcutOptionPreference.getTitle().toString()).isEqualTo(
|
||||||
|
mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_title_software));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void displayPreference_inSuw_verifySummary() {
|
||||||
|
mController.setInSetupWizard(true);
|
||||||
|
mController.displayPreference(mPreferenceScreen);
|
||||||
|
|
||||||
|
assertThat(mShortcutOptionPreference.getSummary().toString()).isEqualTo(
|
||||||
|
mContext.getString(R.string.accessibility_shortcut_edit_dialog_summary_software));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void displayPreference_notInSuw_verifySummary() {
|
||||||
|
mController.setInSetupWizard(false);
|
||||||
|
String expected = mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_software)
|
||||||
|
+ "\n\n"
|
||||||
|
+ mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_software_floating);
|
||||||
|
|
||||||
|
mController.displayPreference(mPreferenceScreen);
|
||||||
|
|
||||||
|
assertThat(mShortcutOptionPreference.getSummary().toString()).isEqualTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_useGestureNavSystemUseFab_returnFalse() {
|
||||||
|
AccessibilityTestUtils.setSoftwareShortcutMode(
|
||||||
|
mContext, /* gestureNavEnabled= */ true, /* floatingButtonEnabled= */ true);
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_useGestureNavSystemNotUseFab_returnFalse() {
|
||||||
|
AccessibilityTestUtils.setSoftwareShortcutMode(
|
||||||
|
mContext, /* gestureNavEnabled= */ true, /* floatingButtonEnabled= */ false);
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_useButtonNavSystemUseFab_returnFalse() {
|
||||||
|
AccessibilityTestUtils.setSoftwareShortcutMode(
|
||||||
|
mContext, /* gestureNavEnabled= */ false, /* floatingButtonEnabled= */ true);
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_useButtonNavSystemNotUseFab_returnTrue() {
|
||||||
|
AccessibilityTestUtils.setSoftwareShortcutMode(
|
||||||
|
mContext, /* gestureNavEnabled= */ false, /* floatingButtonEnabled= */ false);
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isTrue();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
|
||||||
|
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InOrder;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ShortcutOptionPreferenceController}
|
||||||
|
*/
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class ShortcutOptionPreferenceControllerTest {
|
||||||
|
|
||||||
|
private static final String PREF_KEY = "prefKey";
|
||||||
|
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
private ShortcutOptionPreference mShortcutOptionPreference;
|
||||||
|
private ShortcutOptionPreferenceController mController;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mShortcutOptionPreference = spy(new ShortcutOptionPreference(mContext));
|
||||||
|
mShortcutOptionPreference.setKey(PREF_KEY);
|
||||||
|
mController = spy(new ShortcutOptionPreferenceController(mContext, PREF_KEY) {
|
||||||
|
@Override
|
||||||
|
protected boolean isShortcutAvailable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isChecked() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void enableShortcutForTargets(boolean enable) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateState_shortcutControllerIsChecked_shouldSetPreferenceChecked() {
|
||||||
|
when(mController.isChecked()).thenReturn(true);
|
||||||
|
|
||||||
|
mController.updateState(mShortcutOptionPreference);
|
||||||
|
|
||||||
|
assertThat(mShortcutOptionPreference.isChecked()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateState_shortcutControllerIsNotChecked_shouldSetPreferenceUnchecked() {
|
||||||
|
when(mController.isChecked()).thenReturn(false);
|
||||||
|
|
||||||
|
mController.updateState(mShortcutOptionPreference);
|
||||||
|
|
||||||
|
assertThat(mShortcutOptionPreference.isChecked()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_shortcutAvailable_returnAvailableUnsearchable() {
|
||||||
|
when(mController.isShortcutAvailable()).thenReturn(true);
|
||||||
|
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_shortcutUnavailable_returnConditionallyUnavailable() {
|
||||||
|
when(mController.isShortcutAvailable()).thenReturn(false);
|
||||||
|
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPreferenceChanged_callEnableShortcutForTargets() {
|
||||||
|
mController.onPreferenceChange(mShortcutOptionPreference, true);
|
||||||
|
mController.onPreferenceChange(mShortcutOptionPreference, false);
|
||||||
|
|
||||||
|
InOrder inOrder = Mockito.inOrder(mController);
|
||||||
|
inOrder.verify(mController).enableShortcutForTargets(true);
|
||||||
|
inOrder.verify(mController).enableShortcutForTargets(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getShortcutTargets() {
|
||||||
|
Set<String> targets = Set.of("target1", "target2");
|
||||||
|
mController.setShortcutTargets(targets);
|
||||||
|
|
||||||
|
assertThat(mController.getShortcutTargets())
|
||||||
|
.containsExactlyElementsIn(targets);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isInSetupWizard() {
|
||||||
|
mController.setInSetupWizard(true);
|
||||||
|
|
||||||
|
assertThat(mController.isInSetupWizard()).isTrue();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceViewHolder;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for {@link ShortcutOptionPreferenceTest}
|
||||||
|
*/
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class ShortcutOptionPreferenceTest {
|
||||||
|
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
private ShortcutOptionPreference mShortcutOptionPreference;
|
||||||
|
private PreferenceViewHolder mViewHolder;
|
||||||
|
private ImageView mImageView;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mShortcutOptionPreference = new ShortcutOptionPreference(mContext);
|
||||||
|
int layoutId = mShortcutOptionPreference.getLayoutResource();
|
||||||
|
View itemView = LayoutInflater.from(mContext).inflate(layoutId, /* root= */null);
|
||||||
|
mViewHolder = PreferenceViewHolder.createInstanceForTests(itemView);
|
||||||
|
mImageView = (ImageView) mViewHolder.findViewById(R.id.image);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void bindViewHolder_imageResNotSet_shouldHideImageView() {
|
||||||
|
mShortcutOptionPreference.onBindViewHolder(mViewHolder);
|
||||||
|
|
||||||
|
assertThat(mImageView.getVisibility()).isEqualTo(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void bindViewHolder_imageResIdSet_shouldShowImageView() {
|
||||||
|
mShortcutOptionPreference.setIntroImageResId(R.drawable.a11y_shortcut_type_hardware);
|
||||||
|
|
||||||
|
mShortcutOptionPreference.onBindViewHolder(mViewHolder);
|
||||||
|
|
||||||
|
assertThat(mImageView.getVisibility()).isEqualTo(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void bindViewHolder_imageRawResIdSet_shouldShowImageView() {
|
||||||
|
mShortcutOptionPreference.setIntroImageRawResId(
|
||||||
|
com.android.settings.R.raw.accessibility_color_inversion_banner);
|
||||||
|
|
||||||
|
mShortcutOptionPreference.onBindViewHolder(mViewHolder);
|
||||||
|
|
||||||
|
assertThat(mImageView.getVisibility()).isEqualTo(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void bindViewHolder_shouldUpdateSummaryTextLineHeight() {
|
||||||
|
assertThat(mShortcutOptionPreference.getSummaryTextLineHeight()).isEqualTo(0);
|
||||||
|
|
||||||
|
mShortcutOptionPreference.onBindViewHolder(mViewHolder);
|
||||||
|
|
||||||
|
assertThat(mShortcutOptionPreference.getSummaryTextLineHeight()).isNotEqualTo(0);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,330 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||||
|
import android.app.Application;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.ContextWrapper;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.content.pm.ServiceInfo;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.accessibility.AccessibilityManager;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
|
import com.android.internal.accessibility.common.ShortcutConstants;
|
||||||
|
import com.android.internal.accessibility.util.ShortcutUtils;
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.SettingsActivity;
|
||||||
|
import com.android.settings.SubSettings;
|
||||||
|
import com.android.settings.accessibility.AccessibilityButtonFragment;
|
||||||
|
import com.android.settings.accessibility.FloatingMenuSizePreferenceController;
|
||||||
|
import com.android.settings.utils.AnnotationSpan;
|
||||||
|
import com.android.settingslib.accessibility.AccessibilityUtils;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.Robolectric;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.Shadows;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link SoftwareShortcutOptionPreferenceController}
|
||||||
|
*/
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class SoftwareShortcutOptionPreferenceControllerTest {
|
||||||
|
private static final String PREF_KEY = "prefKey";
|
||||||
|
private static final String TARGET_MAGNIFICATION =
|
||||||
|
"com.android.server.accessibility.MagnificationController";
|
||||||
|
private static final ComponentName TARGET_ALWAYS_ON_A11Y_SERVICE =
|
||||||
|
new ComponentName("FakePackage", "AlwaysOnA11yService");
|
||||||
|
private static final ComponentName TARGET_STANDARD_A11Y_SERVICE =
|
||||||
|
new ComponentName("FakePackage", "StandardA11yService");
|
||||||
|
private static final String SOFTWARE_SHORTCUT_SETTING_NAME =
|
||||||
|
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private TestSoftwareShortcutOptionPreferenceController mController;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mContext = spy(Robolectric.buildActivity(FragmentActivity.class).get());
|
||||||
|
|
||||||
|
AccessibilityServiceInfo mAlwaysOnServiceInfo = createAccessibilityServiceInfo(
|
||||||
|
TARGET_ALWAYS_ON_A11Y_SERVICE, /* isAlwaysOnService= */ true);
|
||||||
|
AccessibilityServiceInfo mStandardServiceInfo = createAccessibilityServiceInfo(
|
||||||
|
TARGET_STANDARD_A11Y_SERVICE, /* isAlwaysOnService= */ false);
|
||||||
|
AccessibilityManager am = mock(AccessibilityManager.class);
|
||||||
|
when(mContext.getSystemService(Context.ACCESSIBILITY_SERVICE)).thenReturn(am);
|
||||||
|
when(am.getInstalledAccessibilityServiceList()).thenReturn(
|
||||||
|
List.of(mAlwaysOnServiceInfo, mStandardServiceInfo));
|
||||||
|
|
||||||
|
mController = new TestSoftwareShortcutOptionPreferenceController(mContext, PREF_KEY);
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_MAGNIFICATION));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isChecked_allTargetsHasShortcutConfigured_returnTrue() {
|
||||||
|
Settings.Secure.putString(
|
||||||
|
mContext.getContentResolver(), SOFTWARE_SHORTCUT_SETTING_NAME,
|
||||||
|
String.join(String.valueOf(SERVICES_SEPARATOR),
|
||||||
|
TARGET_MAGNIFICATION,
|
||||||
|
TARGET_STANDARD_A11Y_SERVICE.flattenToString(),
|
||||||
|
TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString())
|
||||||
|
);
|
||||||
|
mController.setShortcutTargets(
|
||||||
|
Set.of(TARGET_MAGNIFICATION,
|
||||||
|
TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString(),
|
||||||
|
TARGET_STANDARD_A11Y_SERVICE.flattenToString()));
|
||||||
|
|
||||||
|
assertThat(mController.isChecked()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isChecked_someTargetsHasShortcutConfigured_returnFalse() {
|
||||||
|
Settings.Secure.putString(
|
||||||
|
mContext.getContentResolver(), SOFTWARE_SHORTCUT_SETTING_NAME,
|
||||||
|
String.join(String.valueOf(SERVICES_SEPARATOR),
|
||||||
|
TARGET_MAGNIFICATION,
|
||||||
|
TARGET_STANDARD_A11Y_SERVICE.flattenToString())
|
||||||
|
);
|
||||||
|
mController.setShortcutTargets(
|
||||||
|
Set.of(TARGET_MAGNIFICATION,
|
||||||
|
TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString(),
|
||||||
|
TARGET_STANDARD_A11Y_SERVICE.flattenToString()));
|
||||||
|
|
||||||
|
assertThat(mController.isChecked()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isChecked_noTargetsHasShortcutConfigured_returnFalse() {
|
||||||
|
Settings.Secure.putString(
|
||||||
|
mContext.getContentResolver(), SOFTWARE_SHORTCUT_SETTING_NAME, "");
|
||||||
|
mController.setShortcutTargets(
|
||||||
|
Set.of(TARGET_MAGNIFICATION,
|
||||||
|
TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString(),
|
||||||
|
TARGET_STANDARD_A11Y_SERVICE.flattenToString()));
|
||||||
|
|
||||||
|
assertThat(mController.isChecked()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getCustomizedAccessibilityButtonLink_verifyText() {
|
||||||
|
String expected =
|
||||||
|
mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_software_floating);
|
||||||
|
|
||||||
|
CharSequence spannable = mController.getCustomizeAccessibilityButtonLink();
|
||||||
|
|
||||||
|
assertThat(spannable.toString()).isEqualTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getCustomizedAccessibilityButtonLink_verifyClickAction() {
|
||||||
|
String expected =
|
||||||
|
mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_software_floating);
|
||||||
|
|
||||||
|
CharSequence spannable = mController.getCustomizeAccessibilityButtonLink();
|
||||||
|
|
||||||
|
assertThat(spannable).isInstanceOf(SpannableStringBuilder.class);
|
||||||
|
AnnotationSpan[] spans = ((SpannableStringBuilder) spannable).getSpans(
|
||||||
|
0, expected.length(), AnnotationSpan.class);
|
||||||
|
spans[0].onClick(new View(mContext));
|
||||||
|
assertLaunchSettingsPage(AccessibilityButtonFragment.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enableShortcutForTargets_enableShortcut_shortcutTurnedOn() {
|
||||||
|
String target = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
|
||||||
|
mController.setShortcutTargets(Set.of(target));
|
||||||
|
assertThat(ShortcutUtils.isComponentIdExistingInSettings(
|
||||||
|
mContext, ShortcutConstants.UserShortcutType.SOFTWARE, target
|
||||||
|
)).isFalse();
|
||||||
|
|
||||||
|
mController.enableShortcutForTargets(true);
|
||||||
|
|
||||||
|
assertThat(ShortcutUtils.isComponentIdExistingInSettings(
|
||||||
|
mContext, ShortcutConstants.UserShortcutType.SOFTWARE, target
|
||||||
|
)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enableShortcutForTargets_disableShortcut_shortcutTurnedOff() {
|
||||||
|
String target = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
|
||||||
|
ShortcutUtils.optInValueToSettings(
|
||||||
|
mContext, ShortcutConstants.UserShortcutType.SOFTWARE, target);
|
||||||
|
assertThat(ShortcutUtils.isComponentIdExistingInSettings(
|
||||||
|
mContext, ShortcutConstants.UserShortcutType.SOFTWARE, target
|
||||||
|
)).isTrue();
|
||||||
|
mController.setShortcutTargets(Set.of(target));
|
||||||
|
|
||||||
|
mController.enableShortcutForTargets(false);
|
||||||
|
|
||||||
|
assertThat(ShortcutUtils.isComponentIdExistingInSettings(
|
||||||
|
mContext, ShortcutConstants.UserShortcutType.SOFTWARE, target
|
||||||
|
)).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enableShortcutForTargets_enableShortcutWithMagnification_menuSizeIncreased() {
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_MAGNIFICATION));
|
||||||
|
|
||||||
|
mController.enableShortcutForTargets(true);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
Settings.Secure.getInt(
|
||||||
|
mContext.getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
|
||||||
|
FloatingMenuSizePreferenceController.Size.UNKNOWN))
|
||||||
|
.isEqualTo(FloatingMenuSizePreferenceController.Size.LARGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enableShortcutForTargets_enableShortcutWithMagnification_userConfigureSmallMenuSize_menuSizeNotChanged() {
|
||||||
|
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
|
||||||
|
FloatingMenuSizePreferenceController.Size.SMALL);
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_MAGNIFICATION));
|
||||||
|
|
||||||
|
mController.enableShortcutForTargets(true);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
Settings.Secure.getInt(
|
||||||
|
mContext.getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
|
||||||
|
FloatingMenuSizePreferenceController.Size.UNKNOWN))
|
||||||
|
.isEqualTo(FloatingMenuSizePreferenceController.Size.SMALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enableShortcutForTargets_enableAlwaysOnServiceShortcut_turnsOnAlwaysOnService() {
|
||||||
|
mController.setShortcutTargets(
|
||||||
|
Set.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()));
|
||||||
|
|
||||||
|
mController.enableShortcutForTargets(true);
|
||||||
|
|
||||||
|
assertThat(AccessibilityUtils.getEnabledServicesFromSettings(mContext))
|
||||||
|
.contains(TARGET_ALWAYS_ON_A11Y_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enableShortcutForTargets_disableAlwaysOnServiceShortcut_turnsOffAlwaysOnService() {
|
||||||
|
mController.setShortcutTargets(
|
||||||
|
Set.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()));
|
||||||
|
|
||||||
|
mController.enableShortcutForTargets(false);
|
||||||
|
|
||||||
|
assertThat(AccessibilityUtils.getEnabledServicesFromSettings(mContext))
|
||||||
|
.doesNotContain(TARGET_ALWAYS_ON_A11Y_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enableShortcutForTargets_enableStandardServiceShortcut_wontTurnOnService() {
|
||||||
|
mController.setShortcutTargets(
|
||||||
|
Set.of(TARGET_STANDARD_A11Y_SERVICE.flattenToString()));
|
||||||
|
|
||||||
|
mController.enableShortcutForTargets(true);
|
||||||
|
|
||||||
|
assertThat(AccessibilityUtils.getEnabledServicesFromSettings(mContext))
|
||||||
|
.doesNotContain(TARGET_STANDARD_A11Y_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enableShortcutForTargets_disableStandardServiceShortcutWithServiceOn_wontTurnOffService() {
|
||||||
|
mController.setShortcutTargets(
|
||||||
|
Set.of(TARGET_STANDARD_A11Y_SERVICE.flattenToString()));
|
||||||
|
AccessibilityUtils.setAccessibilityServiceState(
|
||||||
|
mContext, TARGET_STANDARD_A11Y_SERVICE, /* enabled= */ true);
|
||||||
|
|
||||||
|
mController.enableShortcutForTargets(false);
|
||||||
|
|
||||||
|
assertThat(AccessibilityUtils.getEnabledServicesFromSettings(mContext))
|
||||||
|
.contains(TARGET_STANDARD_A11Y_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertLaunchSettingsPage(String page) {
|
||||||
|
ContextWrapper applicationContext = (Application) mContext.getApplicationContext();
|
||||||
|
final Intent intent = Shadows.shadowOf(applicationContext).getNextStartedActivity();
|
||||||
|
assertThat(intent).isNotNull();
|
||||||
|
assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MAIN);
|
||||||
|
assertThat(intent.getComponent()).isEqualTo(
|
||||||
|
new ComponentName(applicationContext, SubSettings.class));
|
||||||
|
assertThat(intent.getExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)).isEqualTo(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AccessibilityServiceInfo createAccessibilityServiceInfo(
|
||||||
|
ComponentName componentName, boolean isAlwaysOnService) {
|
||||||
|
final ApplicationInfo applicationInfo = new ApplicationInfo();
|
||||||
|
applicationInfo.targetSdkVersion = Build.VERSION_CODES.R;
|
||||||
|
final ServiceInfo serviceInfo = new ServiceInfo();
|
||||||
|
applicationInfo.packageName = componentName.getPackageName();
|
||||||
|
serviceInfo.packageName = componentName.getPackageName();
|
||||||
|
serviceInfo.name = componentName.getClassName();
|
||||||
|
serviceInfo.applicationInfo = applicationInfo;
|
||||||
|
|
||||||
|
final ResolveInfo resolveInfo = new ResolveInfo();
|
||||||
|
resolveInfo.serviceInfo = serviceInfo;
|
||||||
|
try {
|
||||||
|
final AccessibilityServiceInfo info = new AccessibilityServiceInfo(resolveInfo,
|
||||||
|
mContext);
|
||||||
|
info.setComponentName(componentName);
|
||||||
|
if (isAlwaysOnService) {
|
||||||
|
info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
} catch (XmlPullParserException | IOException e) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TestSoftwareShortcutOptionPreferenceController
|
||||||
|
extends SoftwareShortcutOptionPreferenceController {
|
||||||
|
|
||||||
|
TestSoftwareShortcutOptionPreferenceController(
|
||||||
|
Context context, String preferenceKey) {
|
||||||
|
super(context, preferenceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isShortcutAvailable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,185 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
|
||||||
|
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.icu.text.MessageFormat;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.accessibility.AccessibilityUtil;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link TripleTapShortcutOptionController}
|
||||||
|
*/
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class TripleTapShortcutOptionControllerTest {
|
||||||
|
private static final String PREF_KEY = "prefKey";
|
||||||
|
private static final String TARGET_MAGNIFICATION =
|
||||||
|
"com.android.server.accessibility.MagnificationController";
|
||||||
|
private static final String TARGET_FAKE =
|
||||||
|
new ComponentName("FakePackage", "FakeClass").flattenToString();
|
||||||
|
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
private TripleTapShortcutOptionController mController;
|
||||||
|
private ShortcutOptionPreference mShortcutOptionPreference;
|
||||||
|
private PreferenceScreen mPreferenceScreen;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mController = new TripleTapShortcutOptionController(mContext, PREF_KEY);
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_MAGNIFICATION));
|
||||||
|
mShortcutOptionPreference = new ShortcutOptionPreference(mContext);
|
||||||
|
mShortcutOptionPreference.setKey(PREF_KEY);
|
||||||
|
mPreferenceScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
|
||||||
|
mPreferenceScreen.addPreference(mShortcutOptionPreference);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void displayPreference_verifyScreenTestSet() {
|
||||||
|
mController.displayPreference(mPreferenceScreen);
|
||||||
|
|
||||||
|
assertThat(mShortcutOptionPreference.getTitle().toString()).isEqualTo(
|
||||||
|
mContext.getString(R.string.accessibility_shortcut_edit_dialog_title_triple_tap));
|
||||||
|
assertThat(mShortcutOptionPreference.getSummary().toString()).isEqualTo(
|
||||||
|
MessageFormat.format(
|
||||||
|
mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_triple_tap),
|
||||||
|
3));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_targetIsMagnificationAndIsExpanded_returnsAvailableUnsearchable() {
|
||||||
|
mController.setExpanded(true);
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_MAGNIFICATION));
|
||||||
|
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_targetIsMagnificationAndIsNotExpanded_returnsConditionallyUnavailable() {
|
||||||
|
mController.setExpanded(false);
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_MAGNIFICATION));
|
||||||
|
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_targetIsNotMagnificationAndIsNotExpanded_returnsConditionallyUnavailable() {
|
||||||
|
mController.setExpanded(false);
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_FAKE));
|
||||||
|
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_targetIsNotMagnificationAndIsExpanded_returnsConditionallyUnavailable() {
|
||||||
|
mController.setExpanded(true);
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_FAKE));
|
||||||
|
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setExpanded_expand_updateExpandedValue() {
|
||||||
|
mController.setExpanded(true);
|
||||||
|
|
||||||
|
assertThat(mController.isExpanded()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setExpanded_collapse_updateExpandedValue() {
|
||||||
|
mController.setExpanded(false);
|
||||||
|
|
||||||
|
assertThat(mController.isExpanded()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_multipleTargets_returnFalse() {
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_FAKE, TARGET_MAGNIFICATION));
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_magnificationTargetOnly_returnTrue() {
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_MAGNIFICATION));
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_nonMagnificationTarget_returnFalse() {
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_FAKE));
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isChecked_tripleTapConfigured_returnTrue() {
|
||||||
|
mController.enableShortcutForTargets(true);
|
||||||
|
|
||||||
|
assertThat(mController.isChecked()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isChecked_tripleTapNotConfigured_returnFalse() {
|
||||||
|
mController.enableShortcutForTargets(false);
|
||||||
|
|
||||||
|
assertThat(mController.isChecked()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enableShortcutForTargets_enableShortcut_settingUpdated() {
|
||||||
|
mController.enableShortcutForTargets(true);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
Settings.Secure.getInt(
|
||||||
|
mContext.getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
|
||||||
|
AccessibilityUtil.State.OFF)
|
||||||
|
).isEqualTo(AccessibilityUtil.State.ON);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enableShortcutForTargets_disableShortcut_settingUpdated() {
|
||||||
|
mController.enableShortcutForTargets(false);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
Settings.Secure.getInt(
|
||||||
|
mContext.getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
|
||||||
|
AccessibilityUtil.State.OFF)
|
||||||
|
).isEqualTo(AccessibilityUtil.State.OFF);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.icu.text.MessageFormat;
|
||||||
|
import android.platform.test.annotations.RequiresFlagsDisabled;
|
||||||
|
import android.platform.test.annotations.RequiresFlagsEnabled;
|
||||||
|
import android.platform.test.flag.junit.CheckFlagsRule;
|
||||||
|
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.server.accessibility.Flags;
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.accessibility.AccessibilityUtil;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link TwoFingersDoubleTapShortcutOptionController}
|
||||||
|
*/
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class TwoFingersDoubleTapShortcutOptionControllerTest {
|
||||||
|
private static final String PREF_KEY = "prefKey";
|
||||||
|
private static final String TARGET_MAGNIFICATION =
|
||||||
|
"com.android.server.accessibility.MagnificationController";
|
||||||
|
private static final String TARGET_FAKE =
|
||||||
|
new ComponentName("FakePackage", "FakeClass").flattenToString();
|
||||||
|
@Rule
|
||||||
|
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
|
||||||
|
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
private TwoFingersDoubleTapShortcutOptionController mController;
|
||||||
|
private ShortcutOptionPreference mShortcutOptionPreference;
|
||||||
|
|
||||||
|
private PreferenceScreen mPreferenceScreen;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mController = new TwoFingersDoubleTapShortcutOptionController(mContext, PREF_KEY);
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_MAGNIFICATION));
|
||||||
|
mShortcutOptionPreference = new ShortcutOptionPreference(mContext);
|
||||||
|
mShortcutOptionPreference.setKey(PREF_KEY);
|
||||||
|
mPreferenceScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
|
||||||
|
mPreferenceScreen.addPreference(mShortcutOptionPreference);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void displayPreference_verifyScreenTextSet() {
|
||||||
|
mController.displayPreference(mPreferenceScreen);
|
||||||
|
|
||||||
|
assertThat(mShortcutOptionPreference.getTitle().toString()).isEqualTo(
|
||||||
|
mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_title_two_finger_double_tap));
|
||||||
|
assertThat(mShortcutOptionPreference.getSummary().toString()).isEqualTo(
|
||||||
|
MessageFormat.format(mContext.getString(
|
||||||
|
R.string.accessibility_shortcut_edit_dialog_summary_two_finger_double_tap),
|
||||||
|
2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_featureFlagTurnedOff_returnFalse() {
|
||||||
|
assertThat(mController.isShortcutAvailable()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_multipleTargets_returnFalse() {
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_FAKE, TARGET_MAGNIFICATION));
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_magnificationTargetOnly_returnTrue() {
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_MAGNIFICATION));
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_nonMagnificationTarget_returnFalse() {
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET_FAKE));
|
||||||
|
|
||||||
|
assertThat(mController.isShortcutAvailable()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isChecked_twoFingersDoubleTapConfigured_returnTrue() {
|
||||||
|
mController.enableShortcutForTargets(true);
|
||||||
|
|
||||||
|
assertThat(mController.isChecked()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isChecked_twoFingersDoubleTapNotConfigured_returnFalse() {
|
||||||
|
mController.enableShortcutForTargets(false);
|
||||||
|
|
||||||
|
assertThat(mController.isChecked()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enableShortcutForTargets_enableShortcut_settingUpdated() {
|
||||||
|
mController.enableShortcutForTargets(true);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
Settings.Secure.getInt(
|
||||||
|
mContext.getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
|
||||||
|
AccessibilityUtil.State.OFF)
|
||||||
|
).isEqualTo(AccessibilityUtil.State.ON);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enableShortcutForTargets_disableShortcut_settingUpdated() {
|
||||||
|
mController.enableShortcutForTargets(false);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
Settings.Secure.getInt(
|
||||||
|
mContext.getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
|
||||||
|
AccessibilityUtil.State.OFF)
|
||||||
|
).isEqualTo(AccessibilityUtil.State.OFF);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.shortcuts;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.internal.accessibility.common.ShortcutConstants;
|
||||||
|
import com.android.internal.accessibility.util.ShortcutUtils;
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link VolumeKeysShortcutOptionController}
|
||||||
|
*/
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class VolumeKeysShortcutOptionControllerTest {
|
||||||
|
|
||||||
|
private static final String PREF_KEY = "prefKey";
|
||||||
|
private static final String TARGET =
|
||||||
|
new ComponentName("FakePackage", "FakeClass").flattenToString();
|
||||||
|
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
private VolumeKeysShortcutOptionController mController;
|
||||||
|
private ShortcutOptionPreference mShortcutOptionPreference;
|
||||||
|
|
||||||
|
private PreferenceScreen mPreferenceScreen;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mController = new VolumeKeysShortcutOptionController(
|
||||||
|
mContext, PREF_KEY);
|
||||||
|
mController.setShortcutTargets(Set.of(TARGET));
|
||||||
|
mShortcutOptionPreference = new ShortcutOptionPreference(mContext);
|
||||||
|
mShortcutOptionPreference.setKey(PREF_KEY);
|
||||||
|
mPreferenceScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
|
||||||
|
mPreferenceScreen.addPreference(mShortcutOptionPreference);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void displayPreference_verifyScreenTextSet() {
|
||||||
|
mController.displayPreference(mPreferenceScreen);
|
||||||
|
|
||||||
|
assertThat(mShortcutOptionPreference.getTitle().toString()).isEqualTo(
|
||||||
|
mContext.getString(R.string.accessibility_shortcut_edit_dialog_title_hardware));
|
||||||
|
assertThat(mShortcutOptionPreference.getSummary().toString()).isEqualTo(
|
||||||
|
mContext.getString(R.string.accessibility_shortcut_edit_dialog_summary_hardware));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isShortcutAvailable_returnsTrue() {
|
||||||
|
assertThat(mController.isShortcutAvailable()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isChecked_targetUseVolumeKeyShortcut_returnTrue() {
|
||||||
|
ShortcutUtils.optInValueToSettings(
|
||||||
|
mContext, ShortcutConstants.UserShortcutType.HARDWARE, TARGET);
|
||||||
|
|
||||||
|
assertThat(mController.isChecked()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isChecked_targetNotUseVolumeKeyShortcut_returnFalse() {
|
||||||
|
ShortcutUtils.optOutValueFromSettings(
|
||||||
|
mContext, ShortcutConstants.UserShortcutType.HARDWARE, TARGET);
|
||||||
|
|
||||||
|
assertThat(mController.isChecked()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enableShortcutForTargets_enableVolumeKeysShortcut_shortcutSet() {
|
||||||
|
mController.enableShortcutForTargets(true);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
ShortcutUtils.isComponentIdExistingInSettings(
|
||||||
|
mContext, ShortcutConstants.UserShortcutType.HARDWARE, TARGET)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enableShortcutForTargets_disableVolumeKeysShortcut_shortcutNotSet() {
|
||||||
|
mController.enableShortcutForTargets(false);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
ShortcutUtils.isComponentIdExistingInSettings(
|
||||||
|
mContext, ShortcutConstants.UserShortcutType.HARDWARE, TARGET)).isFalse();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.testutils;
|
||||||
|
|
||||||
|
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
|
||||||
|
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
|
||||||
|
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import com.android.settings.testutils.shadow.SettingsShadowResources;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for common methods used in the accessibility feature related tests
|
||||||
|
*/
|
||||||
|
public class AccessibilityTestUtils {
|
||||||
|
|
||||||
|
public static void setSoftwareShortcutMode(
|
||||||
|
Context context, boolean gestureNavEnabled, boolean floatingButtonEnabled) {
|
||||||
|
int mode = floatingButtonEnabled ? ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU : -1;
|
||||||
|
|
||||||
|
Settings.Secure.putInt(context.getContentResolver(),
|
||||||
|
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, mode);
|
||||||
|
|
||||||
|
if (gestureNavEnabled) {
|
||||||
|
SettingsShadowResources.overrideResource(
|
||||||
|
com.android.internal.R.integer.config_navBarInteractionMode,
|
||||||
|
NAV_BAR_MODE_GESTURAL);
|
||||||
|
} else {
|
||||||
|
SettingsShadowResources.overrideResource(
|
||||||
|
com.android.internal.R.integer.config_navBarInteractionMode,
|
||||||
|
NAV_BAR_MODE_3BUTTON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user