• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014, 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.inputmethod.latin;
18 
19 import android.content.res.Resources;
20 import android.util.Log;
21 import android.util.Pair;
22 import android.view.KeyEvent;
23 
24 import com.android.inputmethod.keyboard.KeyboardSwitcher;
25 import com.android.inputmethod.latin.settings.Settings;
26 
27 import java.util.ArrayList;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Set;
31 
32 import javax.annotation.Nonnull;
33 
34 /**
35  * A class for detecting Emoji-Alt physical key.
36  */
37 final class EmojiAltPhysicalKeyDetector {
38     private static final String TAG = "EmojiAltPhysicalKeyDetector";
39     private static final boolean DEBUG = false;
40 
41     private List<EmojiHotKeys> mHotKeysList;
42 
43     private static class HotKeySet extends HashSet<Pair<Integer, Integer>> { };
44 
45     private abstract class EmojiHotKeys {
46         private final String mName;
47         private final HotKeySet mKeySet;
48 
49         boolean mCanFire;
50         int mMetaState;
51 
EmojiHotKeys(final String name, HotKeySet keySet)52         public EmojiHotKeys(final String name, HotKeySet keySet) {
53             mName = name;
54             mKeySet = keySet;
55             mCanFire = false;
56         }
57 
onKeyDown(@onnull final KeyEvent keyEvent)58         public void onKeyDown(@Nonnull final KeyEvent keyEvent) {
59             if (DEBUG) {
60                 Log.d(TAG, "EmojiHotKeys.onKeyDown() - " + mName + " - considering " + keyEvent);
61             }
62 
63             final Pair<Integer, Integer> key =
64                     Pair.create(keyEvent.getKeyCode(), keyEvent.getMetaState());
65             if (mKeySet.contains(key)) {
66                 if (DEBUG) {
67                    Log.d(TAG, "EmojiHotKeys.onKeyDown() - " + mName + " - enabling action");
68                 }
69                 mCanFire = true;
70                 mMetaState = keyEvent.getMetaState();
71             } else if (mCanFire) {
72                 if (DEBUG) {
73                    Log.d(TAG, "EmojiHotKeys.onKeyDown() - " + mName + " - disabling action");
74                 }
75                 mCanFire = false;
76             }
77         }
78 
onKeyUp(@onnull final KeyEvent keyEvent)79         public void onKeyUp(@Nonnull final KeyEvent keyEvent) {
80             if (DEBUG) {
81                 Log.d(TAG, "EmojiHotKeys.onKeyUp() - " + mName + " - considering " + keyEvent);
82             }
83 
84             final int keyCode = keyEvent.getKeyCode();
85             int metaState = keyEvent.getMetaState();
86             if (KeyEvent.isModifierKey(keyCode)) {
87                  // Try restoring meta stat in case the released key was a modifier.
88                  // I am sure one can come up with scenarios to break this, but it
89                  // seems to work well in practice.
90                  metaState |= mMetaState;
91             }
92 
93             final Pair<Integer, Integer> key = Pair.create(keyCode, metaState);
94             if (mKeySet.contains(key)) {
95                 if (mCanFire) {
96                     if (!keyEvent.isCanceled()) {
97                         if (DEBUG) {
98                             Log.d(TAG, "EmojiHotKeys.onKeyUp() - " + mName + " - firing action");
99                         }
100                         action();
101                     } else {
102                         // This key up event was a part of key combinations and
103                         // should be ignored.
104                         if (DEBUG) {
105                             Log.d(TAG, "EmojiHotKeys.onKeyUp() - " + mName + " - canceled, ignoring action");
106                         }
107                     }
108                     mCanFire = false;
109                 }
110             }
111 
112             if (mCanFire) {
113                 if (DEBUG) {
114                     Log.d(TAG, "EmojiHotKeys.onKeyUp() - " + mName + " - disabling action");
115                 }
116                 mCanFire = false;
117             }
118         }
119 
action()120         protected abstract void action();
121     }
122 
EmojiAltPhysicalKeyDetector(@onnull final Resources resources)123     public EmojiAltPhysicalKeyDetector(@Nonnull final Resources resources) {
124         mHotKeysList = new ArrayList<EmojiHotKeys>();
125 
126         final HotKeySet emojiSwitchSet = parseHotKeys(
127                 resources, R.array.keyboard_switcher_emoji);
128         final EmojiHotKeys emojiHotKeys = new EmojiHotKeys("emoji", emojiSwitchSet) {
129             @Override
130             protected void action() {
131                 final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance();
132                 switcher.onToggleKeyboard(KeyboardSwitcher.KeyboardSwitchState.EMOJI);
133             }
134         };
135         mHotKeysList.add(emojiHotKeys);
136 
137         final HotKeySet symbolsSwitchSet = parseHotKeys(
138                 resources, R.array.keyboard_switcher_symbols_shifted);
139         final EmojiHotKeys symbolsHotKeys = new EmojiHotKeys("symbols", symbolsSwitchSet) {
140             @Override
141             protected void action() {
142                 final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance();
143                 switcher.onToggleKeyboard(KeyboardSwitcher.KeyboardSwitchState.SYMBOLS_SHIFTED);
144             }
145         };
146         mHotKeysList.add(symbolsHotKeys);
147     }
148 
onKeyDown(@onnull final KeyEvent keyEvent)149     public void onKeyDown(@Nonnull final KeyEvent keyEvent) {
150         if (DEBUG) {
151             Log.d(TAG, "onKeyDown(): " + keyEvent);
152         }
153 
154         if (shouldProcessEvent(keyEvent)) {
155             for (EmojiHotKeys hotKeys : mHotKeysList) {
156                 hotKeys.onKeyDown(keyEvent);
157             }
158         }
159     }
160 
onKeyUp(@onnull final KeyEvent keyEvent)161     public void onKeyUp(@Nonnull final KeyEvent keyEvent) {
162         if (DEBUG) {
163             Log.d(TAG, "onKeyUp(): " + keyEvent);
164         }
165 
166         if (shouldProcessEvent(keyEvent)) {
167             for (EmojiHotKeys hotKeys : mHotKeysList) {
168                 hotKeys.onKeyUp(keyEvent);
169             }
170         }
171     }
172 
shouldProcessEvent(@onnull final KeyEvent keyEvent)173     private static boolean shouldProcessEvent(@Nonnull final KeyEvent keyEvent) {
174         if (!Settings.getInstance().getCurrent().mEnableEmojiAltPhysicalKey) {
175             // The feature is disabled.
176             if (DEBUG) {
177                 Log.d(TAG, "shouldProcessEvent(): Disabled");
178             }
179             return false;
180         }
181 
182         return true;
183     }
184 
parseHotKeys( @onnull final Resources resources, final int resourceId)185     private static HotKeySet parseHotKeys(
186             @Nonnull final Resources resources, final int resourceId) {
187         final HotKeySet keySet = new HotKeySet();
188         final String name = resources.getResourceEntryName(resourceId);
189         final String[] values = resources.getStringArray(resourceId);
190         for (int i = 0; values != null && i < values.length; i++) {
191             String[] valuePair = values[i].split(",");
192             if (valuePair.length != 2) {
193                 Log.w(TAG, "Expected 2 integers in " + name + "[" + i + "] : " + values[i]);
194             }
195             try {
196                 final Integer keyCode = Integer.parseInt(valuePair[0]);
197                 final Integer metaState = Integer.parseInt(valuePair[1]);
198                 final Pair<Integer, Integer> key = Pair.create(
199                         keyCode, KeyEvent.normalizeMetaState(metaState));
200                 keySet.add(key);
201             } catch (NumberFormatException e) {
202                 Log.w(TAG, "Failed to parse " + name + "[" + i + "] : " + values[i], e);
203             }
204         }
205         return keySet;
206     }
207 }
208