blob: 2a97ff457e982ad48fb25a816029a69d31dd8ed6 [file] [log] [blame]
/*
* 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);
}
}
}