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