• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CursorBackedSuggestionCursor(String userQuery, Cursor cursor)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 
getUserQuery()77     public String getUserQuery() {
78         return mUserQuery;
79     }
80 
getSuggestionSource()81     public abstract Source getSuggestionSource();
82 
getSuggestionLogType()83     public String getSuggestionLogType() {
84         return getStringOrNull(SUGGEST_COLUMN_LOG_TYPE);
85     }
86 
close()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
finalize()104     protected void finalize() {
105         if (!mClosed) {
106             Log.e(TAG, "LEAK! Finalized without being closed: " + toString());
107         }
108     }
109 
getCount()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 
moveTo(int pos)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 
moveToNext()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 
getPosition()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 
getShortcutId()164     public String getShortcutId() {
165         return getStringOrNull(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID);
166     }
167 
getSuggestionFormat()168     public String getSuggestionFormat() {
169         return getStringOrNull(mFormatCol);
170     }
171 
getSuggestionText1()172     public String getSuggestionText1() {
173         return getStringOrNull(mText1Col);
174     }
175 
getSuggestionText2()176     public String getSuggestionText2() {
177         return getStringOrNull(mText2Col);
178     }
179 
getSuggestionText2Url()180     public String getSuggestionText2Url() {
181         return getStringOrNull(mText2UrlCol);
182     }
183 
getSuggestionIcon1()184     public String getSuggestionIcon1() {
185         return getStringOrNull(mIcon1Col);
186     }
187 
getSuggestionIcon2()188     public String getSuggestionIcon2() {
189         return getStringOrNull(mIcon2Col);
190     }
191 
isSpinnerWhileRefreshing()192     public boolean isSpinnerWhileRefreshing() {
193         return "true".equals(getStringOrNull(mRefreshSpinnerCol));
194     }
195 
196     /**
197      * Gets the intent action for the current suggestion.
198      */
getSuggestionIntentAction()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 
getSuggestionIntentComponent()205     public abstract ComponentName getSuggestionIntentComponent();
206 
207     /**
208      * Gets the query for the current suggestion.
209      */
getSuggestionQuery()210     public String getSuggestionQuery() {
211         return getStringOrNull(SearchManager.SUGGEST_COLUMN_QUERY);
212     }
213 
getSuggestionIntentDataString()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      */
getSuggestionIntentExtraData()233     public String getSuggestionIntentExtraData() {
234         return getStringOrNull(SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
235     }
236 
isWebSearchSuggestion()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      */
getColumnIndex(String colName)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      */
getStringOrNull(int col)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      */
getStringOrNull(String colName)283     protected String getStringOrNull(String colName) {
284         int col = getColumnIndex(colName);
285         return getStringOrNull(col);
286     }
287 
registerDataSetObserver(DataSetObserver observer)288     public void registerDataSetObserver(DataSetObserver observer) {
289         // We don't watch Cursor-backed SuggestionCursors for changes
290     }
291 
unregisterDataSetObserver(DataSetObserver observer)292     public void unregisterDataSetObserver(DataSetObserver observer) {
293         // We don't watch Cursor-backed SuggestionCursors for changes
294     }
295 
296     @Override
toString()297     public String toString() {
298         return getClass().getSimpleName() + "[" + mUserQuery + "]";
299     }
300 
301 }
302