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/res/drawable-xxhdpi/deco_swipe_spatial.png b/res/drawable-xxhdpi/deco_swipe_spatial.png
new file mode 100644
index 0000000..d95ef9b
--- /dev/null
+++ b/res/drawable-xxhdpi/deco_swipe_spatial.png
Binary files differ
diff --git a/res/drawable-xxhdpi/deco_swipe_temporal.png b/res/drawable-xxhdpi/deco_swipe_temporal.png
new file mode 100644
index 0000000..30529c1
--- /dev/null
+++ b/res/drawable-xxhdpi/deco_swipe_temporal.png
Binary files differ
diff --git a/res/drawable-xxhdpi/left_shade.png b/res/drawable-xxhdpi/left_shade.png
new file mode 100644
index 0000000..94f7c1c
--- /dev/null
+++ b/res/drawable-xxhdpi/left_shade.png
Binary files differ
diff --git a/res/drawable-xxhdpi/right_shade.png b/res/drawable-xxhdpi/right_shade.png
new file mode 100644
index 0000000..3ae50fa
--- /dev/null
+++ b/res/drawable-xxhdpi/right_shade.png
Binary files differ
diff --git a/res/layout/tab.xml b/res/layout/tab.xml
index 8bf3327..0c25901 100755
--- a/res/layout/tab.xml
+++ b/res/layout/tab.xml
@@ -19,21 +19,127 @@
Currently, the only such element is the Geolocation permissions prompt.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.browser.DraggableFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/draggable_mainframe"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <ImageView android:id="@+id/navview_opacity"
+ android:layout_gravity="top"
+ android:scaleType="fitStart"
+ android:background="@android:color/black"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <ImageView android:id="@+id/stationary_navview"
+ android:layout_gravity="top"
+ android:scaleType="fitStart"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
<!-- Wrapper layout for the WebView, which must be in a FrameLayout. -->
<FrameLayout android:id="@+id/webview_wrapper"
android:layout_width="match_parent"
- android:layout_height="0dip"
+ android:layout_height="match_parent"
android:layout_weight="1" />
+ <ImageView android:id="@+id/sliding_navview"
+ android:layout_gravity="top"
+ android:scaleType="fitStart"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <ImageView android:id="@+id/sliding_navview_shadow"
+ android:background="@drawable/left_shade"
+ android:layout_gravity="top"
+ android:scaleType="fitStart"
+ android:visibility="gone"
+ android:layout_width="10dp"
+ android:layout_height="match_parent" />
+
+ <LinearLayout android:id="@+id/edge_sliding_settings"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ android:background="#00838f"
+ android:alpha="0.85"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center_horizontal|center_vertical"
+ android:layout_weight=".25"
+ android:textSize="32sp"
+ android:textColor="@color/white"
+ android:text="@string/pref_edge_swipe_title"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:textSize="21sp"
+ android:textColor="@color/white"
+ android:text="@string/pref_edge_swipe_option_msg"/>
+
+ <RadioGroup
+ android:id="@+id/edge_sliding_settings_options"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight=".25"
+ android:layout_gravity="center_horizontal"
+ android:padding="10dp"
+ android:orientation="vertical">
+ <RadioButton
+ android:id="@+id/edge_sliding_settings_options_temporal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/pref_temporal_edge_swipe"
+ android:textColor="@color/white"
+ android:drawableEnd="@drawable/deco_swipe_temporal"
+ android:textSize="18sp"
+ android:padding="10dp"/>
+ <RadioButton
+ android:id="@+id/edge_sliding_settings_options_spatial"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/pref_spatial_edge_swipe"
+ android:textColor="@color/white"
+ android:drawableEnd="@drawable/deco_swipe_spatial"
+ android:textSize="18sp"
+ android:visibility="gone"
+ android:padding="10dp"/>
+ <RadioButton
+ android:id="@+id/edge_sliding_settings_options_disabled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/pref_disable_edge_swipe"
+ android:textColor="@color/white"
+ android:textSize="18sp"
+ android:padding="10dp"/>
+ </RadioGroup>
+
+ <Button
+ android:id="@+id/edge_sliding_settings_close_btn"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/pref_edge_swipe_option_close"
+ android:padding="10dp"
+ android:gravity="center_horizontal|center_vertical"
+ android:layout_gravity="center"
+ style="@android:style/Widget.Holo.Button.Borderless"/>
+
+ </LinearLayout>
+
<!-- Geolocation permissions prompt -->
<ViewStub android:id="@+id/geolocation_permissions_prompt"
android:layout="@layout/geolocation_permissions_prompt"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
-</LinearLayout>
+
+</com.android.browser.DraggableFrameLayout>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 7f0e63e..0cb0edf 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -503,4 +503,11 @@
<!-- Toast message displayed when user attempts to modify a setting disabled by the administrator -->
<string name="mdm_managed_alert">被管理员禁用</string>
+
+ <string name="pref_edge_swipe_title">边缘滑动</string>
+ <string name="pref_edge_swipe_option_msg">滑过屏幕边缘时,您想:</string>
+ <string name="pref_temporal_edge_swipe">上一个/下一个网站</string>
+ <string name="pref_spatial_edge_swipe">更换标签页</string>
+ <string name="pref_disable_edge_swipe">禁用边缘滑动</string>
+ <string name="pref_edge_swipe_option_close">关闭</string>
</resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 225ba12..77340b0 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -500,4 +500,10 @@
<string name="accessibility_transition_navscreen" msgid="2734915619351519547">"標籤管理"</string>
<string name="accessibility_button_bookmarks_folder_up" msgid="9179389954714270505">"上一個資料夾"</string>
<string name="mdm_managed_alert">被管理員禁用</string>
+ <string name="pref_edge_swipe_title">邊緣滑動</string>
+ <string name="pref_edge_swipe_option_msg">滑過邊緣時,您想:</string>
+ <string name="pref_temporal_edge_swipe">上一個/下一個網站</string>
+ <string name="pref_spatial_edge_swipe">更換標籤頁</string>
+ <string name="pref_disable_edge_swipe">禁用邊緣滑動</string>
+ <string name="pref_edge_swipe_option_close">關閉</string>
</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 3ff6708..0c3486d 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -412,4 +412,10 @@
<string name="help">"幫助"</string>
<string name="feedback">"反饋"</string>
<string name="mdm_managed_alert">被管理員禁用</string>
+ <string name="pref_edge_swipe_title">邊緣滑動</string>
+ <string name="pref_edge_swipe_option_msg">滑過邊緣時,您想:</string>
+ <string name="pref_temporal_edge_swipe">上一個/下一個網站</string>
+ <string name="pref_spatial_edge_swipe">更換標籤頁</string>
+ <string name="pref_disable_edge_swipe">禁用邊緣滑動</string>
+ <string name="pref_edge_swipe_option_close">關閉</string>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index fdfcc9d..7d2ed69 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -638,6 +638,36 @@
<item>auto</item>
</string-array>
<string name="pref_default_text_encoding_default">UTF-8</string>
+ <string name="pref_edge_swipe_title">Edge Swipe</string>
+ <string name="pref_edge_swipe_option_msg">When swiping, would you like:</string>
+ <string name="pref_temporal_edge_swipe">Previous/Next Web Site</string>
+ <string name="pref_spatial_edge_swipe">Change Tab</string>
+ <string name="pref_disable_edge_swipe">Disable edge swipe</string>
+ <string name="pref_edge_swipe_option_close">Close</string>
+ <string name="pref_temporal_edge_swipe_enabled_toast">Swipe from edge to navigate</string>
+ <string name="pref_spatial_edge_swipe_enabled_toast">Swipe from edge to change tabs</string>
+ <string name="pref_edge_swipe_disabled_toast">Swiping from edge disabled</string>
+ <string-array name="pref_edge_swiping_choices">
+ <item>@string/pref_temporal_edge_swipe</item>
+<!--
+ <item>@string/pref_spatial_edge_swipe</item>
+-->
+ <item>@string/pref_disable_edge_swipe</item>
+ </string-array>
+
+ <string name="value_temporal_edge_swipe" translatable="false">temporal_edge_swipe</string>
+ <string name="value_spatial_edge_swipe" translatable="false">spatial_edge_swipe</string>
+ <string name="value_disable_edge_swipe" translatable="false">disable_edge_swipe</string>
+ <string name="value_unknown_edge_swipe" translatable="false">unknown_edge_swipe</string>
+
+ <string-array name="pref_edge_swiping_values" translatable="false">
+ <item>@string/value_temporal_edge_swipe</item>
+<!--
+ <item>@string/value_spatial_edge_swipe</item>
+-->
+ <item>@string/value_disable_edge_swipe</item>
+ </string-array>
+
<!-- Title for accessibility settings [CHAR LIMIT=25] -->
<string name="pref_accessibility_title">Accessibility</string>
<!-- Font size settings category under accessibility settings [CHAR LIMIT=50] -->
diff --git a/res/xml/general_preferences.xml b/res/xml/general_preferences.xml
index 662ef30..abd7b73 100644
--- a/res/xml/general_preferences.xml
+++ b/res/xml/general_preferences.xml
@@ -200,6 +200,13 @@
android:summary="@string/pref_extras_website_settings_summary"
android:title="@string/pref_extras_website_settings" />
+ <ListPreference
+ android:dialogTitle="@string/pref_edge_swipe_option_msg"
+ android:entries="@array/pref_edge_swiping_choices"
+ android:entryValues="@array/pref_edge_swiping_values"
+ android:key="edge_swiping_action"
+ android:title="@string/pref_edge_swipe_title" />
+
<com.android.browser.BrowserYesNoPreference
android:key="reset_default_preferences"
android:title="@string/pref_extras_reset_default"
diff --git a/src/com/android/browser/BaseUi.java b/src/com/android/browser/BaseUi.java
index 089c7f0..c24cc48 100644
--- a/src/com/android/browser/BaseUi.java
+++ b/src/com/android/browser/BaseUi.java
@@ -109,6 +109,9 @@
private NavigationBarBase mNavigationBar;
private boolean mBlockFocusAnimations;
+ private EdgeSwipeController mEdgeSwipeController;
+ private EdgeSwipeSettings mEdgeSwipeSettings;
+
public BaseUi(Activity browser, UiController controller) {
mActivity = browser;
mUiController = controller;
@@ -205,6 +208,12 @@
}
public void onConfigurationChanged(Configuration config) {
+ if (mEdgeSwipeController != null) {
+ mEdgeSwipeController.onConfigurationChanged();
+ }
+ if (mEdgeSwipeSettings != null) {
+ mEdgeSwipeSettings.onConfigurationChanged();
+ }
}
public Activity getActivity() {
@@ -442,9 +451,49 @@
}
mContentView.addView(container, COVER_SCREEN_PARAMS);
}
+
+ refreshEdgeSwipeController(container);
+
mUiController.attachSubWindow(tab);
}
+ public void refreshEdgeSwipeController(View container) {
+ if (mEdgeSwipeController != null) {
+ mEdgeSwipeController.cleanup();
+ }
+
+ mEdgeSwipeSettings = null;
+
+ String action = BrowserSettings.getInstance().getEdgeSwipeAction();
+
+ if (action.equalsIgnoreCase(
+ mActivity.getResources().getString(R.string.value_temporal_edge_swipe))) {
+ mEdgeSwipeController = new EdgeSwipeController(
+ container,
+ R.id.stationary_navview,
+ R.id.sliding_navview,
+ R.id.sliding_navview_shadow,
+ R.id.navview_opacity,
+ R.id.webview_wrapper,
+ R.id.draggable_mainframe,
+ this);
+ } else if (action.equalsIgnoreCase(
+ mActivity.getResources().getString(R.string.value_unknown_edge_swipe))) {
+ mEdgeSwipeSettings = new EdgeSwipeSettings(
+ container,
+ R.id.stationary_navview,
+ R.id.edge_sliding_settings,
+ R.id.sliding_navview_shadow,
+ R.id.webview_wrapper,
+ R.id.draggable_mainframe,
+ this);
+ } else {
+ DraggableFrameLayout draggableView = (DraggableFrameLayout)
+ container.findViewById(R.id.draggable_mainframe);
+ draggableView.setDragHelper(null);
+ }
+ }
+
private void removeTabFromContentView(Tab tab) {
hideTitleBar();
// Remove the container that contains the main WebView.
diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index 36f28b1..a60a109 100644
--- a/src/com/android/browser/BrowserSettings.java
+++ b/src/com/android/browser/BrowserSettings.java
@@ -221,6 +221,12 @@
mPrefs.edit().putString(PREF_DEFAULT_TEXT_ENCODING, "auto").apply();
}
+ if (!mPrefs.contains(PREF_EDGE_SWIPE)) {
+ mPrefs.edit().putString(PREF_EDGE_SWIPE,
+ mContext.getResources().getString(
+ R.string.value_unknown_edge_swipe)).apply();
+ }
+
if (sFactoryResetUrl.indexOf("{CID}") != -1) {
sFactoryResetUrl = sFactoryResetUrl.replace("{CID}",
BrowserProvider.getClientId(mContext.getContentResolver()));
@@ -759,6 +765,26 @@
return mPrefs.getString(PREF_DEFAULT_TEXT_ENCODING, "auto");
}
+ public String getEdgeSwipeAction() {
+ return mPrefs.getString(PREF_EDGE_SWIPE,
+ mContext.getResources().getString(R.string.value_unknown_edge_swipe));
+ }
+
+ public void setEdgeSwipeTemporal() {
+ mPrefs.edit().putString(PREF_EDGE_SWIPE,
+ mContext.getResources().getString(R.string.value_temporal_edge_swipe)).apply();
+ }
+
+ public void setEdgeSwipeSpatial() {
+ mPrefs.edit().putString(PREF_EDGE_SWIPE,
+ mContext.getResources().getString(R.string.value_spatial_edge_swipe)).apply();
+ }
+
+ public void setEdgeSwipeDisabled() {
+ mPrefs.edit().putString(PREF_EDGE_SWIPE,
+ mContext.getResources().getString(R.string.value_disable_edge_swipe)).apply();
+ }
+
// -----------------------------
// getter/setters for general_preferences.xml
// -----------------------------
diff --git a/src/com/android/browser/DraggableFrameLayout.java b/src/com/android/browser/DraggableFrameLayout.java
new file mode 100644
index 0000000..e2f9699
--- /dev/null
+++ b/src/com/android/browser/DraggableFrameLayout.java
@@ -0,0 +1,90 @@
+/*
+ * 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.content.Context;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.widget.ViewDragHelper;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+public class DraggableFrameLayout extends FrameLayout {
+ private ViewDragHelper mDragger;
+ public DraggableFrameLayout(Context ctx) {
+ super(ctx);
+ }
+ public DraggableFrameLayout(Context ctx, AttributeSet set) {
+ super(ctx, set);
+ }
+ public DraggableFrameLayout(Context ctx, AttributeSet set, int defStyleAttr) {
+ super(ctx, set, defStyleAttr);
+ }
+ public DraggableFrameLayout(Context ctx, AttributeSet set, int defStyleAttr, int defStyleRes) {
+ super(ctx, set, defStyleAttr, defStyleRes);
+ }
+
+ public void setDragHelper(ViewDragHelper dragger) {
+ mDragger = dragger;
+ }
+
+ public final ViewGroup getParentViewGroup() {
+ return (ViewGroup) DraggableFrameLayout.this.getParent();
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (mDragger == null)
+ return super.onInterceptTouchEvent(event);
+
+ return mDragger.shouldInterceptTouchEvent(event) ?
+ true : super.onInterceptTouchEvent(event);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (mDragger == null)
+ return super.onTouchEvent(event);
+
+ mDragger.processTouchEvent(event);
+ return true;
+ }
+
+ @Override
+ public void computeScroll() {
+ super.computeScroll();
+ if (mDragger != null && mDragger.continueSettling(true)) {
+ ViewCompat.postInvalidateOnAnimation(this);
+ }
+ }
+}
+
diff --git a/src/com/android/browser/EdgeSwipeController.java b/src/com/android/browser/EdgeSwipeController.java
new file mode 100644
index 0000000..87ad2e7
--- /dev/null
+++ b/src/com/android/browser/EdgeSwipeController.java
@@ -0,0 +1,543 @@
+/*
+ * 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.graphics.Canvas;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Paint;
+import android.os.CountDownTimer;
+import android.support.v4.widget.ViewDragHelper;
+import android.view.View;
+
+import org.codeaurora.swe.WebHistoryItem;
+import org.codeaurora.swe.util.Activator;
+import org.codeaurora.swe.util.Observable;
+
+public class EdgeSwipeController extends ViewDragHelper.Callback {
+ private ViewDragHelper mDragHelper;
+ private int mState = ViewDragHelper.STATE_IDLE;
+ private int mFromEdge = ViewDragHelper.EDGE_LEFT;
+ private boolean mbNavigated = false;
+ private int mOldX = 0;
+ private int mOldDx = 0;
+ private Observable mPageLoadTarget;
+ private Observable mPageLoadObservable;
+
+ private boolean mbCurrBMSynced = false;
+
+ private Tab mActiveTab;
+ private TitleBar mTitleBar;
+
+ private static final float mMinAlpha = 0.5f;
+ private static final int mMinProgress = 85;
+ private static final int mProgressWaitMS = 1000;
+ private static final int EDGE_SWIPE_INVALID_INDEX = -2;
+
+ private CountDownTimer mLoadTimer, mCommitTimer;
+
+ private int mCurrIndex = EDGE_SWIPE_INVALID_INDEX;
+ private int mPrevIndex;
+ private int mNextIndex;
+ private int mMaxIndex;
+
+ private EdgeSwipeModel mModel;
+ private EdgeSwipeView mView;
+
+ public EdgeSwipeController(View container,
+ int stationaryViewId,
+ int slidingViewId,
+ int slidingViewShadowId,
+ int opacityViewId,
+ int liveViewId,
+ int viewGroupId,
+ BaseUi ui) {
+ DraggableFrameLayout viewGroup = (DraggableFrameLayout)
+ container.findViewById(viewGroupId);
+
+ mActiveTab = ui.getActiveTab();
+ mTitleBar = ui.getTitleBar();
+
+ mModel = new EdgeSwipeModel(mActiveTab, mTitleBar);
+ mView = new EdgeSwipeView(
+ container,
+ stationaryViewId,
+ slidingViewId,
+ slidingViewShadowId,
+ opacityViewId,
+ liveViewId,
+ viewGroupId,
+ mTitleBar);
+
+ mPageLoadTarget = mActiveTab.getTabHistoryUpdateObservable();
+ mPageLoadObservable = Activator.activate(
+ new Observable.Observer() {
+ @Override
+ public void onChange(Object... params) {
+ if (mDragHelper == null ||
+ mPageLoadTarget == null) {
+ return;
+ }
+
+ synchronized (this) {
+ int index = (int) params[0];
+ if (mState == ViewDragHelper.STATE_IDLE && index == mCurrIndex) {
+ monitorProgressAtHistoryUpdate(index);
+ }
+ }
+ }
+ },
+ mPageLoadTarget
+ );
+
+ mDragHelper = ViewDragHelper.create(viewGroup, 0.5f, this);
+ mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT | ViewDragHelper.EDGE_RIGHT);
+ viewGroup.setDragHelper(mDragHelper);
+ }
+
+ private void swipeSessionCleanup() {
+ mView.goLive();
+ mModel.cleanup();
+ mCurrIndex = EDGE_SWIPE_INVALID_INDEX;
+ mState = ViewDragHelper.STATE_IDLE;
+ }
+
+ private boolean setState(int curState, int newState) {
+ if (mState == curState) {
+ mState = newState;
+ return true;
+ }
+ return false;
+ }
+
+ public void cleanup() {
+ if (mPageLoadObservable != null) {
+ mPageLoadObservable.onOff(false);
+ synchronized (this) {
+ mDragHelper.cancel();
+ swipeSessionCleanup();
+ }
+ }
+ }
+
+ public void onConfigurationChanged() {
+ synchronized (this) {
+ swipeSessionCleanup();
+ }
+ }
+
+ private void showCurrBMInStationaryView() {
+ if (!mbCurrBMSynced) {
+ Bitmap currBM = mModel.readSnapshot(mCurrIndex);
+ if (currBM != null) {
+ mView.setStationaryViewBitmap(currBM);
+ mbCurrBMSynced = true;
+ }
+ }
+ }
+
+ private void showCurrBMInSlidingView() {
+ if (!mbCurrBMSynced) {
+ Bitmap currBM = mModel.readSnapshot(mCurrIndex);
+ mView.setSlidingViewBitmap(currBM);
+ if (currBM != null) {
+ mbCurrBMSynced = true;
+ }
+ }
+ }
+
+ private Bitmap getGrayscale(Bitmap bitmap)
+ {
+ if (bitmap == null)
+ return null;
+
+ int height = bitmap.getHeight();
+ int width = bitmap.getWidth();
+
+ Bitmap gray = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(gray);
+ Paint paint = new Paint();
+ ColorMatrix cm = new ColorMatrix();
+
+ cm.setSaturation(0);
+
+ ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
+
+ paint.setColorFilter(f);
+
+ c.drawBitmap(bitmap, 0, 0, paint);
+ return gray;
+ }
+
+ private void monitorProgressAtLoad(final int pageIndex) {
+ if (mLoadTimer != null) {
+ mLoadTimer.cancel();
+ }
+
+ mLoadTimer = new CountDownTimer(mProgressWaitMS * 5, mProgressWaitMS) {
+ boolean mGrayBM = false;
+
+ public void onTick(long msRemain) {
+ if (msRemain > mProgressWaitMS * 4) {
+ return;
+ }
+ synchronized (this) {
+ if (mTitleBar.getProgressView().getProgressPercent() >= mMinProgress) {
+ if (mState == ViewDragHelper.STATE_IDLE && pageIndex == mCurrIndex) {
+ swipeSessionCleanup();
+
+ }
+ cancel();
+ } else {
+ if (mGrayBM) {
+ return;
+ }
+ mView.setStationaryViewBitmap(
+ getGrayscale(getSnapshotOrFavicon(pageIndex)));
+ mGrayBM = true;
+ }
+ }
+ }
+
+ public void onFinish() {
+ mGrayBM = false;
+ synchronized (this) {
+ if (mTitleBar.getProgressView().getProgressPercent() >= mMinProgress) {
+ if (mState == ViewDragHelper.STATE_IDLE && pageIndex == mCurrIndex) {
+ swipeSessionCleanup();
+ }
+ cancel();
+ }
+ }
+ }
+ }.start();
+ }
+
+ private void monitorProgressAtHistoryUpdate(final int pageIndex) {
+ if (mCommitTimer != null) {
+ mCommitTimer.cancel();
+ }
+
+ if (mTitleBar.getProgressView().getProgressPercent() >= mMinProgress
+ && mActiveTab.getWebView().getLastCommittedHistoryIndex() == pageIndex) {
+ swipeSessionCleanup();
+ return;
+ }
+
+ mCommitTimer = new CountDownTimer(mProgressWaitMS * 5, mProgressWaitMS) {
+ public void onTick(long msRemain) {
+ synchronized (this) {
+ if (mTitleBar.getProgressView().getProgressPercent() >= mMinProgress) {
+ if (mState == ViewDragHelper.STATE_IDLE && pageIndex == mCurrIndex) {
+ swipeSessionCleanup();
+
+ }
+ cancel();
+ }
+ }
+ }
+
+ public void onFinish() {
+ synchronized (this) {
+ if (mState == ViewDragHelper.STATE_IDLE && pageIndex == mCurrIndex) {
+ swipeSessionCleanup();
+ }
+ }
+ }
+ }.start();
+ }
+
+ private boolean isPortrait(Bitmap bitmap) {
+ return (bitmap.getHeight() < bitmap.getWidth());
+ }
+
+ private Bitmap getSnapshotOrFavicon(int index) {
+ Bitmap bm = mModel.readSnapshot(index);
+ if (bm == null || mView.isPortrait() != isPortrait(bm)) {
+ WebHistoryItem item = mActiveTab.getWebView()
+ .copyBackForwardList().getItemAtIndex(index);
+ if (item != null) {
+ bm = item.getFavicon();
+ }
+ }
+ return bm;
+ }
+
+ public void onViewDragStateChanged(int state) {
+ synchronized (this) {
+ if (mState != ViewDragHelper.STATE_SETTLING || state != ViewDragHelper.STATE_IDLE) {
+ return;
+ }
+
+ mView.hideSlidingViews();
+
+ if (mView.isLive()) {
+ return;
+ }
+
+ if (mbNavigated) {
+ mView.setStationaryViewBitmap(getSnapshotOrFavicon(mCurrIndex));
+ mView.setStationaryViewAlpha(1.0f);
+ } else {
+ swipeSessionCleanup();
+ }
+
+ mView.invalidate();
+
+ setState(ViewDragHelper.STATE_SETTLING, ViewDragHelper.STATE_IDLE);
+ }
+ }
+
+ public void onViewReleased(View releasedChild, float xvel, float yvel) {
+ synchronized (this) {
+ if (!setState(ViewDragHelper.STATE_DRAGGING, ViewDragHelper.STATE_SETTLING)) {
+ mOldX = 0;
+ mOldDx = 0;
+ return;
+ }
+
+ mbNavigated = true;
+
+ boolean bCrossedEventHorizon = Math.abs(mOldX) > mView.getWidth() / 2;
+
+ if (mCurrIndex >= 0) {
+ if ((xvel > 0 || (xvel == 0 && mOldX > 0 && bCrossedEventHorizon))
+ && mFromEdge == ViewDragHelper.EDGE_LEFT
+ && mActiveTab.getWebView().canGoToHistoryIndex(mCurrIndex - 1)) {
+ mCurrIndex -= 1;
+ mActiveTab.getWebView().stopLoading();
+ mActiveTab.getWebView().goToHistoryIndex(mCurrIndex);
+ monitorProgressAtLoad(mCurrIndex);
+ mDragHelper.settleCapturedViewAt(
+ releasedChild.getMeasuredWidth(),
+ releasedChild.getTop());
+ } else if ((xvel < 0 || (xvel == 0 && mOldX < 0 && bCrossedEventHorizon))
+ && mFromEdge == ViewDragHelper.EDGE_RIGHT
+ && mActiveTab.getWebView().canGoToHistoryIndex(mCurrIndex + 1)) {
+ mCurrIndex += 1;
+ mActiveTab.getWebView().stopLoading();
+ mActiveTab.getWebView().goToHistoryIndex(mCurrIndex);
+ monitorProgressAtLoad(mCurrIndex);
+ mDragHelper.settleCapturedViewAt(
+ -releasedChild.getMeasuredWidth(),
+ releasedChild.getTop());
+ mView.goDormant();
+ } else {
+ mbNavigated = false;
+ mDragHelper.settleCapturedViewAt(0, releasedChild.getTop());
+ }
+ }
+ mOldX = 0;
+ mOldDx = 0;
+
+ mView.invalidate();
+ }
+ }
+
+ public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
+ float alpha = ((float) Math.abs(left)) / mView.getMeasuredWidth();
+
+ synchronized (this) {
+ switch (mFromEdge) {
+ case ViewDragHelper.EDGE_LEFT:
+ if (mView.isLive()) {
+ return;
+ }
+
+ if (mState != ViewDragHelper.STATE_IDLE) {
+ mView.moveShadowView(left);
+ }
+
+ showCurrBMInSlidingView();
+
+ if (mPrevIndex >= 0) {
+ if (!mView.stationaryViewHasImage()) {
+ mView.setStationaryViewBitmap(getSnapshotOrFavicon(mPrevIndex));
+ }
+
+ if (mActiveTab.getWebView().canGoToHistoryIndex(mPrevIndex)) {
+ mView.setStationaryViewAlpha(mMinAlpha + alpha * (1 - mMinAlpha));
+ }
+ }
+ break;
+ case ViewDragHelper.EDGE_RIGHT:
+ if (mState != ViewDragHelper.STATE_IDLE) {
+ mView.moveShadowView(mView.getMeasuredWidth() + left);
+
+ if (!mView.slidingViewHasImage() && mNextIndex < mMaxIndex) {
+ mView.setSlidingViewBitmap(getSnapshotOrFavicon(mNextIndex));
+ }
+
+ showCurrBMInStationaryView();
+ if (mbCurrBMSynced) {
+ mView.goDormant();
+ }
+ }
+ if (mNextIndex < mMaxIndex &&
+ mActiveTab.getWebView().canGoToHistoryIndex(mNextIndex)) {
+ mView.setStationaryViewAlpha(mMinAlpha + (1 - alpha) * (1 - mMinAlpha));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ public void onEdgeDragStarted(int edgeFlags, int pointerId) {
+ synchronized (this) {
+ if (mActiveTab.isPrivateBrowsingEnabled()) {
+ mDragHelper.abort();
+ return;
+ }
+
+ if (mDragHelper.getViewDragState() != ViewDragHelper.STATE_IDLE ||
+ !setState(ViewDragHelper.STATE_IDLE, ViewDragHelper.STATE_DRAGGING)) {
+ mDragHelper.abort();
+ return;
+ }
+
+ if ((edgeFlags & mFromEdge) != mFromEdge || mCurrIndex == EDGE_SWIPE_INVALID_INDEX) {
+ onEdgeTouched(edgeFlags, pointerId);
+ }
+
+ mbCurrBMSynced = false;
+
+ switch (mFromEdge) {
+ case ViewDragHelper.EDGE_LEFT:
+ mView.showSlidingViews();
+ mView.goDormant();
+ mPrevIndex = mCurrIndex - 1;
+ mView.setStationaryViewBitmap(getSnapshotOrFavicon(mPrevIndex));
+ showCurrBMInSlidingView();
+ break;
+ case ViewDragHelper.EDGE_RIGHT:
+ mView.showSlidingViews();
+ mNextIndex = mCurrIndex + 1;
+ mView.setSlidingViewBitmap(getSnapshotOrFavicon(mNextIndex));
+ showCurrBMInStationaryView();
+ if (mbCurrBMSynced)
+ mView.goDormant();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ public int getOrderedChildIndex(int index) {
+ return mView.slidingViewIndex();
+ }
+
+ public void onEdgeTouched (int edgeFlags, int pointerId) {
+ synchronized (this) {
+ if (mActiveTab.isPrivateBrowsingEnabled()) {
+ mDragHelper.abort();
+ return;
+ }
+
+ if (mState != ViewDragHelper.STATE_IDLE && mCurrIndex != EDGE_SWIPE_INVALID_INDEX) {
+ mDragHelper.abort();
+ return;
+ }
+
+ mView.init();
+
+ if (mCurrIndex == EDGE_SWIPE_INVALID_INDEX) {
+ mCurrIndex = mActiveTab.getWebView().getLastCommittedHistoryIndex();
+ }
+
+ mMaxIndex = mActiveTab.getWebView().copyBackForwardList().getSize() - 1;
+ mModel.updateSnapshot(mCurrIndex);
+
+ if (ViewDragHelper.EDGE_LEFT == (edgeFlags & ViewDragHelper.EDGE_LEFT)) {
+ mFromEdge = ViewDragHelper.EDGE_LEFT;
+ mView.slidingViewTouched(mFromEdge);
+ if (mCurrIndex > 0) {
+ mModel.fetchSnapshot(mCurrIndex - 1);
+ }
+ } else if (ViewDragHelper.EDGE_RIGHT == (edgeFlags & ViewDragHelper.EDGE_RIGHT)) {
+ mFromEdge = ViewDragHelper.EDGE_RIGHT;
+ mView.slidingViewTouched(mFromEdge);
+ if (mCurrIndex < mMaxIndex) {
+ mModel.fetchSnapshot(mCurrIndex + 1);
+ }
+ }
+ }
+ }
+
+ public int getViewHorizontalDragRange(View child) {
+ return child.getMeasuredWidth();
+ }
+
+ public boolean tryCaptureView(View child, int pointerId) {
+ return (mState == ViewDragHelper.STATE_DRAGGING && mView.allowCapture(child));
+ }
+
+ public int clampViewPositionHorizontal(View child, int left, int dx) {
+ if (mOldX != 0 && Math.signum(dx) != Math.signum(mOldDx)) {
+ mOldDx = dx;
+ return mOldX;
+ }
+
+ switch (mFromEdge) {
+ case ViewDragHelper.EDGE_LEFT:
+ if (left < 0) {
+ mOldDx = dx;
+ return mOldX;
+ }
+ if (!mActiveTab.getWebView().canGoToHistoryIndex(mPrevIndex)) {
+ if (Math.abs(left) >= child.getMeasuredWidth() / 3) {
+ return child.getMeasuredWidth() / 3;
+ }
+ }
+ break;
+ case ViewDragHelper.EDGE_RIGHT:
+ if (left > 0) {
+ mOldDx = dx;
+ return mOldX;
+ }
+ if (!mActiveTab.getWebView().canGoToHistoryIndex(mNextIndex)) {
+ if (Math.abs(left) >= child.getMeasuredWidth() / 3) {
+ return -child.getMeasuredWidth() / 3;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ mOldX = left;
+ mOldDx = dx;
+ return left;
+ }
+}
+
diff --git a/src/com/android/browser/EdgeSwipeModel.java b/src/com/android/browser/EdgeSwipeModel.java
new file mode 100644
index 0000000..51a5dc9
--- /dev/null
+++ b/src/com/android/browser/EdgeSwipeModel.java
@@ -0,0 +1,118 @@
+/*
+ * 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.util.SparseArray;
+import android.webkit.ValueCallback;
+
+public class EdgeSwipeModel {
+ private SparseArray<Bitmap> mBitmaps;
+ private Tab mTab;
+ private TitleBar mBar;
+
+ private static final int mMinProgress = 85;
+
+ private static final int mMaxBitmaps = 5;
+
+ public EdgeSwipeModel(Tab tab, TitleBar bar) {
+ mTab = tab;
+ mBar = bar;
+ mBitmaps = new SparseArray<>();
+ }
+
+ public void updateSnapshot(final int index) {
+ if (mBitmaps.get(index) != null) {
+ return;
+ }
+
+ int captureIndex = mTab.getCaptureIndex(index);
+
+ boolean bitmapExists = mTab.getWebView().hasSnapshot(captureIndex);
+
+ if (!mTab.isFirstVisualPixelPainted()) {
+ fetchSnapshot(index);
+ return;
+ }
+
+ int progress = mBar.getProgressView().getProgressPercent();
+
+ if (bitmapExists && progress < mMinProgress) {
+ fetchSnapshot(index);
+ return;
+ }
+
+ mTab.getWebView().captureSnapshot(captureIndex,
+ new ValueCallback<Bitmap>() {
+ @Override
+ public void onReceiveValue(Bitmap value) {
+ mBitmaps.put(index, value);
+ }
+ }
+ );
+ }
+
+ public void fetchSnapshot(final int index) {
+ if (mBitmaps.get(index) != null) {
+ return;
+ }
+
+ int captureIndex = mTab.getCaptureIndex(index);
+
+ mTab.getWebView().getSnapshot(captureIndex,
+ new ValueCallback<Bitmap>() {
+ @Override
+ public void onReceiveValue(Bitmap bitmap) {
+ mBitmaps.put(index, bitmap);
+ }
+ }
+ );
+ }
+
+ public Bitmap readSnapshot(int index) {
+ if (index < 0) {
+ return null;
+ }
+
+ if (index > (mTab.getWebView().copyBackForwardList().getSize() - 1)) {
+ return null;
+ }
+
+ return mBitmaps.get(index);
+ }
+
+ public void deleteSnapshot(int index) {
+ mBitmaps.delete(index);
+ }
+
+ public void cleanup() {
+ mBitmaps.clear();
+ }
+}
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;
+ }
+}
+
diff --git a/src/com/android/browser/EdgeSwipeView.java b/src/com/android/browser/EdgeSwipeView.java
new file mode 100644
index 0000000..a777c26
--- /dev/null
+++ b/src/com/android/browser/EdgeSwipeView.java
@@ -0,0 +1,235 @@
+/*
+ * 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.graphics.Color;
+import android.support.v4.widget.ViewDragHelper;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+public class EdgeSwipeView {
+ private ImageView mStationaryView;
+ private ImageView mSlidingView;
+ private ImageView mSlidingViewShadow;
+ private View mOpacityView;
+ private FrameLayout mLiveView;
+ private DraggableFrameLayout mViewGroup;
+
+ private int mSlidingViewIndex;
+
+ private boolean mbShowingLive = true;
+
+ private boolean mbStationaryViewBMSet = false;
+ private boolean mbSlidingViewBMSet = false;
+
+ private TitleBar mTitleBar;
+
+ public EdgeSwipeView(
+ View container,
+ int stationaryViewId,
+ int slidingViewId,
+ int slidingViewShadowId,
+ int opacityViewId,
+ int liveViewId,
+ int viewGroupId,
+ TitleBar titleBar) {
+ mStationaryView = (ImageView) container.findViewById(stationaryViewId);
+ mSlidingView = (ImageView) container.findViewById(slidingViewId);
+ mSlidingViewShadow = (ImageView) container.findViewById(slidingViewShadowId);
+ mOpacityView = container.findViewById(opacityViewId);
+ mLiveView = (FrameLayout) container.findViewById(liveViewId);
+ mViewGroup = (DraggableFrameLayout) container.findViewById(viewGroupId);
+ mSlidingViewShadow.setBackgroundResource(R.drawable.left_shade);
+
+ mSlidingView.setVisibility(View.GONE);
+ mSlidingViewShadow.setVisibility(View.GONE);
+ mOpacityView.setVisibility(View.GONE);
+
+ final int childCount = mViewGroup.getChildCount();
+ for (int i = childCount - 1; i >= 0; i--) {
+ final View child = mViewGroup.getChildAt(i);
+ if (mSlidingView == child) {
+ mSlidingViewIndex = i;
+ break;
+ }
+ }
+
+ mTitleBar = titleBar;
+ }
+
+ public void goLive() {
+ if (mbShowingLive)
+ return;
+
+ mLiveView.setVisibility(View.VISIBLE);
+ mStationaryView.setVisibility(View.GONE);
+ mSlidingView.setVisibility(View.GONE);
+ mSlidingViewShadow.setVisibility(View.GONE);
+ mOpacityView.setVisibility(View.GONE);
+ mbShowingLive = true;
+ }
+
+ public void goDormant() {
+ if (!mbShowingLive)
+ return;
+
+ mSlidingView.setVisibility(View.VISIBLE);
+ mStationaryView.setVisibility(View.VISIBLE);
+ mOpacityView.setVisibility(View.VISIBLE);
+ mLiveView.setVisibility(View.GONE);
+ mbShowingLive = false;
+ }
+
+ public boolean isLive() {
+ return mbShowingLive;
+ }
+
+ private Bitmap getColorBitmap(int color)
+ {
+ int height = mViewGroup.getMeasuredHeight();
+ int width = mViewGroup.getMeasuredWidth();
+ height -= (mTitleBar.getY()>= 0) ? mTitleBar.getNavigationBar().getMeasuredHeight() : 0;
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
+ bitmap.eraseColor(color);
+ return bitmap;
+ }
+
+ 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 boolean isPortrait() {
+ return (mViewGroup.getHeight() < mViewGroup.getWidth());
+ }
+
+ private void setBitmap(ImageView view, Bitmap bitmap) {
+ clampViewIfNeeded(view);
+ if (bitmap == null) {
+ bitmap = getColorBitmap(Color.DKGRAY);
+ }
+
+ int offset = 0;
+ if (mTitleBar.getY() >= 0) {
+ offset = mTitleBar.getNavigationBar().getMeasuredHeight();
+ }
+
+ int bitmap_height = bitmap.getHeight();
+
+ if (view.getMeasuredHeight() != 0) {
+ bitmap_height = (view.getMeasuredHeight() - offset) * bitmap.getWidth() /
+ view.getMeasuredWidth();
+ }
+
+ if ((bitmap.getHeight() - bitmap_height) > 5) {
+ Bitmap cropped = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap_height);
+ view.setImageBitmap(cropped);
+ } else {
+ view.setImageBitmap(bitmap);
+ }
+ }
+
+ public void setStationaryViewBitmap(Bitmap bitmap) {
+ mbStationaryViewBMSet = null != bitmap;
+ setBitmap(mStationaryView, bitmap);
+ }
+
+ public void setStationaryViewAlpha(float alpha) {
+ mStationaryView.setAlpha(alpha);
+ }
+
+ public void setSlidingViewBitmap(Bitmap bitmap) {
+ mbSlidingViewBMSet = null != bitmap;
+ setBitmap(mSlidingView, bitmap);
+ }
+
+ public boolean slidingViewHasImage() {
+ return mbSlidingViewBMSet;
+ }
+
+ public boolean stationaryViewHasImage() {
+ return mbStationaryViewBMSet;
+ }
+
+ public void slidingViewTouched(int edge) {
+ if (edge == ViewDragHelper.EDGE_RIGHT) {
+ mSlidingView.setTranslationX(mViewGroup.getMeasuredWidth());
+ } else {
+ mSlidingView.setTranslationX(0);
+ }
+ }
+
+ public void hideSlidingViews() {
+ mSlidingViewShadow.setVisibility(View.GONE);
+ mSlidingView.setVisibility(View.GONE);
+ }
+
+ public void showSlidingViews() {
+ mSlidingViewShadow.setVisibility(View.VISIBLE);
+ mSlidingView.setVisibility(View.VISIBLE);
+ }
+
+ public int slidingViewIndex() {
+ return mSlidingViewIndex;
+ }
+
+ public void moveShadowView(float x) {
+ x -= mSlidingViewShadow.getMeasuredWidth();
+ mSlidingViewShadow.setX(x);
+ mSlidingViewShadow.setVisibility(View.VISIBLE);
+ mOpacityView.setVisibility(View.VISIBLE);
+ }
+
+ public boolean allowCapture(View view) {
+ return (view == mSlidingView);
+ }
+
+ public int getMeasuredWidth() {
+ return mViewGroup.getMeasuredWidth();
+ }
+
+ public int getWidth() {
+ return mViewGroup.getWidth();
+ }
+
+ public void init() {
+ clampViewIfNeeded(mSlidingView);
+ clampViewIfNeeded(mStationaryView);
+ }
+
+ public void invalidate() {
+ mViewGroup.invalidate();
+ }
+}
diff --git a/src/com/android/browser/PageProgressView.java b/src/com/android/browser/PageProgressView.java
index 2b73e45..c316c68 100644
--- a/src/com/android/browser/PageProgressView.java
+++ b/src/com/android/browser/PageProgressView.java
@@ -106,6 +106,10 @@
mHandler.sendEmptyMessage(MSG_UPDATE);
}
+ public int getProgressPercent() {
+ return (100* mCurrentProgress) / MAX_PROGRESS;
+ }
+
@Override
public void onDraw(Canvas canvas) {
// super.onDraw(canvas);
diff --git a/src/com/android/browser/PreferenceKeys.java b/src/com/android/browser/PreferenceKeys.java
index 2df0ceb..ab11940 100644
--- a/src/com/android/browser/PreferenceKeys.java
+++ b/src/com/android/browser/PreferenceKeys.java
@@ -131,4 +131,6 @@
static final String PREF_USER_AGENT = "user_agent";
static final String PREF_HELP = "help_about";
static final String PREF_FEEDBACK = "feedback";
+
+ static final String PREF_EDGE_SWIPE = "edge_swiping_action";
}
diff --git a/src/com/android/browser/Tab.java b/src/com/android/browser/Tab.java
index 5e1f614..ccaa522 100644
--- a/src/com/android/browser/Tab.java
+++ b/src/com/android/browser/Tab.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
@@ -48,7 +49,6 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewStub;
-import android.view.View.OnClickListener;
import android.webkit.ConsoleMessage;
import android.webkit.GeolocationPermissions;
import android.webkit.URLUtil;
@@ -58,8 +58,6 @@
import android.webkit.ValueCallback;
import android.widget.CheckBox;
import android.widget.Toast;
-import android.widget.FrameLayout;
-import android.widget.Button;
import com.android.browser.TabControl.OnThumbnailUpdatedListener;
import com.android.browser.homepages.HomeProvider;
@@ -73,21 +71,19 @@
import org.codeaurora.swe.SslErrorHandler;
import org.codeaurora.swe.WebBackForwardList;
import org.codeaurora.swe.WebChromeClient;
-import org.codeaurora.swe.WebHistoryItem;
import org.codeaurora.swe.WebView;
import org.codeaurora.swe.WebView.PictureListener;
import org.codeaurora.swe.WebView.CreateWindowParams;
import org.codeaurora.swe.WebViewClient;
+import org.codeaurora.swe.util.Observable;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.nio.ByteBuffer;
-import java.util.LinkedList;
import java.util.Map;
import java.util.UUID;
import java.util.Vector;
-import java.util.regex.Pattern;
import java.sql.Timestamp;
import java.util.Date;
@@ -198,6 +194,16 @@
// determine if webview is destroyed to MemoryMonitor
private boolean mWebViewDestroyedByMemoryMonitor;
+ private Observable mFirstPixelObservable;
+ private Observable mTabHistoryUpdateObservable;
+
+ Observable getFirstPixelObservable() {
+ return mFirstPixelObservable;
+ }
+
+ Observable getTabHistoryUpdateObservable() {
+ return mTabHistoryUpdateObservable;
+ }
private static synchronized Bitmap getDefaultFavicon(Context context) {
@@ -264,6 +270,35 @@
}
}
+ public boolean isFirstVisualPixelPainted() {
+ return mFirstVisualPixelPainted;
+ }
+
+ public int getCaptureIndex(int navIndex) {
+ int tabPosition = mWebViewController.getTabControl().getCurrentPosition();
+ int orientation = mWebViewController.getActivity().
+ getResources().getConfiguration().orientation;
+
+ int orientationBit = (orientation == Configuration.ORIENTATION_LANDSCAPE) ? 0 : 1;
+
+ int index = orientationBit << 31 | ((tabPosition & 0x7f) << 24) | (navIndex & 0xffffff);
+ return index;
+ }
+
+ public int getTabIdxFromCaptureIdx(int index) {
+ return (index & 0x7f000000) >> 24;
+ }
+
+ public int getOrientationFromCaptureIdx(int index) {
+ return ((index & 0x80000000) == 0) ? Configuration.ORIENTATION_LANDSCAPE :
+ Configuration.ORIENTATION_PORTRAIT;
+
+ }
+
+ public int getNavIdxFromCaptureIdx(int index) {
+ return (index & 0xffffff);
+ }
+
// -------------------------------------------------------------------------
// WebViewClient implementation for the main WebView
// -------------------------------------------------------------------------
@@ -283,6 +318,7 @@
mInPageLoad = true;
mPageFinished = false;
mFirstVisualPixelPainted = false;
+ mFirstPixelObservable.set(false);
mReceivedError = false;
mUpdateThumbnail = true;
mPageLoadProgress = INITIAL_PROGRESS;
@@ -324,6 +360,7 @@
@Override
public void onFirstVisualPixel(WebView view) {
mFirstVisualPixelPainted = true;
+ mFirstPixelObservable.set(true);
}
// return true if want to hijack the url to let another app to handle it
@@ -597,6 +634,50 @@
super.onUnhandledKeyEvent(view, event);
}
}
+
+ @Override
+ public void beforeNavigation(WebView view, String url) {
+ if (isPrivateBrowsingEnabled()) {
+ return;
+ }
+
+ if (!mFirstVisualPixelPainted) {
+ return;
+ }
+
+ final int idx = view.copyBackForwardList().getCurrentIndex();
+ boolean bitmapExists = view.hasSnapshot(idx);
+
+ int progress = 100;
+ Controller controller = (Controller)mWebViewController;
+ UI ui = controller.getUi();
+ if (ui instanceof BaseUi) {
+ BaseUi baseUi = (BaseUi) ui;
+ TitleBar titleBar = baseUi.getTitleBar();
+ progress = titleBar.getProgressView().getProgressPercent();
+ }
+
+ if (bitmapExists && progress < 85) {
+ return;
+ }
+
+ int index = getCaptureIndex(view.getLastCommittedHistoryIndex());
+ view.captureSnapshot(index , null);
+ }
+
+ @Override
+ public void onHistoryItemCommit(WebView view, int index) {
+ mTabHistoryUpdateObservable.set(index);
+ int maxIdx = view.copyBackForwardList().getSize();
+ int[] ids = view.getSnapshotIds();
+ int currentTabIdx = mWebViewController.getTabControl().getCurrentPosition();
+ for (int id : ids) {
+ if (getTabIdxFromCaptureIdx(id) == currentTabIdx &&
+ getNavIdxFromCaptureIdx(id) >= maxIdx) {
+ view.deleteSnapshot(id);
+ }
+ }
+ }
};
private void syncCurrentState(WebView view, String url) {
@@ -1174,6 +1255,10 @@
}
}
};
+
+ mFirstPixelObservable = new Observable();
+ mFirstPixelObservable.set(false);
+ mTabHistoryUpdateObservable = new Observable();
}
public boolean shouldUpdateThumbnail() {
@@ -1319,6 +1404,15 @@
dismissSubWindow();
// save the WebView to call destroy() after detach it from the tab
WebView webView = mMainView;
+ if (!mWebViewDestroyedByMemoryMonitor) {
+ int[] ids = webView.getSnapshotIds();
+ int currentTabIdx = mWebViewController.getTabControl().getCurrentPosition();
+ for (int id : ids) {
+ if (getTabIdxFromCaptureIdx(id) == currentTabIdx) {
+ webView.deleteSnapshot(id);
+ }
+ }
+ }
setWebView(null);
webView.destroy();
}
diff --git a/src/com/android/browser/preferences/AdvancedPreferencesFragment.java b/src/com/android/browser/preferences/AdvancedPreferencesFragment.java
index 4c715ce..9853128 100644
--- a/src/com/android/browser/preferences/AdvancedPreferencesFragment.java
+++ b/src/com/android/browser/preferences/AdvancedPreferencesFragment.java
@@ -100,6 +100,21 @@
downloadPath);
downloadPathPreset.setSummary(downloadPathForUser);
}
+
+ ListPreference edgeSwipePref =
+ (ListPreference) mFragment.findPreference("edge_swiping_action");
+
+ String[] options = mFragment.getResources().getStringArray(
+ R.array.pref_edge_swiping_values);
+
+ String value = BrowserSettings.getInstance().getEdgeSwipeAction();
+
+ for (int i = 0; i < options.length; i++) {
+ if (value.equals(options[i])) {
+ edgeSwipePref.setValueIndex(i);
+ break;
+ }
+ }
}
private Preference.OnPreferenceClickListener onClickDownloadPathSettings() {