1 /*
2  * Copyright (C) 2019 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.car.settings.testutils;
18 
19 import android.provider.Settings;
20 import android.view.inputmethod.InputMethodInfo;
21 import android.view.inputmethod.InputMethodManager;
22 import android.view.inputmethod.InputMethodSubtype;
23 
24 import androidx.annotation.Nullable;
25 
26 import com.android.car.settings.inputmethod.InputMethodUtil;
27 
28 import org.robolectric.RuntimeEnvironment;
29 import org.robolectric.annotation.Implementation;
30 import org.robolectric.annotation.Implements;
31 import org.robolectric.annotation.Resetter;
32 
33 import java.util.ArrayList;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.stream.Collectors;
38 
39 @Implements(value = InputMethodManager.class)
40 public class ShadowInputMethodManager extends org.robolectric.shadows.ShadowInputMethodManager {
41     private static List<InputMethodSubtype> sInputMethodSubtypes = new ArrayList<>();
42     private static List<InputMethodInfo> sInputMethodList = new ArrayList<>();
43     private static Map<String, InputMethodInfo> sInputMethodMap = new HashMap<>();
44 
45     @Resetter
reset()46     public static void reset() {
47         sInputMethodSubtypes.clear();
48         sInputMethodList.clear();
49         sInputMethodMap.clear();
50         org.robolectric.shadows.ShadowInputMethodManager.reset();
51     }
52 
setEnabledInputMethodSubtypeList(List<InputMethodSubtype> list)53     public static void setEnabledInputMethodSubtypeList(List<InputMethodSubtype> list) {
54         sInputMethodSubtypes = list;
55     }
56 
57     @Implementation
getEnabledInputMethodSubtypeList(InputMethodInfo imi, boolean allowsImplicitlySelectedSubtypes)58     protected List<InputMethodSubtype> getEnabledInputMethodSubtypeList(InputMethodInfo imi,
59             boolean allowsImplicitlySelectedSubtypes) {
60         return sInputMethodSubtypes;
61     }
62 
setEnabledInputMethodList(@ullable List<InputMethodInfo> list)63     public static void setEnabledInputMethodList(@Nullable List<InputMethodInfo> list) {
64         if (list == null || list.size() == 0) {
65             Settings.Secure.putString(RuntimeEnvironment.application.getContentResolver(),
66                     Settings.Secure.ENABLED_INPUT_METHODS, "");
67             return;
68         }
69 
70         String concatenatedInputMethodIds = createInputMethodIdString(list.stream().map(
71                 imi -> imi.getId()).collect(Collectors.toList()).toArray(new String[list.size()]));
72         Settings.Secure.putString(RuntimeEnvironment.application.getContentResolver(),
73                 Settings.Secure.ENABLED_INPUT_METHODS, concatenatedInputMethodIds);
74         addInputMethodInfosToMap(list);
75     }
76 
77     @Implementation
getEnabledInputMethodList()78     protected List<InputMethodInfo> getEnabledInputMethodList() {
79         List<InputMethodInfo> enabledInputMethodList = new ArrayList<>();
80 
81         String inputMethodIdString = Settings.Secure.getString(
82                 RuntimeEnvironment.application.getContentResolver(),
83                 Settings.Secure.ENABLED_INPUT_METHODS);
84         if (inputMethodIdString == null || inputMethodIdString.isEmpty()) {
85             return enabledInputMethodList;
86         }
87 
88         InputMethodUtil.sInputMethodSplitter.setString(inputMethodIdString);
89         while (InputMethodUtil.sInputMethodSplitter.hasNext()) {
90             enabledInputMethodList.add(sInputMethodMap.get(InputMethodUtil.sInputMethodSplitter
91                     .next()));
92         }
93         return enabledInputMethodList;
94     }
95 
setInputMethodList(List<InputMethodInfo> inputMethodInfos)96     public void setInputMethodList(List<InputMethodInfo> inputMethodInfos) {
97         sInputMethodList = inputMethodInfos;
98         if (inputMethodInfos == null) {
99             return;
100         }
101 
102         addInputMethodInfosToMap(inputMethodInfos);
103     }
104 
105     @Implementation
getInputMethodList()106     protected List<InputMethodInfo> getInputMethodList() {
107         return sInputMethodList;
108     }
109 
createInputMethodIdString(String... ids)110     private static String createInputMethodIdString(String... ids) {
111         int size = ids == null ? 0 : ids.length;
112 
113         if (size == 1) {
114             return ids[0];
115         }
116 
117         StringBuilder builder = new StringBuilder();
118         for (int i = 0; i < size; i++) {
119             builder.append(ids[i]);
120             if (i != size - 1) {
121                 builder.append(InputMethodUtil.INPUT_METHOD_DELIMITER);
122             }
123         }
124         return builder.toString();
125     }
126 
addInputMethodInfosToMap(List<InputMethodInfo> inputMethodInfos)127     private static void addInputMethodInfosToMap(List<InputMethodInfo> inputMethodInfos) {
128         if (sInputMethodMap == null || sInputMethodMap.size() == 0) {
129             sInputMethodMap = inputMethodInfos.stream().collect(Collectors.toMap(
130                     InputMethodInfo::getId, imi -> imi));
131             return;
132         }
133 
134         inputMethodInfos.forEach(imi -> {
135             sInputMethodMap.put(imi.getId(), imi);
136         });
137     }
138 }
139