1 /*
2  * Copyright (C) 2021 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.inputmethod;
18 
19 import static com.android.settings.dashboard.profileselector.ProfileSelectFragment.EXTRA_PROFILE;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.mockito.ArgumentMatchers.any;
24 import static org.mockito.ArgumentMatchers.anyInt;
25 import static org.mockito.Mockito.doReturn;
26 import static org.mockito.Mockito.mock;
27 import static org.mockito.Mockito.spy;
28 import static org.mockito.Mockito.times;
29 import static org.mockito.Mockito.verify;
30 import static org.mockito.Mockito.when;
31 
32 import android.content.Context;
33 import android.content.pm.ApplicationInfo;
34 import android.content.pm.ResolveInfo;
35 import android.content.pm.ServiceInfo;
36 import android.os.Bundle;
37 import android.os.UserHandle;
38 import android.os.UserManager;
39 import android.provider.SearchIndexableResource;
40 import android.view.inputmethod.InputMethodInfo;
41 import android.view.inputmethod.InputMethodManager;
42 import android.view.inputmethod.InputMethodSubtype;
43 
44 import androidx.preference.PreferenceManager;
45 import androidx.preference.PreferenceScreen;
46 
47 import com.android.settings.R;
48 import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
49 import com.android.settings.testutils.shadow.ShadowInputMethodManagerWithMethodList;
50 import com.android.settings.testutils.shadow.ShadowSecureSettings;
51 import com.android.settingslib.inputmethod.InputMethodPreference;
52 import com.android.settingslib.inputmethod.InputMethodSettingValuesWrapper;
53 
54 import org.junit.Before;
55 import org.junit.Test;
56 import org.junit.runner.RunWith;
57 import org.mockito.Mock;
58 import org.mockito.MockitoAnnotations;
59 import org.robolectric.RobolectricTestRunner;
60 import org.robolectric.RuntimeEnvironment;
61 import org.robolectric.annotation.Config;
62 import org.robolectric.util.ReflectionHelpers;
63 
64 import java.util.ArrayList;
65 import java.util.List;
66 
67 @RunWith(RobolectricTestRunner.class)
68 @Config(shadows = {
69         ShadowSecureSettings.class,
70         ShadowInputMethodManagerWithMethodList.class
71 })
72 public class AvailableVirtualKeyboardFragmentTest {
73 
74     @Mock
75     private InputMethodManager mInputMethodManager;
76     @Mock
77     private UserManager mUserManager;
78     @Mock
79     private InputMethodSettingValuesWrapper mValuesWrapper;
80     @Mock
81     private PreferenceScreen mPreferenceScreen;
82     @Mock
83     private PreferenceManager mPreferenceManager;
84     @Mock
85     private InputMethodPreference mInputMethodPreference;
86     private Context mContext;
87     private AvailableVirtualKeyboardFragment mFragment;
88 
89     @Before
setUp()90     public void setUp() {
91         MockitoAnnotations.initMocks(this);
92         mContext = spy(RuntimeEnvironment.application);
93         initFragment();
94         initMock();
95     }
96 
97     @Test
onAttachPersonalProfile_noProfileParent()98     public void onAttachPersonalProfile_noProfileParent() {
99         doReturn(null).when(mUserManager).getProfileParent(any(UserHandle.class));
100 
101         mFragment.onAttach(mContext);
102 
103         assertThat(mFragment.mUserAwareContext).isEqualTo(mContext);
104     }
105 
106     @Test
onAttachPersonalProfile_hasProfileParent()107     public void onAttachPersonalProfile_hasProfileParent() {
108         final UserHandle profileParent = new UserHandle(0);
109         final Context mockContext = mock(Context.class);
110         doReturn(profileParent).when(mUserManager).getProfileParent(any(UserHandle.class));
111         doReturn(mockContext).when(mContext).createContextAsUser(any(UserHandle.class), anyInt());
112 
113         mFragment.onAttach(mContext);
114 
115         assertThat(mFragment.mUserAwareContext).isEqualTo(mockContext);
116     }
117 
118     @Test
onCreatePreferences_shouldAddResource()119     public void onCreatePreferences_shouldAddResource() {
120         mFragment.onAttach(mContext);
121 
122         mFragment.onCreatePreferences(new Bundle(), "test");
123 
124         verify(mFragment).addPreferencesFromResource(R.xml.available_virtual_keyboard);
125     }
126 
127     @Test
onResume_refreshAllInputMethodAndSubtypes()128     public void onResume_refreshAllInputMethodAndSubtypes() {
129         mFragment.onAttach(mContext);
130 
131         mFragment.onResume();
132 
133         // One invocation is in onResume(), another is in updateInputMethodPreferenceViews().
134         verify(mValuesWrapper, times(2)).refreshAllInputMethodAndSubtypes();
135     }
136 
137     @Test
onResume_updateInputMethodPreferenceViews()138     public void onResume_updateInputMethodPreferenceViews() {
139         mFragment.onAttach(mContext);
140 
141         mFragment.onResume();
142 
143         verify(mFragment).updateInputMethodPreferenceViews();
144     }
145 
146     @Test
onSaveInputMethodPreference_refreshAllInputMethodAndSubtypes()147     public void onSaveInputMethodPreference_refreshAllInputMethodAndSubtypes() {
148         mFragment.onAttach(mContext);
149 
150         mFragment.onSaveInputMethodPreference(mInputMethodPreference);
151 
152         verify(mValuesWrapper).refreshAllInputMethodAndSubtypes();
153     }
154 
155     @Test
updateInputMethodPreferenceViews_callsExpectedMethods()156     public void updateInputMethodPreferenceViews_callsExpectedMethods() {
157         mFragment.onAttach(mContext);
158 
159         mFragment.updateInputMethodPreferenceViews();
160 
161         verify(mValuesWrapper).getInputMethodList();
162         verify(mInputMethodManager).getEnabledInputMethodListAsUser(any(UserHandle.class));
163     }
164 
165     @Test
updateInputMethodPreferenceViews_addExpectedInputMethodPreference()166     public void updateInputMethodPreferenceViews_addExpectedInputMethodPreference() {
167         final int inputMethodNums = 5;
168         mFragment.onAttach(mContext);
169         when(mValuesWrapper.getInputMethodList()).thenReturn(createFakeInputMethodInfoList(
170                 "test", inputMethodNums));
171 
172         mFragment.updateInputMethodPreferenceViews();
173 
174         assertThat(mFragment.mInputMethodPreferenceList).hasSize(inputMethodNums);
175     }
176 
177     @Test
searchIndexProvider_shouldIndexResource()178     public void searchIndexProvider_shouldIndexResource() {
179         final List<SearchIndexableResource> indexRes =
180                 AvailableVirtualKeyboardFragment.SEARCH_INDEX_DATA_PROVIDER
181                         .getXmlResourcesToIndex(RuntimeEnvironment.application, true /* enabled */);
182 
183         assertThat(indexRes).isNotNull();
184         assertThat(indexRes.get(0).xmlResId).isEqualTo(mFragment.getPreferenceScreenResId());
185     }
186 
initFragment()187     private void initFragment() {
188         final Bundle bundle = new Bundle();
189         bundle.putInt(EXTRA_PROFILE, ProfileSelectFragment.ProfileType.PERSONAL);
190         mFragment = spy(new AvailableVirtualKeyboardFragment());
191         mFragment.setArguments(bundle);
192         mFragment.mInputMethodSettingValues = mValuesWrapper;
193         ReflectionHelpers.setField(mFragment, "mPreferenceManager", mPreferenceManager);
194     }
195 
initMock()196     private void initMock() {
197         when(mFragment.getContext()).thenReturn(mContext);
198         when(mFragment.getPreferenceScreen()).thenReturn(mPreferenceScreen);
199         when(mPreferenceManager.getContext()).thenReturn(mContext);
200         when(mContext.getSystemService(InputMethodManager.class)).thenReturn(mInputMethodManager);
201         when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
202     }
203 
createFakeInputMethodInfoList(final String name, int num)204     private List<InputMethodInfo> createFakeInputMethodInfoList(final String name, int num) {
205         List<InputMethodSubtype> subtypes = new ArrayList<>();
206 
207         subtypes.add(new InputMethodSubtype.InputMethodSubtypeBuilder()
208                 .build());
209         subtypes.add(new InputMethodSubtype.InputMethodSubtypeBuilder()
210                 .build());
211 
212         final ResolveInfo resolveInfo = new ResolveInfo();
213         resolveInfo.serviceInfo = new ServiceInfo();
214         resolveInfo.serviceInfo.packageName = "com.android.ime";
215         resolveInfo.serviceInfo.name = name;
216         resolveInfo.serviceInfo.applicationInfo = new ApplicationInfo();
217         resolveInfo.serviceInfo.applicationInfo.enabled = true;
218 
219         List<InputMethodInfo> inputMethodInfoList = new ArrayList<>();
220         for (int i = 0; i < num; i++) {
221             inputMethodInfoList.add(new InputMethodInfo(
222                     resolveInfo,
223                     false /* isAuxIme */,
224                     "TestSettingsActivity",
225                     subtypes,
226                     0 /* isDefaultResId */,
227                     true /* forceDefault */));
228         }
229         return inputMethodInfoList;
230     }
231 }
232