/* * 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.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 implements SuggestionsAdapter { private static final boolean DBG = false; private static final String TAG = "QSB.SuggestionsAdapter"; private DataSetObserver mDataSetObserver; private SuggestionCursor mCurrentSuggestions; private final HashMap 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(); for (String viewType : mViewFactory.getSuggestionViewTypes()) { if (!mViewTypeMap.containsKey(viewType)) { mViewTypeMap.put(viewType, mViewTypeMap.size()); } } } @Override public abstract boolean isEmpty(); public boolean isClosed() { return mClosed; } public void close() { setSuggestions(null); mClosed = true; } @Override public void setSuggestionClickListener(SuggestionClickListener listener) { mSuggestionClickListener = listener; } @Override public void setOnFocusChangeListener(OnFocusChangeListener l) { mOnFocusChangeListener = l; } @Override 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(); } @Override public Suggestions getSuggestions() { return mSuggestions; } @Override public abstract SuggestionPosition getSuggestion(long suggestionId); protected int getCount() { return mCurrentSuggestions == null ? 0 : mCurrentSuggestions.getCount(); } protected SuggestionPosition getSuggestion(int position) { if (mCurrentSuggestions == null) return null; return new SuggestionPosition(mCurrentSuggestions, 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 = null; if (mSuggestions != null) { cursor = mSuggestions.getResult(); } changeSuggestions(cursor); } public SuggestionCursor getCurrentSuggestions() { return mCurrentSuggestions; } /** * Replace the cursor. * * This does not close the old cursor. Instead, all the cursors are closed in * {@link #setSuggestions(Suggestions)}. */ private void changeSuggestions(SuggestionCursor newCursor) { if (DBG) { Log.d(TAG, "changeCursor(" + newCursor + ") count=" + (newCursor == null ? 0 : newCursor.getCount())); } if (newCursor == mCurrentSuggestions) { if (newCursor != null) { // Shortcuts may have changed without the cursor changing. notifyDataSetChanged(); } return; } mCurrentSuggestions = newCursor; if (mCurrentSuggestions != null) { notifyDataSetChanged(); } else { notifyDataSetInvalidated(); } } @Override public void onSuggestionClicked(long suggestionId) { if (mClosed) { Log.w(TAG, "onSuggestionClicked after close"); } else if (mSuggestionClickListener != null) { mSuggestionClickListener.onSuggestionClicked(this, suggestionId); } } @Override public void onSuggestionQueryRefineClicked(long suggestionId) { if (mClosed) { Log.w(TAG, "onSuggestionQueryRefineClicked after close"); } else if (mSuggestionClickListener != null) { mSuggestionClickListener.onSuggestionQueryRefineClicked(this, suggestionId); } } @Override 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; } @Override public void onClick(View v) { onSuggestionClicked(mSuggestionId); } } }