SuggestionsAdapter.java revision 2fd5aa82682f077660faa8ec60c04238026a8731
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 com.android.quicksearchbox.Corpus;
20import com.android.quicksearchbox.SuggestionCursor;
21import com.android.quicksearchbox.SuggestionPosition;
22import com.android.quicksearchbox.Suggestions;
23
24import android.database.DataSetObserver;
25import android.util.Log;
26import android.view.View;
27import android.view.ViewGroup;
28import android.widget.BaseAdapter;
29
30/**
31 * Uses a {@link Suggestions} object to back a {@link SuggestionsView}.
32 */
33public class SuggestionsAdapter extends BaseAdapter {
34
35    private static final boolean DBG = false;
36    private static final String TAG = "QSB.SuggestionsAdapter";
37
38    private DataSetObserver mDataSetObserver;
39
40    private final SuggestionViewFactory mViewFactory;
41
42    private SuggestionCursor mCursor;
43
44    private Corpus mCorpus = null;
45
46    private Suggestions mSuggestions;
47
48    private boolean mClosed = false;
49
50    public SuggestionsAdapter(SuggestionViewFactory viewFactory) {
51        mViewFactory = viewFactory;
52    }
53
54    public boolean isClosed() {
55        return mClosed;
56    }
57
58    public void close() {
59        setSuggestions(null);
60        mCorpus = null;
61        mClosed = true;
62    }
63
64    public void setSuggestions(Suggestions suggestions) {
65        if (mSuggestions == suggestions) {
66            return;
67        }
68        if (mClosed) {
69            if (suggestions != null) {
70                suggestions.close();
71            }
72            return;
73        }
74        if (mDataSetObserver == null) {
75            mDataSetObserver = new MySuggestionsObserver();
76        }
77        // TODO: delay the change if there are no suggestions for the currently visible tab.
78        if (mSuggestions != null) {
79            mSuggestions.unregisterDataSetObserver(mDataSetObserver);
80            mSuggestions.close();
81        }
82        mSuggestions = suggestions;
83        if (mSuggestions != null) {
84            mSuggestions.registerDataSetObserver(mDataSetObserver);
85        }
86        onSuggestionsChanged();
87    }
88
89    public Suggestions getSuggestions() {
90        return mSuggestions;
91    }
92
93    /**
94     * Gets the source whose results are displayed.
95     */
96    public Corpus getCorpus() {
97        return mCorpus;
98    }
99
100    /**
101     * Sets the source whose results are displayed.
102     */
103    public void setCorpus(Corpus corpus) {
104        if (mSuggestions != null) {
105            if ((mCorpus == null) && (corpus != null)) {
106                // we've just switched from the 'All' corpus to a specific corpus
107                // we can filter the existing results immediately.
108                if (DBG) Log.d(TAG, "setCorpus(" + corpus.getName() + ") Filter suggestions");
109                mSuggestions.filterByCorpus(corpus);
110            } else if (corpus != null) {
111                // Note, when switching from a specific corpus to 'All' we do not change the
112                // suggestions, since they're still relevant for 'All'. Hence 'corpus != null'
113                if (DBG) Log.d(TAG, "setCorpus(" + corpus.getName() + ") Clear suggestions");
114                mSuggestions.unregisterDataSetObserver(mDataSetObserver);
115                mSuggestions.close();
116                mSuggestions = null;
117            }
118        }
119        mCorpus = corpus;
120        onSuggestionsChanged();
121    }
122
123    public int getCount() {
124        return mCursor == null ? 0 : mCursor.getCount();
125    }
126
127    public SuggestionPosition getItem(int position) {
128        if (mCursor == null) return null;
129        return new SuggestionPosition(mCursor, position);
130    }
131
132    public long getItemId(int position) {
133        return position;
134    }
135
136    @Override
137    public int getViewTypeCount() {
138        return mViewFactory.getSuggestionViewTypeCount();
139    }
140
141    @Override
142    public int getItemViewType(int position) {
143        if (mCursor == null) {
144            return 0;
145        }
146        mCursor.moveTo(position);
147        return mViewFactory.getSuggestionViewType(mCursor);
148    }
149
150    public View getView(int position, View convertView, ViewGroup parent) {
151        if (mCursor == null) {
152            throw new IllegalStateException("getView() called with null cursor");
153        }
154        mCursor.moveTo(position);
155        int viewType = mViewFactory.getSuggestionViewType(mCursor);
156        SuggestionView view = mViewFactory.getSuggestionView(viewType, convertView, parent);
157        view.bindAsSuggestion(mCursor);
158        return (View) view;
159    }
160
161    protected void onSuggestionsChanged() {
162        if (DBG) Log.d(TAG, "onSuggestionsChanged(" + mSuggestions + ")");
163        SuggestionCursor cursor = getCorpusCursor(mSuggestions, mCorpus);
164        changeCursor(cursor);
165    }
166
167    /**
168     * Gets the cursor containing the currently shown suggestions. The caller should not hold
169     * on to or modify the returned cursor.
170     */
171    public SuggestionCursor getCurrentSuggestions() {
172        return mCursor;
173    }
174
175    /**
176     * Gets the cursor for the given source.
177     */
178    protected SuggestionCursor getCorpusCursor(Suggestions suggestions, Corpus corpus) {
179        if (suggestions == null) return null;
180        return suggestions.getPromoted();
181    }
182
183    /**
184     * Replace the cursor.
185     *
186     * This does not close the old cursor. Instead, all the cursors are closed in
187     * {@link #setSuggestions(Suggestions)}.
188     */
189    private void changeCursor(SuggestionCursor newCursor) {
190        if (DBG) Log.d(TAG, "changeCursor(" + newCursor + ")");
191        if (newCursor == mCursor) {
192            // Shortcuts may have changed without the cursor changing.
193            notifyDataSetChanged();
194            return;
195        }
196        mCursor = newCursor;
197        if (mCursor != null) {
198            // TODO: Register observers here to watch for
199            // changes in the cursor, e.g. shortcut refreshes?
200            notifyDataSetChanged();
201        } else {
202            notifyDataSetInvalidated();
203        }
204    }
205
206    private class MySuggestionsObserver extends DataSetObserver {
207        @Override
208        public void onChanged() {
209            onSuggestionsChanged();
210        }
211    }
212
213}
214