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