1 /*
2  * Copyright (C) 2023 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.server.inputmethod;
18 
19 import android.annotation.AnyThread;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.UserIdInt;
23 import android.view.inputmethod.InputMethodInfo;
24 import android.view.inputmethod.InputMethodSubtype;
25 
26 import com.android.internal.annotations.GuardedBy;
27 import com.android.internal.inputmethod.InputMethodSubtypeHandle;
28 
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.Objects;
32 
33 final class HardwareKeyboardShortcutController {
34     @GuardedBy("ImfLock.class")
35     private final ArrayList<InputMethodSubtypeHandle> mSubtypeHandles = new ArrayList<>();
36 
37     @UserIdInt
38     private final int mUserId;
39 
40     @AnyThread
41     @UserIdInt
getUserId()42     int getUserId() {
43         return mUserId;
44     }
45 
HardwareKeyboardShortcutController(@onNull InputMethodMap methodMap, @UserIdInt int userId)46     HardwareKeyboardShortcutController(@NonNull InputMethodMap methodMap, @UserIdInt int userId) {
47         mUserId = userId;
48         reset(methodMap);
49     }
50 
51     @GuardedBy("ImfLock.class")
reset(@onNull InputMethodMap methodMap)52     void reset(@NonNull InputMethodMap methodMap) {
53         mSubtypeHandles.clear();
54         final InputMethodSettings settings = InputMethodSettings.create(methodMap, mUserId);
55         final List<InputMethodInfo> inputMethods = settings.getEnabledInputMethodList();
56         for (int i = 0; i < inputMethods.size(); ++i) {
57             final InputMethodInfo imi = inputMethods.get(i);
58             if (!imi.shouldShowInInputMethodPicker()) {
59                 continue;
60             }
61             final List<InputMethodSubtype> subtypes =
62                     settings.getEnabledInputMethodSubtypeList(imi, true);
63             if (subtypes.isEmpty()) {
64                 mSubtypeHandles.add(InputMethodSubtypeHandle.of(imi, null));
65             } else {
66                 for (final InputMethodSubtype subtype : subtypes) {
67                     if (subtype.isSuitableForPhysicalKeyboardLayoutMapping()) {
68                         mSubtypeHandles.add(InputMethodSubtypeHandle.of(imi, subtype));
69                     }
70                 }
71             }
72         }
73     }
74 
75     @AnyThread
76     @Nullable
getNeighborItem(@onNull List<T> list, @NonNull T value, boolean next)77     static <T> T getNeighborItem(@NonNull List<T> list, @NonNull T value, boolean next) {
78         final int size = list.size();
79         for (int i = 0; i < size; ++i) {
80             if (Objects.equals(value, list.get(i))) {
81                 final int nextIndex = (i + (next ? 1 : -1) + size) % size;
82                 return list.get(nextIndex);
83             }
84         }
85         return null;
86     }
87 
88     @GuardedBy("ImfLock.class")
89     @Nullable
onSubtypeSwitch( @onNull InputMethodSubtypeHandle currentImeAndSubtype, boolean forward)90     InputMethodSubtypeHandle onSubtypeSwitch(
91             @NonNull InputMethodSubtypeHandle currentImeAndSubtype, boolean forward) {
92         return getNeighborItem(mSubtypeHandles, currentImeAndSubtype, forward);
93     }
94 }
95