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