CursorBackedSuggestionCursor.java revision 94e8a2be78530170f50e7895a558bf8011bbf8e8
13e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert/*
23e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Copyright (C) 2009 The Android Open Source Project
33e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
43e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License");
53e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * you may not use this file except in compliance with the License.
63e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * You may obtain a copy of the License at
73e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
83e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *      http://www.apache.org/licenses/LICENSE-2.0
93e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Unless required by applicable law or agreed to in writing, software
113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS,
123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * See the License for the specific language governing permissions and
143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * limitations under the License.
153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert */
163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertpackage com.android.quicksearchbox;
183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.app.SearchManager;
203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.ComponentName;
213e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.Context;
223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.Intent;
233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.database.Cursor;
243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.graphics.Rect;
253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.net.Uri;
263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.os.Bundle;
2794e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinneyimport android.os.Looper;
283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.util.Log;
293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.view.KeyEvent;
303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport java.net.URISyntaxException;
323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertpublic abstract class CursorBackedSuggestionCursor extends AbstractSourceSuggestionCursor {
343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public static final String SUGGEST_COLUMN_SECONDARY_INTENT = "suggestion_secondary_intent";
363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public static final String TARGET_RECT_KEY = "target_rect";
373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
38cef2c4c9d54f513babd74801dbed5cbf709b9b79Bjorn Bringert    private static final boolean DBG = false;
393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected static final String TAG = "QSB.CursorBackedSuggestionCursor";
403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /** The suggestions, or {@code null} if the suggestions query failed. */
423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected final Cursor mCursor;
433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /** Column index of {@link SearchManager.SUGGEST_COLUMN_FORMAT} in @{link mCursor}. */
453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private final int mFormatCol;
463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /** Column index of {@link SearchManager.SUGGEST_COLUMN_TEXT_1} in @{link mCursor}. */
483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private final int mText1Col;
493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /** Column index of {@link SearchManager.SUGGEST_COLUMN_TEXT_2} in @{link mCursor}. */
513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private final int mText2Col;
523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /** Column index of {@link SearchManager.SUGGEST_COLUMN_ICON_1} in @{link mCursor}. */
543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private final int mIcon1Col;
553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /** Column index of {@link SearchManager.SUGGEST_COLUMN_ICON_1} in @{link mCursor}. */
573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private final int mIcon2Col;
583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5994e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    /** Column index of {@link SearchManager.SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING}
6094e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney     * in @{link mCursor}.
6194e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney     **/
6294e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    private final int mRefreshSpinnerCol;
6394e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /** True if this result has been closed. */
653e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private boolean mClosed = false;
663e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public CursorBackedSuggestionCursor(String userQuery, Cursor cursor) {
683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        super(userQuery);
693e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mCursor = cursor;
703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mFormatCol = getColumnIndex(SearchManager.SUGGEST_COLUMN_FORMAT);
713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mText1Col = getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
723e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mText2Col = getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2);
733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mIcon1Col = getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
743e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mIcon2Col = getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2);
7594e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        mRefreshSpinnerCol = getColumnIndex(SearchManager.SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING);
763e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
773e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
783e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected String getDefaultIntentAction() {
793e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getSource().getDefaultIntentAction();
803e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
813e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
823e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected String getDefaultIntentData() {
833e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getSource().getDefaultIntentData();
843e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
853e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
863e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected boolean shouldRewriteQueryFromData() {
873e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getSource().shouldRewriteQueryFromData();
883e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
893e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
903e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected boolean shouldRewriteQueryFromText() {
913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getSource().shouldRewriteQueryFromText();
923e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public boolean isFailed() {
953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return mCursor == null;
963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
983e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public void close() {
99185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert        if (DBG) Log.d(TAG, "close()");
1003e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (mClosed) {
101185bb2e3881452c084fde44d9bee657f65881b0eBjorn Bringert            throw new IllegalStateException("Double close()");
1023e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
1033e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mClosed = true;
1043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (mCursor != null) {
1053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            // TODO: all operations on cross-process cursors can throw random exceptions
1063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            mCursor.close();
1073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
1083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1093e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    @Override
1113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected void finalize() {
1123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (!mClosed) {
1133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            Log.e(TAG, "LEAK! Finalized without being closed: " + toString());
1143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            close();
1153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
1163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public int getCount() {
1193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (mClosed) {
1203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            throw new IllegalStateException("getCount() after close()");
1213e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
1223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (mCursor == null) return 0;
1233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // TODO: all operations on cross-process cursors can throw random exceptions
1243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return mCursor.getCount();
1253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public void moveTo(int pos) {
1283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (mClosed) {
1293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            throw new IllegalStateException("moveTo(" + pos + ") after close()");
1303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
1313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // TODO: all operations on cross-process cursors can throw random exceptions
1323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (mCursor == null || pos < 0 || pos >= mCursor.getCount()) {
1333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            throw new IndexOutOfBoundsException(pos + ", count=" + getCount());
1343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
1353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // TODO: all operations on cross-process cursors can throw random exceptions
1363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        mCursor.moveToPosition(pos);
1373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public int getPosition() {
1403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (mClosed) {
1413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            throw new IllegalStateException("getPosition after close()");
1423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
1433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return mCursor.getPosition();
1443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public String getSuggestionDisplayQuery() {
1473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String query = getSuggestionQuery();
1483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (query != null) {
1493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            return query;
1503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
1513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (shouldRewriteQueryFromData()) {
1523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            String data = getSuggestionIntentDataString();
1533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            if (data != null) {
1543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                return data;
1553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
1563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
1573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (shouldRewriteQueryFromText()) {
1583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            String text1 = getSuggestionText1();
1593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            if (text1 != null) {
1603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                return text1;
1613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
1623e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
1633e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return null;
1643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1653e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1663e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public String getShortcutId() {
1673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getStringOrNull(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID);
1683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1693e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public String getSuggestionFormat() {
1713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getStringOrNull(mFormatCol);
1723e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1743e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public String getSuggestionText1() {
1753e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getStringOrNull(mText1Col);
1763e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1773e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1783e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public String getSuggestionText2() {
1793e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getStringOrNull(mText2Col);
1803e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1813e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1823e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public String getSuggestionIcon1() {
1833e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getStringOrNull(mIcon1Col);
1843e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1853e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1863e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public String getSuggestionIcon2() {
1873e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getStringOrNull(mIcon2Col);
1883e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1893e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
19094e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    public boolean isSpinnerWhileRefreshing() {
19194e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        return "true".equals(getStringOrNull(mRefreshSpinnerCol));
19294e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    }
19394e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
1943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public Intent getSuggestionIntent(Context context, Bundle appSearchData,
1953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            int actionKey, String actionMsg) {
1963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String action = getSuggestionIntentAction();
1973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        Uri data = getSuggestionIntentData();
1983e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String query = getSuggestionQuery();
1993e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String userQuery = getUserQuery();
2003e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String extraData = getSuggestionIntentExtraData();
2013e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2023e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // Now build the Intent
2033e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        Intent intent = new Intent(action);
2043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // We need CLEAR_TOP to avoid reusing an old task that has other activities
2063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // on top of the one we want.
2073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
2083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (data != null) {
2093e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            intent.setData(data);
2103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
2113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        intent.putExtra(SearchManager.USER_QUERY, userQuery);
2123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (query != null) {
2133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            intent.putExtra(SearchManager.QUERY, query);
2143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
2153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (extraData != null) {
2163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
2173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
2183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (appSearchData != null) {
2193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            intent.putExtra(SearchManager.APP_DATA, appSearchData);
2203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
2213e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
2223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            intent.putExtra(SearchManager.ACTION_KEY, actionKey);
2233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
2243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
2253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // TODO: Use this to tell sources this comes form global search
2263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // The constants are currently hidden.
2273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        //        intent.putExtra(SearchManager.SEARCH_MODE,
2283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        //                SearchManager.MODE_GLOBAL_SEARCH_SUGGESTION);
2293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        intent.setComponent(getSuggestionIntentComponent(context, intent));
2303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return intent;
2313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public Intent getSecondarySuggestionIntent(Context context, Bundle appSearchData, Rect target) {
2343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String intentString = getStringOrNull(SUGGEST_COLUMN_SECONDARY_INTENT);
2353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (intentString != null) {
2363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            try {
2373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                Intent intent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME);
2383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                if (appSearchData != null) {
2393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                    intent.putExtra(SearchManager.APP_DATA, appSearchData);
2403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                }
2413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                // TODO: Do we need to pass action keys?
2423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                // TODO: Should we try to use defaults such as getDefaultIntentData?
2433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                intent.putExtra(TARGET_RECT_KEY, target);
2443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                intent.setComponent(getSuggestionIntentComponent(context, intent));
2453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                return intent;
2463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }  catch (URISyntaxException e) {
2473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                Log.w(TAG, "Unable to parse secondary intent " + intentString);
2483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
2493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
2503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return null;
2513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
2543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Updates the intent with the component to which intents created
2553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * from the current suggestion should be sent.
2563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
2573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected ComponentName getSuggestionIntentComponent(Context context, Intent intent) {
2583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        ComponentName component = getSourceComponentName();
2593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // Limit intent resolution to the source package.
2603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        intent.setPackage(component.getPackageName());
2613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        ComponentName resolvedComponent = intent.resolveActivity(context.getPackageManager());
2623e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (resolvedComponent != null) {
2633e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            // It's ok if the intent resolves to an activity in the same
2643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            // package as component.  We set the component explicitly to
2653e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            // avoid having to re-resolve, and to prevent race conditions.
2663e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            return resolvedComponent;
2673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        } else {
2683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            return component;
2693e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
2703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
2713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
2723e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public boolean hasSecondaryIntent() {
2733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert           return getStringOrNull(SUGGEST_COLUMN_SECONDARY_INTENT) != null;
2743e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert       }
2753e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
276782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    public String getActionKeyMsg(int keyCode) {
277782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        String result = null;
278782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        String column = getSource().getSuggestActionMsgColumn(keyCode);
279782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        if (column != null) {
280782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            result = getStringOrNull(column);
281782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        }
282782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        // If the cursor didn't give us a message, see if there's a single message defined
283782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        // for the actionkey (for all suggestions)
284782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        if (result == null) {
285782dd228e78e9294692d639597f96c26283968bbBjorn Bringert            result = getSource().getSuggestActionMsg(keyCode);
286782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        }
287782dd228e78e9294692d639597f96c26283968bbBjorn Bringert        return result;
288782dd228e78e9294692d639597f96c26283968bbBjorn Bringert    }
289782dd228e78e9294692d639597f96c26283968bbBjorn Bringert
2903e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
2913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Gets the intent action for the current suggestion.
2923e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
2933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected String getSuggestionIntentAction() {
2943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // use specific action if supplied, or default action if supplied, or fixed default
2953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String action = getStringOrNull(SearchManager.SUGGEST_COLUMN_INTENT_ACTION);
2963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (action == null) {
2973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            action = getDefaultIntentAction();
2983e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            if (action == null) {
2993e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                action = Intent.ACTION_SEARCH;
3003e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
3013e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
3023e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return action;
3033e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
3063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Gets the query for the current suggestion.
3073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
3083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected String getSuggestionQuery() {
3093e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getStringOrNull(SearchManager.SUGGEST_COLUMN_QUERY);
3103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private String getSuggestionIntentDataString() {
3133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert         // use specific data if supplied, or default data if supplied
3143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert         String data = getStringOrNull(SearchManager.SUGGEST_COLUMN_INTENT_DATA);
3153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert         if (data == null) {
3163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert             data = getDefaultIntentData();
3173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert         }
3183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert         // then, if an ID was provided, append it.
3193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert         if (data != null) {
3203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert             String id = getStringOrNull(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
3213e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert             if (id != null) {
3223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                 data = data + "/" + Uri.encode(id);
3233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert             }
3243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert         }
3253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert         return data;
3263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     }
3273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3283e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
3293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Gets the intent data for the current suggestion.
3303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
3313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected Uri getSuggestionIntentData() {
3323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String data = getSuggestionIntentDataString();
3333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return (data == null) ? null : Uri.parse(data);
3343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
3373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Gets the intent extra data for the current suggestion.
3383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
33994e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    public String getSuggestionIntentExtraData() {
3403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getStringOrNull(SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
3413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
3443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Gets the index of a column in {@link mCursor} by name.
3453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *
3463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * @return The index, or {@code -1} if the column was not found.
3473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
3483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected int getColumnIndex(String colName) {
3493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (mCursor == null) return -1;
3503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // TODO: all operations on cross-process cursors can throw random exceptions
3513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return mCursor.getColumnIndex(colName);
3523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
3553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Gets the string value of a column in {@link mCursor} by column index.
3563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *
3573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * @param col Column index.
3583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * @return The string value, or {@code null}.
3593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
3603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected String getStringOrNull(int col) {
3613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (mCursor == null) return null;
3623e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (col == -1) {
3633e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            return null;
3643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
3653e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        try {
3663e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            // TODO: all operations on cross-process cursors can throw random exceptions
3673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            return mCursor.getString(col);
3683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        } catch (Exception e) {
3693e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            Log.e(TAG,
3703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                    "unexpected error retrieving valid column from cursor, "
3713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                            + "did the remote process die?", e);
3723e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            return null;
3733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
3743e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3753e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3763e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
3773e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Gets the string value of a column in {@link mCursor} by column name.
3783e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *
3793e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * @param colName Column name.
3803e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * @return The string value, or {@code null}.
3813e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
3823e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    protected String getStringOrNull(String colName) {
3833e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        int col = getColumnIndex(colName);
3843e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return getStringOrNull(col);
3853e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3863e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3873e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private String makeKeyComponent(String str) {
3883e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return str == null ? "" : str;
3893e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
3903e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
3913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public String getSuggestionKey() {
3923e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String action = makeKeyComponent(getSuggestionIntentAction());
3933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String data = makeKeyComponent(getSuggestionIntentDataString());
3943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String query = makeKeyComponent(getSuggestionQuery());
3953e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // calculating accurate size of string builder avoids an allocation vs starting with
3963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        // the default size and having to expand.
3973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        int size = action.length() + 2 + data.length() + query.length();
3983e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return new StringBuilder(size)
3993e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                .append(action)
4003e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                .append('#')
4013e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                .append(data)
4023e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                .append('#')
4033e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                .append(query)
4043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                .toString();
4053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
4063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert}
407