1 /* 2 * Copyright (C) 2010 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 17 package com.android.quicksearchbox; 18 19 import android.app.SearchManager; 20 import android.content.ComponentName; 21 import android.content.ContentProvider; 22 import android.content.ContentValues; 23 import android.content.UriMatcher; 24 import android.content.pm.PackageManager; 25 import android.database.Cursor; 26 import android.net.Uri; 27 import android.os.Binder; 28 import android.text.TextUtils; 29 import android.util.Log; 30 31 /** 32 * Handles broadcast intents for adding shortcuts to QSB. 33 */ 34 public class ShortcutsProvider extends ContentProvider { 35 36 private static final boolean DBG = false; 37 private static final String TAG = "QSB.ExternalShortcutReceiver"; 38 39 public static final String EXTRA_SHORTCUT_SOURCE = "shortcut_source"; 40 41 private static final int URI_CODE_SHORTCUTS = 0; 42 43 private UriMatcher mUriMatcher; 44 45 @Override onCreate()46 public boolean onCreate() { 47 mUriMatcher = buildUriMatcher(); 48 return true; 49 } 50 buildUriMatcher()51 private UriMatcher buildUriMatcher() { 52 String authority = getAuthority(); 53 UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); 54 matcher.addURI(authority, "shortcuts", URI_CODE_SHORTCUTS); 55 return matcher; 56 } 57 getAuthority()58 private String getAuthority() { 59 return getContext().getPackageName() + ".shortcuts"; 60 } 61 62 @Override getType(Uri uri)63 public String getType(Uri uri) { 64 switch (mUriMatcher.match(uri)) { 65 case URI_CODE_SHORTCUTS: 66 return SearchManager.SUGGEST_MIME_TYPE; 67 default: 68 throw new IllegalArgumentException("Unknown URI: " + uri); 69 } 70 } 71 72 @Override insert(Uri uri, ContentValues values)73 public Uri insert(Uri uri, ContentValues values) { 74 switch (mUriMatcher.match(uri)) { 75 case URI_CODE_SHORTCUTS: 76 addShortcut(values); 77 return null; 78 default: 79 throw new IllegalArgumentException("Unknown URI: " + uri); 80 } 81 } 82 83 @Override delete(Uri uri, String selection, String[] selectionArgs)84 public int delete(Uri uri, String selection, String[] selectionArgs) { 85 throw new UnsupportedOperationException(); 86 } 87 88 @Override query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)89 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 90 String sortOrder) { 91 throw new UnsupportedOperationException(); 92 } 93 94 @Override update(Uri uri, ContentValues values, String selection, String[] selectionArgs)95 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 96 throw new UnsupportedOperationException(); 97 } 98 addShortcut(final ContentValues shortcut)99 private void addShortcut(final ContentValues shortcut) { 100 String sourceName = shortcut.getAsString(EXTRA_SHORTCUT_SOURCE); 101 if (TextUtils.isEmpty(sourceName)) { 102 Log.e(TAG, "Missing " + EXTRA_SHORTCUT_SOURCE); 103 return; 104 } 105 final ComponentName sourceComponent = ComponentName.unflattenFromString(sourceName); 106 if (!checkCallingPackage(sourceComponent.getPackageName())) { 107 Log.w(TAG, "Got shortcut for " + sourceComponent + " from a different process"); 108 return; 109 } 110 111 getQsbApplication().runOnUiThread(new Runnable() { 112 public void run() { 113 storeShortcut(sourceComponent, shortcut); 114 } 115 }); 116 } 117 118 // Called on the main thread storeShortcut(ComponentName sourceComponent, ContentValues shortcut)119 private void storeShortcut(ComponentName sourceComponent, ContentValues shortcut) { 120 if (DBG) Log.d(TAG, "Adding (PID: " + Binder.getCallingPid() + "): " + shortcut); 121 122 Source source = getCorpora().getSource(sourceComponent.flattenToShortString()); 123 if (source == null) { 124 Log.w(TAG, "Unknown shortcut source " + sourceComponent); 125 return; 126 } 127 128 String userQuery = shortcut.getAsString(SearchManager.USER_QUERY); 129 if (userQuery == null) userQuery = ""; 130 131 ListSuggestionCursor cursor = new ListSuggestionCursor(userQuery); 132 cursor.add(makeSuggestion(source, shortcut)); 133 getShortcutRepository().reportClick(cursor, 0); 134 } 135 checkCallingPackage(String packageName)136 private boolean checkCallingPackage(String packageName) { 137 int callingUid = Binder.getCallingUid(); 138 PackageManager pm = getContext().getPackageManager(); 139 String[] uidPkgs = pm.getPackagesForUid(callingUid); 140 if (uidPkgs == null) return false; 141 for (String uidPkg : uidPkgs) { 142 if (packageName.equals(uidPkg)) return true; 143 } 144 return false; 145 } 146 makeSuggestion(Source source, ContentValues shortcut)147 private SuggestionData makeSuggestion(Source source, ContentValues shortcut) { 148 String format = shortcut.getAsString(SearchManager.SUGGEST_COLUMN_FORMAT); 149 String text1 = shortcut.getAsString(SearchManager.SUGGEST_COLUMN_TEXT_1); 150 String text2 = shortcut.getAsString(SearchManager.SUGGEST_COLUMN_TEXT_2); 151 String text2Url = shortcut.getAsString(SearchManager.SUGGEST_COLUMN_TEXT_2_URL); 152 String icon1 = shortcut.getAsString(SearchManager.SUGGEST_COLUMN_ICON_1); 153 String icon2 = shortcut.getAsString(SearchManager.SUGGEST_COLUMN_ICON_2); 154 String shortcutId = shortcut.getAsString(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID); 155 boolean spinnerWhileRefreshing = unboxBoolean( 156 shortcut.getAsBoolean(SearchManager.SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING), 157 false); 158 String intentAction = shortcut.getAsString(SearchManager.SUGGEST_COLUMN_INTENT_ACTION); 159 String intentData = shortcut.getAsString(SearchManager.SUGGEST_COLUMN_INTENT_DATA); 160 String intentExtraData = 161 shortcut.getAsString(SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA); 162 String query = shortcut.getAsString(SearchManager.SUGGEST_COLUMN_QUERY); 163 164 SuggestionData suggestion = new SuggestionData(source); 165 suggestion.setFormat(format); 166 suggestion.setText1(text1); 167 suggestion.setText2(text2); 168 suggestion.setText2Url(text2Url); 169 suggestion.setIcon1(icon1); 170 suggestion.setIcon2(icon2); 171 suggestion.setShortcutId(shortcutId); 172 suggestion.setSpinnerWhileRefreshing(spinnerWhileRefreshing); 173 suggestion.setIntentAction(intentAction); 174 suggestion.setIntentData(intentData); 175 suggestion.setIntentExtraData(intentExtraData); 176 suggestion.setSuggestionQuery(query); 177 return suggestion; 178 } 179 unboxBoolean(Boolean value, boolean defValue)180 private static boolean unboxBoolean(Boolean value, boolean defValue) { 181 return value == null ? defValue : value; 182 } 183 getQsbApplication()184 private QsbApplication getQsbApplication() { 185 return QsbApplication.get(getContext()); 186 } 187 getShortcutRepository()188 private ShortcutRepository getShortcutRepository() { 189 return getQsbApplication().getShortcutRepository(); 190 } 191 getCorpora()192 private Corpora getCorpora() { 193 return getQsbApplication().getCorpora(); 194 } 195 196 } 197