SuggestionsAdapter.java revision 7010c51b51c97fa43d7b24d2158ecbc1d064e0a6
1/*
2 * Copyright (C) 2009 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
17package com.android.quicksearchbox.ui;
18
19import android.database.DataSetObserver;
20import android.util.Log;
21import android.view.View;
22import android.view.View.OnFocusChangeListener;
23import android.view.ViewGroup;
24import android.widget.BaseAdapter;
25
26import com.android.quicksearchbox.Corpus;
27import com.android.quicksearchbox.Promoter;
28import com.android.quicksearchbox.SuggestionCursor;
29import com.android.quicksearchbox.SuggestionPosition;
30import com.android.quicksearchbox.Suggestions;
31
32/**
33 * Uses a {@link Suggestions} object to back a {@link SuggestionsView}.
34 */
35public class SuggestionsAdapter extends BaseAdapter {
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 final SuggestionViewFactory mViewFactory;
45
46    private int mMaxPromoted;
47
48    private SuggestionCursor mCursor;
49
50    private Corpus mCorpus = null;
51
52    private Suggestions mSuggestions;
53
54    private SuggestionClickListener mSuggestionClickListener;
55    private OnFocusChangeListener mOnFocusChangeListener;
56
57    private boolean mClosed = false;
58
59    public SuggestionsAdapter(SuggestionViewFactory viewFactory) {
60        mViewFactory = viewFactory;
61    }
62
63    public void setMaxPromoted(int maxPromoted) {
64        mMaxPromoted = maxPromoted;
65        onSuggestionsChanged();
66    }
67
68    public boolean isClosed() {
69        return mClosed;
70    }
71
72    public void close() {
73        setSuggestions(null);
74        mCorpus = null;
75        mClosed = true;
76    }
77
78    public void setPromoter(Promoter promoter) {
79        mPromoter = promoter;
80        onSuggestionsChanged();
81    }
82
83    public void setSuggestionClickListener(SuggestionClickListener listener) {
84        mSuggestionClickListener = listener;
85    }
86
87    public void setOnFocusChangeListener(OnFocusChangeListener l) {
88        mOnFocusChangeListener = l;
89    }
90
91    public void setSuggestions(Suggestions suggestions) {
92        if (mSuggestions == suggestions) {
93            return;
94        }
95        if (mClosed) {
96            if (suggestions != null) {
97                suggestions.release();
98            }
99            return;
100        }
101        if (mDataSetObserver == null) {
102            mDataSetObserver = new MySuggestionsObserver();
103        }
104        // TODO: delay the change if there are no suggestions for the currently visible tab.
105        if (mSuggestions != null) {
106            mSuggestions.unregisterDataSetObserver(mDataSetObserver);
107            mSuggestions.release();
108        }
109        mSuggestions = suggestions;
110        if (mSuggestions != null) {
111            mSuggestions.registerDataSetObserver(mDataSetObserver);
112        }
113        onSuggestionsChanged();
114    }
115
116    public Suggestions getSuggestions() {
117        return mSuggestions;
118    }
119
120    /**
121     * Gets the source whose results are displayed.
122     */
123    public Corpus getCorpus() {
124        return mCorpus;
125    }
126
127    /**
128     * Sets the source whose results are displayed.
129     */
130    public void setCorpus(Corpus corpus) {
131        if (mSuggestions != null) {
132            // TODO: if (mCorpus == null && corpus != null)
133            // we've just switched from the 'All' corpus to a specific corpus
134            // we can filter the existing results immediately.
135            if (corpus != null) {
136                // Note, when switching from a specific corpus to 'All' we do not change the
137                // suggestions, since they're still relevant for 'All'. Hence 'corpus != null'
138                if (DBG) Log.d(TAG, "setCorpus(" + corpus.getName() + ") Clear suggestions");
139                mSuggestions.unregisterDataSetObserver(mDataSetObserver);
140                mSuggestions.release();
141                mSuggestions = null;
142            }
143        }
144        mCorpus = corpus;
145        onSuggestionsChanged();
146    }
147
148    public int getCount() {
149        return mCursor == null ? 0 : mCursor.getCount();
150    }
151
152    public SuggestionPosition getItem(int position) {
153        if (mCursor == null) return null;
154        return new SuggestionPosition(mCursor, position);
155    }
156
157    public long getItemId(int position) {
158        return position;
159    }
160
161    @Override
162    public int getViewTypeCount() {
163        return mViewFactory.getSuggestionViewTypeCount();
164    }
165
166    @Override
167    public int getItemViewType(int position) {
168        if (mCursor == null) {
169            return 0;
170        }
171        mCursor.moveTo(position);
172        return mViewFactory.getSuggestionViewType(mCursor);
173    }
174
175    // Implements Adapter#getView()
176    public View getView(int position, View convertView, ViewGroup parent) {
177        if (mCursor == null) {
178            throw new IllegalStateException("getView() called with null cursor");
179        }
180        mCursor.moveTo(position);
181        int viewType = mViewFactory.getSuggestionViewType(mCursor);
182        SuggestionView view = mViewFactory.getSuggestionView(viewType, convertView, parent);
183        view.bindAsSuggestion(mCursor, this);
184        View v = (View) view;
185        if (mOnFocusChangeListener != null) {
186            v.setOnFocusChangeListener(mOnFocusChangeListener);
187        }
188        return v;
189    }
190
191    protected void onSuggestionsChanged() {
192        if (DBG) Log.d(TAG, "onSuggestionsChanged(" + mSuggestions + ")");
193        SuggestionCursor cursor = getPromoted(mSuggestions, mCorpus);
194        changeCursor(cursor);
195    }
196
197    /**
198     * Gets the cursor containing the currently shown suggestions. The caller should not hold
199     * on to or modify the returned cursor.
200     */
201    public SuggestionCursor getCurrentSuggestions() {
202        return mCursor;
203    }
204
205    /**
206     * Gets the cursor for the given source.
207     */
208    protected SuggestionCursor getPromoted(Suggestions suggestions, Corpus corpus) {
209        if (suggestions == null) return null;
210        return suggestions.getPromoted(mPromoter, mMaxPromoted);
211    }
212
213    /**
214     * Replace the cursor.
215     *
216     * This does not close the old cursor. Instead, all the cursors are closed in
217     * {@link #setSuggestions(Suggestions)}.
218     */
219    private void changeCursor(SuggestionCursor newCursor) {
220        if (DBG) Log.d(TAG, "changeCursor(" + newCursor + ")");
221        if (newCursor == mCursor) {
222            if (newCursor != null) {
223                // Shortcuts may have changed without the cursor changing.
224                notifyDataSetChanged();
225            }
226            return;
227        }
228        mCursor = newCursor;
229        if (mCursor != null) {
230            notifyDataSetChanged();
231        } else {
232            notifyDataSetInvalidated();
233        }
234    }
235
236    public void onSuggestionClicked(int position) {
237        if (mSuggestionClickListener != null) {
238            mSuggestionClickListener.onSuggestionClicked(this, position);
239        }
240    }
241
242    public void onSuggestionQuickContactClicked(int position) {
243        if (mSuggestionClickListener != null) {
244            mSuggestionClickListener.onSuggestionQuickContactClicked(this, position);
245        }
246    }
247
248    public boolean onSuggestionLongClicked(int position) {
249        if (mSuggestionClickListener != null) {
250            return mSuggestionClickListener.onSuggestionLongClicked(this, position);
251        }
252        return false;
253    }
254
255    public void onSuggestionQueryRefineClicked(int position) {
256        if (mSuggestionClickListener != null) {
257            mSuggestionClickListener.onSuggestionQueryRefineClicked(this, position);
258        }
259    }
260
261    private class MySuggestionsObserver extends DataSetObserver {
262        @Override
263        public void onChanged() {
264            onSuggestionsChanged();
265        }
266    }
267
268}
269