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