| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.quicksearchbox; |
| |
| import android.app.SearchManager; |
| import android.content.ComponentName; |
| import android.content.Intent; |
| import android.database.Cursor; |
| import android.database.DataSetObserver; |
| import android.net.Uri; |
| import android.util.Log; |
| |
| public abstract class CursorBackedSuggestionCursor implements SuggestionCursor { |
| |
| private static final boolean DBG = false; |
| protected static final String TAG = "QSB.CursorBackedSuggestionCursor"; |
| |
| public static final String SUGGEST_COLUMN_LOG_TYPE = "suggest_log_type"; |
| |
| private final String mUserQuery; |
| |
| /** The suggestions, or {@code null} if the suggestions query failed. */ |
| protected final Cursor mCursor; |
| |
| /** Column index of {@link SearchManager#SUGGEST_COLUMN_FORMAT} in @{link mCursor}. */ |
| private final int mFormatCol; |
| |
| /** Column index of {@link SearchManager#SUGGEST_COLUMN_TEXT_1} in @{link mCursor}. */ |
| private final int mText1Col; |
| |
| /** Column index of {@link SearchManager#SUGGEST_COLUMN_TEXT_2} in @{link mCursor}. */ |
| private final int mText2Col; |
| |
| /** Column index of {@link SearchManager#SUGGEST_COLUMN_TEXT_2_URL} in @{link mCursor}. */ |
| private final int mText2UrlCol; |
| |
| /** Column index of {@link SearchManager#SUGGEST_COLUMN_ICON_1} in @{link mCursor}. */ |
| private final int mIcon1Col; |
| |
| /** Column index of {@link SearchManager#SUGGEST_COLUMN_ICON_1} in @{link mCursor}. */ |
| private final int mIcon2Col; |
| |
| /** Column index of {@link SearchManager#SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING} |
| * in @{link mCursor}. |
| **/ |
| private final int mRefreshSpinnerCol; |
| |
| /** True if this result has been closed. */ |
| private boolean mClosed = false; |
| |
| public CursorBackedSuggestionCursor(String userQuery, Cursor cursor) { |
| mUserQuery = userQuery; |
| mCursor = cursor; |
| mFormatCol = getColumnIndex(SearchManager.SUGGEST_COLUMN_FORMAT); |
| mText1Col = getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1); |
| mText2Col = getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2); |
| mText2UrlCol = getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2_URL); |
| mIcon1Col = getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1); |
| mIcon2Col = getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2); |
| mRefreshSpinnerCol = getColumnIndex(SearchManager.SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING); |
| } |
| |
| public String getUserQuery() { |
| return mUserQuery; |
| } |
| |
| public abstract Source getSuggestionSource(); |
| |
| public String getSuggestionLogType() { |
| return getStringOrNull(SUGGEST_COLUMN_LOG_TYPE); |
| } |
| |
| public void close() { |
| if (DBG) Log.d(TAG, "close()"); |
| if (mClosed) { |
| throw new IllegalStateException("Double close()"); |
| } |
| mClosed = true; |
| if (mCursor != null) { |
| try { |
| mCursor.close(); |
| } catch (RuntimeException ex) { |
| // all operations on cross-process cursors can throw random exceptions |
| Log.e(TAG, "close() failed, ", ex); |
| } |
| } |
| } |
| |
| @Override |
| protected void finalize() { |
| if (!mClosed) { |
| Log.e(TAG, "LEAK! Finalized without being closed: " + toString()); |
| } |
| } |
| |
| public int getCount() { |
| if (mClosed) { |
| throw new IllegalStateException("getCount() after close()"); |
| } |
| if (mCursor == null) return 0; |
| try { |
| return mCursor.getCount(); |
| } catch (RuntimeException ex) { |
| // all operations on cross-process cursors can throw random exceptions |
| Log.e(TAG, "getCount() failed, ", ex); |
| return 0; |
| } |
| } |
| |
| public void moveTo(int pos) { |
| if (mClosed) { |
| throw new IllegalStateException("moveTo(" + pos + ") after close()"); |
| } |
| try { |
| if (!mCursor.moveToPosition(pos)) { |
| Log.e(TAG, "moveToPosition(" + pos + ") failed, count=" + getCount()); |
| } |
| } catch (RuntimeException ex) { |
| // all operations on cross-process cursors can throw random exceptions |
| Log.e(TAG, "moveToPosition() failed, ", ex); |
| } |
| } |
| |
| public boolean moveToNext() { |
| if (mClosed) { |
| throw new IllegalStateException("moveToNext() after close()"); |
| } |
| try { |
| return mCursor.moveToNext(); |
| } catch (RuntimeException ex) { |
| // all operations on cross-process cursors can throw random exceptions |
| Log.e(TAG, "moveToNext() failed, ", ex); |
| return false; |
| } |
| } |
| |
| public int getPosition() { |
| if (mClosed) { |
| throw new IllegalStateException("getPosition after close()"); |
| } |
| try { |
| return mCursor.getPosition(); |
| } catch (RuntimeException ex) { |
| // all operations on cross-process cursors can throw random exceptions |
| Log.e(TAG, "getPosition() failed, ", ex); |
| return -1; |
| } |
| } |
| |
| public String getShortcutId() { |
| return getStringOrNull(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID); |
| } |
| |
| public String getSuggestionFormat() { |
| return getStringOrNull(mFormatCol); |
| } |
| |
| public String getSuggestionText1() { |
| return getStringOrNull(mText1Col); |
| } |
| |
| public String getSuggestionText2() { |
| return getStringOrNull(mText2Col); |
| } |
| |
| public String getSuggestionText2Url() { |
| return getStringOrNull(mText2UrlCol); |
| } |
| |
| public String getSuggestionIcon1() { |
| return getStringOrNull(mIcon1Col); |
| } |
| |
| public String getSuggestionIcon2() { |
| return getStringOrNull(mIcon2Col); |
| } |
| |
| public boolean isSpinnerWhileRefreshing() { |
| return "true".equals(getStringOrNull(mRefreshSpinnerCol)); |
| } |
| |
| /** |
| * Gets the intent action for the current suggestion. |
| */ |
| public String getSuggestionIntentAction() { |
| String action = getStringOrNull(SearchManager.SUGGEST_COLUMN_INTENT_ACTION); |
| if (action != null) return action; |
| return getSuggestionSource().getDefaultIntentAction(); |
| } |
| |
| public abstract ComponentName getSuggestionIntentComponent(); |
| |
| /** |
| * Gets the query for the current suggestion. |
| */ |
| public String getSuggestionQuery() { |
| return getStringOrNull(SearchManager.SUGGEST_COLUMN_QUERY); |
| } |
| |
| public String getSuggestionIntentDataString() { |
| // use specific data if supplied, or default data if supplied |
| String data = getStringOrNull(SearchManager.SUGGEST_COLUMN_INTENT_DATA); |
| if (data == null) { |
| data = getSuggestionSource().getDefaultIntentData(); |
| } |
| // then, if an ID was provided, append it. |
| if (data != null) { |
| String id = getStringOrNull(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID); |
| if (id != null) { |
| data = data + "/" + Uri.encode(id); |
| } |
| } |
| return data; |
| } |
| |
| /** |
| * Gets the intent extra data for the current suggestion. |
| */ |
| public String getSuggestionIntentExtraData() { |
| return getStringOrNull(SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA); |
| } |
| |
| public boolean isWebSearchSuggestion() { |
| return Intent.ACTION_WEB_SEARCH.equals(getSuggestionIntentAction()); |
| } |
| |
| /** |
| * Gets the index of a column in {@link #mCursor} by name. |
| * |
| * @return The index, or {@code -1} if the column was not found. |
| */ |
| protected int getColumnIndex(String colName) { |
| if (mCursor == null) return -1; |
| try { |
| return mCursor.getColumnIndex(colName); |
| } catch (RuntimeException ex) { |
| // all operations on cross-process cursors can throw random exceptions |
| Log.e(TAG, "getColumnIndex() failed, ", ex); |
| return -1; |
| } |
| } |
| |
| /** |
| * Gets the string value of a column in {@link #mCursor} by column index. |
| * |
| * @param col Column index. |
| * @return The string value, or {@code null}. |
| */ |
| protected String getStringOrNull(int col) { |
| if (mCursor == null) return null; |
| if (col == -1) { |
| return null; |
| } |
| try { |
| return mCursor.getString(col); |
| } catch (RuntimeException ex) { |
| // all operations on cross-process cursors can throw random exceptions |
| Log.e(TAG, "getString() failed, ", ex); |
| return null; |
| } |
| } |
| |
| /** |
| * Gets the string value of a column in {@link #mCursor} by column name. |
| * |
| * @param colName Column name. |
| * @return The string value, or {@code null}. |
| */ |
| protected String getStringOrNull(String colName) { |
| int col = getColumnIndex(colName); |
| return getStringOrNull(col); |
| } |
| |
| public void registerDataSetObserver(DataSetObserver observer) { |
| // We don't watch Cursor-backed SuggestionCursors for changes |
| } |
| |
| public void unregisterDataSetObserver(DataSetObserver observer) { |
| // We don't watch Cursor-backed SuggestionCursors for changes |
| } |
| |
| @Override |
| public String toString() { |
| return getClass().getSimpleName() + "[" + mUserQuery + "]"; |
| } |
| |
| } |