Home | History | Annotate | Download | only in quicksearchbox
      1 /*
      2  * Copyright (C) 2010 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 com.google.common.annotations.VisibleForTesting;
     20 
     21 import android.os.Handler;
     22 import android.util.Log;
     23 
     24 import java.util.HashSet;
     25 
     26 /**
     27  * A SuggestionCursor that allows shortcuts to be updated by overlaying
     28  * with results from another cursor.
     29  */
     30 public class ShortcutCursor extends ListSuggestionCursor {
     31 
     32     private static final boolean DBG = false;
     33     private static final String TAG = "QSB.ShortcutCursor";
     34 
     35     // mShortcuts is used to close the underlying cursor when we're closed.
     36     private final SuggestionCursor mShortcuts;
     37     // mRefreshed contains all the cursors that have been refreshed, so that
     38     // they can be closed when ShortcutCursor is closed.
     39     private final HashSet<SuggestionCursor> mRefreshed;
     40 
     41     private boolean mClosed = false;
     42 
     43     private final ShortcutRefresher mRefresher;
     44     private final ShortcutRepository mShortcutRepo;
     45     private final Handler mUiThread;
     46 
     47     private ShortcutCursor(String query, SuggestionCursor shortcuts, Handler uiThread,
     48             ShortcutRefresher refresher, ShortcutRepository repository) {
     49         super(query);
     50         mShortcuts = shortcuts;
     51         mUiThread = uiThread;
     52         mRefresher = refresher;
     53         mShortcutRepo = repository;
     54         mRefreshed = new HashSet<SuggestionCursor>();
     55     }
     56 
     57     @VisibleForTesting
     58     ShortcutCursor(String query, Handler uiThread,
     59             ShortcutRefresher refresher, ShortcutRepository repository) {
     60         this(query, null, uiThread, refresher, repository);
     61     }
     62 
     63     @VisibleForTesting
     64     ShortcutCursor(SuggestionCursor suggestions) {
     65         this(suggestions, true, null, null, null);
     66     }
     67 
     68     public ShortcutCursor(SuggestionCursor suggestions, boolean allowWebSearchShortcuts,
     69             Handler uiThread, ShortcutRefresher refresher, ShortcutRepository repository) {
     70         this(suggestions.getUserQuery(), suggestions, uiThread, refresher, repository);
     71         int count = suggestions.getCount();
     72         if (DBG) Log.d(TAG, "Total shortcuts: " + count);
     73         for (int i = 0; i < count; i++) {
     74             suggestions.moveTo(i);
     75             if (suggestions.getSuggestionSource() != null
     76                     && (allowWebSearchShortcuts || !suggestions.isWebSearchSuggestion())) {
     77                 add(new SuggestionPosition(suggestions));
     78             } else {
     79                 if (DBG) Log.d(TAG, "Skipping shortcut " + i);
     80             }
     81         }
     82     }
     83 
     84     @Override
     85     public boolean isSuggestionShortcut() {
     86         // Needed to make refreshed shortcuts be treated as shortcuts
     87         return true;
     88     }
     89 
     90     /**
     91      * Refresh a shortcut from this cursor.
     92      *
     93      * @param shortcut The shortcut to refresh. Should be a shortcut taken from this cursor.
     94      */
     95     public void refresh(Suggestion shortcut) {
     96         mRefresher.refresh(shortcut, new ShortcutRefresher.Listener() {
     97             public void onShortcutRefreshed(final Source source,
     98                     final String shortcutId, final SuggestionCursor refreshed) {
     99                 if (DBG) Log.d(TAG, "Shortcut refreshed: " + shortcutId);
    100                 mShortcutRepo.updateShortcut(source, shortcutId, refreshed);
    101                 mUiThread.post(new Runnable() {
    102                     public void run() {
    103                         refresh(source, shortcutId, refreshed);
    104                     }
    105                 });
    106             }
    107         });
    108     }
    109 
    110     /**
    111      * Updates this SuggestionCursor with a refreshed result from another.
    112      * Since this modifies the cursor, it should be called on the UI thread.
    113      * This class assumes responsibility for closing refreshed.
    114      */
    115     private void refresh(Source source, String shortcutId, SuggestionCursor refreshed) {
    116         if (DBG) Log.d(TAG, "refresh " + shortcutId);
    117         if (mClosed) {
    118             if (refreshed != null) {
    119                 refreshed.close();
    120             }
    121             return;
    122         }
    123         if (refreshed != null) {
    124             mRefreshed.add(refreshed);
    125         }
    126         for (int i = 0; i < getCount(); i++) {
    127             moveTo(i);
    128             if (shortcutId.equals(getShortcutId()) && source.equals(getSuggestionSource())) {
    129                 if (refreshed != null && refreshed.getCount() > 0) {
    130                     if (DBG) Log.d(TAG, "replacing row " + i);
    131                     replaceRow(new SuggestionPosition(refreshed));
    132                 } else {
    133                     if (DBG) Log.d(TAG, "removing row " + i);
    134                     removeRow();
    135                 }
    136                 notifyDataSetChanged();
    137                 break;
    138             }
    139         }
    140     }
    141 
    142     @Override
    143     public void close() {
    144         if (DBG) Log.d(TAG, "close()");
    145         if (mClosed) {
    146             throw new IllegalStateException("double close");
    147         }
    148         mClosed = true;
    149         if (mShortcuts != null) {
    150             mShortcuts.close();
    151         }
    152         for (SuggestionCursor cursor : mRefreshed) {
    153             cursor.close();
    154         }
    155         super.close();
    156     }
    157 }