Bug 1500644 - Make GeckoView example toolbar dynamic at bottom by using CoordinatorLayout. r=geckoview-reviewers,snorp
This way is basically how android-components provides the dynamic toolbar feature but there are a couple of caveats on this change as of now. 1) There is no option to switch the toolbar position at top 2) This change forces the toolbar to be dynamic, there is no way to make it static Each corresponding file in android-components is; NestedGeckoView.java <- NestedGeckoView.kt [1] ToolbarBottomBehavior.java <- BrowserToolbarBottomBehavior.kt [2] GeckoViewBottomBehavior <- EngineViewBottomBehavior.kt [3] [1]b6a5c64441/components/browser/engine-gecko-nightly/src/main/java/mozilla/components/browser/engine/gecko/NestedGeckoView.kt[2]b6a5c64441/components/browser/toolbar/src/main/java/mozilla/components/browser/toolbar/behavior/BrowserToolbarBottomBehavior.kt[3]b6a5c64441/components/feature/session/src/main/java/mozilla/components/feature/session/behavior/EngineViewBottomBehavior.ktDifferential Revision: https://phabricator.services.mozilla.com/D92357
This commit is contained in:
@@ -841,6 +841,8 @@ public class GeckoViewActivity
|
||||
loadFromIntent(getIntent());
|
||||
}
|
||||
|
||||
mGeckoView.setDynamicToolbarMaxHeight(findViewById(R.id.toolbar).getLayoutParams().height);
|
||||
|
||||
mToolbarView.getLocationView().setCommitListener(mCommitListener);
|
||||
mToolbarView.updateTabCount();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.mozilla.geckoview_example;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import org.mozilla.geckoview.GeckoView;
|
||||
|
||||
public class GeckoViewBottomBehavior extends CoordinatorLayout.Behavior<GeckoView> {
|
||||
public GeckoViewBottomBehavior(Context context, AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean layoutDependsOn(CoordinatorLayout parent, GeckoView child, View dependency) {
|
||||
return dependency instanceof Toolbar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDependentViewChanged(CoordinatorLayout parent, GeckoView child, View dependency) {
|
||||
child.setVerticalClipping(Math.round(-dependency.getTranslationY()));
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.geckoview_example;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import androidx.core.view.NestedScrollingChild;
|
||||
import androidx.core.view.NestedScrollingChildHelper;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import org.mozilla.geckoview.GeckoResult;
|
||||
import org.mozilla.geckoview.GeckoView;
|
||||
import org.mozilla.geckoview.PanZoomController;
|
||||
|
||||
|
||||
/**
|
||||
* GeckoView that supports nested scrolls (for using in a CoordinatorLayout).
|
||||
*
|
||||
* This code is a simplified version of the NestedScrollView implementation
|
||||
* which can be found in the support library:
|
||||
* [android.support.v4.widget.NestedScrollView]
|
||||
*
|
||||
* Based on:
|
||||
* https://github.com/takahirom/webview-in-coordinatorlayout
|
||||
*/
|
||||
|
||||
public class NestedGeckoView extends GeckoView implements NestedScrollingChild {
|
||||
|
||||
private int mLastY;
|
||||
private final int[] mScrollOffset = new int[2];
|
||||
private final int[] mScrollConsumed = new int[2];
|
||||
private int mNestedOffsetY;
|
||||
private NestedScrollingChildHelper mChildHelper;
|
||||
|
||||
/**
|
||||
* Integer indicating how user's MotionEvent was handled.
|
||||
*
|
||||
* There must be a 1-1 relation between this values and [EngineView.InputResult]'s.
|
||||
*/
|
||||
private int mInputResult = PanZoomController.INPUT_RESULT_UNHANDLED;
|
||||
|
||||
public NestedGeckoView(final Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public NestedGeckoView(final Context context, final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mChildHelper = new NestedScrollingChildHelper(this);
|
||||
setNestedScrollingEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
MotionEvent event = MotionEvent.obtain(ev);
|
||||
final int action = event.getActionMasked();
|
||||
int eventY = (int) event.getY();
|
||||
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
final boolean allowScroll = !shouldPinOnScreen() &&
|
||||
mInputResult == PanZoomController.INPUT_RESULT_HANDLED;
|
||||
int deltaY = mLastY - eventY;
|
||||
|
||||
if (allowScroll && dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {
|
||||
deltaY -= mScrollConsumed[1];
|
||||
event.offsetLocation(0f, -mScrollOffset[1]);
|
||||
mNestedOffsetY += mScrollOffset[1];
|
||||
}
|
||||
|
||||
mLastY = eventY - mScrollOffset[1];
|
||||
|
||||
if (allowScroll && dispatchNestedScroll(0, mScrollOffset[1], 0, deltaY, mScrollOffset)) {
|
||||
mLastY -= mScrollOffset[1];
|
||||
event.offsetLocation(0f, mScrollOffset[1]);
|
||||
mNestedOffsetY += mScrollOffset[1];
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// A new gesture started. Reset handled status and ask GV if it can handle this.
|
||||
mInputResult = PanZoomController.INPUT_RESULT_UNHANDLED;
|
||||
updateInputResult(event);
|
||||
|
||||
mNestedOffsetY = 0;
|
||||
mLastY = eventY;
|
||||
|
||||
// The event should be handled either by onTouchEvent,
|
||||
// either by onTouchEventForResult, never by both.
|
||||
// Early return if we sent it to updateInputResult(..) which calls onTouchEventForResult.
|
||||
event.recycle();
|
||||
return true;
|
||||
|
||||
// We don't care about other touch events
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
stopNestedScroll();
|
||||
break;
|
||||
}
|
||||
|
||||
// Execute event handler from parent class in all cases
|
||||
final boolean eventHandled = callSuperOnTouchEvent(event);
|
||||
|
||||
// Recycle previously obtained event
|
||||
event.recycle();
|
||||
|
||||
return eventHandled;
|
||||
}
|
||||
|
||||
private boolean callSuperOnTouchEvent(MotionEvent event) {
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
private void updateInputResult(MotionEvent event) {
|
||||
super.onTouchEventForResult(event).accept(inputResult -> {
|
||||
mInputResult = inputResult;
|
||||
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
|
||||
});
|
||||
}
|
||||
|
||||
public int getInputResult() {
|
||||
return mInputResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNestedScrollingEnabled(boolean enabled) {
|
||||
mChildHelper.setNestedScrollingEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNestedScrollingEnabled() {
|
||||
return mChildHelper.isNestedScrollingEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startNestedScroll(int axes) {
|
||||
return mChildHelper.startNestedScroll(axes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopNestedScroll() {
|
||||
mChildHelper.stopNestedScroll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNestedScrollingParent() {
|
||||
return mChildHelper.hasNestedScrollingParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchNestedScroll(int dxConsumed,
|
||||
int dyConsumed,
|
||||
int dxUnconsumed,
|
||||
int dyUnconsumed,
|
||||
int[] offsetInWindow) {
|
||||
return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
|
||||
return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
|
||||
return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
|
||||
return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.mozilla.geckoview_example;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.MotionEvent;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import org.mozilla.geckoview.GeckoView;
|
||||
import org.mozilla.geckoview.PanZoomController;
|
||||
|
||||
public class ToolbarBottomBehavior extends CoordinatorLayout.Behavior<View> {
|
||||
public ToolbarBottomBehavior(Context context, AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,
|
||||
View child,
|
||||
View directTargetChild,
|
||||
View target,
|
||||
int axes,
|
||||
int type) {
|
||||
NestedGeckoView geckoView = (NestedGeckoView)target;
|
||||
if (axes == ViewCompat.SCROLL_AXIS_VERTICAL &&
|
||||
geckoView.getInputResult() == PanZoomController.INPUT_RESULT_HANDLED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (geckoView.getInputResult() == PanZoomController.INPUT_RESULT_UNHANDLED) {
|
||||
// Restore the toolbar to the original (visible) state, this is what A-C does.
|
||||
child.setTranslationY(0f);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout,
|
||||
View child,
|
||||
View target,
|
||||
int type) {
|
||||
// Snap up or down the user stops scrolling.
|
||||
if (child.getTranslationY() >= (child.getHeight() / 2f)) {
|
||||
child.setTranslationY(child.getHeight());
|
||||
} else {
|
||||
child.setTranslationY(0f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout,
|
||||
View child,
|
||||
View target,
|
||||
int dx,
|
||||
int dy,
|
||||
int[] consumed,
|
||||
int type) {
|
||||
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
|
||||
child.setTranslationY(Math.max(0f, Math.min(child.getHeight(), child.getTranslationY() + dy)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +1,26 @@
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<org.mozilla.geckoview.GeckoView
|
||||
<org.mozilla.geckoview_example.NestedGeckoView
|
||||
android:id="@+id/gecko_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_above="@id/toolbar"
|
||||
android:scrollbars="none"
|
||||
app:layout_behavior="org.mozilla.geckoview_example.GeckoViewBottomBehavior"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:actionBarSize"
|
||||
android:layout_alignParentBottom="true"/>
|
||||
android:layout_gravity="bottom"
|
||||
android:background="#eeeeee"
|
||||
app:layout_behavior="org.mozilla.geckoview_example.ToolbarBottomBehavior"
|
||||
app:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/page_progress"
|
||||
@@ -24,4 +28,5 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="3dp"
|
||||
android:layout_alignTop="@id/gecko_view" />
|
||||
</RelativeLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
Reference in New Issue
Block a user