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