194e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney/*
294e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney * Copyright (C) 2010 The Android Open Source Project
394e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney *
494e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney * Licensed under the Apache License, Version 2.0 (the "License");
594e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney * you may not use this file except in compliance with the License.
694e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney * You may obtain a copy of the License at
794e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney *
894e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney *      http://www.apache.org/licenses/LICENSE-2.0
994e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney *
1094e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney * Unless required by applicable law or agreed to in writing, software
1194e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney * distributed under the License is distributed on an "AS IS" BASIS,
1294e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1394e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney * See the License for the specific language governing permissions and
1494e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney * limitations under the License.
1594e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney */
1694e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
1794e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinneypackage com.android.quicksearchbox;
1894e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
19120040ef31d79c1d69138b13ca7f256841f3298eMathew Inwoodimport com.google.common.annotations.VisibleForTesting;
20120040ef31d79c1d69138b13ca7f256841f3298eMathew Inwood
219038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwoodimport android.os.Handler;
2294e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinneyimport android.util.Log;
2394e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
2494e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinneyimport java.util.HashSet;
2594e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
2694e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney/**
2794e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney * A SuggestionCursor that allows shortcuts to be updated by overlaying
2894e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney * with results from another cursor.
2994e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney */
300a73d81f02118d0343d3f1c9219a8354466f72b3Mathew Inwoodpublic class ShortcutCursor extends ListSuggestionCursor {
31b5fc08b7f16a32d3865f44b7f26d8aaa5304a2adBjorn Bringert
32b5fc08b7f16a32d3865f44b7f26d8aaa5304a2adBjorn Bringert    private static final boolean DBG = false;
3394e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    private static final String TAG = "QSB.ShortcutCursor";
3494e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
3594e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    // mShortcuts is used to close the underlying cursor when we're closed.
369038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood    private final SuggestionCursor mShortcuts;
3794e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    // mRefreshed contains all the cursors that have been refreshed, so that
3894e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    // they can be closed when ShortcutCursor is closed.
3994e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    private final HashSet<SuggestionCursor> mRefreshed;
4094e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
41b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    private boolean mClosed = false;
42b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert
439038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood    private final ShortcutRefresher mRefresher;
449038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood    private final ShortcutRepository mShortcutRepo;
459038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood    private final Handler mUiThread;
469038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood
479038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood    private ShortcutCursor(String query, SuggestionCursor shortcuts, Handler uiThread,
489038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood            ShortcutRefresher refresher, ShortcutRepository repository) {
499038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood        super(query);
5094e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        mShortcuts = shortcuts;
519038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood        mUiThread = uiThread;
529038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood        mRefresher = refresher;
539038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood        mShortcutRepo = repository;
5494e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        mRefreshed = new HashSet<SuggestionCursor>();
559038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood    }
569038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood
57120040ef31d79c1d69138b13ca7f256841f3298eMathew Inwood    @VisibleForTesting
58b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    ShortcutCursor(String query, Handler uiThread,
599038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood            ShortcutRefresher refresher, ShortcutRepository repository) {
609038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood        this(query, null, uiThread, refresher, repository);
619038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood    }
629038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood
63b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    @VisibleForTesting
64b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    ShortcutCursor(SuggestionCursor suggestions) {
658749e77dddec9e7984ee86a7be6f5ba4fce44362Bjorn Bringert        this(suggestions, true, null, null, null);
66b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert    }
67b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert
688749e77dddec9e7984ee86a7be6f5ba4fce44362Bjorn Bringert    public ShortcutCursor(SuggestionCursor suggestions, boolean allowWebSearchShortcuts,
698749e77dddec9e7984ee86a7be6f5ba4fce44362Bjorn Bringert            Handler uiThread, ShortcutRefresher refresher, ShortcutRepository repository) {
709038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood        this(suggestions.getUserQuery(), suggestions, uiThread, refresher, repository);
719038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood        int count = suggestions.getCount();
7293ba3b9bf77395f7a148d384bac9a80ba8ce6cedMathew Inwood        if (DBG) Log.d(TAG, "Total shortcuts: " + count);
7394e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        for (int i = 0; i < count; i++) {
749038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood            suggestions.moveTo(i);
758749e77dddec9e7984ee86a7be6f5ba4fce44362Bjorn Bringert            if (suggestions.getSuggestionSource() != null
768749e77dddec9e7984ee86a7be6f5ba4fce44362Bjorn Bringert                    && (allowWebSearchShortcuts || !suggestions.isWebSearchSuggestion())) {
779038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood                add(new SuggestionPosition(suggestions));
786d5cbd67f7a5f824babb5c892b0f30bfd9f4ff23Bjorn Bringert            } else {
796d5cbd67f7a5f824babb5c892b0f30bfd9f4ff23Bjorn Bringert                if (DBG) Log.d(TAG, "Skipping shortcut " + i);
8094e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            }
8194e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        }
8294e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    }
8394e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
84fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert    @Override
85fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert    public boolean isSuggestionShortcut() {
86fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert        // Needed to make refreshed shortcuts be treated as shortcuts
87fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert        return true;
88fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert    }
89fb8ce18922dae59db424fce906b5c113797fe81eBjorn Bringert
9094e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    /**
919038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood     * Refresh a shortcut from this cursor.
929038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood     *
93120040ef31d79c1d69138b13ca7f256841f3298eMathew Inwood     * @param shortcut The shortcut to refresh. Should be a shortcut taken from this cursor.
949038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood     */
959038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood    public void refresh(Suggestion shortcut) {
969038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood        mRefresher.refresh(shortcut, new ShortcutRefresher.Listener() {
979038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood            public void onShortcutRefreshed(final Source source,
989038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood                    final String shortcutId, final SuggestionCursor refreshed) {
999038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood                if (DBG) Log.d(TAG, "Shortcut refreshed: " + shortcutId);
1009038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood                mShortcutRepo.updateShortcut(source, shortcutId, refreshed);
1019038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood                mUiThread.post(new Runnable() {
1029038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood                    public void run() {
1039038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood                        refresh(source, shortcutId, refreshed);
1049038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood                    }
1059038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood                });
1069038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood            }
1079038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood        });
1089038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood    }
1099038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood
1109038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood    /**
11194e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney     * Updates this SuggestionCursor with a refreshed result from another.
11294e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney     * Since this modifies the cursor, it should be called on the UI thread.
11394e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney     * This class assumes responsibility for closing refreshed.
11494e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney     */
1159038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood    private void refresh(Source source, String shortcutId, SuggestionCursor refreshed) {
11694e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        if (DBG) Log.d(TAG, "refresh " + shortcutId);
117b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        if (mClosed) {
11894e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            if (refreshed != null) {
11994e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney                refreshed.close();
12094e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            }
12194e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            return;
12294e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        }
12394e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        if (refreshed != null) {
12494e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            mRefreshed.add(refreshed);
12594e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        }
12693ba3b9bf77395f7a148d384bac9a80ba8ce6cedMathew Inwood        for (int i = 0; i < getCount(); i++) {
12794e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            moveTo(i);
128fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert            if (shortcutId.equals(getShortcutId()) && source.equals(getSuggestionSource())) {
1299038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood                if (refreshed != null && refreshed.getCount() > 0) {
1309038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood                    if (DBG) Log.d(TAG, "replacing row " + i);
1319038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood                    replaceRow(new SuggestionPosition(refreshed));
1329038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood                } else {
1339038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood                    if (DBG) Log.d(TAG, "removing row " + i);
1349038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood                    removeRow();
1359038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood                }
1369038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood                notifyDataSetChanged();
1379038d65a5a8ebcfada1ec3067f81a26f05622088Mathew Inwood                break;
13894e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney            }
13994e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        }
14094e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    }
14194e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney
14294e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    @Override
14394e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    public void close() {
14494e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        if (DBG) Log.d(TAG, "close()");
145b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        if (mClosed) {
146b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert            throw new IllegalStateException("double close");
147b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        }
148b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        mClosed = true;
149b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        if (mShortcuts != null) {
150b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert            mShortcuts.close();
151b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        }
152b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        for (SuggestionCursor cursor : mRefreshed) {
153b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert            cursor.close();
15494e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney        }
155b83882b9efa37ec0f20a0f1c85cf5ccc93194aeeBjorn Bringert        super.close();
15694e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney    }
15794e8a2be78530170f50e7895a558bf8011bbf8e8Bryan Mawhinney}