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.example.android.autofillkeyboard;
18 
19 import android.util.SparseArray;
20 import android.view.LayoutInflater;
21 import android.view.View;
22 import android.widget.TextView;
23 
24 /** Controls the visible virtual keyboard view. */
25 final class Keyboard {
26 
27     private static final int NUM_STATES = 4;
28     private static final int STATE_SHIFT = 1;
29     private static final int STATE_SYMBOL = 2;
30 
31     private final AutofillImeService mAutofillImeService;
32     private final int mViewResId;
33     private final SparseArray<String> mKeyMapping;
34     private View mKeyboardView;
35     private int mState;
36 
Keyboard(AutofillImeService autofillImeService, int viewResId, SparseArray<String> keyMapping)37     private Keyboard(AutofillImeService autofillImeService, int viewResId,
38             SparseArray<String> keyMapping) {
39         this.mAutofillImeService = autofillImeService;
40         this.mViewResId = viewResId;
41         this.mKeyMapping = keyMapping;
42         this.mState = 0;
43     }
44 
getLabel(String data)45     private static String getLabel(String data) {
46         if ("SHI".equals(data)) {
47             return "↑";
48         } else if ("DEL".equals(data)) {
49             return "←";
50         } else if ("SYM".equals(data)) {
51             return "?123";
52         } else if ("SPA".equals(data)) {
53             return "[            ]";
54         } else if ("ENT".equals(data)) {
55             return "↩";
56         } else {
57             return data;
58         }
59     }
60 
qwerty(AutofillImeService autofillImeService)61     static Keyboard qwerty(AutofillImeService autofillImeService) {
62         SparseArray<String> keyMapping = new SparseArray<>();
63         keyMapping.put(R.id.key_pos_0_0, "qQ1\u007E");
64         keyMapping.put(R.id.key_pos_0_1, "wW2\u0060");
65         keyMapping.put(R.id.key_pos_0_2, "eE3\u007C");
66         keyMapping.put(R.id.key_pos_0_3, "rR4\u2022");
67         keyMapping.put(R.id.key_pos_0_4, "tT5\u221A");
68         keyMapping.put(R.id.key_pos_0_5, "yY6\u03C0");
69         keyMapping.put(R.id.key_pos_0_6, "uU7\u00F7");
70         keyMapping.put(R.id.key_pos_0_7, "iI8\u00D7");
71         keyMapping.put(R.id.key_pos_0_8, "oO9\u00B6");
72         keyMapping.put(R.id.key_pos_0_9, "pP0\u2206");
73         keyMapping.put(R.id.key_pos_1_0, "aA@\u00A3");
74         keyMapping.put(R.id.key_pos_1_1, "sS#\u00A2");
75         keyMapping.put(R.id.key_pos_1_2, "dD$\u20AC");
76         keyMapping.put(R.id.key_pos_1_3, "fF_\u00A5");
77         keyMapping.put(R.id.key_pos_1_4, "gG&\u005E");
78         keyMapping.put(R.id.key_pos_1_5, "hH-=");
79         keyMapping.put(R.id.key_pos_1_6, "jJ+{");
80         keyMapping.put(R.id.key_pos_1_7, "kK(}");
81         keyMapping.put(R.id.key_pos_1_8, "lL)\\");
82         keyMapping.put(R.id.key_pos_2_0, "zZ*%");
83         keyMapping.put(R.id.key_pos_2_1, "xX\"\u00A9");
84         keyMapping.put(R.id.key_pos_2_2, "cC'\u00AE");
85         keyMapping.put(R.id.key_pos_2_3, "vV:\u2122");
86         keyMapping.put(R.id.key_pos_2_4, "bB;\u2713");
87         keyMapping.put(R.id.key_pos_2_5, "nN![");
88         keyMapping.put(R.id.key_pos_2_6, "mM?]");
89         keyMapping.put(R.id.key_pos_bottom_0, ",,,<");
90         keyMapping.put(R.id.key_pos_bottom_1, "...>");
91         keyMapping.put(R.id.key_pos_shift, "SHI");
92         keyMapping.put(R.id.key_pos_del, "DEL");
93         keyMapping.put(R.id.key_pos_symbol, "SYM");
94         keyMapping.put(R.id.key_pos_space, "SPA");
95         keyMapping.put(R.id.key_pos_enter, "ENT");
96         return new Keyboard(autofillImeService, R.layout.keyboard_10_9_9, keyMapping);
97     }
98 
inflateKeyboardView(LayoutInflater inflater, InputView inputView)99     View inflateKeyboardView(LayoutInflater inflater, InputView inputView) {
100         mKeyboardView = inflater.inflate(mViewResId, inputView, false);
101         mapKeys();
102         return mKeyboardView;
103     }
104 
reset()105     void reset() {
106         mState = 0;
107         mapKeys();
108     }
109 
mapKeys()110     private void mapKeys() {
111         for (int i = 0; i < mKeyMapping.size(); i++) {
112             TextView softkey = mKeyboardView.findViewById(mKeyMapping.keyAt(i));
113             String rawData = mKeyMapping.valueAt(i);
114             String data = rawData.length() != NUM_STATES ? rawData : rawData.substring(mState,
115                     mState + 1);
116             softkey.setText(getLabel(data));
117             softkey.setOnClickListener(v -> handle(data));
118         }
119     }
120 
handle(String data)121     private void handle(String data) {
122         if ("SHI".equals(data)) {
123             // Toggle STATE_SHIFT.
124             mState = mState ^ STATE_SHIFT;
125             mapKeys();
126         } else if ("SYM".equals(data)) {
127             // Toggle STATE_SYMBOL and clear STATE_SHIFT.
128             mState = (mState ^ STATE_SYMBOL) & ~STATE_SHIFT;
129             mapKeys();
130         } else {
131             mAutofillImeService.handle(data);
132         }
133     }
134 }
135