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}