/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.policy; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.XmlResourceParser; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; import android.view.KeyCharacterMap; import android.view.KeyEvent; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; /** * Manages quick launch shortcuts by: * <li> Keeping the local copy in sync with the database (this is an observer) * <li> Returning a shortcut-matching intent to clients */ class ShortcutManager { private static final String TAG = "ShortcutManager"; private static final String TAG_BOOKMARKS = "bookmarks"; private static final String TAG_BOOKMARK = "bookmark"; private static final String ATTRIBUTE_PACKAGE = "package"; private static final String ATTRIBUTE_CLASS = "class"; private static final String ATTRIBUTE_SHORTCUT = "shortcut"; private static final String ATTRIBUTE_CATEGORY = "category"; private static final String ATTRIBUTE_SHIFT = "shift"; private final SparseArray<ShortcutInfo> mShortcuts = new SparseArray<>(); private final SparseArray<ShortcutInfo> mShiftShortcuts = new SparseArray<>(); private final Context mContext; public ShortcutManager(Context context) { mContext = context; loadShortcuts(); } /** * Gets the shortcut intent for a given keycode+modifier. Make sure you * strip whatever modifier is used for invoking shortcuts (for example, * if 'Sym+A' should invoke a shortcut on 'A', you should strip the * 'Sym' bit from the modifiers before calling this method. * <p> * This will first try an exact match (with modifiers), and then try a * match without modifiers (primary character on a key). * * @param kcm The key character map of the device on which the key was pressed. * @param keyCode The key code. * @param metaState The meta state, omitting any modifiers that were used * to invoke the shortcut. * @return The intent that matches the shortcut, or null if not found. */ public Intent getIntent(KeyCharacterMap kcm, int keyCode, int metaState) { ShortcutInfo shortcut = null; // If the Shift key is pressed, then search for the shift shortcuts. boolean isShiftOn = (metaState & KeyEvent.META_SHIFT_ON) == KeyEvent.META_SHIFT_ON; SparseArray<ShortcutInfo> shortcutMap = isShiftOn ? mShiftShortcuts : mShortcuts; // First try the exact keycode (with modifiers). int shortcutChar = kcm.get(keyCode, metaState); if (shortcutChar != 0) { shortcut = shortcutMap.get(shortcutChar); } // Next try the primary character on that key. if (shortcut == null) { shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode)); if (shortcutChar != 0) { shortcut = shortcutMap.get(shortcutChar); } } return (shortcut != null) ? shortcut.intent : null; } private void loadShortcuts() { PackageManager packageManager = mContext.getPackageManager(); try { XmlResourceParser parser = mContext.getResources().getXml( com.android.internal.R.xml.bookmarks); XmlUtils.beginDocument(parser, TAG_BOOKMARKS); while (true) { XmlUtils.nextElement(parser); if (parser.getEventType() == XmlPullParser.END_DOCUMENT) { break; } if (!TAG_BOOKMARK.equals(parser.getName())) { break; } String packageName = parser.getAttributeValue(null, ATTRIBUTE_PACKAGE); String className = parser.getAttributeValue(null, ATTRIBUTE_CLASS); String shortcutName = parser.getAttributeValue(null, ATTRIBUTE_SHORTCUT); String categoryName = parser.getAttributeValue(null, ATTRIBUTE_CATEGORY); String shiftName = parser.getAttributeValue(null, ATTRIBUTE_SHIFT); if (TextUtils.isEmpty(shortcutName)) { Log.w(TAG, "Unable to get shortcut for: " + packageName + "/" + className); continue; } final int shortcutChar = shortcutName.charAt(0); final boolean isShiftShortcut = (shiftName != null && shiftName.equals("true")); final Intent intent; final String title; if (packageName != null && className != null) { ActivityInfo info = null; ComponentName componentName = new ComponentName(packageName, className); try { info = packageManager.getActivityInfo(componentName, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_UNINSTALLED_PACKAGES); } catch (PackageManager.NameNotFoundException e) { String[] packages = packageManager.canonicalToCurrentPackageNames( new String[] { packageName }); componentName = new ComponentName(packages[0], className); try { info = packageManager.getActivityInfo(componentName, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_UNINSTALLED_PACKAGES); } catch (PackageManager.NameNotFoundException e1) { Log.w(TAG, "Unable to add bookmark: " + packageName + "/" + className, e); continue; } } intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); intent.setComponent(componentName); title = info.loadLabel(packageManager).toString(); } else if (categoryName != null) { intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, categoryName); title = ""; } else { Log.w(TAG, "Unable to add bookmark for shortcut " + shortcutName + ": missing package/class or category attributes"); continue; } ShortcutInfo shortcut = new ShortcutInfo(title, intent); if (isShiftShortcut) { mShiftShortcuts.put(shortcutChar, shortcut); } else { mShortcuts.put(shortcutChar, shortcut); } } } catch (XmlPullParserException e) { Log.w(TAG, "Got exception parsing bookmarks.", e); } catch (IOException e) { Log.w(TAG, "Got exception parsing bookmarks.", e); } } private static final class ShortcutInfo { public final String title; public final Intent intent; public ShortcutInfo(String title, Intent intent) { this.title = title; this.intent = intent; } } }