1d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh/* 2d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * 3d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * Licensed under the Apache License, Version 2.0 (the "License"); 4d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * you may not use this file except in compliance with the License. 5d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * You may obtain a copy of the License at 6d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * 7d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * http://www.apache.org/licenses/LICENSE-2.0 8d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * 9d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * Unless required by applicable law or agreed to in writing, software 10d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * distributed under the License is distributed on an "AS IS" BASIS, 11d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * See the License for the specific language governing permissions and 13d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * limitations under the License. 14d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh */ 15d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh 16d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Kohpackage com.android.internal.policy.impl; 17d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh 18d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Kohimport android.content.ComponentName; 19d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Kohimport android.content.Context; 20d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Kohimport android.content.Intent; 21d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Kohimport android.content.res.Resources; 22d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Kohimport android.content.res.XmlResourceParser; 23d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Kohimport android.os.UserHandle; 24d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Kohimport android.util.Log; 25d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Kohimport android.util.SparseArray; 26d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Kohimport android.view.KeyEvent; 27d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh 28d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Kohimport com.android.internal.util.XmlUtils; 29d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh 30d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Kohimport org.xmlpull.v1.XmlPullParserException; 31d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh 32d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Kohimport java.io.IOException; 33d7c0c2ee45dd5367ac7863517de2e385fbd424f3Tim Kilbournimport java.io.PrintWriter; 34d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh 35d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh/** 36d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * Stores a mapping of global keys. 37d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * <p> 38d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * A global key will NOT go to the foreground application and instead only ever be sent via targeted 39d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * broadcast to the specified component. The action of the intent will be 40d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * {@link Intent#ACTION_GLOBAL_BUTTON} and the KeyEvent will be included in the intent with 41d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * {@link Intent#EXTRA_KEY_EVENT}. 42d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh */ 43d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Kohfinal class GlobalKeyManager { 44d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh 45d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh private static final String TAG = "GlobalKeyManager"; 46d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh 47d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh private static final String TAG_GLOBAL_KEYS = "global_keys"; 48d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh private static final String ATTR_VERSION = "version"; 49d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh private static final String TAG_KEY = "key"; 50d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh private static final String ATTR_KEY_CODE = "keyCode"; 51d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh private static final String ATTR_COMPONENT = "component"; 52d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh 53d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh private static final int GLOBAL_KEY_FILE_VERSION = 1; 54d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh 55d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh private SparseArray<ComponentName> mKeyMapping; 56d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh 57d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh public GlobalKeyManager(Context context) { 58d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh mKeyMapping = new SparseArray<ComponentName>(); 59d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh loadGlobalKeys(context); 60d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh } 61d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh 62d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh /** 63d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * Broadcasts an intent if the keycode is part of the global key mapping. 64d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * 65d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * @param context context used to broadcast the event 66d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * @param keyCode keyCode which triggered this function 67d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * @param event keyEvent which trigged this function 68d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * @return {@code true} if this was handled 69d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh */ 70d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh boolean handleGlobalKey(Context context, int keyCode, KeyEvent event) { 71d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh if (mKeyMapping.size() > 0) { 72d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh ComponentName component = mKeyMapping.get(keyCode); 73d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh if (component != null) { 74d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh Intent intent = new Intent(Intent.ACTION_GLOBAL_BUTTON) 75d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh .setComponent(component) 76d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh .putExtra(Intent.EXTRA_KEY_EVENT, event); 77d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh context.sendBroadcastAsUser(intent, UserHandle.CURRENT, null); 78d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh return true; 79d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh } 80d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh } 81d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh return false; 82d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh } 83d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh 84d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh /** 85d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh * Returns {@code true} if the key will be handled globally. 86d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh */ 87d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh boolean shouldHandleGlobalKey(int keyCode, KeyEvent event) { 88d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh return mKeyMapping.get(keyCode) != null; 89d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh } 90d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh 91d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh private void loadGlobalKeys(Context context) { 92d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh XmlResourceParser parser = null; 93d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh try { 94d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh parser = context.getResources().getXml(com.android.internal.R.xml.global_keys); 95d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh XmlUtils.beginDocument(parser, TAG_GLOBAL_KEYS); 96d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh int version = parser.getAttributeIntValue(null, ATTR_VERSION, 0); 97d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh if (GLOBAL_KEY_FILE_VERSION == version) { 98d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh while (true) { 99d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh XmlUtils.nextElement(parser); 100d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh String element = parser.getName(); 101d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh if (element == null) { 102d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh break; 103d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh } 104d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh if (TAG_KEY.equals(element)) { 105d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh String keyCodeName = parser.getAttributeValue(null, ATTR_KEY_CODE); 106d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh String componentName = parser.getAttributeValue(null, ATTR_COMPONENT); 107d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh int keyCode = KeyEvent.keyCodeFromString(keyCodeName); 108d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { 109d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh mKeyMapping.put(keyCode, ComponentName.unflattenFromString( 110d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh componentName)); 111d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh } 112d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh } 113d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh } 114d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh } 115d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh } catch (Resources.NotFoundException e) { 116d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh Log.w(TAG, "global keys file not found", e); 117d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh } catch (XmlPullParserException e) { 118d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh Log.w(TAG, "XML parser exception reading global keys file", e); 119d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh } catch (IOException e) { 120d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh Log.w(TAG, "I/O exception reading global keys file", e); 121d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh } finally { 122d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh if (parser != null) { 123d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh parser.close(); 124d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh } 125d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh } 126d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh } 127d7c0c2ee45dd5367ac7863517de2e385fbd424f3Tim Kilbourn 128d7c0c2ee45dd5367ac7863517de2e385fbd424f3Tim Kilbourn public void dump(String prefix, PrintWriter pw) { 129d7c0c2ee45dd5367ac7863517de2e385fbd424f3Tim Kilbourn final int numKeys = mKeyMapping.size(); 130d7c0c2ee45dd5367ac7863517de2e385fbd424f3Tim Kilbourn if (numKeys == 0) { 131d7c0c2ee45dd5367ac7863517de2e385fbd424f3Tim Kilbourn pw.print(prefix); pw.println("mKeyMapping.size=0"); 132d7c0c2ee45dd5367ac7863517de2e385fbd424f3Tim Kilbourn return; 133d7c0c2ee45dd5367ac7863517de2e385fbd424f3Tim Kilbourn } 134d7c0c2ee45dd5367ac7863517de2e385fbd424f3Tim Kilbourn pw.print(prefix); pw.println("mKeyMapping={"); 135d7c0c2ee45dd5367ac7863517de2e385fbd424f3Tim Kilbourn for (int i = 0; i < numKeys; ++i) { 136d7c0c2ee45dd5367ac7863517de2e385fbd424f3Tim Kilbourn pw.print(" "); 137d7c0c2ee45dd5367ac7863517de2e385fbd424f3Tim Kilbourn pw.print(prefix); 138d7c0c2ee45dd5367ac7863517de2e385fbd424f3Tim Kilbourn pw.print(KeyEvent.keyCodeToString(mKeyMapping.keyAt(i))); 139d7c0c2ee45dd5367ac7863517de2e385fbd424f3Tim Kilbourn pw.print("="); 140d7c0c2ee45dd5367ac7863517de2e385fbd424f3Tim Kilbourn pw.println(mKeyMapping.valueAt(i).flattenToString()); 141d7c0c2ee45dd5367ac7863517de2e385fbd424f3Tim Kilbourn } 142d7c0c2ee45dd5367ac7863517de2e385fbd424f3Tim Kilbourn pw.print(prefix); pw.println("}"); 143d7c0c2ee45dd5367ac7863517de2e385fbd424f3Tim Kilbourn } 144d378ad74c61b9bd3fdaa32951c4c470fcd579adeJustin Koh} 145