• 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 package com.android.quicksearchbox.ui;
17 
18 import com.android.quicksearchbox.Promoter;
19 import com.android.quicksearchbox.Suggestion;
20 import com.android.quicksearchbox.SuggestionCursor;
21 import com.android.quicksearchbox.SuggestionPosition;
22 import com.android.quicksearchbox.Suggestions;
23 
24 import android.database.DataSetObserver;
25 import android.util.Log;
26 import android.view.View;
27 import android.view.View.OnFocusChangeListener;
28 import android.view.ViewGroup;
29 
30 import java.util.HashMap;
31 
32 /**
33  * Base class for suggestions adapters. The templated class A is the list adapter class.
34  */
35 public abstract class SuggestionsAdapterBase<A> implements SuggestionsAdapter<A> {
36 
37     private static final boolean DBG = false;
38     private static final String TAG = "QSB.SuggestionsAdapter";
39 
40     private DataSetObserver mDataSetObserver;
41 
42     private Promoter mPromoter;
43 
44     private int mMaxPromoted;
45 
46     private SuggestionCursor mPromotedSuggestions;
47     private final HashMap<String, Integer> mViewTypeMap;
48     private final SuggestionViewFactory mViewFactory;
49 
50     private Suggestions mSuggestions;
51 
52     private SuggestionClickListener mSuggestionClickListener;
53     private OnFocusChangeListener mOnFocusChangeListener;
54 
55     private boolean mClosed = false;
56 
SuggestionsAdapterBase(SuggestionViewFactory viewFactory)57     protected SuggestionsAdapterBase(SuggestionViewFactory viewFactory) {
58         mViewFactory = viewFactory;
59         mViewTypeMap = new HashMap<String, Integer>();
60         for (String viewType : mViewFactory.getSuggestionViewTypes()) {
61             if (!mViewTypeMap.containsKey(viewType)) {
62                 mViewTypeMap.put(viewType, mViewTypeMap.size());
63             }
64         }
65     }
66 
isEmpty()67     public abstract boolean isEmpty();
68 
69     /**
70      * Indicates if this adapter will publish suggestions other than those in the promoted list.
71      */
willPublishNonPromotedSuggestions()72     public abstract boolean willPublishNonPromotedSuggestions();
73 
setMaxPromoted(int maxPromoted)74     public void setMaxPromoted(int maxPromoted) {
75         if (DBG) Log.d(TAG, "setMaxPromoted " + maxPromoted);
76         mMaxPromoted = maxPromoted;
77         onSuggestionsChanged();
78     }
79 
isClosed()80     public boolean isClosed() {
81         return mClosed;
82     }
83 
close()84     public void close() {
85         setSuggestions(null);
86         mClosed = true;
87     }
88 
setPromoter(Promoter promoter)89     public void setPromoter(Promoter promoter) {
90         mPromoter = promoter;
91         onSuggestionsChanged();
92     }
93 
setSuggestionClickListener(SuggestionClickListener listener)94     public void setSuggestionClickListener(SuggestionClickListener listener) {
95         mSuggestionClickListener = listener;
96     }
97 
setOnFocusChangeListener(OnFocusChangeListener l)98     public void setOnFocusChangeListener(OnFocusChangeListener l) {
99         mOnFocusChangeListener = l;
100     }
101 
setSuggestions(Suggestions suggestions)102     public void setSuggestions(Suggestions suggestions) {
103         if (mSuggestions == suggestions) {
104             return;
105         }
106         if (mClosed) {
107             if (suggestions != null) {
108                 suggestions.release();
109             }
110             return;
111         }
112         if (mDataSetObserver == null) {
113             mDataSetObserver = new MySuggestionsObserver();
114         }
115         // TODO: delay the change if there are no suggestions for the currently visible tab.
116         if (mSuggestions != null) {
117             mSuggestions.unregisterDataSetObserver(mDataSetObserver);
118             mSuggestions.release();
119         }
120         mSuggestions = suggestions;
121         if (mSuggestions != null) {
122             mSuggestions.registerDataSetObserver(mDataSetObserver);
123         }
124         onSuggestionsChanged();
125     }
126 
getSuggestions()127     public Suggestions getSuggestions() {
128         return mSuggestions;
129     }
130 
getSuggestion(long suggestionId)131     public abstract SuggestionPosition getSuggestion(long suggestionId);
132 
getPromotedCount()133     protected int getPromotedCount() {
134         return mPromotedSuggestions == null ? 0 : mPromotedSuggestions.getCount();
135     }
136 
getPromotedSuggestion(int position)137     protected SuggestionPosition getPromotedSuggestion(int position) {
138         if (mPromotedSuggestions == null) return null;
139         return new SuggestionPosition(mPromotedSuggestions, position);
140     }
141 
getViewTypeCount()142     protected int getViewTypeCount() {
143         return mViewTypeMap.size();
144     }
145 
suggestionViewType(Suggestion suggestion)146     private String suggestionViewType(Suggestion suggestion) {
147         String viewType = mViewFactory.getViewType(suggestion);
148         if (!mViewTypeMap.containsKey(viewType)) {
149             throw new IllegalStateException("Unknown viewType " + viewType);
150         }
151         return viewType;
152     }
153 
getSuggestionViewType(SuggestionCursor cursor, int position)154     protected int getSuggestionViewType(SuggestionCursor cursor, int position) {
155         if (cursor == null) {
156             return 0;
157         }
158         cursor.moveTo(position);
159         return mViewTypeMap.get(suggestionViewType(cursor));
160     }
161 
getSuggestionViewTypeCount()162     protected int getSuggestionViewTypeCount() {
163         return mViewTypeMap.size();
164     }
165 
getView(SuggestionCursor suggestions, int position, long suggestionId, View convertView, ViewGroup parent)166     protected View getView(SuggestionCursor suggestions, int position, long suggestionId,
167             View convertView, ViewGroup parent) {
168         suggestions.moveTo(position);
169         View v = mViewFactory.getView(suggestions, suggestions.getUserQuery(), convertView, parent);
170         if (v instanceof SuggestionView) {
171             ((SuggestionView) v).bindAdapter(this, suggestionId);
172         } else {
173             SuggestionViewClickListener l = new SuggestionViewClickListener(suggestionId);
174             v.setOnClickListener(l);
175         }
176 
177         if (mOnFocusChangeListener != null) {
178             v.setOnFocusChangeListener(mOnFocusChangeListener);
179         }
180         return v;
181     }
182 
onSuggestionsChanged()183     protected void onSuggestionsChanged() {
184         if (DBG) Log.d(TAG, "onSuggestionsChanged(" + mSuggestions + ")");
185         SuggestionCursor cursor = getPromoted(mSuggestions);
186         changePromoted(cursor);
187     }
188 
189     /**
190      * Gets the cursor containing the currently shown suggestions. The caller should not hold
191      * on to or modify the returned cursor.
192      */
getCurrentPromotedSuggestions()193     public SuggestionCursor getCurrentPromotedSuggestions() {
194         return mPromotedSuggestions;
195     }
196 
197     /**
198      * Gets the cursor for the given source.
199      */
getPromoted(Suggestions suggestions)200     protected SuggestionCursor getPromoted(Suggestions suggestions) {
201         if (suggestions == null) return null;
202         return suggestions.getPromoted(mPromoter, mMaxPromoted);
203     }
204 
205     /**
206      * Replace the cursor.
207      *
208      * This does not close the old cursor. Instead, all the cursors are closed in
209      * {@link #setSuggestions(Suggestions)}.
210      */
changePromoted(SuggestionCursor newCursor)211     private void changePromoted(SuggestionCursor newCursor) {
212         if (DBG) {
213             Log.d(TAG, "changeCursor(" + newCursor + ") count=" +
214                     (newCursor == null ? 0 : newCursor.getCount()));
215         }
216         if (newCursor == mPromotedSuggestions) {
217             if (newCursor != null) {
218                 // Shortcuts may have changed without the cursor changing.
219                 notifyDataSetChanged();
220             }
221             return;
222         }
223         mPromotedSuggestions = newCursor;
224         if (mPromotedSuggestions != null) {
225             notifyDataSetChanged();
226         } else {
227             notifyDataSetInvalidated();
228         }
229     }
230 
onSuggestionClicked(long suggestionId)231     public void onSuggestionClicked(long suggestionId) {
232         if (mClosed) {
233             Log.w(TAG, "onSuggestionClicked after close");
234         } else if (mSuggestionClickListener != null) {
235             mSuggestionClickListener.onSuggestionClicked(this, suggestionId);
236         }
237     }
238 
onSuggestionQuickContactClicked(long suggestionId)239     public void onSuggestionQuickContactClicked(long suggestionId) {
240         if (mClosed) {
241             Log.w(TAG, "onSuggestionQuickContactClicked after close");
242         } else if (mSuggestionClickListener != null) {
243             mSuggestionClickListener.onSuggestionQuickContactClicked(this, suggestionId);
244         }
245     }
246 
onSuggestionRemoveFromHistoryClicked(long suggestionId)247     public void onSuggestionRemoveFromHistoryClicked(long suggestionId) {
248         if (mClosed) {
249             Log.w(TAG, "onSuggestionRemoveFromHistoryClicked after close");
250         } else if (mSuggestionClickListener != null) {
251             mSuggestionClickListener.onSuggestionRemoveFromHistoryClicked(this, suggestionId);
252         }
253     }
254 
onSuggestionQueryRefineClicked(long suggestionId)255     public void onSuggestionQueryRefineClicked(long suggestionId) {
256         if (mClosed) {
257             Log.w(TAG, "onSuggestionQueryRefineClicked after close");
258         } else if (mSuggestionClickListener != null) {
259             mSuggestionClickListener.onSuggestionQueryRefineClicked(this, suggestionId);
260         }
261     }
262 
getListAdapter()263     public abstract A getListAdapter();
264 
notifyDataSetInvalidated()265     protected abstract void notifyDataSetInvalidated();
266 
notifyDataSetChanged()267     protected abstract void notifyDataSetChanged();
268 
269     private class MySuggestionsObserver extends DataSetObserver {
270         @Override
onChanged()271         public void onChanged() {
272             onSuggestionsChanged();
273         }
274     }
275 
276     private class SuggestionViewClickListener implements View.OnClickListener {
277         private final long mSuggestionId;
SuggestionViewClickListener(long suggestionId)278         public SuggestionViewClickListener(long suggestionId) {
279             mSuggestionId = suggestionId;
280         }
onClick(View v)281         public void onClick(View v) {
282             onSuggestionClicked(mSuggestionId);
283         }
284     }
285 
286 }
287