ShortcutManager.java revision 720e0d2cb774f74617c90e2e1da54c107a34b79b
1/* 2 * Copyright (C) 2007 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 17package com.android.server.policy; 18 19import android.content.ComponentName; 20import android.content.Context; 21import android.content.Intent; 22import android.content.pm.ActivityInfo; 23import android.content.pm.PackageManager; 24import android.content.res.XmlResourceParser; 25import android.text.TextUtils; 26import android.util.Log; 27import android.util.SparseArray; 28import android.view.KeyCharacterMap; 29import android.view.KeyEvent; 30import com.android.internal.util.XmlUtils; 31import org.xmlpull.v1.XmlPullParser; 32import org.xmlpull.v1.XmlPullParserException; 33 34import java.io.IOException; 35 36/** 37 * Manages quick launch shortcuts by: 38 * <li> Keeping the local copy in sync with the database (this is an observer) 39 * <li> Returning a shortcut-matching intent to clients 40 */ 41class ShortcutManager { 42 private static final String TAG = "ShortcutManager"; 43 44 private static final String TAG_BOOKMARKS = "bookmarks"; 45 private static final String TAG_BOOKMARK = "bookmark"; 46 47 private static final String ATTRIBUTE_PACKAGE = "package"; 48 private static final String ATTRIBUTE_CLASS = "class"; 49 private static final String ATTRIBUTE_SHORTCUT = "shortcut"; 50 private static final String ATTRIBUTE_CATEGORY = "category"; 51 private static final String ATTRIBUTE_SHIFT = "shift"; 52 53 private final SparseArray<ShortcutInfo> mShortcuts = new SparseArray<>(); 54 private final SparseArray<ShortcutInfo> mShiftShortcuts = new SparseArray<>(); 55 56 private final Context mContext; 57 58 public ShortcutManager(Context context) { 59 mContext = context; 60 loadShortcuts(); 61 } 62 63 /** 64 * Gets the shortcut intent for a given keycode+modifier. Make sure you 65 * strip whatever modifier is used for invoking shortcuts (for example, 66 * if 'Sym+A' should invoke a shortcut on 'A', you should strip the 67 * 'Sym' bit from the modifiers before calling this method. 68 * <p> 69 * This will first try an exact match (with modifiers), and then try a 70 * match without modifiers (primary character on a key). 71 * 72 * @param kcm The key character map of the device on which the key was pressed. 73 * @param keyCode The key code. 74 * @param metaState The meta state, omitting any modifiers that were used 75 * to invoke the shortcut. 76 * @return The intent that matches the shortcut, or null if not found. 77 */ 78 public Intent getIntent(KeyCharacterMap kcm, int keyCode, int metaState) { 79 ShortcutInfo shortcut = null; 80 81 // If the Shift key is preesed, then search for the shift shortcuts. 82 boolean isShiftOn = (metaState & KeyEvent.META_SHIFT_ON) == KeyEvent.META_SHIFT_ON; 83 SparseArray<ShortcutInfo> shortcutMap = isShiftOn ? mShiftShortcuts : mShortcuts; 84 85 // First try the exact keycode (with modifiers). 86 int shortcutChar = kcm.get(keyCode, metaState); 87 if (shortcutChar != 0) { 88 shortcut = shortcutMap.get(shortcutChar); 89 } 90 91 // Next try the primary character on that key. 92 if (shortcut == null) { 93 shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode)); 94 if (shortcutChar != 0) { 95 shortcut = shortcutMap.get(shortcutChar); 96 } 97 } 98 99 return (shortcut != null) ? shortcut.intent : null; 100 } 101 102 private void loadShortcuts() { 103 PackageManager packageManager = mContext.getPackageManager(); 104 try { 105 XmlResourceParser parser = mContext.getResources().getXml( 106 com.android.internal.R.xml.bookmarks); 107 XmlUtils.beginDocument(parser, TAG_BOOKMARKS); 108 109 while (true) { 110 XmlUtils.nextElement(parser); 111 112 if (parser.getEventType() == XmlPullParser.END_DOCUMENT) { 113 break; 114 } 115 116 if (!TAG_BOOKMARK.equals(parser.getName())) { 117 break; 118 } 119 120 String packageName = parser.getAttributeValue(null, ATTRIBUTE_PACKAGE); 121 String className = parser.getAttributeValue(null, ATTRIBUTE_CLASS); 122 String shortcutName = parser.getAttributeValue(null, ATTRIBUTE_SHORTCUT); 123 String categoryName = parser.getAttributeValue(null, ATTRIBUTE_CATEGORY); 124 String shiftName = parser.getAttributeValue(null, ATTRIBUTE_SHIFT); 125 126 if (TextUtils.isEmpty(shortcutName)) { 127 Log.w(TAG, "Unable to get shortcut for: " + packageName + "/" + className); 128 continue; 129 } 130 131 final int shortcutChar = shortcutName.charAt(0); 132 final boolean isShiftShortcut = (shiftName != null && shiftName.equals("true")); 133 134 final Intent intent; 135 final String title; 136 if (packageName != null && className != null) { 137 ActivityInfo info = null; 138 ComponentName componentName = new ComponentName(packageName, className); 139 try { 140 info = packageManager.getActivityInfo(componentName, 141 PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE 142 | PackageManager.MATCH_UNINSTALLED_PACKAGES); 143 } catch (PackageManager.NameNotFoundException e) { 144 String[] packages = packageManager.canonicalToCurrentPackageNames( 145 new String[] { packageName }); 146 componentName = new ComponentName(packages[0], className); 147 try { 148 info = packageManager.getActivityInfo(componentName, 149 PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE 150 | PackageManager.MATCH_UNINSTALLED_PACKAGES); 151 } catch (PackageManager.NameNotFoundException e1) { 152 Log.w(TAG, "Unable to add bookmark: " + packageName 153 + "/" + className, e); 154 continue; 155 } 156 } 157 158 intent = new Intent(Intent.ACTION_MAIN); 159 intent.addCategory(Intent.CATEGORY_LAUNCHER); 160 intent.setComponent(componentName); 161 title = info.loadLabel(packageManager).toString(); 162 } else if (categoryName != null) { 163 intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, categoryName); 164 title = ""; 165 } else { 166 Log.w(TAG, "Unable to add bookmark for shortcut " + shortcutName 167 + ": missing package/class or category attributes"); 168 continue; 169 } 170 171 ShortcutInfo shortcut = new ShortcutInfo(title, intent); 172 if (isShiftShortcut) { 173 mShiftShortcuts.put(shortcutChar, shortcut); 174 } else { 175 mShortcuts.put(shortcutChar, shortcut); 176 } 177 } 178 } catch (XmlPullParserException e) { 179 Log.w(TAG, "Got exception parsing bookmarks.", e); 180 } catch (IOException e) { 181 Log.w(TAG, "Got exception parsing bookmarks.", e); 182 } 183 } 184 185 private static final class ShortcutInfo { 186 public final String title; 187 public final Intent intent; 188 189 public ShortcutInfo(String title, Intent intent) { 190 this.title = title; 191 this.intent = intent; 192 } 193 } 194} 195