mobile/android/base/home/PanelLayout.java

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 package org.mozilla.gecko.home;
michael@0 7
michael@0 8 import org.mozilla.gecko.R;
michael@0 9 import org.mozilla.gecko.db.BrowserContract.HomeItems;
michael@0 10 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
michael@0 11 import org.mozilla.gecko.home.HomeConfig.EmptyViewConfig;
michael@0 12 import org.mozilla.gecko.home.HomeConfig.ItemHandler;
michael@0 13 import org.mozilla.gecko.home.HomeConfig.PanelConfig;
michael@0 14 import org.mozilla.gecko.home.HomeConfig.ViewConfig;
michael@0 15 import org.mozilla.gecko.util.StringUtils;
michael@0 16
michael@0 17 import android.content.Context;
michael@0 18 import android.database.Cursor;
michael@0 19 import android.os.Parcel;
michael@0 20 import android.os.Parcelable;
michael@0 21 import android.text.TextUtils;
michael@0 22 import android.util.Log;
michael@0 23 import android.util.SparseArray;
michael@0 24 import android.view.KeyEvent;
michael@0 25 import android.view.LayoutInflater;
michael@0 26 import android.view.View;
michael@0 27 import android.view.ViewGroup;
michael@0 28 import android.widget.FrameLayout;
michael@0 29 import android.widget.ImageView;
michael@0 30 import android.widget.LinearLayout;
michael@0 31 import android.widget.TextView;
michael@0 32
michael@0 33 import java.lang.ref.SoftReference;
michael@0 34 import java.util.EnumSet;
michael@0 35 import java.util.LinkedList;
michael@0 36 import java.util.Map;
michael@0 37 import java.util.WeakHashMap;
michael@0 38
michael@0 39 import com.squareup.picasso.Picasso;
michael@0 40
michael@0 41 /**
michael@0 42 * {@code PanelLayout} is the base class for custom layouts to be
michael@0 43 * used in {@code DynamicPanel}. It provides the basic framework
michael@0 44 * that enables custom layouts to request and reset datasets and
michael@0 45 * create panel views. Furthermore, it automates most of the process
michael@0 46 * of binding panel views with their respective datasets.
michael@0 47 *
michael@0 48 * {@code PanelLayout} abstracts the implemention details of how
michael@0 49 * datasets are actually loaded through the {@DatasetHandler} interface.
michael@0 50 * {@code DatasetHandler} provides two operations: request and reset.
michael@0 51 * The results of the dataset requests done via the {@code DatasetHandler}
michael@0 52 * are delivered to the {@code PanelLayout} with the {@code deliverDataset()}
michael@0 53 * method.
michael@0 54 *
michael@0 55 * Subclasses of {@code PanelLayout} should simply use the utilities
michael@0 56 * provided by {@code PanelLayout}. Namely:
michael@0 57 *
michael@0 58 * {@code requestDataset()} - To fetch datasets and auto-bind them to
michael@0 59 * the existing panel views backed by them.
michael@0 60 *
michael@0 61 * {@code resetDataset()} - To release any resources associated with a
michael@0 62 * previously loaded dataset.
michael@0 63 *
michael@0 64 * {@code createPanelView()} - To create a panel view for a ViewConfig
michael@0 65 * associated with the panel.
michael@0 66 *
michael@0 67 * {@code disposePanelView()} - To dispose any dataset references associated
michael@0 68 * with the given view.
michael@0 69 *
michael@0 70 * {@code PanelLayout} subclasses should always use {@code createPanelView()}
michael@0 71 * to create the views dynamically created based on {@code ViewConfig}. This
michael@0 72 * allows {@code PanelLayout} to auto-bind datasets with panel views.
michael@0 73 * {@code PanelLayout} subclasses are free to have any type of views to arrange
michael@0 74 * the panel views in different ways.
michael@0 75 */
michael@0 76 abstract class PanelLayout extends FrameLayout {
michael@0 77 private static final String LOGTAG = "GeckoPanelLayout";
michael@0 78
michael@0 79 protected final SparseArray<ViewState> mViewStates;
michael@0 80 private final PanelConfig mPanelConfig;
michael@0 81 private final DatasetHandler mDatasetHandler;
michael@0 82 private final OnUrlOpenListener mUrlOpenListener;
michael@0 83 private final ContextMenuRegistry mContextMenuRegistry;
michael@0 84
michael@0 85 /**
michael@0 86 * To be used by panel views to express that they are
michael@0 87 * backed by datasets.
michael@0 88 */
michael@0 89 public interface DatasetBacked {
michael@0 90 public void setDataset(Cursor cursor);
michael@0 91 public void setFilterManager(FilterManager manager);
michael@0 92 }
michael@0 93
michael@0 94 /**
michael@0 95 * To be used by requests made to {@code DatasetHandler}s to couple dataset ID with current
michael@0 96 * filter for queries on the database.
michael@0 97 */
michael@0 98 public static class DatasetRequest implements Parcelable {
michael@0 99 public enum Type implements Parcelable {
michael@0 100 DATASET_LOAD,
michael@0 101 FILTER_PUSH,
michael@0 102 FILTER_POP;
michael@0 103
michael@0 104 @Override
michael@0 105 public int describeContents() {
michael@0 106 return 0;
michael@0 107 }
michael@0 108
michael@0 109 @Override
michael@0 110 public void writeToParcel(Parcel dest, int flags) {
michael@0 111 dest.writeInt(ordinal());
michael@0 112 }
michael@0 113
michael@0 114 public static final Creator<Type> CREATOR = new Creator<Type>() {
michael@0 115 @Override
michael@0 116 public Type createFromParcel(final Parcel source) {
michael@0 117 return Type.values()[source.readInt()];
michael@0 118 }
michael@0 119
michael@0 120 @Override
michael@0 121 public Type[] newArray(final int size) {
michael@0 122 return new Type[size];
michael@0 123 }
michael@0 124 };
michael@0 125 }
michael@0 126
michael@0 127 private final int mViewIndex;
michael@0 128 private final Type mType;
michael@0 129 private final String mDatasetId;
michael@0 130 private final FilterDetail mFilterDetail;
michael@0 131
michael@0 132 private DatasetRequest(Parcel in) {
michael@0 133 this.mViewIndex = in.readInt();
michael@0 134 this.mType = (Type) in.readParcelable(getClass().getClassLoader());
michael@0 135 this.mDatasetId = in.readString();
michael@0 136 this.mFilterDetail = (FilterDetail) in.readParcelable(getClass().getClassLoader());
michael@0 137 }
michael@0 138
michael@0 139 public DatasetRequest(int index, String datasetId, FilterDetail filterDetail) {
michael@0 140 this(index, Type.DATASET_LOAD, datasetId, filterDetail);
michael@0 141 }
michael@0 142
michael@0 143 public DatasetRequest(int index, Type type, String datasetId, FilterDetail filterDetail) {
michael@0 144 this.mViewIndex = index;
michael@0 145 this.mType = type;
michael@0 146 this.mDatasetId = datasetId;
michael@0 147 this.mFilterDetail = filterDetail;
michael@0 148 }
michael@0 149
michael@0 150 public int getViewIndex() {
michael@0 151 return mViewIndex;
michael@0 152 }
michael@0 153
michael@0 154 public Type getType() {
michael@0 155 return mType;
michael@0 156 }
michael@0 157
michael@0 158 public String getDatasetId() {
michael@0 159 return mDatasetId;
michael@0 160 }
michael@0 161
michael@0 162 public String getFilter() {
michael@0 163 return (mFilterDetail != null ? mFilterDetail.filter : null);
michael@0 164 }
michael@0 165
michael@0 166 public FilterDetail getFilterDetail() {
michael@0 167 return mFilterDetail;
michael@0 168 }
michael@0 169
michael@0 170 @Override
michael@0 171 public int describeContents() {
michael@0 172 return 0;
michael@0 173 }
michael@0 174
michael@0 175 @Override
michael@0 176 public void writeToParcel(Parcel dest, int flags) {
michael@0 177 dest.writeInt(mViewIndex);
michael@0 178 dest.writeParcelable(mType, 0);
michael@0 179 dest.writeString(mDatasetId);
michael@0 180 dest.writeParcelable(mFilterDetail, 0);
michael@0 181 }
michael@0 182
michael@0 183 public String toString() {
michael@0 184 return "{ index: " + mViewIndex +
michael@0 185 ", type: " + mType +
michael@0 186 ", dataset: " + mDatasetId +
michael@0 187 ", filter: " + mFilterDetail +
michael@0 188 " }";
michael@0 189 }
michael@0 190
michael@0 191 public static final Creator<DatasetRequest> CREATOR = new Creator<DatasetRequest>() {
michael@0 192 public DatasetRequest createFromParcel(Parcel in) {
michael@0 193 return new DatasetRequest(in);
michael@0 194 }
michael@0 195
michael@0 196 public DatasetRequest[] newArray(int size) {
michael@0 197 return new DatasetRequest[size];
michael@0 198 }
michael@0 199 };
michael@0 200 }
michael@0 201
michael@0 202 /**
michael@0 203 * Defines the contract with the component that is responsible
michael@0 204 * for handling datasets requests.
michael@0 205 */
michael@0 206 public interface DatasetHandler {
michael@0 207 /**
michael@0 208 * Requests a dataset to be fetched and auto-bound to the
michael@0 209 * panel views backed by it.
michael@0 210 */
michael@0 211 public void requestDataset(DatasetRequest request);
michael@0 212
michael@0 213 /**
michael@0 214 * Releases any resources associated with a panel view. It will
michael@0 215 * do nothing if the view with the given index been created
michael@0 216 * before.
michael@0 217 */
michael@0 218 public void resetDataset(int viewIndex);
michael@0 219 }
michael@0 220
michael@0 221 public interface PanelView {
michael@0 222 public void setOnItemOpenListener(OnItemOpenListener listener);
michael@0 223 public void setOnKeyListener(OnKeyListener listener);
michael@0 224 public void setContextMenuInfoFactory(HomeContextMenuInfo.Factory factory);
michael@0 225 }
michael@0 226
michael@0 227 public interface FilterManager {
michael@0 228 public FilterDetail getPreviousFilter();
michael@0 229 public boolean canGoBack();
michael@0 230 public void goBack();
michael@0 231 }
michael@0 232
michael@0 233 public interface ContextMenuRegistry {
michael@0 234 public void register(View view);
michael@0 235 }
michael@0 236
michael@0 237 public PanelLayout(Context context, PanelConfig panelConfig, DatasetHandler datasetHandler,
michael@0 238 OnUrlOpenListener urlOpenListener, ContextMenuRegistry contextMenuRegistry) {
michael@0 239 super(context);
michael@0 240 mViewStates = new SparseArray<ViewState>();
michael@0 241 mPanelConfig = panelConfig;
michael@0 242 mDatasetHandler = datasetHandler;
michael@0 243 mUrlOpenListener = urlOpenListener;
michael@0 244 mContextMenuRegistry = contextMenuRegistry;
michael@0 245 }
michael@0 246
michael@0 247 @Override
michael@0 248 public void onDetachedFromWindow() {
michael@0 249 super.onDetachedFromWindow();
michael@0 250
michael@0 251 final int count = mViewStates.size();
michael@0 252 for (int i = 0; i < count; i++) {
michael@0 253 final ViewState viewState = mViewStates.valueAt(i);
michael@0 254
michael@0 255 final View view = viewState.getView();
michael@0 256 if (view != null) {
michael@0 257 maybeSetDataset(view, null);
michael@0 258 }
michael@0 259 }
michael@0 260 mViewStates.clear();
michael@0 261 }
michael@0 262
michael@0 263 /**
michael@0 264 * Delivers the dataset as a {@code Cursor} to be bound to the
michael@0 265 * panel view backed by it. This is used by the {@code DatasetHandler}
michael@0 266 * in response to a dataset request.
michael@0 267 */
michael@0 268 public final void deliverDataset(DatasetRequest request, Cursor cursor) {
michael@0 269 Log.d(LOGTAG, "Delivering request: " + request);
michael@0 270 final ViewState viewState = mViewStates.get(request.getViewIndex());
michael@0 271 if (viewState == null) {
michael@0 272 return;
michael@0 273 }
michael@0 274
michael@0 275 switch (request.getType()) {
michael@0 276 case FILTER_PUSH:
michael@0 277 viewState.pushFilter(request.getFilterDetail());
michael@0 278 break;
michael@0 279 case FILTER_POP:
michael@0 280 viewState.popFilter();
michael@0 281 break;
michael@0 282 }
michael@0 283
michael@0 284 final View activeView = viewState.getActiveView();
michael@0 285 if (activeView == null) {
michael@0 286 throw new IllegalStateException("No active view for view state: " + viewState.getIndex());
michael@0 287 }
michael@0 288
michael@0 289 final ViewConfig viewConfig = viewState.getViewConfig();
michael@0 290
michael@0 291 final View newView;
michael@0 292 if (cursor == null || cursor.getCount() == 0) {
michael@0 293 newView = createEmptyView(viewConfig);
michael@0 294 maybeSetDataset(activeView, null);
michael@0 295 } else {
michael@0 296 newView = createPanelView(viewConfig);
michael@0 297 maybeSetDataset(newView, cursor);
michael@0 298 }
michael@0 299
michael@0 300 if (activeView != newView) {
michael@0 301 replacePanelView(activeView, newView);
michael@0 302 }
michael@0 303 }
michael@0 304
michael@0 305 /**
michael@0 306 * Releases any references to the given dataset from all
michael@0 307 * existing panel views.
michael@0 308 */
michael@0 309 public final void releaseDataset(int viewIndex) {
michael@0 310 Log.d(LOGTAG, "Releasing dataset: " + viewIndex);
michael@0 311 final ViewState viewState = mViewStates.get(viewIndex);
michael@0 312 if (viewState == null) {
michael@0 313 return;
michael@0 314 }
michael@0 315
michael@0 316 final View view = viewState.getView();
michael@0 317 if (view != null) {
michael@0 318 maybeSetDataset(view, null);
michael@0 319 }
michael@0 320 }
michael@0 321
michael@0 322 /**
michael@0 323 * Requests a dataset to be loaded and bound to any existing
michael@0 324 * panel view backed by it.
michael@0 325 */
michael@0 326 protected final void requestDataset(DatasetRequest request) {
michael@0 327 Log.d(LOGTAG, "Requesting request: " + request);
michael@0 328 if (mViewStates.get(request.getViewIndex()) == null) {
michael@0 329 return;
michael@0 330 }
michael@0 331
michael@0 332 mDatasetHandler.requestDataset(request);
michael@0 333 }
michael@0 334
michael@0 335 /**
michael@0 336 * Releases any resources associated with a panel view.
michael@0 337 * e.g. close any associated {@code Cursor}.
michael@0 338 */
michael@0 339 protected final void resetDataset(int viewIndex) {
michael@0 340 Log.d(LOGTAG, "Resetting view with index: " + viewIndex);
michael@0 341 if (mViewStates.get(viewIndex) == null) {
michael@0 342 return;
michael@0 343 }
michael@0 344
michael@0 345 mDatasetHandler.resetDataset(viewIndex);
michael@0 346 }
michael@0 347
michael@0 348 /**
michael@0 349 * Factory method to create instance of panels from a given
michael@0 350 * {@code ViewConfig}. All panel views defined in {@code PanelConfig}
michael@0 351 * should be created using this method so that {@PanelLayout} can
michael@0 352 * keep track of panel views and their associated datasets.
michael@0 353 */
michael@0 354 protected final View createPanelView(ViewConfig viewConfig) {
michael@0 355 Log.d(LOGTAG, "Creating panel view: " + viewConfig.getType());
michael@0 356
michael@0 357 ViewState viewState = mViewStates.get(viewConfig.getIndex());
michael@0 358 if (viewState == null) {
michael@0 359 viewState = new ViewState(viewConfig);
michael@0 360 mViewStates.put(viewConfig.getIndex(), viewState);
michael@0 361 }
michael@0 362
michael@0 363 View view = viewState.getView();
michael@0 364 if (view == null) {
michael@0 365 switch(viewConfig.getType()) {
michael@0 366 case LIST:
michael@0 367 view = new PanelListView(getContext(), viewConfig);
michael@0 368 break;
michael@0 369
michael@0 370 case GRID:
michael@0 371 view = new PanelGridView(getContext(), viewConfig);
michael@0 372 break;
michael@0 373
michael@0 374 default:
michael@0 375 throw new IllegalStateException("Unrecognized view type in " + getClass().getSimpleName());
michael@0 376 }
michael@0 377
michael@0 378 PanelView panelView = (PanelView) view;
michael@0 379 panelView.setOnItemOpenListener(new PanelOnItemOpenListener(viewState));
michael@0 380 panelView.setOnKeyListener(new PanelKeyListener(viewState));
michael@0 381 panelView.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
michael@0 382 @Override
michael@0 383 public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
michael@0 384 final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id);
michael@0 385 info.url = cursor.getString(cursor.getColumnIndexOrThrow(HomeItems.URL));
michael@0 386 info.title = cursor.getString(cursor.getColumnIndexOrThrow(HomeItems.TITLE));
michael@0 387 return info;
michael@0 388 }
michael@0 389 });
michael@0 390
michael@0 391 mContextMenuRegistry.register(view);
michael@0 392
michael@0 393 if (view instanceof DatasetBacked) {
michael@0 394 DatasetBacked datasetBacked = (DatasetBacked) view;
michael@0 395 datasetBacked.setFilterManager(new PanelFilterManager(viewState));
michael@0 396
michael@0 397 // XXX: Disabled because of bug 1010986
michael@0 398 // if (viewConfig.isRefreshEnabled()) {
michael@0 399 // view = new PanelRefreshLayout(getContext(), view,
michael@0 400 // mPanelConfig.getId(), viewConfig.getIndex());
michael@0 401 // }
michael@0 402 }
michael@0 403
michael@0 404 viewState.setView(view);
michael@0 405 }
michael@0 406
michael@0 407 return view;
michael@0 408 }
michael@0 409
michael@0 410 /**
michael@0 411 * Dispose any dataset references associated with the
michael@0 412 * given view.
michael@0 413 */
michael@0 414 protected final void disposePanelView(View view) {
michael@0 415 Log.d(LOGTAG, "Disposing panel view");
michael@0 416 final int count = mViewStates.size();
michael@0 417 for (int i = 0; i < count; i++) {
michael@0 418 final ViewState viewState = mViewStates.valueAt(i);
michael@0 419
michael@0 420 if (viewState.getView() == view) {
michael@0 421 maybeSetDataset(view, null);
michael@0 422 mViewStates.remove(viewState.getIndex());
michael@0 423 break;
michael@0 424 }
michael@0 425 }
michael@0 426 }
michael@0 427
michael@0 428 private void maybeSetDataset(View view, Cursor cursor) {
michael@0 429 if (view instanceof DatasetBacked) {
michael@0 430 final DatasetBacked dsb = (DatasetBacked) view;
michael@0 431 dsb.setDataset(cursor);
michael@0 432 }
michael@0 433 }
michael@0 434
michael@0 435 private View createEmptyView(ViewConfig viewConfig) {
michael@0 436 Log.d(LOGTAG, "Creating empty view: " + viewConfig.getType());
michael@0 437
michael@0 438 ViewState viewState = mViewStates.get(viewConfig.getIndex());
michael@0 439 if (viewState == null) {
michael@0 440 throw new IllegalStateException("No view state found for view index: " + viewConfig.getIndex());
michael@0 441 }
michael@0 442
michael@0 443 View view = viewState.getEmptyView();
michael@0 444 if (view == null) {
michael@0 445 view = LayoutInflater.from(getContext()).inflate(R.layout.home_empty_panel, null);
michael@0 446
michael@0 447 final EmptyViewConfig emptyViewConfig = viewConfig.getEmptyViewConfig();
michael@0 448
michael@0 449 // XXX: Refactor this into a custom view (bug 985134)
michael@0 450 final String text = (emptyViewConfig == null) ? null : emptyViewConfig.getText();
michael@0 451 final TextView textView = (TextView) view.findViewById(R.id.home_empty_text);
michael@0 452 if (TextUtils.isEmpty(text)) {
michael@0 453 textView.setText(R.string.home_default_empty);
michael@0 454 } else {
michael@0 455 textView.setText(text);
michael@0 456 }
michael@0 457
michael@0 458 final String imageUrl = (emptyViewConfig == null) ? null : emptyViewConfig.getImageUrl();
michael@0 459 final ImageView imageView = (ImageView) view.findViewById(R.id.home_empty_image);
michael@0 460
michael@0 461 if (TextUtils.isEmpty(imageUrl)) {
michael@0 462 imageView.setImageResource(R.drawable.icon_home_empty_firefox);
michael@0 463 } else {
michael@0 464 Picasso.with(getContext())
michael@0 465 .load(imageUrl)
michael@0 466 .error(R.drawable.icon_home_empty_firefox)
michael@0 467 .into(imageView);
michael@0 468 }
michael@0 469
michael@0 470 viewState.setEmptyView(view);
michael@0 471 }
michael@0 472
michael@0 473 return view;
michael@0 474 }
michael@0 475
michael@0 476 private void replacePanelView(View currentView, View newView) {
michael@0 477 final ViewGroup parent = (ViewGroup) currentView.getParent();
michael@0 478 parent.addView(newView, parent.indexOfChild(currentView), currentView.getLayoutParams());
michael@0 479 parent.removeView(currentView);
michael@0 480 }
michael@0 481
michael@0 482 /**
michael@0 483 * Must be implemented by {@code PanelLayout} subclasses to define
michael@0 484 * what happens then the layout is first loaded. Should set initial
michael@0 485 * UI state and request any necessary datasets.
michael@0 486 */
michael@0 487 public abstract void load();
michael@0 488
michael@0 489 /**
michael@0 490 * Represents a 'live' instance of a panel view associated with
michael@0 491 * the {@code PanelLayout}. Is responsible for tracking the history stack of filters.
michael@0 492 */
michael@0 493 protected class ViewState {
michael@0 494 private final ViewConfig mViewConfig;
michael@0 495 private SoftReference<View> mView;
michael@0 496 private SoftReference<View> mEmptyView;
michael@0 497 private LinkedList<FilterDetail> mFilterStack;
michael@0 498
michael@0 499 public ViewState(ViewConfig viewConfig) {
michael@0 500 mViewConfig = viewConfig;
michael@0 501 mView = new SoftReference<View>(null);
michael@0 502 mEmptyView = new SoftReference<View>(null);
michael@0 503 }
michael@0 504
michael@0 505 public ViewConfig getViewConfig() {
michael@0 506 return mViewConfig;
michael@0 507 }
michael@0 508
michael@0 509 public int getIndex() {
michael@0 510 return mViewConfig.getIndex();
michael@0 511 }
michael@0 512
michael@0 513 public View getView() {
michael@0 514 return mView.get();
michael@0 515 }
michael@0 516
michael@0 517 public void setView(View view) {
michael@0 518 mView = new SoftReference<View>(view);
michael@0 519 }
michael@0 520
michael@0 521 public View getEmptyView() {
michael@0 522 return mEmptyView.get();
michael@0 523 }
michael@0 524
michael@0 525 public void setEmptyView(View view) {
michael@0 526 mEmptyView = new SoftReference<View>(view);
michael@0 527 }
michael@0 528
michael@0 529 public View getActiveView() {
michael@0 530 final View view = getView();
michael@0 531 if (view != null && view.getParent() != null) {
michael@0 532 return view;
michael@0 533 }
michael@0 534
michael@0 535 final View emptyView = getEmptyView();
michael@0 536 if (emptyView != null && emptyView.getParent() != null) {
michael@0 537 return emptyView;
michael@0 538 }
michael@0 539
michael@0 540 return null;
michael@0 541 }
michael@0 542
michael@0 543 public String getDatasetId() {
michael@0 544 return mViewConfig.getDatasetId();
michael@0 545 }
michael@0 546
michael@0 547 public ItemHandler getItemHandler() {
michael@0 548 return mViewConfig.getItemHandler();
michael@0 549 }
michael@0 550
michael@0 551 /**
michael@0 552 * Get the current filter that this view is displaying, or null if none.
michael@0 553 */
michael@0 554 public FilterDetail getCurrentFilter() {
michael@0 555 if (mFilterStack == null) {
michael@0 556 return null;
michael@0 557 } else {
michael@0 558 return mFilterStack.peek();
michael@0 559 }
michael@0 560 }
michael@0 561
michael@0 562 /**
michael@0 563 * Get the previous filter that this view was displaying, or null if none.
michael@0 564 */
michael@0 565 public FilterDetail getPreviousFilter() {
michael@0 566 if (!canPopFilter()) {
michael@0 567 return null;
michael@0 568 }
michael@0 569
michael@0 570 return mFilterStack.get(1);
michael@0 571 }
michael@0 572
michael@0 573 /**
michael@0 574 * Adds a filter to the history stack for this view.
michael@0 575 */
michael@0 576 public void pushFilter(FilterDetail filter) {
michael@0 577 if (mFilterStack == null) {
michael@0 578 mFilterStack = new LinkedList<FilterDetail>();
michael@0 579
michael@0 580 // Initialize with the initial filter.
michael@0 581 mFilterStack.push(new FilterDetail(mViewConfig.getFilter(),
michael@0 582 mPanelConfig.getTitle()));
michael@0 583 }
michael@0 584
michael@0 585 mFilterStack.push(filter);
michael@0 586 }
michael@0 587
michael@0 588 /**
michael@0 589 * Remove the most recent filter from the stack.
michael@0 590 *
michael@0 591 * @return whether the filter was popped
michael@0 592 */
michael@0 593 public boolean popFilter() {
michael@0 594 if (!canPopFilter()) {
michael@0 595 return false;
michael@0 596 }
michael@0 597
michael@0 598 mFilterStack.pop();
michael@0 599 return true;
michael@0 600 }
michael@0 601
michael@0 602 public boolean canPopFilter() {
michael@0 603 return (mFilterStack != null && mFilterStack.size() > 1);
michael@0 604 }
michael@0 605 }
michael@0 606
michael@0 607 static class FilterDetail implements Parcelable {
michael@0 608 final String filter;
michael@0 609 final String title;
michael@0 610
michael@0 611 private FilterDetail(Parcel in) {
michael@0 612 this.filter = in.readString();
michael@0 613 this.title = in.readString();
michael@0 614 }
michael@0 615
michael@0 616 public FilterDetail(String filter, String title) {
michael@0 617 this.filter = filter;
michael@0 618 this.title = title;
michael@0 619 }
michael@0 620
michael@0 621 @Override
michael@0 622 public int describeContents() {
michael@0 623 return 0;
michael@0 624 }
michael@0 625
michael@0 626 @Override
michael@0 627 public void writeToParcel(Parcel dest, int flags) {
michael@0 628 dest.writeString(filter);
michael@0 629 dest.writeString(title);
michael@0 630 }
michael@0 631
michael@0 632 public static final Creator<FilterDetail> CREATOR = new Creator<FilterDetail>() {
michael@0 633 public FilterDetail createFromParcel(Parcel in) {
michael@0 634 return new FilterDetail(in);
michael@0 635 }
michael@0 636
michael@0 637 public FilterDetail[] newArray(int size) {
michael@0 638 return new FilterDetail[size];
michael@0 639 }
michael@0 640 };
michael@0 641 }
michael@0 642
michael@0 643 /**
michael@0 644 * Pushes filter to {@code ViewState}'s stack and makes request for new filter value.
michael@0 645 */
michael@0 646 private void pushFilterOnView(ViewState viewState, FilterDetail filterDetail) {
michael@0 647 final int index = viewState.getIndex();
michael@0 648 final String datasetId = viewState.getDatasetId();
michael@0 649
michael@0 650 mDatasetHandler.requestDataset(new DatasetRequest(index,
michael@0 651 DatasetRequest.Type.FILTER_PUSH,
michael@0 652 datasetId,
michael@0 653 filterDetail));
michael@0 654 }
michael@0 655
michael@0 656 /**
michael@0 657 * Pops filter from {@code ViewState}'s stack and makes request for previous filter value.
michael@0 658 *
michael@0 659 * @return whether the filter has changed
michael@0 660 */
michael@0 661 private boolean popFilterOnView(ViewState viewState) {
michael@0 662 if (viewState.canPopFilter()) {
michael@0 663 final int index = viewState.getIndex();
michael@0 664 final String datasetId = viewState.getDatasetId();
michael@0 665 final FilterDetail filterDetail = viewState.getPreviousFilter();
michael@0 666
michael@0 667 mDatasetHandler.requestDataset(new DatasetRequest(index,
michael@0 668 DatasetRequest.Type.FILTER_POP,
michael@0 669 datasetId,
michael@0 670 filterDetail));
michael@0 671
michael@0 672 return true;
michael@0 673 } else {
michael@0 674 return false;
michael@0 675 }
michael@0 676 }
michael@0 677
michael@0 678 public interface OnItemOpenListener {
michael@0 679 public void onItemOpen(String url, String title);
michael@0 680 }
michael@0 681
michael@0 682 private class PanelOnItemOpenListener implements OnItemOpenListener {
michael@0 683 private ViewState mViewState;
michael@0 684
michael@0 685 public PanelOnItemOpenListener(ViewState viewState) {
michael@0 686 mViewState = viewState;
michael@0 687 }
michael@0 688
michael@0 689 @Override
michael@0 690 public void onItemOpen(String url, String title) {
michael@0 691 if (StringUtils.isFilterUrl(url)) {
michael@0 692 FilterDetail filterDetail = new FilterDetail(StringUtils.getFilterFromUrl(url), title);
michael@0 693 pushFilterOnView(mViewState, filterDetail);
michael@0 694 } else {
michael@0 695 EnumSet<OnUrlOpenListener.Flags> flags = EnumSet.noneOf(OnUrlOpenListener.Flags.class);
michael@0 696 if (mViewState.getItemHandler() == ItemHandler.INTENT) {
michael@0 697 flags.add(OnUrlOpenListener.Flags.OPEN_WITH_INTENT);
michael@0 698 }
michael@0 699
michael@0 700 mUrlOpenListener.onUrlOpen(url, flags);
michael@0 701 }
michael@0 702 }
michael@0 703 }
michael@0 704
michael@0 705 private class PanelKeyListener implements View.OnKeyListener {
michael@0 706 private ViewState mViewState;
michael@0 707
michael@0 708 public PanelKeyListener(ViewState viewState) {
michael@0 709 mViewState = viewState;
michael@0 710 }
michael@0 711
michael@0 712 @Override
michael@0 713 public boolean onKey(View v, int keyCode, KeyEvent event) {
michael@0 714 if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
michael@0 715 return popFilterOnView(mViewState);
michael@0 716 }
michael@0 717
michael@0 718 return false;
michael@0 719 }
michael@0 720 }
michael@0 721
michael@0 722 private class PanelFilterManager implements FilterManager {
michael@0 723 private final ViewState mViewState;
michael@0 724
michael@0 725 public PanelFilterManager(ViewState viewState) {
michael@0 726 mViewState = viewState;
michael@0 727 }
michael@0 728
michael@0 729 @Override
michael@0 730 public FilterDetail getPreviousFilter() {
michael@0 731 return mViewState.getPreviousFilter();
michael@0 732 }
michael@0 733
michael@0 734 @Override
michael@0 735 public boolean canGoBack() {
michael@0 736 return mViewState.canPopFilter();
michael@0 737 }
michael@0 738
michael@0 739 @Override
michael@0 740 public void goBack() {
michael@0 741 popFilterOnView(mViewState);
michael@0 742 }
michael@0 743 }
michael@0 744 }

mercurial