Temporal edge navigation feature
- User can swipe from left and right edge to go
back and forward in history of a tab
Change-Id: I941f91122510b004bdcb6a718eb6c05730694960
diff --git a/src/com/android/browser/EdgeSwipeSettings.java b/src/com/android/browser/EdgeSwipeSettings.java
new file mode 100644
index 0000000..3425142
--- /dev/null
+++ b/src/com/android/browser/EdgeSwipeSettings.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package com.android.browser;
+
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+import android.support.v4.widget.ViewDragHelper;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.Toast;
+
+public class EdgeSwipeSettings extends ViewDragHelper.Callback {
+ private ViewDragHelper mDragHelper;
+ private int mFromEdge = ViewDragHelper.EDGE_TOP;
+ private int mLeft = 0;
+
+ private ImageView mSlidingViewShadow;
+ private LinearLayout mSettingsView;
+ private DraggableFrameLayout mViewGroup;
+ private View mLiveView;
+ private ImageView mStationaryView;
+
+ private int mSlidingViewIndex;
+ private int mCurrIndex;
+
+ private EdgeSwipeModel mModel;
+ private Tab mActiveTab;
+ private TitleBar mTitleBar;
+
+ private boolean mbWaitForSettings = false;
+
+ public EdgeSwipeSettings(final View container,
+ int stationaryViewId,
+ int settingViewId,
+ int slidingViewShadowId,
+ int liveViewId,
+ int viewGroupId,
+ final BaseUi ui) {
+ DraggableFrameLayout viewGroup = (DraggableFrameLayout)
+ container.findViewById(viewGroupId);
+
+ mSlidingViewShadow = (ImageView) container.findViewById(slidingViewShadowId);
+ mSettingsView = (LinearLayout) container.findViewById(settingViewId);
+ mStationaryView = (ImageView) container.findViewById(stationaryViewId);
+ mLiveView = container.findViewById(liveViewId);
+ mViewGroup = (DraggableFrameLayout) container.findViewById(viewGroupId);
+
+ final int childCount = mViewGroup.getChildCount();
+
+ for (int i = childCount - 1; i >= 0; i--) {
+ final View child = mViewGroup.getChildAt(i);
+ if (mSettingsView == child) {
+ mSlidingViewIndex = i;
+ break;
+ }
+ }
+
+ mActiveTab = ui.getActiveTab();
+ mTitleBar = ui.getTitleBar();
+ mModel = new EdgeSwipeModel(mActiveTab, mTitleBar);
+
+ mDragHelper = ViewDragHelper.create(viewGroup, 0.5f, this);
+ mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT | ViewDragHelper.EDGE_RIGHT);
+ mViewGroup.setDragHelper(mDragHelper);
+
+ final Button closeBtn =
+ (Button) container.findViewById(R.id.edge_sliding_settings_close_btn);
+ closeBtn.setOnClickListener(
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ mbWaitForSettings = false;
+ mSettingsView.setVisibility(View.GONE);
+ goLive();
+ }
+ }
+ );
+
+ final RadioButton temporalNavButton =
+ (RadioButton) container.findViewById(R.id.edge_sliding_settings_options_temporal);
+ temporalNavButton.setOnClickListener(
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ mbWaitForSettings = false;
+ mSettingsView.setVisibility(View.GONE);
+ BrowserSettings.getInstance().setEdgeSwipeTemporal();
+ goLive();
+ applySettingsAndRefresh(ui, container);
+ Toast toast = Toast.makeText(ui.getActivity().getApplicationContext(),
+ R.string.pref_temporal_edge_swipe_enabled_toast, Toast.LENGTH_SHORT);
+ toast.show();
+ }
+ }
+ );
+
+ final RadioButton spatialNavButton =
+ (RadioButton) container.findViewById(R.id.edge_sliding_settings_options_spatial);
+ spatialNavButton.setOnClickListener(
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ mbWaitForSettings = false;
+ mSettingsView.setVisibility(View.GONE);
+ BrowserSettings.getInstance().setEdgeSwipeSpatial();
+ goLive();
+ applySettingsAndRefresh(ui, container);
+ Toast toast = Toast.makeText(ui.getActivity().getApplicationContext(),
+ R.string.pref_spatial_edge_swipe_enabled_toast, Toast.LENGTH_SHORT);
+ toast.show();
+ }
+ }
+ );
+
+ final RadioButton disabledNavButton =
+ (RadioButton) container.findViewById(R.id.edge_sliding_settings_options_disabled);
+ disabledNavButton.setOnClickListener(
+ new View.OnClickListener() {
+ public void onClick(View v) {
+ mbWaitForSettings = false;
+ mSettingsView.setVisibility(View.GONE);
+ BrowserSettings.getInstance().setEdgeSwipeDisabled();
+ goLive();
+ applySettingsAndRefresh(ui, container);
+ Toast toast = Toast.makeText(ui.getActivity().getApplicationContext(),
+ R.string.pref_edge_swipe_disabled_toast, Toast.LENGTH_SHORT);
+ toast.show();
+ }
+ }
+ );
+ }
+
+ private void applySettingsAndRefresh(final BaseUi ui, final View container) {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... unused) {
+ mDragHelper = null;
+ ui.refreshEdgeSwipeController(container);
+ return null;
+ }
+ }.execute();
+ }
+
+ private void goLive() {
+ mFromEdge = ViewDragHelper.EDGE_TOP;
+ mLiveView.setVisibility(View.VISIBLE);
+ mStationaryView.setVisibility(View.GONE);
+ mSlidingViewShadow.setVisibility(View.GONE);
+ mViewGroup.invalidate();
+ }
+
+ private void goDormant() {
+ mLiveView.setVisibility(View.GONE);
+ mStationaryView.setVisibility(View.VISIBLE);
+ mViewGroup.invalidate();
+ }
+
+ public void onConfigurationChanged() {
+ mSettingsView.setVisibility(View.GONE);
+ goLive();
+ }
+
+ private void showCurrBitmap() {
+ if (mStationaryView.getVisibility() == View.VISIBLE) {
+ return;
+ }
+
+ Bitmap currBM = mModel.readSnapshot(mCurrIndex);
+ if (currBM != null) {
+ clampViewIfNeeded(mStationaryView);
+ mStationaryView.setImageBitmap(currBM);
+ goDormant();
+ mModel.deleteSnapshot(mCurrIndex);
+ }
+ }
+
+ private void clampViewIfNeeded(View view) {
+ int offset = 0;
+ if (mTitleBar.getY() >= 0) {
+ offset = mTitleBar.getNavigationBar().getMeasuredHeight();
+ }
+ view.setPadding(0, offset - view.getTop(), 0, 0);
+ }
+
+ public void onViewDragStateChanged(int state) {
+ if (ViewDragHelper.STATE_IDLE == state && !mbWaitForSettings) {
+ mSettingsView.setVisibility(View.GONE);
+ goLive();
+ }
+ }
+
+ public void onViewReleased(View releasedChild, float xvel, float yvel) {
+ boolean bCrossedEventHorizon = Math.abs(mLeft) > mViewGroup.getWidth() / 2;
+
+ switch (mFromEdge) {
+ case ViewDragHelper.EDGE_LEFT:
+ if (xvel > 0 || (xvel == 0 && mLeft > 0 && bCrossedEventHorizon)) {
+ showCurrBitmap();
+ mbWaitForSettings = true;
+ mDragHelper.settleCapturedViewAt(
+ releasedChild.getMeasuredWidth(),
+ releasedChild.getTop());
+ break;
+ }
+ mDragHelper.settleCapturedViewAt(0, releasedChild.getTop());
+ break;
+ case ViewDragHelper.EDGE_RIGHT:
+ if (xvel < 0 || (xvel == 0 && mLeft < 0 && bCrossedEventHorizon)) {
+ showCurrBitmap();
+ mbWaitForSettings = true;
+ mDragHelper.settleCapturedViewAt(
+ -releasedChild.getMeasuredWidth(),
+ releasedChild.getTop());
+ break;
+ }
+ mDragHelper.settleCapturedViewAt(0, releasedChild.getTop());
+ break;
+ default:
+ mDragHelper.settleCapturedViewAt(0, releasedChild.getTop());
+ break;
+ }
+ mLeft = 0;
+ mViewGroup.invalidate();
+ }
+
+ public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
+ showCurrBitmap();
+ switch (mFromEdge) {
+ case ViewDragHelper.EDGE_LEFT:
+ mSlidingViewShadow.setX(left);
+ mViewGroup.invalidate();
+ break;
+ case ViewDragHelper.EDGE_RIGHT:
+ mSlidingViewShadow.setX(mViewGroup.getMeasuredWidth() + left
+ - mSlidingViewShadow.getMeasuredWidth());
+ mViewGroup.invalidate();
+ break;
+ default:
+ break;
+ }
+ }
+
+ public void onEdgeDragStarted(int edgeFlags, int pointerId) {
+ if (mFromEdge != ViewDragHelper.EDGE_TOP) {
+ return;
+ }
+
+ mCurrIndex = mActiveTab.getWebView().copyBackForwardList().getCurrentIndex();
+
+ mModel.updateSnapshot(mCurrIndex);
+
+ clampViewIfNeeded(mSettingsView);
+
+ if (ViewDragHelper.EDGE_LEFT == (edgeFlags & ViewDragHelper.EDGE_LEFT)) {
+ mFromEdge = ViewDragHelper.EDGE_LEFT;
+
+ mSettingsView.setTranslationX(-mViewGroup.getMeasuredWidth());
+ mSlidingViewShadow.setBackgroundResource(R.drawable.right_shade);
+ } else if (ViewDragHelper.EDGE_RIGHT == (edgeFlags & ViewDragHelper.EDGE_RIGHT)) {
+ mFromEdge = ViewDragHelper.EDGE_RIGHT;
+
+ mSettingsView.setTranslationX(mViewGroup.getMeasuredWidth());
+ mSlidingViewShadow.setBackgroundResource(R.drawable.left_shade);
+ }
+
+ mSettingsView.setVisibility(View.VISIBLE);
+ mSlidingViewShadow.setVisibility(View.VISIBLE);
+
+ showCurrBitmap();
+
+ mViewGroup.invalidate();
+ }
+
+ public int getOrderedChildIndex(int index) {
+ return mSlidingViewIndex;
+ }
+
+ public int getViewHorizontalDragRange(View child) {
+ return child.getMeasuredWidth();
+ }
+
+ public boolean tryCaptureView(View child, int pointerId) {
+ return (child == mSettingsView);
+ }
+
+ public int clampViewPositionHorizontal(View child, int left, int dx) {
+ mLeft = left;
+ return left;
+ }
+}
+