blob: 76f56bc5bcc67684a65c486211408f34611febd3 [file] [log] [blame]
The Android Open Source Project1f838aa2009-03-03 19:32:13 -08001/*
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
Jorim Jaggib10e33f2015-02-04 21:57:40 +010017package com.android.server.policy;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080018
Svetoslav683914b2015-01-15 14:22:26 -080019import android.content.ComponentName;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080020import android.content.Context;
21import android.content.Intent;
Svetoslav683914b2015-01-15 14:22:26 -080022import android.content.pm.ActivityInfo;
23import android.content.pm.PackageManager;
24import android.content.res.XmlResourceParser;
25import android.text.TextUtils;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080026import android.util.Log;
27import android.util.SparseArray;
28import android.view.KeyCharacterMap;
Svetoslav683914b2015-01-15 14:22:26 -080029import com.android.internal.util.XmlUtils;
30import org.xmlpull.v1.XmlPullParser;
31import org.xmlpull.v1.XmlPullParserException;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080032
Svetoslav683914b2015-01-15 14:22:26 -080033import java.io.IOException;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080034
35/**
36 * Manages quick launch shortcuts by:
37 * <li> Keeping the local copy in sync with the database (this is an observer)
38 * <li> Returning a shortcut-matching intent to clients
39 */
Svetoslav683914b2015-01-15 14:22:26 -080040class ShortcutManager {
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080041 private static final String TAG = "ShortcutManager";
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080042
Svetoslav683914b2015-01-15 14:22:26 -080043 private static final String TAG_BOOKMARKS = "bookmarks";
44 private static final String TAG_BOOKMARK = "bookmark";
45
46 private static final String ATTRIBUTE_PACKAGE = "package";
47 private static final String ATTRIBUTE_CLASS = "class";
48 private static final String ATTRIBUTE_SHORTCUT = "shortcut";
49 private static final String ATTRIBUTE_CATEGORY = "category";
50
51 private final SparseArray<ShortcutInfo> mShortcuts = new SparseArray<>();
52
53 private final Context mContext;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080054
Svetoslav683914b2015-01-15 14:22:26 -080055 public ShortcutManager(Context context) {
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080056 mContext = context;
Svetoslav683914b2015-01-15 14:22:26 -080057 loadShortcuts();
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080058 }
59
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080060 /**
61 * Gets the shortcut intent for a given keycode+modifier. Make sure you
62 * strip whatever modifier is used for invoking shortcuts (for example,
63 * if 'Sym+A' should invoke a shortcut on 'A', you should strip the
64 * 'Sym' bit from the modifiers before calling this method.
65 * <p>
66 * This will first try an exact match (with modifiers), and then try a
67 * match without modifiers (primary character on a key).
68 *
Jeff Brownc1fb48d2010-12-08 16:52:09 -080069 * @param kcm The key character map of the device on which the key was pressed.
70 * @param keyCode The key code.
71 * @param metaState The meta state, omitting any modifiers that were used
72 * to invoke the shortcut.
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080073 * @return The intent that matches the shortcut, or null if not found.
74 */
Jeff Brownc1fb48d2010-12-08 16:52:09 -080075 public Intent getIntent(KeyCharacterMap kcm, int keyCode, int metaState) {
Svetoslav683914b2015-01-15 14:22:26 -080076 ShortcutInfo shortcut = null;
Jeff Brown1f245102010-11-18 20:53:46 -080077
Jeff Brownc1fb48d2010-12-08 16:52:09 -080078 // First try the exact keycode (with modifiers).
Svetoslav683914b2015-01-15 14:22:26 -080079 int shortcutChar = kcm.get(keyCode, metaState);
80 if (shortcutChar != 0) {
81 shortcut = mShortcuts.get(shortcutChar);
Jeff Brownc1fb48d2010-12-08 16:52:09 -080082 }
83
84 // Next try the primary character on that key.
Svetoslav683914b2015-01-15 14:22:26 -080085 if (shortcut == null) {
86 shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode));
87 if (shortcutChar != 0) {
88 shortcut = mShortcuts.get(shortcutChar);
Jeff Brownc1fb48d2010-12-08 16:52:09 -080089 }
90 }
91
Svetoslav683914b2015-01-15 14:22:26 -080092 return (shortcut != null) ? shortcut.intent : null;
The Android Open Source Project1f838aa2009-03-03 19:32:13 -080093 }
94
Svetoslav683914b2015-01-15 14:22:26 -080095 private void loadShortcuts() {
96 PackageManager packageManager = mContext.getPackageManager();
97 try {
98 XmlResourceParser parser = mContext.getResources().getXml(
99 com.android.internal.R.xml.bookmarks);
100 XmlUtils.beginDocument(parser, TAG_BOOKMARKS);
101
102 while (true) {
103 XmlUtils.nextElement(parser);
104
105 if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
106 break;
107 }
108
109 if (!TAG_BOOKMARK.equals(parser.getName())) {
110 break;
111 }
112
113 String packageName = parser.getAttributeValue(null, ATTRIBUTE_PACKAGE);
114 String className = parser.getAttributeValue(null, ATTRIBUTE_CLASS);
115 String shortcutName = parser.getAttributeValue(null, ATTRIBUTE_SHORTCUT);
116 String categoryName = parser.getAttributeValue(null, ATTRIBUTE_CATEGORY);
117
118 if (TextUtils.isEmpty(shortcutName)) {
119 Log.w(TAG, "Unable to get shortcut for: " + packageName + "/" + className);
120 continue;
121 }
122
123 final int shortcutChar = shortcutName.charAt(0);
124
125 final Intent intent;
126 final String title;
127 if (packageName != null && className != null) {
128 ActivityInfo info = null;
129 ComponentName componentName = new ComponentName(packageName, className);
130 try {
131 info = packageManager.getActivityInfo(componentName, 0);
132 } catch (PackageManager.NameNotFoundException e) {
133 String[] packages = packageManager.canonicalToCurrentPackageNames(
134 new String[] { packageName });
135 componentName = new ComponentName(packages[0], className);
136 try {
137 info = packageManager.getActivityInfo(componentName, 0);
138 } catch (PackageManager.NameNotFoundException e1) {
139 Log.w(TAG, "Unable to add bookmark: " + packageName
140 + "/" + className, e);
141 continue;
142 }
143 }
144
145 intent = new Intent(Intent.ACTION_MAIN);
146 intent.addCategory(Intent.CATEGORY_LAUNCHER);
147 intent.setComponent(componentName);
148 title = info.loadLabel(packageManager).toString();
149 } else if (categoryName != null) {
150 intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, categoryName);
151 title = "";
152 } else {
153 Log.w(TAG, "Unable to add bookmark for shortcut " + shortcutName
154 + ": missing package/class or category attributes");
155 continue;
156 }
157
158 ShortcutInfo shortcut = new ShortcutInfo(title, intent);
159 mShortcuts.put(shortcutChar, shortcut);
160 }
161 } catch (XmlPullParserException e) {
162 Log.w(TAG, "Got exception parsing bookmarks.", e);
163 } catch (IOException e) {
164 Log.w(TAG, "Got exception parsing bookmarks.", e);
165 }
166 }
167
168 private static final class ShortcutInfo {
169 public final String title;
170 public final Intent intent;
171
172 public ShortcutInfo(String title, Intent intent) {
173 this.title = title;
174 this.intent = intent;
175 }
176 }
The Android Open Source Project1f838aa2009-03-03 19:32:13 -0800177}