| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.quicksearchbox.ui; |
| |
| import com.android.quicksearchbox.Promoter; |
| import com.android.quicksearchbox.Suggestion; |
| import com.android.quicksearchbox.SuggestionCursor; |
| import com.android.quicksearchbox.SuggestionPosition; |
| import com.android.quicksearchbox.Suggestions; |
| |
| import android.database.DataSetObserver; |
| import android.util.Log; |
| import android.view.View; |
| import android.view.View.OnFocusChangeListener; |
| import android.view.ViewGroup; |
| |
| import java.util.HashMap; |
| |
| /** |
| * Base class for suggestions adapters. The templated class A is the list adapter class. |
| */ |
| public abstract class SuggestionsAdapterBase<A> implements SuggestionsAdapter<A> { |
| |
| private static final boolean DBG = false; |
| private static final String TAG = "QSB.SuggestionsAdapter"; |
| |
| private DataSetObserver mDataSetObserver; |
| |
| private Promoter mPromoter; |
| |
| private int mMaxPromoted; |
| |
| private SuggestionCursor mPromotedSuggestions; |
| private final HashMap<String, Integer> mViewTypeMap; |
| private final SuggestionViewFactory mViewFactory; |
| |
| private Suggestions mSuggestions; |
| |
| private SuggestionClickListener mSuggestionClickListener; |
| private OnFocusChangeListener mOnFocusChangeListener; |
| |
| private boolean mClosed = false; |
| |
| protected SuggestionsAdapterBase(SuggestionViewFactory viewFactory) { |
| mViewFactory = viewFactory; |
| mViewTypeMap = new HashMap<String, Integer>(); |
| for (String viewType : mViewFactory.getSuggestionViewTypes()) { |
| if (!mViewTypeMap.containsKey(viewType)) { |
| mViewTypeMap.put(viewType, mViewTypeMap.size()); |
| } |
| } |
| } |
| |
| public abstract boolean isEmpty(); |
| |
| /** |
| * Indicates if this adapter will publish suggestions other than those in the promoted list. |
| */ |
| public abstract boolean willPublishNonPromotedSuggestions(); |
| |
| public void setMaxPromoted(int maxPromoted) { |
| if (DBG) Log.d(TAG, "setMaxPromoted " + maxPromoted); |
| mMaxPromoted = maxPromoted; |
| onSuggestionsChanged(); |
| } |
| |
| public boolean isClosed() { |
| return mClosed; |
| } |
| |
| public void close() { |
| setSuggestions(null); |
| mClosed = true; |
| } |
| |
| public void setPromoter(Promoter promoter) { |
| mPromoter = promoter; |
| onSuggestionsChanged(); |
| } |
| |
| public void setSuggestionClickListener(SuggestionClickListener listener) { |
| mSuggestionClickListener = listener; |
| } |
| |
| public void setOnFocusChangeListener(OnFocusChangeListener l) { |
| mOnFocusChangeListener = l; |
| } |
| |
| public void setSuggestions(Suggestions suggestions) { |
| if (mSuggestions == suggestions) { |
| return; |
| } |
| if (mClosed) { |
| if (suggestions != null) { |
| suggestions.release(); |
| } |
| return; |
| } |
| if (mDataSetObserver == null) { |
| mDataSetObserver = new MySuggestionsObserver(); |
| } |
| // TODO: delay the change if there are no suggestions for the currently visible tab. |
| if (mSuggestions != null) { |
| mSuggestions.unregisterDataSetObserver(mDataSetObserver); |
| mSuggestions.release(); |
| } |
| mSuggestions = suggestions; |
| if (mSuggestions != null) { |
| mSuggestions.registerDataSetObserver(mDataSetObserver); |
| } |
| onSuggestionsChanged(); |
| } |
| |
| public Suggestions getSuggestions() { |
| return mSuggestions; |
| } |
| |
| public abstract SuggestionPosition getSuggestion(long suggestionId); |
| |
| protected int getPromotedCount() { |
| return mPromotedSuggestions == null ? 0 : mPromotedSuggestions.getCount(); |
| } |
| |
| protected SuggestionPosition getPromotedSuggestion(int position) { |
| if (mPromotedSuggestions == null) return null; |
| return new SuggestionPosition(mPromotedSuggestions, position); |
| } |
| |
| protected int getViewTypeCount() { |
| return mViewTypeMap.size(); |
| } |
| |
| private String suggestionViewType(Suggestion suggestion) { |
| String viewType = mViewFactory.getViewType(suggestion); |
| if (!mViewTypeMap.containsKey(viewType)) { |
| throw new IllegalStateException("Unknown viewType " + viewType); |
| } |
| return viewType; |
| } |
| |
| protected int getSuggestionViewType(SuggestionCursor cursor, int position) { |
| if (cursor == null) { |
| return 0; |
| } |
| cursor.moveTo(position); |
| return mViewTypeMap.get(suggestionViewType(cursor)); |
| } |
| |
| protected int getSuggestionViewTypeCount() { |
| return mViewTypeMap.size(); |
| } |
| |
| protected View getView(SuggestionCursor suggestions, int position, long suggestionId, |
| View convertView, ViewGroup parent) { |
| suggestions.moveTo(position); |
| View v = mViewFactory.getView(suggestions, suggestions.getUserQuery(), convertView, parent); |
| if (v instanceof SuggestionView) { |
| ((SuggestionView) v).bindAdapter(this, suggestionId); |
| } else { |
| SuggestionViewClickListener l = new SuggestionViewClickListener(suggestionId); |
| v.setOnClickListener(l); |
| } |
| |
| if (mOnFocusChangeListener != null) { |
| v.setOnFocusChangeListener(mOnFocusChangeListener); |
| } |
| return v; |
| } |
| |
| protected void onSuggestionsChanged() { |
| if (DBG) Log.d(TAG, "onSuggestionsChanged(" + mSuggestions + ")"); |
| SuggestionCursor cursor = getPromoted(mSuggestions); |
| changePromoted(cursor); |
| } |
| |
| /** |
| * Gets the cursor containing the currently shown suggestions. The caller should not hold |
| * on to or modify the returned cursor. |
| */ |
| public SuggestionCursor getCurrentPromotedSuggestions() { |
| return mPromotedSuggestions; |
| } |
| |
| /** |
| * Gets the cursor for the given source. |
| */ |
| protected SuggestionCursor getPromoted(Suggestions suggestions) { |
| if (suggestions == null) return null; |
| return suggestions.getPromoted(mPromoter, mMaxPromoted); |
| } |
| |
| /** |
| * Replace the cursor. |
| * |
| * This does not close the old cursor. Instead, all the cursors are closed in |
| * {@link #setSuggestions(Suggestions)}. |
| */ |
| private void changePromoted(SuggestionCursor newCursor) { |
| if (DBG) { |
| Log.d(TAG, "changeCursor(" + newCursor + ") count=" + |
| (newCursor == null ? 0 : newCursor.getCount())); |
| } |
| if (newCursor == mPromotedSuggestions) { |
| if (newCursor != null) { |
| // Shortcuts may have changed without the cursor changing. |
| notifyDataSetChanged(); |
| } |
| return; |
| } |
| mPromotedSuggestions = newCursor; |
| if (mPromotedSuggestions != null) { |
| notifyDataSetChanged(); |
| } else { |
| notifyDataSetInvalidated(); |
| } |
| } |
| |
| public void onSuggestionClicked(long suggestionId) { |
| if (mClosed) { |
| Log.w(TAG, "onSuggestionClicked after close"); |
| } else if (mSuggestionClickListener != null) { |
| mSuggestionClickListener.onSuggestionClicked(this, suggestionId); |
| } |
| } |
| |
| public void onSuggestionQuickContactClicked(long suggestionId) { |
| if (mClosed) { |
| Log.w(TAG, "onSuggestionQuickContactClicked after close"); |
| } else if (mSuggestionClickListener != null) { |
| mSuggestionClickListener.onSuggestionQuickContactClicked(this, suggestionId); |
| } |
| } |
| |
| public void onSuggestionRemoveFromHistoryClicked(long suggestionId) { |
| if (mClosed) { |
| Log.w(TAG, "onSuggestionRemoveFromHistoryClicked after close"); |
| } else if (mSuggestionClickListener != null) { |
| mSuggestionClickListener.onSuggestionRemoveFromHistoryClicked(this, suggestionId); |
| } |
| } |
| |
| public void onSuggestionQueryRefineClicked(long suggestionId) { |
| if (mClosed) { |
| Log.w(TAG, "onSuggestionQueryRefineClicked after close"); |
| } else if (mSuggestionClickListener != null) { |
| mSuggestionClickListener.onSuggestionQueryRefineClicked(this, suggestionId); |
| } |
| } |
| |
| public abstract A getListAdapter(); |
| |
| protected abstract void notifyDataSetInvalidated(); |
| |
| protected abstract void notifyDataSetChanged(); |
| |
| private class MySuggestionsObserver extends DataSetObserver { |
| @Override |
| public void onChanged() { |
| onSuggestionsChanged(); |
| } |
| } |
| |
| private class SuggestionViewClickListener implements View.OnClickListener { |
| private final long mSuggestionId; |
| public SuggestionViewClickListener(long suggestionId) { |
| mSuggestionId = suggestionId; |
| } |
| public void onClick(View v) { |
| onSuggestionClicked(mSuggestionId); |
| } |
| } |
| |
| } |