Home | History | Annotate | Download | only in quicksearchbox
      1 /*
      2  * Copyright (C) 2009 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.Intent;
     22 import android.database.Cursor;
     23 import android.database.DataSetObserver;
     24 import android.net.Uri;
     25 import android.util.Log;
     26 
     27 public abstract class CursorBackedSuggestionCursor implements SuggestionCursor {
     28 
     29     private static final boolean DBG = false;
     30     protected static final String TAG = "QSB.CursorBackedSuggestionCursor";
     31 
     32     public static final String SUGGEST_COLUMN_LOG_TYPE = "suggest_log_type";
     33 
     34     private final String mUserQuery;
     35 
     36     /** The suggestions, or {@code null} if the suggestions query failed. */
     37     protected final Cursor mCursor;
     38 
     39     /** Column index of {@link SearchManager#SUGGEST_COLUMN_FORMAT} in @{link mCursor}. */
     40     private final int mFormatCol;
     41 
     42     /** Column index of {@link SearchManager#SUGGEST_COLUMN_TEXT_1} in @{link mCursor}. */
     43     private final int mText1Col;
     44 
     45     /** Column index of {@link SearchManager#SUGGEST_COLUMN_TEXT_2} in @{link mCursor}. */
     46     private final int mText2Col;
     47 
     48     /** Column index of {@link SearchManager#SUGGEST_COLUMN_TEXT_2_URL} in @{link mCursor}. */
     49     private final int mText2UrlCol;
     50 
     51     /** Column index of {@link SearchManager#SUGGEST_COLUMN_ICON_1} in @{link mCursor}. */
     52     private final int mIcon1Col;
     53 
     54     /** Column index of {@link SearchManager#SUGGEST_COLUMN_ICON_1} in @{link mCursor}. */
     55     private final int mIcon2Col;
     56 
     57     /** Column index of {@link SearchManager#SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING}
     58      * in @{link mCursor}.
     59      **/
     60     private final int mRefreshSpinnerCol;
     61 
     62     /** True if this result has been closed. */
     63     private boolean mClosed = false;
     64 
     65     public CursorBackedSuggestionCursor(String userQuery, Cursor cursor) {
     66         mUserQuery = userQuery;
     67         mCursor = cursor;
     68         mFormatCol = getColumnIndex(SearchManager.SUGGEST_COLUMN_FORMAT);
     69         mText1Col = getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
     70         mText2Col = getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2);
     71         mText2UrlCol = getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2_URL);
     72         mIcon1Col = getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
     73         mIcon2Col = getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2);
     74         mRefreshSpinnerCol = getColumnIndex(SearchManager.SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING);
     75     }
     76 
     77     public String getUserQuery() {
     78         return mUserQuery;
     79     }
     80 
     81     public abstract Source getSuggestionSource();
     82 
     83     public String getSuggestionLogType() {
     84         return getStringOrNull(SUGGEST_COLUMN_LOG_TYPE);
     85     }
     86 
     87     public void close() {
     88         if (DBG) Log.d(TAG, "close()");
     89         if (mClosed) {
     90             throw new IllegalStateException("Double close()");
     91         }
     92         mClosed = true;
     93         if (mCursor != null) {
     94             try {
     95                 mCursor.close();
     96             } catch (RuntimeException ex) {
     97                 // all operations on cross-process cursors can throw random exceptions
     98                 Log.e(TAG, "close() failed, ", ex);
     99             }
    100         }
    101     }
    102 
    103     @Override
    104     protected void finalize() {
    105         if (!mClosed) {
    106             Log.e(TAG, "LEAK! Finalized without being closed: " + toString());
    107         }
    108     }
    109 
    110     public int getCount() {
    111         if (mClosed) {
    112             throw new IllegalStateException("getCount() after close()");
    113         }
    114         if (mCursor == null) return 0;
    115         try {
    116             return mCursor.getCount();
    117         } catch (RuntimeException ex) {
    118             // all operations on cross-process cursors can throw random exceptions
    119             Log.e(TAG, "getCount() failed, ", ex);
    120             return 0;
    121         }
    122     }
    123 
    124     public void moveTo(int pos) {
    125         if (mClosed) {
    126             throw new IllegalStateException("moveTo(" + pos + ") after close()");
    127         }
    128         try {
    129             if (!mCursor.moveToPosition(pos)) {
    130                 Log.e(TAG, "moveToPosition(" + pos + ") failed, count=" + getCount());
    131             }
    132         } catch (RuntimeException ex) {
    133             // all operations on cross-process cursors can throw random exceptions
    134             Log.e(TAG, "moveToPosition() failed, ", ex);
    135         }
    136     }
    137 
    138     public boolean moveToNext() {
    139         if (mClosed) {
    140             throw new IllegalStateException("moveToNext() after close()");
    141         }
    142         try {
    143             return mCursor.moveToNext();
    144         } catch (RuntimeException ex) {
    145             // all operations on cross-process cursors can throw random exceptions
    146             Log.e(TAG, "moveToNext() failed, ", ex);
    147             return false;
    148         }
    149     }
    150 
    151     public int getPosition() {
    152         if (mClosed) {
    153             throw new IllegalStateException("getPosition after close()");
    154         }
    155         try {
    156             return mCursor.getPosition();
    157         } catch (RuntimeException ex) {
    158             // all operations on cross-process cursors can throw random exceptions
    159             Log.e(TAG, "getPosition() failed, ", ex);
    160             return -1;
    161         }
    162     }
    163 
    164     public String getShortcutId() {
    165         return getStringOrNull(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID);
    166     }
    167 
    168     public String getSuggestionFormat() {
    169         return getStringOrNull(mFormatCol);
    170     }
    171 
    172     public String getSuggestionText1() {
    173         return getStringOrNull(mText1Col);
    174     }
    175 
    176     public String getSuggestionText2() {
    177         return getStringOrNull(mText2Col);
    178     }
    179 
    180     public String getSuggestionText2Url() {
    181         return getStringOrNull(mText2UrlCol);
    182     }
    183 
    184     public String getSuggestionIcon1() {
    185         return getStringOrNull(mIcon1Col);
    186     }
    187 
    188     public String getSuggestionIcon2() {
    189         return getStringOrNull(mIcon2Col);
    190     }
    191 
    192     public boolean isSpinnerWhileRefreshing() {
    193         return "true".equals(getStringOrNull(mRefreshSpinnerCol));
    194     }
    195 
    196     /**
    197      * Gets the intent action for the current suggestion.
    198      */
    199     public String getSuggestionIntentAction() {
    200         String action = getStringOrNull(SearchManager.SUGGEST_COLUMN_INTENT_ACTION);
    201         if (action != null) return action;
    202         return getSuggestionSource().getDefaultIntentAction();
    203     }
    204 
    205     public abstract ComponentName getSuggestionIntentComponent();
    206 
    207     /**
    208      * Gets the query for the current suggestion.
    209      */
    210     public String getSuggestionQuery() {
    211         return getStringOrNull(SearchManager.SUGGEST_COLUMN_QUERY);
    212     }
    213 
    214     public String getSuggestionIntentDataString() {
    215          // use specific data if supplied, or default data if supplied
    216          String data = getStringOrNull(SearchManager.SUGGEST_COLUMN_INTENT_DATA);
    217          if (data == null) {
    218              data = getSuggestionSource().getDefaultIntentData();
    219          }
    220          // then, if an ID was provided, append it.
    221          if (data != null) {
    222              String id = getStringOrNull(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
    223              if (id != null) {
    224                  data = data + "/" + Uri.encode(id);
    225              }
    226          }
    227          return data;
    228      }
    229 
    230     /**
    231      * Gets the intent extra data for the current suggestion.
    232      */
    233     public String getSuggestionIntentExtraData() {
    234         return getStringOrNull(SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
    235     }
    236 
    237     public boolean isWebSearchSuggestion() {
    238         return Intent.ACTION_WEB_SEARCH.equals(getSuggestionIntentAction());
    239     }
    240 
    241     /**
    242      * Gets the index of a column in {@link #mCursor} by name.
    243      *
    244      * @return The index, or {@code -1} if the column was not found.
    245      */
    246     protected int getColumnIndex(String colName) {
    247         if (mCursor == null) return -1;
    248         try {
    249             return mCursor.getColumnIndex(colName);
    250         } catch (RuntimeException ex) {
    251             // all operations on cross-process cursors can throw random exceptions
    252             Log.e(TAG, "getColumnIndex() failed, ", ex);
    253             return -1;
    254         }
    255     }
    256 
    257     /**
    258      * Gets the string value of a column in {@link #mCursor} by column index.
    259      *
    260      * @param col Column index.
    261      * @return The string value, or {@code null}.
    262      */
    263     protected String getStringOrNull(int col) {
    264         if (mCursor == null) return null;
    265         if (col == -1) {
    266             return null;
    267         }
    268         try {
    269             return mCursor.getString(col);
    270         } catch (RuntimeException ex) {
    271             // all operations on cross-process cursors can throw random exceptions
    272             Log.e(TAG, "getString() failed, ", ex);
    273             return null;
    274         }
    275     }
    276 
    277     /**
    278      * Gets the string value of a column in {@link #mCursor} by column name.
    279      *
    280      * @param colName Column name.
    281      * @return The string value, or {@code null}.
    282      */
    283     protected String getStringOrNull(String colName) {
    284         int col = getColumnIndex(colName);
    285         return getStringOrNull(col);
    286     }
    287 
    288     public void registerDataSetObserver(DataSetObserver observer) {
    289         // We don't watch Cursor-backed SuggestionCursors for changes
    290     }
    291 
    292     public void unregisterDataSetObserver(DataSetObserver observer) {
    293         // We don't watch Cursor-backed SuggestionCursors for changes
    294     }
    295 
    296     @Override
    297     public String toString() {
    298         return getClass().getSimpleName() + "[" + mUserQuery + "]";
    299     }
    300 
    301 }
    302