michael@0: /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.home; michael@0: michael@0: import org.mozilla.gecko.R; michael@0: import org.mozilla.gecko.db.BrowserContract.HomeItems; michael@0: import org.mozilla.gecko.home.HomePager.OnUrlOpenListener; michael@0: import org.mozilla.gecko.home.HomeConfig.EmptyViewConfig; michael@0: import org.mozilla.gecko.home.HomeConfig.ItemHandler; michael@0: import org.mozilla.gecko.home.HomeConfig.PanelConfig; michael@0: import org.mozilla.gecko.home.HomeConfig.ViewConfig; michael@0: import org.mozilla.gecko.util.StringUtils; michael@0: michael@0: import android.content.Context; michael@0: import android.database.Cursor; michael@0: import android.os.Parcel; michael@0: import android.os.Parcelable; michael@0: import android.text.TextUtils; michael@0: import android.util.Log; michael@0: import android.util.SparseArray; michael@0: import android.view.KeyEvent; michael@0: import android.view.LayoutInflater; michael@0: import android.view.View; michael@0: import android.view.ViewGroup; michael@0: import android.widget.FrameLayout; michael@0: import android.widget.ImageView; michael@0: import android.widget.LinearLayout; michael@0: import android.widget.TextView; michael@0: michael@0: import java.lang.ref.SoftReference; michael@0: import java.util.EnumSet; michael@0: import java.util.LinkedList; michael@0: import java.util.Map; michael@0: import java.util.WeakHashMap; michael@0: michael@0: import com.squareup.picasso.Picasso; michael@0: michael@0: /** michael@0: * {@code PanelLayout} is the base class for custom layouts to be michael@0: * used in {@code DynamicPanel}. It provides the basic framework michael@0: * that enables custom layouts to request and reset datasets and michael@0: * create panel views. Furthermore, it automates most of the process michael@0: * of binding panel views with their respective datasets. michael@0: * michael@0: * {@code PanelLayout} abstracts the implemention details of how michael@0: * datasets are actually loaded through the {@DatasetHandler} interface. michael@0: * {@code DatasetHandler} provides two operations: request and reset. michael@0: * The results of the dataset requests done via the {@code DatasetHandler} michael@0: * are delivered to the {@code PanelLayout} with the {@code deliverDataset()} michael@0: * method. michael@0: * michael@0: * Subclasses of {@code PanelLayout} should simply use the utilities michael@0: * provided by {@code PanelLayout}. Namely: michael@0: * michael@0: * {@code requestDataset()} - To fetch datasets and auto-bind them to michael@0: * the existing panel views backed by them. michael@0: * michael@0: * {@code resetDataset()} - To release any resources associated with a michael@0: * previously loaded dataset. michael@0: * michael@0: * {@code createPanelView()} - To create a panel view for a ViewConfig michael@0: * associated with the panel. michael@0: * michael@0: * {@code disposePanelView()} - To dispose any dataset references associated michael@0: * with the given view. michael@0: * michael@0: * {@code PanelLayout} subclasses should always use {@code createPanelView()} michael@0: * to create the views dynamically created based on {@code ViewConfig}. This michael@0: * allows {@code PanelLayout} to auto-bind datasets with panel views. michael@0: * {@code PanelLayout} subclasses are free to have any type of views to arrange michael@0: * the panel views in different ways. michael@0: */ michael@0: abstract class PanelLayout extends FrameLayout { michael@0: private static final String LOGTAG = "GeckoPanelLayout"; michael@0: michael@0: protected final SparseArray mViewStates; michael@0: private final PanelConfig mPanelConfig; michael@0: private final DatasetHandler mDatasetHandler; michael@0: private final OnUrlOpenListener mUrlOpenListener; michael@0: private final ContextMenuRegistry mContextMenuRegistry; michael@0: michael@0: /** michael@0: * To be used by panel views to express that they are michael@0: * backed by datasets. michael@0: */ michael@0: public interface DatasetBacked { michael@0: public void setDataset(Cursor cursor); michael@0: public void setFilterManager(FilterManager manager); michael@0: } michael@0: michael@0: /** michael@0: * To be used by requests made to {@code DatasetHandler}s to couple dataset ID with current michael@0: * filter for queries on the database. michael@0: */ michael@0: public static class DatasetRequest implements Parcelable { michael@0: public enum Type implements Parcelable { michael@0: DATASET_LOAD, michael@0: FILTER_PUSH, michael@0: FILTER_POP; michael@0: michael@0: @Override michael@0: public int describeContents() { michael@0: return 0; michael@0: } michael@0: michael@0: @Override michael@0: public void writeToParcel(Parcel dest, int flags) { michael@0: dest.writeInt(ordinal()); michael@0: } michael@0: michael@0: public static final Creator CREATOR = new Creator() { michael@0: @Override michael@0: public Type createFromParcel(final Parcel source) { michael@0: return Type.values()[source.readInt()]; michael@0: } michael@0: michael@0: @Override michael@0: public Type[] newArray(final int size) { michael@0: return new Type[size]; michael@0: } michael@0: }; michael@0: } michael@0: michael@0: private final int mViewIndex; michael@0: private final Type mType; michael@0: private final String mDatasetId; michael@0: private final FilterDetail mFilterDetail; michael@0: michael@0: private DatasetRequest(Parcel in) { michael@0: this.mViewIndex = in.readInt(); michael@0: this.mType = (Type) in.readParcelable(getClass().getClassLoader()); michael@0: this.mDatasetId = in.readString(); michael@0: this.mFilterDetail = (FilterDetail) in.readParcelable(getClass().getClassLoader()); michael@0: } michael@0: michael@0: public DatasetRequest(int index, String datasetId, FilterDetail filterDetail) { michael@0: this(index, Type.DATASET_LOAD, datasetId, filterDetail); michael@0: } michael@0: michael@0: public DatasetRequest(int index, Type type, String datasetId, FilterDetail filterDetail) { michael@0: this.mViewIndex = index; michael@0: this.mType = type; michael@0: this.mDatasetId = datasetId; michael@0: this.mFilterDetail = filterDetail; michael@0: } michael@0: michael@0: public int getViewIndex() { michael@0: return mViewIndex; michael@0: } michael@0: michael@0: public Type getType() { michael@0: return mType; michael@0: } michael@0: michael@0: public String getDatasetId() { michael@0: return mDatasetId; michael@0: } michael@0: michael@0: public String getFilter() { michael@0: return (mFilterDetail != null ? mFilterDetail.filter : null); michael@0: } michael@0: michael@0: public FilterDetail getFilterDetail() { michael@0: return mFilterDetail; michael@0: } michael@0: michael@0: @Override michael@0: public int describeContents() { michael@0: return 0; michael@0: } michael@0: michael@0: @Override michael@0: public void writeToParcel(Parcel dest, int flags) { michael@0: dest.writeInt(mViewIndex); michael@0: dest.writeParcelable(mType, 0); michael@0: dest.writeString(mDatasetId); michael@0: dest.writeParcelable(mFilterDetail, 0); michael@0: } michael@0: michael@0: public String toString() { michael@0: return "{ index: " + mViewIndex + michael@0: ", type: " + mType + michael@0: ", dataset: " + mDatasetId + michael@0: ", filter: " + mFilterDetail + michael@0: " }"; michael@0: } michael@0: michael@0: public static final Creator CREATOR = new Creator() { michael@0: public DatasetRequest createFromParcel(Parcel in) { michael@0: return new DatasetRequest(in); michael@0: } michael@0: michael@0: public DatasetRequest[] newArray(int size) { michael@0: return new DatasetRequest[size]; michael@0: } michael@0: }; michael@0: } michael@0: michael@0: /** michael@0: * Defines the contract with the component that is responsible michael@0: * for handling datasets requests. michael@0: */ michael@0: public interface DatasetHandler { michael@0: /** michael@0: * Requests a dataset to be fetched and auto-bound to the michael@0: * panel views backed by it. michael@0: */ michael@0: public void requestDataset(DatasetRequest request); michael@0: michael@0: /** michael@0: * Releases any resources associated with a panel view. It will michael@0: * do nothing if the view with the given index been created michael@0: * before. michael@0: */ michael@0: public void resetDataset(int viewIndex); michael@0: } michael@0: michael@0: public interface PanelView { michael@0: public void setOnItemOpenListener(OnItemOpenListener listener); michael@0: public void setOnKeyListener(OnKeyListener listener); michael@0: public void setContextMenuInfoFactory(HomeContextMenuInfo.Factory factory); michael@0: } michael@0: michael@0: public interface FilterManager { michael@0: public FilterDetail getPreviousFilter(); michael@0: public boolean canGoBack(); michael@0: public void goBack(); michael@0: } michael@0: michael@0: public interface ContextMenuRegistry { michael@0: public void register(View view); michael@0: } michael@0: michael@0: public PanelLayout(Context context, PanelConfig panelConfig, DatasetHandler datasetHandler, michael@0: OnUrlOpenListener urlOpenListener, ContextMenuRegistry contextMenuRegistry) { michael@0: super(context); michael@0: mViewStates = new SparseArray(); michael@0: mPanelConfig = panelConfig; michael@0: mDatasetHandler = datasetHandler; michael@0: mUrlOpenListener = urlOpenListener; michael@0: mContextMenuRegistry = contextMenuRegistry; michael@0: } michael@0: michael@0: @Override michael@0: public void onDetachedFromWindow() { michael@0: super.onDetachedFromWindow(); michael@0: michael@0: final int count = mViewStates.size(); michael@0: for (int i = 0; i < count; i++) { michael@0: final ViewState viewState = mViewStates.valueAt(i); michael@0: michael@0: final View view = viewState.getView(); michael@0: if (view != null) { michael@0: maybeSetDataset(view, null); michael@0: } michael@0: } michael@0: mViewStates.clear(); michael@0: } michael@0: michael@0: /** michael@0: * Delivers the dataset as a {@code Cursor} to be bound to the michael@0: * panel view backed by it. This is used by the {@code DatasetHandler} michael@0: * in response to a dataset request. michael@0: */ michael@0: public final void deliverDataset(DatasetRequest request, Cursor cursor) { michael@0: Log.d(LOGTAG, "Delivering request: " + request); michael@0: final ViewState viewState = mViewStates.get(request.getViewIndex()); michael@0: if (viewState == null) { michael@0: return; michael@0: } michael@0: michael@0: switch (request.getType()) { michael@0: case FILTER_PUSH: michael@0: viewState.pushFilter(request.getFilterDetail()); michael@0: break; michael@0: case FILTER_POP: michael@0: viewState.popFilter(); michael@0: break; michael@0: } michael@0: michael@0: final View activeView = viewState.getActiveView(); michael@0: if (activeView == null) { michael@0: throw new IllegalStateException("No active view for view state: " + viewState.getIndex()); michael@0: } michael@0: michael@0: final ViewConfig viewConfig = viewState.getViewConfig(); michael@0: michael@0: final View newView; michael@0: if (cursor == null || cursor.getCount() == 0) { michael@0: newView = createEmptyView(viewConfig); michael@0: maybeSetDataset(activeView, null); michael@0: } else { michael@0: newView = createPanelView(viewConfig); michael@0: maybeSetDataset(newView, cursor); michael@0: } michael@0: michael@0: if (activeView != newView) { michael@0: replacePanelView(activeView, newView); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Releases any references to the given dataset from all michael@0: * existing panel views. michael@0: */ michael@0: public final void releaseDataset(int viewIndex) { michael@0: Log.d(LOGTAG, "Releasing dataset: " + viewIndex); michael@0: final ViewState viewState = mViewStates.get(viewIndex); michael@0: if (viewState == null) { michael@0: return; michael@0: } michael@0: michael@0: final View view = viewState.getView(); michael@0: if (view != null) { michael@0: maybeSetDataset(view, null); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Requests a dataset to be loaded and bound to any existing michael@0: * panel view backed by it. michael@0: */ michael@0: protected final void requestDataset(DatasetRequest request) { michael@0: Log.d(LOGTAG, "Requesting request: " + request); michael@0: if (mViewStates.get(request.getViewIndex()) == null) { michael@0: return; michael@0: } michael@0: michael@0: mDatasetHandler.requestDataset(request); michael@0: } michael@0: michael@0: /** michael@0: * Releases any resources associated with a panel view. michael@0: * e.g. close any associated {@code Cursor}. michael@0: */ michael@0: protected final void resetDataset(int viewIndex) { michael@0: Log.d(LOGTAG, "Resetting view with index: " + viewIndex); michael@0: if (mViewStates.get(viewIndex) == null) { michael@0: return; michael@0: } michael@0: michael@0: mDatasetHandler.resetDataset(viewIndex); michael@0: } michael@0: michael@0: /** michael@0: * Factory method to create instance of panels from a given michael@0: * {@code ViewConfig}. All panel views defined in {@code PanelConfig} michael@0: * should be created using this method so that {@PanelLayout} can michael@0: * keep track of panel views and their associated datasets. michael@0: */ michael@0: protected final View createPanelView(ViewConfig viewConfig) { michael@0: Log.d(LOGTAG, "Creating panel view: " + viewConfig.getType()); michael@0: michael@0: ViewState viewState = mViewStates.get(viewConfig.getIndex()); michael@0: if (viewState == null) { michael@0: viewState = new ViewState(viewConfig); michael@0: mViewStates.put(viewConfig.getIndex(), viewState); michael@0: } michael@0: michael@0: View view = viewState.getView(); michael@0: if (view == null) { michael@0: switch(viewConfig.getType()) { michael@0: case LIST: michael@0: view = new PanelListView(getContext(), viewConfig); michael@0: break; michael@0: michael@0: case GRID: michael@0: view = new PanelGridView(getContext(), viewConfig); michael@0: break; michael@0: michael@0: default: michael@0: throw new IllegalStateException("Unrecognized view type in " + getClass().getSimpleName()); michael@0: } michael@0: michael@0: PanelView panelView = (PanelView) view; michael@0: panelView.setOnItemOpenListener(new PanelOnItemOpenListener(viewState)); michael@0: panelView.setOnKeyListener(new PanelKeyListener(viewState)); michael@0: panelView.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() { michael@0: @Override michael@0: public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) { michael@0: final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id); michael@0: info.url = cursor.getString(cursor.getColumnIndexOrThrow(HomeItems.URL)); michael@0: info.title = cursor.getString(cursor.getColumnIndexOrThrow(HomeItems.TITLE)); michael@0: return info; michael@0: } michael@0: }); michael@0: michael@0: mContextMenuRegistry.register(view); michael@0: michael@0: if (view instanceof DatasetBacked) { michael@0: DatasetBacked datasetBacked = (DatasetBacked) view; michael@0: datasetBacked.setFilterManager(new PanelFilterManager(viewState)); michael@0: michael@0: // XXX: Disabled because of bug 1010986 michael@0: // if (viewConfig.isRefreshEnabled()) { michael@0: // view = new PanelRefreshLayout(getContext(), view, michael@0: // mPanelConfig.getId(), viewConfig.getIndex()); michael@0: // } michael@0: } michael@0: michael@0: viewState.setView(view); michael@0: } michael@0: michael@0: return view; michael@0: } michael@0: michael@0: /** michael@0: * Dispose any dataset references associated with the michael@0: * given view. michael@0: */ michael@0: protected final void disposePanelView(View view) { michael@0: Log.d(LOGTAG, "Disposing panel view"); michael@0: final int count = mViewStates.size(); michael@0: for (int i = 0; i < count; i++) { michael@0: final ViewState viewState = mViewStates.valueAt(i); michael@0: michael@0: if (viewState.getView() == view) { michael@0: maybeSetDataset(view, null); michael@0: mViewStates.remove(viewState.getIndex()); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: private void maybeSetDataset(View view, Cursor cursor) { michael@0: if (view instanceof DatasetBacked) { michael@0: final DatasetBacked dsb = (DatasetBacked) view; michael@0: dsb.setDataset(cursor); michael@0: } michael@0: } michael@0: michael@0: private View createEmptyView(ViewConfig viewConfig) { michael@0: Log.d(LOGTAG, "Creating empty view: " + viewConfig.getType()); michael@0: michael@0: ViewState viewState = mViewStates.get(viewConfig.getIndex()); michael@0: if (viewState == null) { michael@0: throw new IllegalStateException("No view state found for view index: " + viewConfig.getIndex()); michael@0: } michael@0: michael@0: View view = viewState.getEmptyView(); michael@0: if (view == null) { michael@0: view = LayoutInflater.from(getContext()).inflate(R.layout.home_empty_panel, null); michael@0: michael@0: final EmptyViewConfig emptyViewConfig = viewConfig.getEmptyViewConfig(); michael@0: michael@0: // XXX: Refactor this into a custom view (bug 985134) michael@0: final String text = (emptyViewConfig == null) ? null : emptyViewConfig.getText(); michael@0: final TextView textView = (TextView) view.findViewById(R.id.home_empty_text); michael@0: if (TextUtils.isEmpty(text)) { michael@0: textView.setText(R.string.home_default_empty); michael@0: } else { michael@0: textView.setText(text); michael@0: } michael@0: michael@0: final String imageUrl = (emptyViewConfig == null) ? null : emptyViewConfig.getImageUrl(); michael@0: final ImageView imageView = (ImageView) view.findViewById(R.id.home_empty_image); michael@0: michael@0: if (TextUtils.isEmpty(imageUrl)) { michael@0: imageView.setImageResource(R.drawable.icon_home_empty_firefox); michael@0: } else { michael@0: Picasso.with(getContext()) michael@0: .load(imageUrl) michael@0: .error(R.drawable.icon_home_empty_firefox) michael@0: .into(imageView); michael@0: } michael@0: michael@0: viewState.setEmptyView(view); michael@0: } michael@0: michael@0: return view; michael@0: } michael@0: michael@0: private void replacePanelView(View currentView, View newView) { michael@0: final ViewGroup parent = (ViewGroup) currentView.getParent(); michael@0: parent.addView(newView, parent.indexOfChild(currentView), currentView.getLayoutParams()); michael@0: parent.removeView(currentView); michael@0: } michael@0: michael@0: /** michael@0: * Must be implemented by {@code PanelLayout} subclasses to define michael@0: * what happens then the layout is first loaded. Should set initial michael@0: * UI state and request any necessary datasets. michael@0: */ michael@0: public abstract void load(); michael@0: michael@0: /** michael@0: * Represents a 'live' instance of a panel view associated with michael@0: * the {@code PanelLayout}. Is responsible for tracking the history stack of filters. michael@0: */ michael@0: protected class ViewState { michael@0: private final ViewConfig mViewConfig; michael@0: private SoftReference mView; michael@0: private SoftReference mEmptyView; michael@0: private LinkedList mFilterStack; michael@0: michael@0: public ViewState(ViewConfig viewConfig) { michael@0: mViewConfig = viewConfig; michael@0: mView = new SoftReference(null); michael@0: mEmptyView = new SoftReference(null); michael@0: } michael@0: michael@0: public ViewConfig getViewConfig() { michael@0: return mViewConfig; michael@0: } michael@0: michael@0: public int getIndex() { michael@0: return mViewConfig.getIndex(); michael@0: } michael@0: michael@0: public View getView() { michael@0: return mView.get(); michael@0: } michael@0: michael@0: public void setView(View view) { michael@0: mView = new SoftReference(view); michael@0: } michael@0: michael@0: public View getEmptyView() { michael@0: return mEmptyView.get(); michael@0: } michael@0: michael@0: public void setEmptyView(View view) { michael@0: mEmptyView = new SoftReference(view); michael@0: } michael@0: michael@0: public View getActiveView() { michael@0: final View view = getView(); michael@0: if (view != null && view.getParent() != null) { michael@0: return view; michael@0: } michael@0: michael@0: final View emptyView = getEmptyView(); michael@0: if (emptyView != null && emptyView.getParent() != null) { michael@0: return emptyView; michael@0: } michael@0: michael@0: return null; michael@0: } michael@0: michael@0: public String getDatasetId() { michael@0: return mViewConfig.getDatasetId(); michael@0: } michael@0: michael@0: public ItemHandler getItemHandler() { michael@0: return mViewConfig.getItemHandler(); michael@0: } michael@0: michael@0: /** michael@0: * Get the current filter that this view is displaying, or null if none. michael@0: */ michael@0: public FilterDetail getCurrentFilter() { michael@0: if (mFilterStack == null) { michael@0: return null; michael@0: } else { michael@0: return mFilterStack.peek(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Get the previous filter that this view was displaying, or null if none. michael@0: */ michael@0: public FilterDetail getPreviousFilter() { michael@0: if (!canPopFilter()) { michael@0: return null; michael@0: } michael@0: michael@0: return mFilterStack.get(1); michael@0: } michael@0: michael@0: /** michael@0: * Adds a filter to the history stack for this view. michael@0: */ michael@0: public void pushFilter(FilterDetail filter) { michael@0: if (mFilterStack == null) { michael@0: mFilterStack = new LinkedList(); michael@0: michael@0: // Initialize with the initial filter. michael@0: mFilterStack.push(new FilterDetail(mViewConfig.getFilter(), michael@0: mPanelConfig.getTitle())); michael@0: } michael@0: michael@0: mFilterStack.push(filter); michael@0: } michael@0: michael@0: /** michael@0: * Remove the most recent filter from the stack. michael@0: * michael@0: * @return whether the filter was popped michael@0: */ michael@0: public boolean popFilter() { michael@0: if (!canPopFilter()) { michael@0: return false; michael@0: } michael@0: michael@0: mFilterStack.pop(); michael@0: return true; michael@0: } michael@0: michael@0: public boolean canPopFilter() { michael@0: return (mFilterStack != null && mFilterStack.size() > 1); michael@0: } michael@0: } michael@0: michael@0: static class FilterDetail implements Parcelable { michael@0: final String filter; michael@0: final String title; michael@0: michael@0: private FilterDetail(Parcel in) { michael@0: this.filter = in.readString(); michael@0: this.title = in.readString(); michael@0: } michael@0: michael@0: public FilterDetail(String filter, String title) { michael@0: this.filter = filter; michael@0: this.title = title; michael@0: } michael@0: michael@0: @Override michael@0: public int describeContents() { michael@0: return 0; michael@0: } michael@0: michael@0: @Override michael@0: public void writeToParcel(Parcel dest, int flags) { michael@0: dest.writeString(filter); michael@0: dest.writeString(title); michael@0: } michael@0: michael@0: public static final Creator CREATOR = new Creator() { michael@0: public FilterDetail createFromParcel(Parcel in) { michael@0: return new FilterDetail(in); michael@0: } michael@0: michael@0: public FilterDetail[] newArray(int size) { michael@0: return new FilterDetail[size]; michael@0: } michael@0: }; michael@0: } michael@0: michael@0: /** michael@0: * Pushes filter to {@code ViewState}'s stack and makes request for new filter value. michael@0: */ michael@0: private void pushFilterOnView(ViewState viewState, FilterDetail filterDetail) { michael@0: final int index = viewState.getIndex(); michael@0: final String datasetId = viewState.getDatasetId(); michael@0: michael@0: mDatasetHandler.requestDataset(new DatasetRequest(index, michael@0: DatasetRequest.Type.FILTER_PUSH, michael@0: datasetId, michael@0: filterDetail)); michael@0: } michael@0: michael@0: /** michael@0: * Pops filter from {@code ViewState}'s stack and makes request for previous filter value. michael@0: * michael@0: * @return whether the filter has changed michael@0: */ michael@0: private boolean popFilterOnView(ViewState viewState) { michael@0: if (viewState.canPopFilter()) { michael@0: final int index = viewState.getIndex(); michael@0: final String datasetId = viewState.getDatasetId(); michael@0: final FilterDetail filterDetail = viewState.getPreviousFilter(); michael@0: michael@0: mDatasetHandler.requestDataset(new DatasetRequest(index, michael@0: DatasetRequest.Type.FILTER_POP, michael@0: datasetId, michael@0: filterDetail)); michael@0: michael@0: return true; michael@0: } else { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: public interface OnItemOpenListener { michael@0: public void onItemOpen(String url, String title); michael@0: } michael@0: michael@0: private class PanelOnItemOpenListener implements OnItemOpenListener { michael@0: private ViewState mViewState; michael@0: michael@0: public PanelOnItemOpenListener(ViewState viewState) { michael@0: mViewState = viewState; michael@0: } michael@0: michael@0: @Override michael@0: public void onItemOpen(String url, String title) { michael@0: if (StringUtils.isFilterUrl(url)) { michael@0: FilterDetail filterDetail = new FilterDetail(StringUtils.getFilterFromUrl(url), title); michael@0: pushFilterOnView(mViewState, filterDetail); michael@0: } else { michael@0: EnumSet flags = EnumSet.noneOf(OnUrlOpenListener.Flags.class); michael@0: if (mViewState.getItemHandler() == ItemHandler.INTENT) { michael@0: flags.add(OnUrlOpenListener.Flags.OPEN_WITH_INTENT); michael@0: } michael@0: michael@0: mUrlOpenListener.onUrlOpen(url, flags); michael@0: } michael@0: } michael@0: } michael@0: michael@0: private class PanelKeyListener implements View.OnKeyListener { michael@0: private ViewState mViewState; michael@0: michael@0: public PanelKeyListener(ViewState viewState) { michael@0: mViewState = viewState; michael@0: } michael@0: michael@0: @Override michael@0: public boolean onKey(View v, int keyCode, KeyEvent event) { michael@0: if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) { michael@0: return popFilterOnView(mViewState); michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: private class PanelFilterManager implements FilterManager { michael@0: private final ViewState mViewState; michael@0: michael@0: public PanelFilterManager(ViewState viewState) { michael@0: mViewState = viewState; michael@0: } michael@0: michael@0: @Override michael@0: public FilterDetail getPreviousFilter() { michael@0: return mViewState.getPreviousFilter(); michael@0: } michael@0: michael@0: @Override michael@0: public boolean canGoBack() { michael@0: return mViewState.canPopFilter(); michael@0: } michael@0: michael@0: @Override michael@0: public void goBack() { michael@0: popFilterOnView(mViewState); michael@0: } michael@0: } michael@0: }