Home | History | Annotate | Download | only in quicksearchbox
      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
     46     public boolean onCreate() {
     47         mUriMatcher = buildUriMatcher();
     48         return true;
     49     }
     50 
     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 
     58     private String getAuthority() {
     59         return getContext().getPackageName() + ".shortcuts";
     60     }
     61 
     62     @Override
     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
     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
     84     public int delete(Uri uri, String selection, String[] selectionArgs) {
     85         throw new UnsupportedOperationException();
     86     }
     87 
     88     @Override
     89     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
     90             String sortOrder) {
     91         throw new UnsupportedOperationException();
     92     }
     93 
     94     @Override
     95     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
     96         throw new UnsupportedOperationException();
     97     }
     98 
     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
    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 
    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 
    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 
    180     private static boolean unboxBoolean(Boolean value, boolean defValue) {
    181         return value == null ? defValue : value;
    182     }
    183 
    184     private QsbApplication getQsbApplication() {
    185         return QsbApplication.get(getContext());
    186     }
    187 
    188     private ShortcutRepository getShortcutRepository() {
    189         return getQsbApplication().getShortcutRepository();
    190     }
    191 
    192     private Corpora getCorpora() {
    193         return getQsbApplication().getCorpora();
    194     }
    195 
    196 }
    197