11f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project/* 21f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project 31f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * 41f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 51f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * you may not use this file except in compliance with the License. 61f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * You may obtain a copy of the License at 71f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * 81f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 91f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * 101f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 111f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 121f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * See the License for the specific language governing permissions and 141f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * limitations under the License. 151f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project */ 161f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project 17b10e33ff804a831c71be9303146cea892b9aeb5dJorim Jaggipackage com.android.server.policy; 181f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project 19683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport android.content.ComponentName; 201f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Projectimport android.content.Context; 211f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Projectimport android.content.Intent; 22683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport android.content.pm.ActivityInfo; 23683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport android.content.pm.PackageManager; 24683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport android.content.res.XmlResourceParser; 25683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport android.text.TextUtils; 261f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Projectimport android.util.Log; 271f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Projectimport android.util.SparseArray; 281f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Projectimport android.view.KeyCharacterMap; 29683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport com.android.internal.util.XmlUtils; 30683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport org.xmlpull.v1.XmlPullParser; 31683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport org.xmlpull.v1.XmlPullParserException; 321f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project 33683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavimport java.io.IOException; 341f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project 351f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project/** 361f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * Manages quick launch shortcuts by: 371f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * <li> Keeping the local copy in sync with the database (this is an observer) 381f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * <li> Returning a shortcut-matching intent to clients 391f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project */ 40683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslavclass ShortcutManager { 411f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project private static final String TAG = "ShortcutManager"; 421f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project 43683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final String TAG_BOOKMARKS = "bookmarks"; 44683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final String TAG_BOOKMARK = "bookmark"; 451f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project 46683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final String ATTRIBUTE_PACKAGE = "package"; 47683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final String ATTRIBUTE_CLASS = "class"; 48683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final String ATTRIBUTE_SHORTCUT = "shortcut"; 49683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final String ATTRIBUTE_CATEGORY = "category"; 501f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project 51683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private final SparseArray<ShortcutInfo> mShortcuts = new SparseArray<>(); 52683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 53683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private final Context mContext; 541f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project 55683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public ShortcutManager(Context context) { 56683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mContext = context; 57683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav loadShortcuts(); 58683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 59683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 601f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project /** 611f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * Gets the shortcut intent for a given keycode+modifier. Make sure you 621f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * strip whatever modifier is used for invoking shortcuts (for example, 631f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * if 'Sym+A' should invoke a shortcut on 'A', you should strip the 641f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * 'Sym' bit from the modifiers before calling this method. 651f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * <p> 661f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * This will first try an exact match (with modifiers), and then try a 671f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * match without modifiers (primary character on a key). 681f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * 69c1fb48d77ce2db2ca3af1dd2eeb91dc1b0f81b46Jeff Brown * @param kcm The key character map of the device on which the key was pressed. 70c1fb48d77ce2db2ca3af1dd2eeb91dc1b0f81b46Jeff Brown * @param keyCode The key code. 71c1fb48d77ce2db2ca3af1dd2eeb91dc1b0f81b46Jeff Brown * @param metaState The meta state, omitting any modifiers that were used 72c1fb48d77ce2db2ca3af1dd2eeb91dc1b0f81b46Jeff Brown * to invoke the shortcut. 731f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * @return The intent that matches the shortcut, or null if not found. 741f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project */ 75c1fb48d77ce2db2ca3af1dd2eeb91dc1b0f81b46Jeff Brown public Intent getIntent(KeyCharacterMap kcm, int keyCode, int metaState) { 76683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav ShortcutInfo shortcut = null; 771f2451007c660091b7b090c1ea332f9044515d2dJeff Brown 78c1fb48d77ce2db2ca3af1dd2eeb91dc1b0f81b46Jeff Brown // First try the exact keycode (with modifiers). 79683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav int shortcutChar = kcm.get(keyCode, metaState); 80683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (shortcutChar != 0) { 81683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav shortcut = mShortcuts.get(shortcutChar); 82c1fb48d77ce2db2ca3af1dd2eeb91dc1b0f81b46Jeff Brown } 83c1fb48d77ce2db2ca3af1dd2eeb91dc1b0f81b46Jeff Brown 84c1fb48d77ce2db2ca3af1dd2eeb91dc1b0f81b46Jeff Brown // Next try the primary character on that key. 85683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (shortcut == null) { 86683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode)); 87683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (shortcutChar != 0) { 88683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav shortcut = mShortcuts.get(shortcutChar); 89c1fb48d77ce2db2ca3af1dd2eeb91dc1b0f81b46Jeff Brown } 90c1fb48d77ce2db2ca3af1dd2eeb91dc1b0f81b46Jeff Brown } 91c1fb48d77ce2db2ca3af1dd2eeb91dc1b0f81b46Jeff Brown 92683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav return (shortcut != null) ? shortcut.intent : null; 931f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project } 941f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project 95683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private void loadShortcuts() { 96683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav PackageManager packageManager = mContext.getPackageManager(); 97683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav try { 98683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav XmlResourceParser parser = mContext.getResources().getXml( 99683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav com.android.internal.R.xml.bookmarks); 100683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav XmlUtils.beginDocument(parser, TAG_BOOKMARKS); 101683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 102683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav while (true) { 103683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav XmlUtils.nextElement(parser); 104683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 105683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (parser.getEventType() == XmlPullParser.END_DOCUMENT) { 106683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav break; 107683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 108683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 109683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (!TAG_BOOKMARK.equals(parser.getName())) { 110683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav break; 111683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 112683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 113683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav String packageName = parser.getAttributeValue(null, ATTRIBUTE_PACKAGE); 114683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav String className = parser.getAttributeValue(null, ATTRIBUTE_CLASS); 115683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav String shortcutName = parser.getAttributeValue(null, ATTRIBUTE_SHORTCUT); 116683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav String categoryName = parser.getAttributeValue(null, ATTRIBUTE_CATEGORY); 117683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 118683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (TextUtils.isEmpty(shortcutName)) { 119683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Log.w(TAG, "Unable to get shortcut for: " + packageName + "/" + className); 120683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav continue; 121683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 122683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 123683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav final int shortcutChar = shortcutName.charAt(0); 124683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 125683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav final Intent intent; 126683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav final String title; 127683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav if (packageName != null && className != null) { 128683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav ActivityInfo info = null; 129683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav ComponentName componentName = new ComponentName(packageName, className); 130683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav try { 131683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav info = packageManager.getActivityInfo(componentName, 0); 132683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } catch (PackageManager.NameNotFoundException e) { 133683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav String[] packages = packageManager.canonicalToCurrentPackageNames( 134683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav new String[] { packageName }); 135683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav componentName = new ComponentName(packages[0], className); 136683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav try { 137683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav info = packageManager.getActivityInfo(componentName, 0); 138683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } catch (PackageManager.NameNotFoundException e1) { 139683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Log.w(TAG, "Unable to add bookmark: " + packageName 140683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav + "/" + className, e); 141683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav continue; 142683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 143683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 144683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 145683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav intent = new Intent(Intent.ACTION_MAIN); 146683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav intent.addCategory(Intent.CATEGORY_LAUNCHER); 147683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav intent.setComponent(componentName); 148683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav title = info.loadLabel(packageManager).toString(); 149683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } else if (categoryName != null) { 150683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, categoryName); 151683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav title = ""; 152683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } else { 153683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Log.w(TAG, "Unable to add bookmark for shortcut " + shortcutName 154683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav + ": missing package/class or category attributes"); 155683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav continue; 156683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 157683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 158683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav ShortcutInfo shortcut = new ShortcutInfo(title, intent); 159683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav mShortcuts.put(shortcutChar, shortcut); 160683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 161683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } catch (XmlPullParserException e) { 162683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Log.w(TAG, "Got exception parsing bookmarks.", e); 163683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } catch (IOException e) { 164683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav Log.w(TAG, "Got exception parsing bookmarks.", e); 165683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 166683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 167683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 168683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav private static final class ShortcutInfo { 169683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public final String title; 170683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public final Intent intent; 171683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav 172683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav public ShortcutInfo(String title, Intent intent) { 173683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav this.title = title; 174683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav this.intent = intent; 175683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 176683914bfb13908bf380a25258cd45bcf43f13dc9Svetoslav } 1771f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project} 178