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