1e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang/* 2e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang * Copyright (C) 2017 The Android Open Source Project 3e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang * 4e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang * Licensed under the Apache License, Version 2.0 (the "License"); 5e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang * you may not use this file except in compliance with the License. 6e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang * You may obtain a copy of the License at 7e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang * 8e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang * http://www.apache.org/licenses/LICENSE-2.0 9e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang * 10e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang * Unless required by applicable law or agreed to in writing, software 11e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang * distributed under the License is distributed on an "AS IS" BASIS, 12e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang * See the License for the specific language governing permissions and 14e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang * limitations under the License. 15e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang */ 16e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 17e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangpackage com.android.settings.search; 18e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 19e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport static android.content.Context.INPUT_METHOD_SERVICE; 20e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 21e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport android.content.ComponentName; 22e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport android.content.Context; 23e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport android.content.Intent; 24e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport android.content.pm.PackageManager; 25e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport android.content.pm.ServiceInfo; 26e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport android.hardware.input.InputManager; 27e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport android.hardware.input.KeyboardLayout; 28e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport android.support.annotation.VisibleForTesting; 29e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport android.view.InputDevice; 30e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport android.view.inputmethod.InputMethodInfo; 31e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport android.view.inputmethod.InputMethodManager; 32e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport android.view.inputmethod.InputMethodSubtype; 33e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 34e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport com.android.settings.R; 35e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport com.android.settings.dashboard.SiteMapManager; 36e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport com.android.settings.inputmethod.AvailableVirtualKeyboardFragment; 37e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport com.android.settings.inputmethod.PhysicalKeyboardFragment; 38e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport com.android.settings.utils.AsyncLoader; 39e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil; 40e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 41e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport java.util.ArrayList; 42e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport java.util.HashSet; 43e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport java.util.List; 44e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport java.util.Objects; 45e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangimport java.util.Set; 46e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 47e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang/** 48e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang * Search result for input devices (physical/virtual keyboard, game controllers, etc) 49e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang */ 50e3535d9998363cbf352b5f8feb9277475e380944Fan Zhangpublic class InputDeviceResultLoader extends AsyncLoader<Set<? extends SearchResult>> { 51e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang private static final int NAME_NO_MATCH = -1; 52e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 53e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang @VisibleForTesting 54e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang static final String PHYSICAL_KEYBOARD_FRAGMENT = PhysicalKeyboardFragment.class.getName(); 55e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang @VisibleForTesting 56e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang static final String VIRTUAL_KEYBOARD_FRAGMENT = 57e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang AvailableVirtualKeyboardFragment.class.getName(); 58e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 59e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang private final SiteMapManager mSiteMapManager; 60e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang private final InputManager mInputManager; 61e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang private final InputMethodManager mImm; 62e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang private final PackageManager mPackageManager; 63e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang @VisibleForTesting 64e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final String mQuery; 65e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 66e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang private List<String> mPhysicalKeyboardBreadcrumb; 67e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang private List<String> mVirtualKeyboardBreadcrumb; 68e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 69e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang public InputDeviceResultLoader(Context context, String query, SiteMapManager mapManager) { 70e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang super(context); 71e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang mQuery = query; 72e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang mSiteMapManager = mapManager; 73e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE); 74e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang mImm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE); 75e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang mPackageManager = context.getPackageManager(); 76e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 77e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 78e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang @Override 79e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang protected void onDiscardResult(Set<? extends SearchResult> result) { 80e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 81e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 82e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang @Override 83e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang public Set<? extends SearchResult> loadInBackground() { 84e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final Set<SearchResult> results = new HashSet<>(); 85e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang results.addAll(buildPhysicalKeyboardSearchResults()); 86e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang results.addAll(buildVirtualKeyboardSearchResults()); 87e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang return results; 88e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 89e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 90e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang private Set<SearchResult> buildPhysicalKeyboardSearchResults() { 91e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final Set<SearchResult> results = new HashSet<>(); 92e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final Context context = getContext(); 93e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final String screenTitle = context.getString(R.string.physical_keyboard_title); 94e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 95e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang for (final InputDevice device : getPhysicalFullKeyboards()) { 96e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final String deviceName = device.getName(); 97e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final int wordDiff = InstalledAppResultLoader.getWordDifference(deviceName, mQuery); 98e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang if (wordDiff == NAME_NO_MATCH) { 99e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang continue; 100e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 101e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final String keyboardLayoutDescriptor = mInputManager 102e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang .getCurrentKeyboardLayoutForInputDevice(device.getIdentifier()); 103e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final KeyboardLayout keyboardLayout = (keyboardLayoutDescriptor != null) 104e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang ? mInputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null; 105e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final String summary = (keyboardLayout != null) 106e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang ? keyboardLayout.toString() 107e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang : context.getString(R.string.keyboard_layout_default_label); 108e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final String key = deviceName; 109e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 110e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final Intent intent = DatabaseIndexingUtils.buildSubsettingIntent(context, 111e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang PHYSICAL_KEYBOARD_FRAGMENT, key, screenTitle); 112e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang results.add(new SearchResult.Builder() 113e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang .setTitle(deviceName) 114e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang .setPayload(new ResultPayload(intent)) 115e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang .setStableId(Objects.hash(PHYSICAL_KEYBOARD_FRAGMENT, key)) 116e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang .setSummary(summary) 117e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang .setRank(wordDiff) 118e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang .addBreadcrumbs(getPhysicalKeyboardBreadCrumb()) 119e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang .build()); 120e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 121e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang return results; 122e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 123e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 124e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang private Set<SearchResult> buildVirtualKeyboardSearchResults() { 125e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final Set<SearchResult> results = new HashSet<>(); 126e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final Context context = getContext(); 127e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final String screenTitle = context.getString(R.string.add_virtual_keyboard); 128e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final List<InputMethodInfo> inputMethods = mImm.getInputMethodList(); 129e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang for (InputMethodInfo info : inputMethods) { 130e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final String title = info.loadLabel(mPackageManager).toString(); 131e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final String summary = InputMethodAndSubtypeUtil 132e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang .getSubtypeLocaleNameListAsSentence(getAllSubtypesOf(info), context, info); 133e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang int wordDiff = InstalledAppResultLoader.getWordDifference(title, mQuery); 134e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang if (wordDiff == NAME_NO_MATCH) { 135e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang wordDiff = InstalledAppResultLoader.getWordDifference(summary, mQuery); 136e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 137e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang if (wordDiff == NAME_NO_MATCH) { 138e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang continue; 139e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 140e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final ServiceInfo serviceInfo = info.getServiceInfo(); 141e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final String key = new ComponentName(serviceInfo.packageName, serviceInfo.name) 142e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang .flattenToString(); 143e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final Intent intent = DatabaseIndexingUtils.buildSubsettingIntent(context, 144e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang VIRTUAL_KEYBOARD_FRAGMENT, key, screenTitle); 145e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang results.add(new SearchResult.Builder() 146e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang .setTitle(title) 147e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang .setSummary(summary) 148e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang .setRank(wordDiff) 149e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang .setStableId(Objects.hash(VIRTUAL_KEYBOARD_FRAGMENT, key)) 150e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang .addBreadcrumbs(getVirtualKeyboardBreadCrumb()) 151e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang .setPayload(new ResultPayload(intent)) 152e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang .build()); 153e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 154e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang return results; 155e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 156e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 157e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang private List<String> getPhysicalKeyboardBreadCrumb() { 158e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang if (mPhysicalKeyboardBreadcrumb == null || mPhysicalKeyboardBreadcrumb.isEmpty()) { 159e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final Context context = getContext(); 160e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang mPhysicalKeyboardBreadcrumb = mSiteMapManager.buildBreadCrumb( 161e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang context, PHYSICAL_KEYBOARD_FRAGMENT, 162e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang context.getString(R.string.physical_keyboard_title)); 163e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 164e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang return mPhysicalKeyboardBreadcrumb; 165e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 166e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 167e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 168e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang private List<String> getVirtualKeyboardBreadCrumb() { 169e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang if (mVirtualKeyboardBreadcrumb == null || mVirtualKeyboardBreadcrumb.isEmpty()) { 170e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final Context context = getContext(); 171e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang mVirtualKeyboardBreadcrumb = mSiteMapManager.buildBreadCrumb( 172e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang context, VIRTUAL_KEYBOARD_FRAGMENT, 173e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang context.getString(R.string.add_virtual_keyboard)); 174e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 175e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang return mVirtualKeyboardBreadcrumb; 176e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 177e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 178e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang private List<InputDevice> getPhysicalFullKeyboards() { 179e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final List<InputDevice> keyboards = new ArrayList<>(); 180e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final int[] deviceIds = InputDevice.getDeviceIds(); 181e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang if (deviceIds != null) { 182e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang for (int deviceId : deviceIds) { 183e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final InputDevice device = InputDevice.getDevice(deviceId); 184e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang if (device != null && !device.isVirtual() && device.isFullKeyboard()) { 185e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang keyboards.add(device); 186e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 187e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 188e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 189e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang return keyboards; 190e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 191e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang 192e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang private static List<InputMethodSubtype> getAllSubtypesOf(final InputMethodInfo imi) { 193e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final int subtypeCount = imi.getSubtypeCount(); 194e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang final List<InputMethodSubtype> allSubtypes = new ArrayList<>(subtypeCount); 195e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang for (int index = 0; index < subtypeCount; index++) { 196e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang allSubtypes.add(imi.getSubtypeAt(index)); 197e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 198e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang return allSubtypes; 199e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang } 200e3535d9998363cbf352b5f8feb9277475e380944Fan Zhang} 201