• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.Activity;
22 import android.app.LoaderManager;
23 import android.content.AsyncTaskLoader;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.Loader;
27 import android.database.ContentObserver;
28 import android.hardware.input.InputDeviceIdentifier;
29 import android.hardware.input.InputManager;
30 import android.hardware.input.KeyboardLayout;
31 import android.os.Bundle;
32 import android.os.Handler;
33 import android.os.UserHandle;
34 import android.provider.Settings.Secure;
35 import android.support.v14.preference.SwitchPreference;
36 import android.support.v7.preference.Preference;
37 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
38 import android.support.v7.preference.PreferenceCategory;
39 import android.support.v7.preference.PreferenceScreen;
40 import android.text.TextUtils;
41 import android.view.InputDevice;
42 import android.view.inputmethod.InputMethodInfo;
43 import android.view.inputmethod.InputMethodManager;
44 import android.view.inputmethod.InputMethodSubtype;
45 
46 import com.android.internal.inputmethod.InputMethodUtils;
47 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
48 import com.android.internal.util.Preconditions;
49 import com.android.settings.R;
50 import com.android.settings.Settings;
51 import com.android.settings.SettingsPreferenceFragment;
52 import com.android.settings.search.BaseSearchIndexProvider;
53 import com.android.settings.search.Indexable;
54 import com.android.settings.search.SearchIndexableRaw;
55 import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
56 
57 import java.text.Collator;
58 import java.util.ArrayList;
59 import java.util.Collections;
60 import java.util.HashMap;
61 import java.util.HashSet;
62 import java.util.List;
63 import java.util.Objects;
64 
65 public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
66         implements InputManager.InputDeviceListener, Indexable {
67 
68     private static final String KEYBOARD_ASSISTANCE_CATEGORY = "keyboard_assistance_category";
69     private static final String SHOW_VIRTUAL_KEYBOARD_SWITCH = "show_virtual_keyboard_switch";
70     private static final String KEYBOARD_SHORTCUTS_HELPER = "keyboard_shortcuts_helper";
71     private static final String IM_SUBTYPE_MODE_KEYBOARD = "keyboard";
72 
73     @NonNull
74     private final List<HardKeyboardDeviceInfo> mLastHardKeyboards = new ArrayList<>();
75     @NonNull
76     private final List<KeyboardInfoPreference> mTempKeyboardInfoList = new ArrayList<>();
77 
78     @NonNull
79     private final HashSet<Integer> mLoaderIDs = new HashSet<>();
80     private int mNextLoaderId = 0;
81 
82     private InputManager mIm;
83     @NonNull
84     private PreferenceCategory mKeyboardAssistanceCategory;
85     @NonNull
86     private SwitchPreference mShowVirtualKeyboardSwitch;
87     @NonNull
88     private InputMethodUtils.InputMethodSettings mSettings;
89 
90     @Override
onCreatePreferences(Bundle bundle, String s)91     public void onCreatePreferences(Bundle bundle, String s) {
92         Activity activity = Preconditions.checkNotNull(getActivity());
93         addPreferencesFromResource(R.xml.physical_keyboard_settings);
94         mIm = Preconditions.checkNotNull(activity.getSystemService(InputManager.class));
95         mSettings = new InputMethodUtils.InputMethodSettings(
96                 activity.getResources(),
97                 getContentResolver(),
98                 new HashMap<>(),
99                 new ArrayList<>(),
100                 UserHandle.myUserId(),
101                 false /* copyOnWrite */);
102         mKeyboardAssistanceCategory = Preconditions.checkNotNull(
103                 (PreferenceCategory) findPreference(KEYBOARD_ASSISTANCE_CATEGORY));
104         mShowVirtualKeyboardSwitch = Preconditions.checkNotNull(
105                 (SwitchPreference) mKeyboardAssistanceCategory.findPreference(
106                         SHOW_VIRTUAL_KEYBOARD_SWITCH));
107         findPreference(KEYBOARD_SHORTCUTS_HELPER).setOnPreferenceClickListener(
108                 new Preference.OnPreferenceClickListener() {
109                     @Override
110                     public boolean onPreferenceClick(Preference preference) {
111                         toggleKeyboardShortcutsMenu();
112                         return true;
113                     }
114                 });
115     }
116 
117     @Override
onResume()118     public void onResume() {
119         super.onResume();
120         clearLoader();
121         mLastHardKeyboards.clear();
122         updateHardKeyboards();
123         mIm.registerInputDeviceListener(this, null);
124         mShowVirtualKeyboardSwitch.setOnPreferenceChangeListener(
125                 mShowVirtualKeyboardSwitchPreferenceChangeListener);
126         registerShowVirtualKeyboardSettingsObserver();
127     }
128 
129     @Override
onPause()130     public void onPause() {
131         super.onPause();
132         clearLoader();
133         mLastHardKeyboards.clear();
134         mIm.unregisterInputDeviceListener(this);
135         mShowVirtualKeyboardSwitch.setOnPreferenceChangeListener(null);
136         unregisterShowVirtualKeyboardSettingsObserver();
137     }
138 
onLoadFinishedInternal( final int loaderId, @NonNull final List<Keyboards> keyboardsList)139     private void onLoadFinishedInternal(
140             final int loaderId, @NonNull final List<Keyboards> keyboardsList) {
141         if (!mLoaderIDs.remove(loaderId)) {
142             // Already destroyed loader.  Ignore.
143             return;
144         }
145 
146         Collections.sort(keyboardsList);
147         final PreferenceScreen preferenceScreen = getPreferenceScreen();
148         preferenceScreen.removeAll();
149         for (Keyboards keyboards : keyboardsList) {
150             final PreferenceCategory category = new PreferenceCategory(getPrefContext(), null);
151             category.setTitle(keyboards.mDeviceInfo.mDeviceName);
152             category.setOrder(0);
153             preferenceScreen.addPreference(category);
154             for (Keyboards.KeyboardInfo info : keyboards.mKeyboardInfoList) {
155                 mTempKeyboardInfoList.clear();
156                 final InputMethodInfo imi = info.mImi;
157                 final InputMethodSubtype imSubtype = info.mImSubtype;
158                 if (imi != null) {
159                     KeyboardInfoPreference pref =
160                             new KeyboardInfoPreference(getPrefContext(), info);
161                     pref.setOnPreferenceClickListener(preference -> {
162                         showKeyboardLayoutScreen(
163                                 keyboards.mDeviceInfo.mDeviceIdentifier, imi, imSubtype);
164                         return true;
165                     });
166                     mTempKeyboardInfoList.add(pref);
167                     Collections.sort(mTempKeyboardInfoList);
168                 }
169                 for (KeyboardInfoPreference pref : mTempKeyboardInfoList) {
170                     category.addPreference(pref);
171                 }
172             }
173         }
174         mTempKeyboardInfoList.clear();
175         mKeyboardAssistanceCategory.setOrder(1);
176         preferenceScreen.addPreference(mKeyboardAssistanceCategory);
177         updateShowVirtualKeyboardSwitch();
178     }
179 
180     @Override
onInputDeviceAdded(int deviceId)181     public void onInputDeviceAdded(int deviceId) {
182         updateHardKeyboards();
183     }
184 
185     @Override
onInputDeviceRemoved(int deviceId)186     public void onInputDeviceRemoved(int deviceId) {
187         updateHardKeyboards();
188     }
189 
190     @Override
onInputDeviceChanged(int deviceId)191     public void onInputDeviceChanged(int deviceId) {
192         updateHardKeyboards();
193     }
194 
195     @Override
getMetricsCategory()196     public int getMetricsCategory() {
197         return MetricsEvent.PHYSICAL_KEYBOARDS;
198     }
199 
200     @NonNull
getHardKeyboards()201     public static List<HardKeyboardDeviceInfo> getHardKeyboards() {
202         final List<HardKeyboardDeviceInfo> keyboards = new ArrayList<>();
203         final int[] devicesIds = InputDevice.getDeviceIds();
204         for (int deviceId : devicesIds) {
205             final InputDevice device = InputDevice.getDevice(deviceId);
206             if (device != null && !device.isVirtual() && device.isFullKeyboard()) {
207                 keyboards.add(new HardKeyboardDeviceInfo(device.getName(), device.getIdentifier()));
208             }
209         }
210         return keyboards;
211     }
212 
updateHardKeyboards()213     private void updateHardKeyboards() {
214         final List<HardKeyboardDeviceInfo> newHardKeyboards = getHardKeyboards();
215         if (!Objects.equals(newHardKeyboards, mLastHardKeyboards)) {
216             clearLoader();
217             mLastHardKeyboards.clear();
218             mLastHardKeyboards.addAll(newHardKeyboards);
219             mLoaderIDs.add(mNextLoaderId);
220             getLoaderManager().initLoader(mNextLoaderId, null,
221                     new Callbacks(getContext(), this, mLastHardKeyboards));
222             ++mNextLoaderId;
223         }
224     }
225 
showKeyboardLayoutScreen( @onNull InputDeviceIdentifier inputDeviceIdentifier, @NonNull InputMethodInfo imi, @Nullable InputMethodSubtype imSubtype)226     private void showKeyboardLayoutScreen(
227             @NonNull InputDeviceIdentifier inputDeviceIdentifier,
228             @NonNull InputMethodInfo imi,
229             @Nullable InputMethodSubtype imSubtype) {
230         final Intent intent = new Intent(Intent.ACTION_MAIN);
231         intent.setClass(getActivity(), Settings.KeyboardLayoutPickerActivity.class);
232         intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER,
233                 inputDeviceIdentifier);
234         intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_METHOD_INFO, imi);
235         intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_METHOD_SUBTYPE, imSubtype);
236         startActivity(intent);
237     }
238 
clearLoader()239     private void clearLoader() {
240         for (final int loaderId : mLoaderIDs) {
241             getLoaderManager().destroyLoader(loaderId);
242         }
243         mLoaderIDs.clear();
244     }
245 
registerShowVirtualKeyboardSettingsObserver()246     private void registerShowVirtualKeyboardSettingsObserver() {
247         unregisterShowVirtualKeyboardSettingsObserver();
248         getActivity().getContentResolver().registerContentObserver(
249                 Secure.getUriFor(Secure.SHOW_IME_WITH_HARD_KEYBOARD),
250                 false,
251                 mContentObserver,
252                 UserHandle.myUserId());
253         updateShowVirtualKeyboardSwitch();
254     }
255 
unregisterShowVirtualKeyboardSettingsObserver()256     private void unregisterShowVirtualKeyboardSettingsObserver() {
257         getActivity().getContentResolver().unregisterContentObserver(mContentObserver);
258     }
259 
updateShowVirtualKeyboardSwitch()260     private void updateShowVirtualKeyboardSwitch() {
261         mShowVirtualKeyboardSwitch.setChecked(mSettings.isShowImeWithHardKeyboardEnabled());
262     }
263 
toggleKeyboardShortcutsMenu()264     private void toggleKeyboardShortcutsMenu() {
265         getActivity().requestShowKeyboardShortcuts();
266     }
267 
268     private final OnPreferenceChangeListener mShowVirtualKeyboardSwitchPreferenceChangeListener =
269             new OnPreferenceChangeListener() {
270                 @Override
271                 public boolean onPreferenceChange(Preference preference, Object newValue) {
272                     mSettings.setShowImeWithHardKeyboard((Boolean) newValue);
273                     return true;
274                 }
275             };
276 
277     private final ContentObserver mContentObserver = new ContentObserver(new Handler(true)) {
278         @Override
279         public void onChange(boolean selfChange) {
280             updateShowVirtualKeyboardSwitch();
281         }
282     };
283 
284     private static final class Callbacks implements LoaderManager.LoaderCallbacks<List<Keyboards>> {
285         @NonNull
286         final Context mContext;
287         @NonNull
288         final PhysicalKeyboardFragment mPhysicalKeyboardFragment;
289         @NonNull
290         final List<HardKeyboardDeviceInfo> mHardKeyboards;
Callbacks( @onNull Context context, @NonNull PhysicalKeyboardFragment physicalKeyboardFragment, @NonNull List<HardKeyboardDeviceInfo> hardKeyboards)291         public Callbacks(
292                 @NonNull Context context,
293                 @NonNull PhysicalKeyboardFragment physicalKeyboardFragment,
294                 @NonNull List<HardKeyboardDeviceInfo> hardKeyboards) {
295             mContext = context;
296             mPhysicalKeyboardFragment = physicalKeyboardFragment;
297             mHardKeyboards = hardKeyboards;
298         }
299 
300         @Override
onCreateLoader(int id, Bundle args)301         public Loader<List<Keyboards>> onCreateLoader(int id, Bundle args) {
302             return new KeyboardLayoutLoader(mContext, mHardKeyboards);
303         }
304 
305         @Override
onLoadFinished(Loader<List<Keyboards>> loader, List<Keyboards> data)306         public void onLoadFinished(Loader<List<Keyboards>> loader, List<Keyboards> data) {
307             mPhysicalKeyboardFragment.onLoadFinishedInternal(loader.getId(), data);
308         }
309 
310         @Override
onLoaderReset(Loader<List<Keyboards>> loader)311         public void onLoaderReset(Loader<List<Keyboards>> loader) {
312         }
313     }
314 
315     private static final class KeyboardLayoutLoader extends AsyncTaskLoader<List<Keyboards>> {
316         @NonNull
317         private final List<HardKeyboardDeviceInfo> mHardKeyboards;
318 
KeyboardLayoutLoader( @onNull Context context, @NonNull List<HardKeyboardDeviceInfo> hardKeyboards)319         public KeyboardLayoutLoader(
320                 @NonNull Context context,
321                 @NonNull List<HardKeyboardDeviceInfo> hardKeyboards) {
322             super(context);
323             mHardKeyboards = Preconditions.checkNotNull(hardKeyboards);
324         }
325 
loadInBackground(HardKeyboardDeviceInfo deviceInfo)326         private Keyboards loadInBackground(HardKeyboardDeviceInfo deviceInfo) {
327             final ArrayList<Keyboards.KeyboardInfo> keyboardInfoList = new ArrayList<>();
328             final InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
329             final InputManager im = getContext().getSystemService(InputManager.class);
330             if (imm != null && im != null) {
331                 for (InputMethodInfo imi : imm.getEnabledInputMethodList()) {
332                     final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(
333                             imi, true /* allowsImplicitlySelectedSubtypes */);
334                     if (subtypes.isEmpty()) {
335                         // Here we use null to indicate that this IME has no subtype.
336                         final InputMethodSubtype nullSubtype = null;
337                         final KeyboardLayout layout = im.getKeyboardLayoutForInputDevice(
338                                 deviceInfo.mDeviceIdentifier, imi, nullSubtype);
339                         keyboardInfoList.add(new Keyboards.KeyboardInfo(imi, nullSubtype, layout));
340                         continue;
341                     }
342 
343                     // If the IME supports subtypes, we pick up "keyboard" subtypes only.
344                     final int N = subtypes.size();
345                     for (int i = 0; i < N; ++i) {
346                         final InputMethodSubtype subtype = subtypes.get(i);
347                         if (!IM_SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode())) {
348                             continue;
349                         }
350                         final KeyboardLayout layout = im.getKeyboardLayoutForInputDevice(
351                                 deviceInfo.mDeviceIdentifier, imi, subtype);
352                         keyboardInfoList.add(new Keyboards.KeyboardInfo(imi, subtype, layout));
353                     }
354                 }
355             }
356             return new Keyboards(deviceInfo, keyboardInfoList);
357         }
358 
359         @Override
loadInBackground()360         public List<Keyboards> loadInBackground() {
361             List<Keyboards> keyboardsList = new ArrayList<>(mHardKeyboards.size());
362             for (HardKeyboardDeviceInfo deviceInfo : mHardKeyboards) {
363                 keyboardsList.add(loadInBackground(deviceInfo));
364             }
365             return keyboardsList;
366         }
367 
368         @Override
onStartLoading()369         protected void onStartLoading() {
370             super.onStartLoading();
371             forceLoad();
372         }
373 
374         @Override
onStopLoading()375         protected void onStopLoading() {
376             super.onStopLoading();
377             cancelLoad();
378         }
379     }
380 
381     public static final class HardKeyboardDeviceInfo {
382         @NonNull
383         public final String mDeviceName;
384         @NonNull
385         public final InputDeviceIdentifier mDeviceIdentifier;
386 
HardKeyboardDeviceInfo( @ullable final String deviceName, @NonNull final InputDeviceIdentifier deviceIdentifier)387         public HardKeyboardDeviceInfo(
388                 @Nullable final String deviceName,
389                 @NonNull final InputDeviceIdentifier deviceIdentifier) {
390             mDeviceName = deviceName != null ? deviceName : "";
391             mDeviceIdentifier = deviceIdentifier;
392         }
393 
394         @Override
equals(Object o)395         public boolean equals(Object o) {
396             if (o == this) return true;
397             if (o == null) return false;
398 
399             if (!(o instanceof HardKeyboardDeviceInfo)) return false;
400 
401             final HardKeyboardDeviceInfo that = (HardKeyboardDeviceInfo) o;
402             if (!TextUtils.equals(mDeviceName, that.mDeviceName)) {
403                 return false;
404             }
405             if (mDeviceIdentifier.getVendorId() != that.mDeviceIdentifier.getVendorId()) {
406                 return false;
407             }
408             if (mDeviceIdentifier.getProductId() != that.mDeviceIdentifier.getProductId()) {
409                 return false;
410             }
411             if (!TextUtils.equals(mDeviceIdentifier.getDescriptor(),
412                     that.mDeviceIdentifier.getDescriptor())) {
413                 return false;
414             }
415 
416             return true;
417         }
418     }
419 
420     public static final class Keyboards implements Comparable<Keyboards> {
421         @NonNull
422         public final HardKeyboardDeviceInfo mDeviceInfo;
423         @NonNull
424         public final ArrayList<KeyboardInfo> mKeyboardInfoList;
425         @NonNull
426         public final Collator mCollator = Collator.getInstance();
427 
Keyboards( @onNull final HardKeyboardDeviceInfo deviceInfo, @NonNull final ArrayList<KeyboardInfo> keyboardInfoList)428         public Keyboards(
429                 @NonNull final HardKeyboardDeviceInfo deviceInfo,
430                 @NonNull final ArrayList<KeyboardInfo> keyboardInfoList) {
431             mDeviceInfo = deviceInfo;
432             mKeyboardInfoList = keyboardInfoList;
433         }
434 
435         @Override
compareTo(@onNull Keyboards another)436         public int compareTo(@NonNull Keyboards another) {
437             return mCollator.compare(mDeviceInfo.mDeviceName, another.mDeviceInfo.mDeviceName);
438         }
439 
440         public static final class KeyboardInfo {
441             @NonNull
442             public final InputMethodInfo mImi;
443             @Nullable
444             public final InputMethodSubtype mImSubtype;
445             @NonNull
446             public final KeyboardLayout mLayout;
447 
KeyboardInfo( @onNull final InputMethodInfo imi, @Nullable final InputMethodSubtype imSubtype, @NonNull final KeyboardLayout layout)448             public KeyboardInfo(
449                     @NonNull final InputMethodInfo imi,
450                     @Nullable final InputMethodSubtype imSubtype,
451                     @NonNull final KeyboardLayout layout) {
452                 mImi = imi;
453                 mImSubtype = imSubtype;
454                 mLayout = layout;
455             }
456         }
457     }
458 
459     static final class KeyboardInfoPreference extends Preference {
460 
461         @NonNull
462         private final CharSequence mImeName;
463         @Nullable
464         private final CharSequence mImSubtypeName;
465         @NonNull
466         private final Collator collator = Collator.getInstance();
467 
KeyboardInfoPreference( @onNull Context context, @NonNull Keyboards.KeyboardInfo info)468         private KeyboardInfoPreference(
469                 @NonNull Context context, @NonNull Keyboards.KeyboardInfo info) {
470             super(context);
471             mImeName = info.mImi.loadLabel(context.getPackageManager());
472             mImSubtypeName = getImSubtypeName(context, info.mImi, info.mImSubtype);
473             setTitle(formatDisplayName(context, mImeName, mImSubtypeName));
474             if (info.mLayout != null) {
475                 setSummary(info.mLayout.getLabel());
476             }
477         }
478 
479         @NonNull
getDisplayName( @onNull Context context, @NonNull InputMethodInfo imi, @Nullable InputMethodSubtype imSubtype)480         static CharSequence getDisplayName(
481                 @NonNull Context context, @NonNull InputMethodInfo imi,
482                 @Nullable InputMethodSubtype imSubtype) {
483             final CharSequence imeName = imi.loadLabel(context.getPackageManager());
484             final CharSequence imSubtypeName = getImSubtypeName(context, imi, imSubtype);
485             return formatDisplayName(context, imeName, imSubtypeName);
486         }
487 
formatDisplayName( @onNull Context context, @NonNull CharSequence imeName, @Nullable CharSequence imSubtypeName)488         private static CharSequence formatDisplayName(
489                 @NonNull Context context,
490                 @NonNull CharSequence imeName, @Nullable CharSequence imSubtypeName) {
491             if (imSubtypeName == null) {
492                 return imeName;
493             }
494             return String.format(
495                     context.getString(R.string.physical_device_title), imeName, imSubtypeName);
496         }
497 
498         @Nullable
getImSubtypeName( @onNull Context context, @NonNull InputMethodInfo imi, @Nullable InputMethodSubtype imSubtype)499         private static CharSequence getImSubtypeName(
500                 @NonNull Context context, @NonNull InputMethodInfo imi,
501                 @Nullable InputMethodSubtype imSubtype) {
502             if (imSubtype != null) {
503                 return InputMethodAndSubtypeUtil.getSubtypeLocaleNameAsSentence(
504                         imSubtype, context, imi);
505             }
506             return null;
507         }
508 
509         @Override
compareTo(@onNull Preference object)510         public int compareTo(@NonNull Preference object) {
511             if (!(object instanceof KeyboardInfoPreference)) {
512                 return super.compareTo(object);
513             }
514             KeyboardInfoPreference another = (KeyboardInfoPreference) object;
515             int result = compare(mImeName, another.mImeName);
516             if (result == 0) {
517                 result = compare(mImSubtypeName, another.mImSubtypeName);
518             }
519             return result;
520         }
521 
compare(@ullable CharSequence lhs, @Nullable CharSequence rhs)522         private int compare(@Nullable CharSequence lhs, @Nullable CharSequence rhs) {
523             if (!TextUtils.isEmpty(lhs) && !TextUtils.isEmpty(rhs)) {
524                 return collator.compare(lhs.toString(), rhs.toString());
525             } else if (TextUtils.isEmpty(lhs) && TextUtils.isEmpty(rhs)) {
526                 return 0;
527             } else if (!TextUtils.isEmpty(lhs)) {
528                 return -1;
529             } else {
530                 return 1;
531             }
532         }
533     }
534 
getPhysicalFullKeyboards()535     public static List<InputDevice> getPhysicalFullKeyboards() {
536         List<InputDevice> keyboards = null;
537         for (final int deviceId : InputDevice.getDeviceIds()) {
538             final InputDevice device = InputDevice.getDevice(deviceId);
539             if (device != null && !device.isVirtual() && device.isFullKeyboard()) {
540                 if (keyboards == null) keyboards = new ArrayList<>();
541                 keyboards.add(device);
542             }
543         }
544         return (keyboards == null) ? Collections.emptyList() : keyboards;
545     }
546 
547     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
548             new BaseSearchIndexProvider() {
549         @Override
550         public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
551             final InputManager inputManager = (InputManager) context.getSystemService(
552                     Context.INPUT_SERVICE);
553             final String screenTitle = context.getString(R.string.physical_keyboard_title);
554             final List<SearchIndexableRaw> indexes = new ArrayList<>();
555             for (final InputDevice device : getPhysicalFullKeyboards()) {
556                 final String keyboardLayoutDescriptor = inputManager
557                         .getCurrentKeyboardLayoutForInputDevice(device.getIdentifier());
558                 final KeyboardLayout keyboardLayout = (keyboardLayoutDescriptor != null)
559                         ? inputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null;
560                 final String summary = (keyboardLayout != null)
561                         ? keyboardLayout.toString()
562                         : context.getString(R.string.keyboard_layout_default_label);
563                 final SearchIndexableRaw index = new SearchIndexableRaw(context);
564                 index.key = device.getName();
565                 index.title = device.getName();
566                 index.summaryOn = summary;
567                 index.summaryOff = summary;
568                 index.screenTitle = screenTitle;
569                 indexes.add(index);
570             }
571             return indexes;
572         }
573     };
574 }
575