1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.accessibility;
18 
19 import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_SAVED_USER_SHORTCUT_TYPE;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.mockito.Mockito.doReturn;
24 import static org.mockito.Mockito.mock;
25 import static org.mockito.Mockito.spy;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.when;
28 
29 import android.content.ComponentName;
30 import android.content.Context;
31 import android.content.DialogInterface;
32 import android.os.Bundle;
33 import android.provider.Settings;
34 import android.view.LayoutInflater;
35 import android.view.View;
36 import android.view.ViewGroup;
37 
38 import androidx.annotation.XmlRes;
39 import androidx.appcompat.app.AlertDialog;
40 import androidx.fragment.app.FragmentActivity;
41 import androidx.preference.PreferenceManager;
42 import androidx.preference.PreferenceScreen;
43 import androidx.test.core.app.ApplicationProvider;
44 
45 import com.android.settings.R;
46 import com.android.settings.accessibility.AccessibilityDialogUtils.DialogType;
47 import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
48 import com.android.settings.testutils.shadow.ShadowFragment;
49 
50 import org.junit.Before;
51 import org.junit.Test;
52 import org.junit.runner.RunWith;
53 import org.mockito.Answers;
54 import org.mockito.Mock;
55 import org.mockito.MockitoAnnotations;
56 import org.robolectric.RobolectricTestRunner;
57 import org.robolectric.annotation.Config;
58 import org.robolectric.shadows.androidx.fragment.FragmentController;
59 
60 /** Tests for {@link ToggleFeaturePreferenceFragment} */
61 @RunWith(RobolectricTestRunner.class)
62 public class ToggleFeaturePreferenceFragmentTest {
63 
64     private static final String PLACEHOLDER_PACKAGE_NAME = "com.placeholder.example";
65     private static final String PLACEHOLDER_CLASS_NAME = PLACEHOLDER_PACKAGE_NAME + ".placeholder";
66     private static final ComponentName PLACEHOLDER_COMPONENT_NAME = new ComponentName(
67             PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CLASS_NAME);
68     private static final String PLACEHOLDER_DIALOG_TITLE = "title";
69     private static final String DEFAULT_SUMMARY = "default summary";
70     private static final String DEFAULT_DESCRIPTION = "default description";
71 
72     private static final String SOFTWARE_SHORTCUT_KEY =
73             Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
74     private static final String HARDWARE_SHORTCUT_KEY =
75             Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
76 
77     private TestToggleFeaturePreferenceFragment mFragment;
78     private PreferenceScreen mScreen;
79     private Context mContext = ApplicationProvider.getApplicationContext();
80 
81     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
82     private PreferenceManager mPreferenceManager;
83 
84     @Before
setUpTestFragment()85     public void setUpTestFragment() {
86         MockitoAnnotations.initMocks(this);
87 
88         mFragment = spy(new TestToggleFeaturePreferenceFragment());
89         when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
90         when(mFragment.getPreferenceManager().getContext()).thenReturn(mContext);
91         when(mFragment.getContext()).thenReturn(mContext);
92         mScreen = spy(new PreferenceScreen(mContext, null));
93         when(mScreen.getPreferenceManager()).thenReturn(mPreferenceManager);
94         doReturn(mScreen).when(mFragment).getPreferenceScreen();
95     }
96 
97     @Test
createFragment_shouldOnlyAddPreferencesOnce()98     public void createFragment_shouldOnlyAddPreferencesOnce() {
99         FragmentController.setupFragment(mFragment, FragmentActivity.class,
100                 /* containerViewId= */ 0, /* bundle= */ null);
101 
102         // execute exactly once
103         verify(mFragment).addPreferencesFromResource(R.xml.placeholder_prefs);
104     }
105 
106     @Test
updateShortcutPreferenceData_assignDefaultValueToVariable()107     public void updateShortcutPreferenceData_assignDefaultValueToVariable() {
108         mFragment.mComponentName = PLACEHOLDER_COMPONENT_NAME;
109 
110         mFragment.updateShortcutPreferenceData();
111 
112         final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
113                 mFragment.mComponentName.flattenToString(), UserShortcutType.SOFTWARE);
114         // Compare to default UserShortcutType
115         assertThat(expectedType).isEqualTo(UserShortcutType.SOFTWARE);
116     }
117 
118     @Test
updateShortcutPreferenceData_hasValueInSettings_assignToVariable()119     public void updateShortcutPreferenceData_hasValueInSettings_assignToVariable() {
120         mFragment.mComponentName = PLACEHOLDER_COMPONENT_NAME;
121         putStringIntoSettings(SOFTWARE_SHORTCUT_KEY, PLACEHOLDER_COMPONENT_NAME.flattenToString());
122         putStringIntoSettings(HARDWARE_SHORTCUT_KEY, PLACEHOLDER_COMPONENT_NAME.flattenToString());
123 
124         mFragment.updateShortcutPreferenceData();
125 
126         final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
127                 mFragment.mComponentName.flattenToString(), UserShortcutType.SOFTWARE);
128         assertThat(expectedType).isEqualTo(UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE);
129     }
130 
131     @Test
updateShortcutPreferenceData_hasValueInSharedPreference_assignToVariable()132     public void updateShortcutPreferenceData_hasValueInSharedPreference_assignToVariable() {
133         mFragment.mComponentName = PLACEHOLDER_COMPONENT_NAME;
134         final PreferredShortcut hardwareShortcut = new PreferredShortcut(
135                 PLACEHOLDER_COMPONENT_NAME.flattenToString(), UserShortcutType.HARDWARE);
136 
137         putUserShortcutTypeIntoSharedPreference(mContext, hardwareShortcut);
138         mFragment.updateShortcutPreferenceData();
139 
140         final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
141                 mFragment.mComponentName.flattenToString(), UserShortcutType.SOFTWARE);
142         assertThat(expectedType).isEqualTo(UserShortcutType.HARDWARE);
143     }
144 
145     @Test
setupEditShortcutDialog_shortcutPreferenceOff_checkboxIsEmptyValue()146     public void setupEditShortcutDialog_shortcutPreferenceOff_checkboxIsEmptyValue() {
147         mContext.setTheme(R.style.Theme_AppCompat);
148         final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog(
149                 mContext, DialogType.EDIT_SHORTCUT_GENERIC, PLACEHOLDER_DIALOG_TITLE,
150                 this::callEmptyOnClicked);
151         final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */
152                 null);
153         mFragment.mComponentName = PLACEHOLDER_COMPONENT_NAME;
154         mFragment.mShortcutPreference = shortcutPreference;
155 
156         mFragment.mShortcutPreference.setChecked(false);
157         mFragment.setupEditShortcutDialog(dialog);
158 
159         final int checkboxValue = mFragment.getShortcutTypeCheckBoxValue();
160         assertThat(checkboxValue).isEqualTo(UserShortcutType.EMPTY);
161     }
162 
163     @Test
setupEditShortcutDialog_shortcutPreferenceOn_checkboxIsSavedValue()164     public void setupEditShortcutDialog_shortcutPreferenceOn_checkboxIsSavedValue() {
165         mContext.setTheme(R.style.Theme_AppCompat);
166         final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog(
167                 mContext, DialogType.EDIT_SHORTCUT_GENERIC, PLACEHOLDER_DIALOG_TITLE,
168                 this::callEmptyOnClicked);
169         final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */
170                 null);
171         final PreferredShortcut hardwareShortcut = new PreferredShortcut(
172                 PLACEHOLDER_COMPONENT_NAME.flattenToString(), UserShortcutType.HARDWARE);
173         mFragment.mComponentName = PLACEHOLDER_COMPONENT_NAME;
174         mFragment.mShortcutPreference = shortcutPreference;
175 
176         PreferredShortcuts.saveUserShortcutType(mContext, hardwareShortcut);
177         mFragment.mShortcutPreference.setChecked(true);
178         mFragment.setupEditShortcutDialog(dialog);
179 
180         final int checkboxValue = mFragment.getShortcutTypeCheckBoxValue();
181         assertThat(checkboxValue).isEqualTo(UserShortcutType.HARDWARE);
182     }
183 
184     @Test
185     @Config(shadows = ShadowFragment.class)
restoreValueFromSavedInstanceState_assignToVariable()186     public void restoreValueFromSavedInstanceState_assignToVariable() {
187         mContext.setTheme(R.style.Theme_AppCompat);
188         final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog(
189                 mContext, DialogType.EDIT_SHORTCUT_GENERIC, PLACEHOLDER_DIALOG_TITLE,
190                 this::callEmptyOnClicked);
191         final Bundle savedInstanceState = new Bundle();
192         final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */
193                 null);
194         mFragment.mComponentName = PLACEHOLDER_COMPONENT_NAME;
195         mFragment.mShortcutPreference = shortcutPreference;
196 
197         savedInstanceState.putInt(KEY_SAVED_USER_SHORTCUT_TYPE,
198                 UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE);
199         mFragment.onCreate(savedInstanceState);
200         mFragment.setupEditShortcutDialog(dialog);
201         final int value = mFragment.getShortcutTypeCheckBoxValue();
202         mFragment.saveNonEmptyUserShortcutType(value);
203 
204         final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
205                 mFragment.mComponentName.flattenToString(), UserShortcutType.SOFTWARE);
206         assertThat(expectedType).isEqualTo(UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE);
207     }
208 
209     @Test
createFooterPreference_shouldSetAsExpectedValue()210     public void createFooterPreference_shouldSetAsExpectedValue() {
211         mFragment.createFooterPreference(mFragment.getPreferenceScreen(),
212                 DEFAULT_SUMMARY, DEFAULT_DESCRIPTION);
213 
214         AccessibilityFooterPreference accessibilityFooterPreference =
215                 (AccessibilityFooterPreference) mFragment.getPreferenceScreen().getPreference(
216                         mFragment.getPreferenceScreen().getPreferenceCount() - 1);
217         assertThat(accessibilityFooterPreference.getSummary()).isEqualTo(DEFAULT_SUMMARY);
218         assertThat(accessibilityFooterPreference.isSelectable()).isEqualTo(true);
219         assertThat(accessibilityFooterPreference.getOrder()).isEqualTo(Integer.MAX_VALUE - 1);
220     }
221 
putStringIntoSettings(String key, String componentName)222     private void putStringIntoSettings(String key, String componentName) {
223         Settings.Secure.putString(mContext.getContentResolver(), key, componentName);
224     }
225 
putUserShortcutTypeIntoSharedPreference(Context context, PreferredShortcut shortcut)226     private void putUserShortcutTypeIntoSharedPreference(Context context,
227             PreferredShortcut shortcut) {
228         PreferredShortcuts.saveUserShortcutType(context, shortcut);
229     }
230 
callEmptyOnClicked(DialogInterface dialog, int which)231     private void callEmptyOnClicked(DialogInterface dialog, int which) {}
232 
233     public static class TestToggleFeaturePreferenceFragment
234             extends ToggleFeaturePreferenceFragment {
235 
236         @Override
onPreferenceToggled(String preferenceKey, boolean enabled)237         protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
238         }
239 
240         @Override
getMetricsCategory()241         public int getMetricsCategory() {
242             return 0;
243         }
244 
245         @Override
getUserShortcutTypes()246         int getUserShortcutTypes() {
247             return 0;
248         }
249 
250         @Override
getPreferenceScreenResId()251         public int getPreferenceScreenResId() {
252             return R.xml.placeholder_prefs;
253         }
254 
255         @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)256         public View onCreateView(LayoutInflater inflater, ViewGroup container,
257                 Bundle savedInstanceState) {
258             return mock(View.class);
259         }
260 
261         @Override
onViewCreated(View view, Bundle savedInstanceState)262         public void onViewCreated(View view, Bundle savedInstanceState) {
263             // do nothing
264         }
265 
266         @Override
onDestroyView()267         public void onDestroyView() {
268             // do nothing
269         }
270 
271         @Override
addPreferencesFromResource(@mlRes int preferencesResId)272         public void addPreferencesFromResource(@XmlRes int preferencesResId) {
273             // do nothing
274         }
275 
276         @Override
updateShortcutPreference()277         protected void updateShortcutPreference() {
278             // UI related function, do nothing in tests
279         }
280 
281     }
282 }
283