• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ShortcutCursor(String query, SuggestionCursor shortcuts, Handler uiThread, ShortcutRefresher refresher, ShortcutRepository repository)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
ShortcutCursor(String query, Handler uiThread, ShortcutRefresher refresher, ShortcutRepository repository)58     ShortcutCursor(String query, Handler uiThread,
59             ShortcutRefresher refresher, ShortcutRepository repository) {
60         this(query, null, uiThread, refresher, repository);
61     }
62 
63     @VisibleForTesting
ShortcutCursor(SuggestionCursor suggestions)64     ShortcutCursor(SuggestionCursor suggestions) {
65         this(suggestions, true, null, null, null);
66     }
67 
ShortcutCursor(SuggestionCursor suggestions, boolean allowWebSearchShortcuts, Handler uiThread, ShortcutRefresher refresher, ShortcutRepository repository)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
isSuggestionShortcut()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      */
refresh(Suggestion shortcut)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      */
refresh(Source source, String shortcutId, SuggestionCursor refreshed)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
close()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 }