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