Bug 1197811 - Turn the LayerView into a ScrollView that scrolls to shift the surface rather than using setTranslation. r=rbarker

On Gingerbread devices (Android API 9 or 10) the ViewHelper.setTranslationY code
doesn't work to move the SurfaceView. In order to acheive that effect I turned
LayerView into a ScrollView with a dummy element at the top, and allow it to
scroll. This allows the SurfaceView to move as desired. A few places in the code
were assuming that the LayerView and SurfaceView were always at the same screen
location (which was true before but not any more) and so those sites needed
some updating as well.
This commit is contained in:
Kartikaya Gupta
2015-08-28 17:22:17 -04:00
parent 4eff4b6bd7
commit f01f76c98f
7 changed files with 94 additions and 23 deletions

View File

@@ -1636,7 +1636,7 @@ public class BrowserApp extends GeckoApp
if (mLayerView != null && height != mToolbarHeight) { if (mLayerView != null && height != mToolbarHeight) {
mToolbarHeight = height; mToolbarHeight = height;
mLayerView.getDynamicToolbarAnimator().setMaxTranslation(height); mLayerView.setMaxTranslation(height);
mDynamicToolbar.setVisible(true, VisibilityTransition.IMMEDIATE); mDynamicToolbar.setVisible(true, VisibilityTransition.IMMEDIATE);
} }
} }

View File

@@ -125,28 +125,24 @@ class TextSelectionHandle extends ImageView implements View.OnTouchListener {
} }
private void move(float newX, float newY) { private void move(float newX, float newY) {
LayerView layerView = GeckoAppShell.getLayerView();
// newX and newY are in screen coordinates, but mLeft/mTop are relative // newX and newY are in screen coordinates, but mLeft/mTop are relative
// to the ancestor (which is what LayerView is relative to also). So, // to the ancestor (which is what LayerView is relative to also). So,
// we need to adjust them newX/newY. The |ancestorOrigin| variable computed // we need to adjust newX/newY. The |ancestorOrigin| variable computed
// below is the origin of the ancestor relative to the screen coordinates, // below is the origin of the ancestor relative to the screen coordinates,
// so subtracting that from newY puts newY into the desired coordinate // so subtracting that from newY puts newY into the desired coordinate
// space. We also need to include the offset amount of the touch location // space. We also need to include the offset amount of the touch location
// relative to the top left of the handle (mTouchStart). // relative to the top left of the handle (mTouchStart).
float layerViewTranslation = GeckoAppShell.getLayerView().getSurfaceTranslation();
int[] layerViewPosition = new int[2]; int[] layerViewPosition = new int[2];
GeckoAppShell.getLayerView().getLocationOnScreen(layerViewPosition); layerView.getLocationOnScreen(layerViewPosition);
float ancestorOrigin = layerViewPosition[1] - layerViewTranslation; float ancestorOrigin = layerViewPosition[1];
mLeft = newX - mTouchStart.x; mLeft = newX - mTouchStart.x;
mTop = newY - mTouchStart.y - ancestorOrigin; mTop = newY - mTouchStart.y - ancestorOrigin;
LayerView layerView = GeckoAppShell.getLayerView();
if (layerView == null) {
Log.e(LOGTAG, "Can't move selection because layerView is null");
return;
}
// Send x coordinate on the right side of the start handle, left side of the end handle. // Send x coordinate on the right side of the start handle, left side of the end handle.
float layerViewTranslation = layerView.getSurfaceTranslation();
PointF geckoPoint = new PointF(mLeft + adjustLeftForHandle(), PointF geckoPoint = new PointF(mLeft + adjustLeftForHandle(),
mTop - layerViewTranslation); mTop - layerViewTranslation);
geckoPoint = layerView.convertViewPointToLayerPoint(geckoPoint); geckoPoint = layerView.convertViewPointToLayerPoint(geckoPoint);

View File

@@ -165,6 +165,9 @@ public class ZoomedView extends FrameLayout implements LayerView.DynamicToolbarL
layerView.dispatchTouchEvent(actionDownEvent); layerView.dispatchTouchEvent(actionDownEvent);
actionDownEvent.recycle(); actionDownEvent.recycle();
PointF convertedPosition = getUnzoomedPositionFromPointInZoomedView(event.getX(), event.getY()); PointF convertedPosition = getUnzoomedPositionFromPointInZoomedView(event.getX(), event.getY());
// the LayerView expects the coordinates relative to the window, not the surface, so we need
// to adjust that here.
convertedPosition.y += layerView.getSurfaceTranslation();
MotionEvent e = MotionEvent.obtain(event.getDownTime(), event.getEventTime(), MotionEvent e = MotionEvent.obtain(event.getDownTime(), event.getEventTime(),
MotionEvent.ACTION_UP, convertedPosition.x, convertedPosition.y, MotionEvent.ACTION_UP, convertedPosition.x, convertedPosition.y,
event.getMetaState()); event.getMetaState());
@@ -179,6 +182,9 @@ public class ZoomedView extends FrameLayout implements LayerView.DynamicToolbarL
originRawX = event.getRawX(); originRawX = event.getRawX();
originRawY = event.getRawY(); originRawY = event.getRawY();
PointF convertedPosition = getUnzoomedPositionFromPointInZoomedView(event.getX(), event.getY()); PointF convertedPosition = getUnzoomedPositionFromPointInZoomedView(event.getX(), event.getY());
// the LayerView expects the coordinates relative to the window, not the surface, so we need
// to adjust that here.
convertedPosition.y += layerView.getSurfaceTranslation();
actionDownEvent = MotionEvent.obtain(event.getDownTime(), event.getEventTime(), actionDownEvent = MotionEvent.obtain(event.getDownTime(), event.getEventTime(),
MotionEvent.ACTION_DOWN, convertedPosition.x, convertedPosition.y, MotionEvent.ACTION_DOWN, convertedPosition.x, convertedPosition.y,
event.getMetaState()); event.getMetaState());

View File

@@ -148,6 +148,10 @@ public class DynamicToolbarAnimator {
} }
} }
public float getMaxTranslation() {
return mMaxTranslation;
}
public float getToolbarTranslation() { public float getToolbarTranslation() {
return mToolbarTranslation; return mToolbarTranslation;
} }

View File

@@ -10,7 +10,6 @@ import java.nio.IntBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import org.mozilla.gecko.AndroidGamepadManager; import org.mozilla.gecko.AndroidGamepadManager;
import org.mozilla.gecko.animation.ViewHelper;
import org.mozilla.gecko.annotation.RobocopTarget; import org.mozilla.gecko.annotation.RobocopTarget;
import org.mozilla.gecko.annotation.WrapForJNI; import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.AppConstants.Versions; import org.mozilla.gecko.AppConstants.Versions;
@@ -45,12 +44,13 @@ import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnection;
import android.view.InputDevice; import android.view.InputDevice;
import android.widget.FrameLayout; import android.widget.LinearLayout;
import android.widget.ScrollView;
/** /**
* A view rendered by the layer compositor. * A view rendered by the layer compositor.
*/ */
public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener { public class LayerView extends ScrollView implements Tabs.OnTabsChangedListener {
private static final String LOGTAG = "GeckoLayerView"; private static final String LOGTAG = "GeckoLayerView";
private GeckoLayerClient mLayerClient; private GeckoLayerClient mLayerClient;
@@ -66,12 +66,15 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
private SurfaceView mSurfaceView; private SurfaceView mSurfaceView;
private TextureView mTextureView; private TextureView mTextureView;
private View mFillerView;
private Listener mListener; private Listener mListener;
private PointF mInitialTouchPoint; private PointF mInitialTouchPoint;
private boolean mGeckoIsReady; private boolean mGeckoIsReady;
private float mSurfaceTranslation;
/* This should only be modified on the Java UI thread. */ /* This should only be modified on the Java UI thread. */
private final Overscroll mOverscroll; private final Overscroll mOverscroll;
@@ -233,6 +236,7 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
requestFocus(); requestFocus();
} }
event.offsetLocation(0, -mSurfaceTranslation);
if (mToolbarAnimator != null && mToolbarAnimator.onInterceptTouchEvent(event)) { if (mToolbarAnimator != null && mToolbarAnimator.onInterceptTouchEvent(event)) {
return true; return true;
@@ -289,7 +293,25 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
mSurfaceView = new LayerSurfaceView(getContext(), this); mSurfaceView = new LayerSurfaceView(getContext(), this);
mSurfaceView.setBackgroundColor(Color.WHITE); mSurfaceView.setBackgroundColor(Color.WHITE);
addView(mSurfaceView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
// The "filler" view sits behind the URL bar and should never be
// visible. It exists solely to make this LayerView actually
// scrollable so that we can shift the surface around on the screen.
// Once we drop support for pre-Honeycomb Android versions this
// should not be needed; we can just turn LayerView back into a
// FrameLayout that holds mSurfaceView and nothing else.
mFillerView = new View(getContext()) {
@Override protected void onMeasure(int aWidthSpec, int aHeightSpec) {
setMeasuredDimension(0, Math.round(mToolbarAnimator.getMaxTranslation()));
}
};
mFillerView.setBackgroundColor(Color.RED);
LinearLayout container = new LinearLayout(getContext());
container.setOrientation(LinearLayout.VERTICAL);
container.addView(mFillerView);
container.addView(mSurfaceView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
addView(container, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
SurfaceHolder holder = mSurfaceView.getHolder(); SurfaceHolder holder = mSurfaceView.getHolder();
holder.addCallback(new SurfaceListener()); holder.addCallback(new SurfaceListener());
@@ -570,19 +592,48 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
} }
} }
@Override
protected void onMeasure(int aWidthSpec, int aHeightSpec) {
super.onMeasure(aWidthSpec, aHeightSpec);
if (mSurfaceView != null) {
// Because of the crazy setup where this LayerView is a ScrollView
// and the SurfaceView is inside a LinearLayout, the SurfaceView
// doesn't get the right information to size itself the way we want.
// We always want it to be the same size as this LayerView, so we
// use a hack to make sure it sizes itself that way.
((LayerSurfaceView)mSurfaceView).overrideSize(getMeasuredWidth(), getMeasuredHeight());
}
}
/* A subclass of SurfaceView to listen to layout changes, as /* A subclass of SurfaceView to listen to layout changes, as
* View.OnLayoutChangeListener requires API level 11. * View.OnLayoutChangeListener requires API level 11.
*/ */
private class LayerSurfaceView extends SurfaceView { private class LayerSurfaceView extends SurfaceView {
LayerView mParent; private LayerView mParent;
private int mForcedWidth;
private int mForcedHeight;
public LayerSurfaceView(Context aContext, LayerView aParent) { public LayerSurfaceView(Context aContext, LayerView aParent) {
super(aContext); super(aContext);
mParent = aParent; mParent = aParent;
} }
void overrideSize(int aWidth, int aHeight) {
if (mForcedWidth != aWidth || mForcedHeight != aHeight) {
mForcedWidth = aWidth;
mForcedHeight = aHeight;
requestLayout();
}
}
@Override
protected void onMeasure(int aWidthSpec, int aHeightSpec) {
setMeasuredDimension(mForcedWidth, mForcedHeight);
}
@Override @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) { protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed) { if (changed) {
mParent.surfaceChanged(right - left, bottom - top); mParent.surfaceChanged(right - left, bottom - top);
} }
@@ -664,12 +715,24 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
return mFullScreenState; return mFullScreenState;
} }
public void setMaxTranslation(float aMaxTranslation) {
mToolbarAnimator.setMaxTranslation(aMaxTranslation);
if (mFillerView != null) {
mFillerView.requestLayout();
}
}
public void setSurfaceTranslation(float translation) { public void setSurfaceTranslation(float translation) {
ViewHelper.setTranslationY(this, translation); // Once we drop support for pre-Honeycomb Android versions, we can
// revert bug 1197811 and just use ViewHelper here.
if (mSurfaceTranslation != translation) {
mSurfaceTranslation = translation;
scrollTo(0, Math.round(mToolbarAnimator.getMaxTranslation() - translation));
}
} }
public float getSurfaceTranslation() { public float getSurfaceTranslation() {
return ViewHelper.getTranslationY(this); return mSurfaceTranslation;
} }
@Override @Override

View File

@@ -124,24 +124,25 @@ public class OverscrollEdgeEffect implements Overscroll {
return; return;
} }
float fillerSize = mView.getDynamicToolbarAnimator().getMaxTranslation();
PointF visibleEnd = mView.getDynamicToolbarAnimator().getVisibleEndOfLayerView(); PointF visibleEnd = mView.getDynamicToolbarAnimator().getVisibleEndOfLayerView();
// If we're pulling an edge, or fading it out, draw! // If we're pulling an edge, or fading it out, draw!
boolean invalidate = false; boolean invalidate = false;
if (!mEdges[TOP].isFinished()) { if (!mEdges[TOP].isFinished()) {
invalidate |= draw(mEdges[TOP], canvas, 0, 0, 0); invalidate |= draw(mEdges[TOP], canvas, 0, fillerSize, 0);
} }
if (!mEdges[BOTTOM].isFinished()) { if (!mEdges[BOTTOM].isFinished()) {
invalidate |= draw(mEdges[BOTTOM], canvas, visibleEnd.x, visibleEnd.y, 180); invalidate |= draw(mEdges[BOTTOM], canvas, visibleEnd.x, fillerSize + visibleEnd.y, 180);
} }
if (!mEdges[LEFT].isFinished()) { if (!mEdges[LEFT].isFinished()) {
invalidate |= draw(mEdges[LEFT], canvas, 0, visibleEnd.y, 270); invalidate |= draw(mEdges[LEFT], canvas, 0, fillerSize + visibleEnd.y, 270);
} }
if (!mEdges[RIGHT].isFinished()) { if (!mEdges[RIGHT].isFinished()) {
invalidate |= draw(mEdges[RIGHT], canvas, visibleEnd.x, 0, 90); invalidate |= draw(mEdges[RIGHT], canvas, visibleEnd.x, fillerSize, 90);
} }
// If the edge effect is animating off screen, invalidate. // If the edge effect is animating off screen, invalidate.

View File

@@ -12,7 +12,8 @@
<org.mozilla.gecko.GeckoView android:id="@+id/layer_view" <org.mozilla.gecko.GeckoView android:id="@+id/layer_view"
gecko:doinit="false" gecko:doinit="false"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="match_parent"
android:scrollbars="none"/>
<AbsoluteLayout android:id="@+id/plugin_container" <AbsoluteLayout android:id="@+id/plugin_container"
android:background="@android:color/transparent" android:background="@android:color/transparent"