diff options
Diffstat (limited to 'android/experimental/LOAndroid/app/src/main')
68 files changed, 0 insertions, 10400 deletions
diff --git a/android/experimental/LOAndroid/app/src/main/AndroidManifest.xml b/android/experimental/LOAndroid/app/src/main/AndroidManifest.xml deleted file mode 100644 index 3120a69b4247..000000000000 --- a/android/experimental/LOAndroid/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="org.libreoffice" > - - <!-- App requires OpenGL ES 2.0 --> - <uses-feature android:glEsVersion="0x00020000" android:required="true" /> - - <application - android:allowBackup="true" - android:icon="@drawable/ic_launcher" - android:label="@string/app_name" - android:hardwareAccelerated="true" - android:theme="@style/AppTheme" > - <activity - android:name="org.libreoffice.MainActivity" - android:label="@string/app_name" > - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - </application> - -</manifest> diff --git a/android/experimental/LOAndroid/app/src/main/java/org/libreoffice/LOKitShell.java b/android/experimental/LOAndroid/app/src/main/java/org/libreoffice/LOKitShell.java deleted file mode 100644 index cd429d66a2ad..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/libreoffice/LOKitShell.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.libreoffice; - - -import org.mozilla.gecko.gfx.LayerView; - -public class LOKitShell { - public static int getDpi() { - return 96; - } - - public static int getScreenDepth() { - return 24; - } - - public static LayerView getLayerView() { - return null; - } - - public static float computeRenderIntegrity() { - return 0.0f; - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/libreoffice/MainActivity.java b/android/experimental/LOAndroid/app/src/main/java/org/libreoffice/MainActivity.java deleted file mode 100644 index 1963ad2c2b43..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/libreoffice/MainActivity.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.libreoffice; - -import android.app.Activity; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; - - -public class MainActivity extends Activity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - } - - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.main, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. - int id = item.getItemId(); - if (id == R.id.action_settings) { - return true; - } - return super.onOptionsItemSelected(item); - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/libreoffice/MainLayerView.java b/android/experimental/LOAndroid/app/src/main/java/org/libreoffice/MainLayerView.java deleted file mode 100644 index 5721df2806b3..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/libreoffice/MainLayerView.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.libreoffice; - -import android.content.Context; -import android.os.Handler; -import android.util.AttributeSet; - -import org.mozilla.gecko.gfx.LayerView; -import org.mozilla.gecko.util.ThreadUtils; - -public class MainLayerView extends LayerView { - - public MainLayerView(Context context) { - super(context); - init(context); - } - - public MainLayerView(Context context, AttributeSet attrs) { - super(context, attrs); - init(context); - } - - private void init(Context context) { - ThreadUtils.setUiThread(Thread.currentThread(), new Handler()); - } - -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/TouchEventInterceptor.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/TouchEventInterceptor.java deleted file mode 100644 index 41a71dfa5f88..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/TouchEventInterceptor.java +++ /dev/null @@ -1,14 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko; - -import android.view.MotionEvent; -import android.view.View; - -public interface TouchEventInterceptor extends View.OnTouchListener { - /** Override this method for a chance to consume events before the view or its children */ - public boolean onInterceptTouchEvent(View view, MotionEvent event); -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/ZoomConstraints.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/ZoomConstraints.java deleted file mode 100644 index 40d1817c9301..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/ZoomConstraints.java +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko; - -import org.json.JSONException; -import org.json.JSONObject; - -public final class ZoomConstraints { - private final boolean mAllowZoom; - private final float mDefaultZoom; - private final float mMinZoom; - private final float mMaxZoom; - - public ZoomConstraints(boolean allowZoom) { - mAllowZoom = allowZoom; - mDefaultZoom = 0.0f; - mMinZoom = 0.0f; - mMaxZoom = 0.0f; - } - - ZoomConstraints(JSONObject message) throws JSONException { - mAllowZoom = message.getBoolean("allowZoom"); - mDefaultZoom = (float)message.getDouble("defaultZoom"); - mMinZoom = (float)message.getDouble("minZoom"); - mMaxZoom = (float)message.getDouble("maxZoom"); - } - - public final boolean getAllowZoom() { - return mAllowZoom; - } - - public final float getDefaultZoom() { - return mDefaultZoom; - } - - public final float getMinZoom() { - return mMinZoom; - } - - public final float getMaxZoom() { - return mMaxZoom; - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/Axis.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/Axis.java deleted file mode 100644 index 103ee5173539..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/Axis.java +++ /dev/null @@ -1,420 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -//import org.mozilla.gecko.GeckoAppShell; -//import org.mozilla.gecko.PrefsHelper; -import org.libreoffice.LOKitShell; -import org.mozilla.gecko.util.FloatUtils; - -import org.json.JSONArray; - -import android.util.Log; -import android.view.View; - -import java.util.HashMap; -import java.util.Map; - -/** - * This class represents the physics for one axis of movement (i.e. either - * horizontal or vertical). It tracks the different properties of movement - * like displacement, velocity, viewport dimensions, etc. pertaining to - * a particular axis. - */ -abstract class Axis { - private static final String LOGTAG = "GeckoAxis"; - - private static final String PREF_SCROLLING_FRICTION_SLOW = "ui.scrolling.friction_slow"; - private static final String PREF_SCROLLING_FRICTION_FAST = "ui.scrolling.friction_fast"; - private static final String PREF_SCROLLING_MAX_EVENT_ACCELERATION = "ui.scrolling.max_event_acceleration"; - private static final String PREF_SCROLLING_OVERSCROLL_DECEL_RATE = "ui.scrolling.overscroll_decel_rate"; - private static final String PREF_SCROLLING_OVERSCROLL_SNAP_LIMIT = "ui.scrolling.overscroll_snap_limit"; - private static final String PREF_SCROLLING_MIN_SCROLLABLE_DISTANCE = "ui.scrolling.min_scrollable_distance"; - - // This fraction of velocity remains after every animation frame when the velocity is low. - private static float FRICTION_SLOW; - // This fraction of velocity remains after every animation frame when the velocity is high. - private static float FRICTION_FAST; - // Below this velocity (in pixels per frame), the friction starts increasing from FRICTION_FAST - // to FRICTION_SLOW. - private static float VELOCITY_THRESHOLD; - // The maximum velocity change factor between events, per ms, in %. - // Direction changes are excluded. - private static float MAX_EVENT_ACCELERATION; - - // The rate of deceleration when the surface has overscrolled. - private static float OVERSCROLL_DECEL_RATE; - // The percentage of the surface which can be overscrolled before it must snap back. - private static float SNAP_LIMIT; - - // The minimum amount of space that must be present for an axis to be considered scrollable, - // in pixels. - private static float MIN_SCROLLABLE_DISTANCE; - - private static float getFloatPref(Map<String, Integer> prefs, String prefName, int defaultValue) { - Integer value = (prefs == null ? null : prefs.get(prefName)); - return (float)(value == null || value < 0 ? defaultValue : value) / 1000f; - } - - private static int getIntPref(Map<String, Integer> prefs, String prefName, int defaultValue) { - Integer value = (prefs == null ? null : prefs.get(prefName)); - return (value == null || value < 0 ? defaultValue : value); - } - - static void initPrefs() { - final String[] prefs = { PREF_SCROLLING_FRICTION_FAST, - PREF_SCROLLING_FRICTION_SLOW, - PREF_SCROLLING_MAX_EVENT_ACCELERATION, - PREF_SCROLLING_OVERSCROLL_DECEL_RATE, - PREF_SCROLLING_OVERSCROLL_SNAP_LIMIT, - PREF_SCROLLING_MIN_SCROLLABLE_DISTANCE }; - - /*PrefsHelper.getPrefs(prefs, new PrefsHelper.PrefHandlerBase() { - Map<String, Integer> mPrefs = new HashMap<String, Integer>(); - - @Override public void prefValue(String name, int value) { - mPrefs.put(name, value); - } - - @Override public void finish() { - setPrefs(mPrefs); - } - });*/ - } - - static final float MS_PER_FRAME = 1000.0f / 60.0f; - static final long NS_PER_FRAME = Math.round(1000000000f / 60f); - private static final float FRAMERATE_MULTIPLIER = (1000f/60f) / MS_PER_FRAME; - private static final int FLING_VELOCITY_POINTS = 8; - - // The values we use for friction are based on a 16.6ms frame, adjust them to currentNsPerFrame: - static float getFrameAdjustedFriction(float baseFriction, long currentNsPerFrame) { - float framerateMultiplier = (float)currentNsPerFrame / NS_PER_FRAME; - return (float)Math.pow(Math.E, (Math.log(baseFriction) / framerateMultiplier)); - } - - static void setPrefs(Map<String, Integer> prefs) { - FRICTION_SLOW = getFloatPref(prefs, PREF_SCROLLING_FRICTION_SLOW, 850); - FRICTION_FAST = getFloatPref(prefs, PREF_SCROLLING_FRICTION_FAST, 970); - VELOCITY_THRESHOLD = 10 / FRAMERATE_MULTIPLIER; - MAX_EVENT_ACCELERATION = getFloatPref(prefs, PREF_SCROLLING_MAX_EVENT_ACCELERATION, /*GeckoAppShell.getDpi()*/ LOKitShell.getDpi() > 300 ? 100 : 40); - OVERSCROLL_DECEL_RATE = getFloatPref(prefs, PREF_SCROLLING_OVERSCROLL_DECEL_RATE, 40); - SNAP_LIMIT = getFloatPref(prefs, PREF_SCROLLING_OVERSCROLL_SNAP_LIMIT, 300); - MIN_SCROLLABLE_DISTANCE = getFloatPref(prefs, PREF_SCROLLING_MIN_SCROLLABLE_DISTANCE, 500); - Log.i(LOGTAG, "Prefs: " + FRICTION_SLOW + "," + FRICTION_FAST + "," + VELOCITY_THRESHOLD + "," - + MAX_EVENT_ACCELERATION + "," + OVERSCROLL_DECEL_RATE + "," + SNAP_LIMIT + "," + MIN_SCROLLABLE_DISTANCE); - } - - static { - // set the scrolling parameters to default values on startup - setPrefs(null); - } - - private enum FlingStates { - STOPPED, - PANNING, - FLINGING, - } - - private enum Overscroll { - NONE, - MINUS, // Overscrolled in the negative direction - PLUS, // Overscrolled in the positive direction - BOTH, // Overscrolled in both directions (page is zoomed to smaller than screen) - } - - private final SubdocumentScrollHelper mSubscroller; - - private int mOverscrollMode; /* Default to only overscrolling if we're allowed to scroll in a direction */ - private float mFirstTouchPos; /* Position of the first touch event on the current drag. */ - private float mTouchPos; /* Position of the most recent touch event on the current drag. */ - private float mLastTouchPos; /* Position of the touch event before touchPos. */ - private float mVelocity; /* Velocity in this direction; pixels per animation frame. */ - private float[] mRecentVelocities; /* Circular buffer of recent velocities since last touch start. */ - private int mRecentVelocityCount; /* Number of values put into mRecentVelocities (unbounded). */ - private boolean mScrollingDisabled; /* Whether movement on this axis is locked. */ - private boolean mDisableSnap; /* Whether overscroll snapping is disabled. */ - private float mDisplacement; - - private FlingStates mFlingState = FlingStates.STOPPED; /* The fling state we're in on this axis. */ - - protected abstract float getOrigin(); - protected abstract float getViewportLength(); - protected abstract float getPageStart(); - protected abstract float getPageLength(); - protected abstract float getMarginStart(); - protected abstract float getMarginEnd(); - protected abstract boolean marginsHidden(); - - Axis(SubdocumentScrollHelper subscroller) { - mSubscroller = subscroller; - mOverscrollMode = View.OVER_SCROLL_IF_CONTENT_SCROLLS; - mRecentVelocities = new float[FLING_VELOCITY_POINTS]; - } - - // Implementors can override these to show effects when the axis overscrolls - protected void overscrollFling(float velocity) { } - protected void overscrollPan(float displacement) { } - - public void setOverScrollMode(int overscrollMode) { - mOverscrollMode = overscrollMode; - } - - public int getOverScrollMode() { - return mOverscrollMode; - } - - private float getViewportEnd() { - return getOrigin() + getViewportLength(); - } - - private float getPageEnd() { - return getPageStart() + getPageLength(); - } - - void startTouch(float pos) { - mVelocity = 0.0f; - mScrollingDisabled = false; - mFirstTouchPos = mTouchPos = mLastTouchPos = pos; - mRecentVelocityCount = 0; - } - - float panDistance(float currentPos) { - return currentPos - mFirstTouchPos; - } - - void setScrollingDisabled(boolean disabled) { - mScrollingDisabled = disabled; - } - - void saveTouchPos() { - mLastTouchPos = mTouchPos; - } - - void updateWithTouchAt(float pos, float timeDelta) { - float newVelocity = (mTouchPos - pos) / timeDelta * MS_PER_FRAME; - - mRecentVelocities[mRecentVelocityCount % FLING_VELOCITY_POINTS] = newVelocity; - mRecentVelocityCount++; - - // If there's a direction change, or current velocity is very low, - // allow setting of the velocity outright. Otherwise, use the current - // velocity and a maximum change factor to set the new velocity. - boolean curVelocityIsLow = Math.abs(mVelocity) < 1.0f / FRAMERATE_MULTIPLIER; - boolean directionChange = (mVelocity > 0) != (newVelocity > 0); - if (curVelocityIsLow || (directionChange && !FloatUtils.fuzzyEquals(newVelocity, 0.0f))) { - mVelocity = newVelocity; - } else { - float maxChange = Math.abs(mVelocity * timeDelta * MAX_EVENT_ACCELERATION); - mVelocity = Math.min(mVelocity + maxChange, Math.max(mVelocity - maxChange, newVelocity)); - } - - mTouchPos = pos; - } - - boolean overscrolled() { - return getOverscroll() != Overscroll.NONE; - } - - private Overscroll getOverscroll() { - boolean minus = (getOrigin() < getPageStart()); - boolean plus = (getViewportEnd() > getPageEnd()); - if (minus && plus) { - return Overscroll.BOTH; - } else if (minus) { - return Overscroll.MINUS; - } else if (plus) { - return Overscroll.PLUS; - } else { - return Overscroll.NONE; - } - } - - // Returns the amount that the page has been overscrolled. If the page hasn't been - // overscrolled on this axis, returns 0. - private float getExcess() { - switch (getOverscroll()) { - case MINUS: return getPageStart() - getOrigin(); - case PLUS: return getViewportEnd() - getPageEnd(); - case BOTH: return (getViewportEnd() - getPageEnd()) + (getPageStart() - getOrigin()); - default: return 0.0f; - } - } - - /* - * Returns true if the page is zoomed in to some degree along this axis such that scrolling is - * possible and this axis has not been scroll locked while panning. Otherwise, returns false. - */ - boolean scrollable() { - // If we're scrolling a subdocument, ignore the viewport length restrictions (since those - // apply to the top-level document) and only take into account axis locking. - if (mSubscroller.scrolling()) { - return !mScrollingDisabled; - } - - // if we are axis locked, return false - if (mScrollingDisabled) { - return false; - } - - // if there are margins on this axis but they are currently hidden, - // we must be able to scroll in order to make them visible, so allow - // scrolling in that case - if (marginsHidden()) { - return true; - } - - // there is scrollable space, and we're not disabled, or the document fits the viewport - // but we always allow overscroll anyway - return getViewportLength() <= getPageLength() - MIN_SCROLLABLE_DISTANCE || - getOverScrollMode() == View.OVER_SCROLL_ALWAYS; - } - - /* - * Returns the resistance, as a multiplier, that should be taken into account when - * tracking or pinching. - */ - float getEdgeResistance(boolean forPinching) { - float excess = getExcess(); - if (excess > 0.0f && (getOverscroll() == Overscroll.BOTH || !forPinching)) { - // excess can be greater than viewport length, but the resistance - // must never drop below 0.0 - return Math.max(0.0f, SNAP_LIMIT - excess / getViewportLength()); - } - return 1.0f; - } - - /* Returns the velocity. If the axis is locked, returns 0. */ - float getRealVelocity() { - return scrollable() ? mVelocity : 0f; - } - - void startPan() { - mFlingState = FlingStates.PANNING; - } - - private float calculateFlingVelocity() { - int usablePoints = Math.min(mRecentVelocityCount, FLING_VELOCITY_POINTS); - if (usablePoints <= 1) { - return mVelocity; - } - float average = 0; - for (int i = 0; i < usablePoints; i++) { - average += mRecentVelocities[i]; - } - return average / usablePoints; - } - - void startFling(boolean stopped) { - mDisableSnap = mSubscroller.scrolling(); - - if (stopped) { - mFlingState = FlingStates.STOPPED; - } else { - mVelocity = calculateFlingVelocity(); - mFlingState = FlingStates.FLINGING; - } - } - - /* Advances a fling animation by one step. */ - boolean advanceFling(long realNsPerFrame) { - if (mFlingState != FlingStates.FLINGING) { - return false; - } - if (mSubscroller.scrolling() && !mSubscroller.lastScrollSucceeded()) { - // if the subdocument stopped scrolling, it's because it reached the end - // of the subdocument. we don't do overscroll on subdocuments, so there's - // no point in continuing this fling. - return false; - } - - float excess = getExcess(); - Overscroll overscroll = getOverscroll(); - boolean decreasingOverscroll = false; - if ((overscroll == Overscroll.MINUS && mVelocity > 0) || - (overscroll == Overscroll.PLUS && mVelocity < 0)) - { - decreasingOverscroll = true; - } - - if (mDisableSnap || FloatUtils.fuzzyEquals(excess, 0.0f) || decreasingOverscroll) { - // If we aren't overscrolled, just apply friction. - if (Math.abs(mVelocity) >= VELOCITY_THRESHOLD) { - mVelocity *= getFrameAdjustedFriction(FRICTION_FAST, realNsPerFrame); - } else { - float t = mVelocity / VELOCITY_THRESHOLD; - mVelocity *= FloatUtils.interpolate(getFrameAdjustedFriction(FRICTION_SLOW, realNsPerFrame), - getFrameAdjustedFriction(FRICTION_FAST, realNsPerFrame), t); - } - } else { - // Otherwise, decrease the velocity linearly. - float elasticity = 1.0f - excess / (getViewportLength() * SNAP_LIMIT); - float overscrollDecelRate = getFrameAdjustedFriction(OVERSCROLL_DECEL_RATE, realNsPerFrame); - if (overscroll == Overscroll.MINUS) { - mVelocity = Math.min((mVelocity + overscrollDecelRate) * elasticity, 0.0f); - } else { // must be Overscroll.PLUS - mVelocity = Math.max((mVelocity - overscrollDecelRate) * elasticity, 0.0f); - } - } - - return true; - } - - void stopFling() { - mVelocity = 0.0f; - mFlingState = FlingStates.STOPPED; - } - - // Performs displacement of the viewport position according to the current velocity. - void displace() { - // if this isn't scrollable just return - if (!scrollable()) - return; - - if (mFlingState == FlingStates.PANNING) - mDisplacement += (mLastTouchPos - mTouchPos) * getEdgeResistance(false); - else - mDisplacement += mVelocity * getEdgeResistance(false); - - // if overscroll is disabled and we're trying to overscroll, reset the displacement - // to remove any excess. Using getExcess alone isn't enough here since it relies on - // getOverscroll which doesn't take into account any new displacment being applied. - // If we using a subscroller, we don't want to alter the scrolling being done - if (getOverScrollMode() == View.OVER_SCROLL_NEVER && !mSubscroller.scrolling()) { - float originalDisplacement = mDisplacement; - - if (mDisplacement + getOrigin() < getPageStart() - getMarginStart()) { - mDisplacement = getPageStart() - getMarginStart() - getOrigin(); - } else if (mDisplacement + getViewportEnd() > getPageEnd() + getMarginEnd()) { - mDisplacement = getPageEnd() - getMarginEnd() - getViewportEnd(); - } - - // Return the amount of overscroll so that the overscroll controller can draw it for us - if (originalDisplacement != mDisplacement) { - if (mFlingState == FlingStates.FLINGING) { - overscrollFling(mVelocity / MS_PER_FRAME * 1000); - stopFling(); - } else if (mFlingState == FlingStates.PANNING) { - overscrollPan(originalDisplacement - mDisplacement); - } - } - } - } - - float resetDisplacement() { - float d = mDisplacement; - mDisplacement = 0.0f; - return d; - } - - void setAutoscrollVelocity(float velocity) { - if (mFlingState != FlingStates.STOPPED) { - Log.e(LOGTAG, "Setting autoscroll velocity while in a fling is not allowed!"); - return; - } - mVelocity = velocity; - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/BitmapUtils.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/BitmapUtils.java deleted file mode 100644 index 9dba802f6d38..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/BitmapUtils.java +++ /dev/null @@ -1,368 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import org.mozilla.gecko.util.ThreadUtils; -import org.mozilla.gecko.util.UiAsyncTask; -//import org.mozilla.gecko.util.GeckoJarReader; -//import org.mozilla.gecko.R; - -import org.libreoffice.R; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.BitmapDrawable; -import android.net.Uri; -import android.os.AsyncTask; -import android.util.Base64; -import android.util.Log; -import android.text.TextUtils; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Field; -import java.lang.NoSuchFieldException; -import java.net.MalformedURLException; -import java.net.URL; - -public final class BitmapUtils { - private static final String LOGTAG = "GeckoBitmapUtils"; - - private BitmapUtils() {} - - public interface BitmapLoader { - public void onBitmapFound(Drawable d); - } - - public static void getDrawable(final Context context, final String data, final BitmapLoader loader) { - if (TextUtils.isEmpty(data)) { - loader.onBitmapFound(null); - return; - } - - if (data.startsWith("data")) { - BitmapDrawable d = new BitmapDrawable(context.getResources(), getBitmapFromDataURI(data)); - loader.onBitmapFound(d); - return; - } - - if (data.startsWith("jar:") || data.startsWith("file://")) { - (new UiAsyncTask<Void, Void, Drawable>(ThreadUtils.getBackgroundHandler()) { - @Override - public Drawable doInBackground(Void... params) { - try { - //if (data.startsWith("jar:jar")) { - // return GeckoJarReader.getBitmapDrawable(context.getResources(), data); - //} - - // Don't attempt to validate the JAR signature when loading an add-on icon - //if (data.startsWith("jar:file")) { - // return GeckoJarReader.getBitmapDrawable(context.getResources(), Uri.decode(data)); - //} - - URL url = new URL(data); - InputStream is = (InputStream) url.getContent(); - try { - return Drawable.createFromStream(is, "src"); - } finally { - is.close(); - } - } catch (Exception e) { - Log.w(LOGTAG, "Unable to set icon", e); - } - return null; - } - - @Override - public void onPostExecute(Drawable drawable) { - loader.onBitmapFound(drawable); - } - }).execute(); - return; - } - - if(data.startsWith("-moz-icon://")) { - Uri imageUri = Uri.parse(data); - String resource = imageUri.getSchemeSpecificPart(); - resource = resource.substring(resource.lastIndexOf('/') + 1); - - try { - Drawable d = context.getPackageManager().getApplicationIcon(resource); - loader.onBitmapFound(d); - } catch(Exception ex) { } - - return; - } - - if(data.startsWith("drawable://")) { - Uri imageUri = Uri.parse(data); - int id = getResource(imageUri, R.drawable.ic_status_logo); - Drawable d = context.getResources().getDrawable(id); - - loader.onBitmapFound(d); - return; - } - - loader.onBitmapFound(null); - } - - public static Bitmap decodeByteArray(byte[] bytes) { - return decodeByteArray(bytes, null); - } - - public static Bitmap decodeByteArray(byte[] bytes, BitmapFactory.Options options) { - return decodeByteArray(bytes, 0, bytes.length, options); - } - - public static Bitmap decodeByteArray(byte[] bytes, int offset, int length) { - return decodeByteArray(bytes, offset, length, null); - } - - public static Bitmap decodeByteArray(byte[] bytes, int offset, int length, BitmapFactory.Options options) { - if (bytes.length <= 0) { - throw new IllegalArgumentException("bytes.length " + bytes.length - + " must be a positive number"); - } - - Bitmap bitmap = null; - try { - bitmap = BitmapFactory.decodeByteArray(bytes, offset, length, options); - } catch (OutOfMemoryError e) { - Log.e(LOGTAG, "decodeByteArray(bytes.length=" + bytes.length - + ", options= " + options + ") OOM!", e); - return null; - } - - if (bitmap == null) { - Log.w(LOGTAG, "decodeByteArray() returning null because BitmapFactory returned null"); - return null; - } - - if (bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) { - Log.w(LOGTAG, "decodeByteArray() returning null because BitmapFactory returned " - + "a bitmap with dimensions " + bitmap.getWidth() - + "x" + bitmap.getHeight()); - return null; - } - - return bitmap; - } - - public static Bitmap decodeStream(InputStream inputStream) { - try { - return BitmapFactory.decodeStream(inputStream); - } catch (OutOfMemoryError e) { - Log.e(LOGTAG, "decodeStream() OOM!", e); - return null; - } - } - - public static Bitmap decodeUrl(Uri uri) { - return decodeUrl(uri.toString()); - } - - public static Bitmap decodeUrl(String urlString) { - URL url; - - try { - url = new URL(urlString); - } catch(MalformedURLException e) { - Log.w(LOGTAG, "decodeUrl: malformed URL " + urlString); - return null; - } - - return decodeUrl(url); - } - - public static Bitmap decodeUrl(URL url) { - InputStream stream = null; - - try { - stream = url.openStream(); - } catch(IOException e) { - Log.w(LOGTAG, "decodeUrl: IOException downloading " + url); - return null; - } - - if (stream == null) { - Log.w(LOGTAG, "decodeUrl: stream not found downloading " + url); - return null; - } - - Bitmap bitmap = decodeStream(stream); - - try { - stream.close(); - } catch(IOException e) { - Log.w(LOGTAG, "decodeUrl: IOException closing stream " + url, e); - } - - return bitmap; - } - - public static Bitmap decodeResource(Context context, int id) { - return decodeResource(context, id, null); - } - - public static Bitmap decodeResource(Context context, int id, BitmapFactory.Options options) { - Resources resources = context.getResources(); - try { - return BitmapFactory.decodeResource(resources, id, options); - } catch (OutOfMemoryError e) { - Log.e(LOGTAG, "decodeResource() OOM! Resource id=" + id, e); - return null; - } - } - - public static int getDominantColor(Bitmap source) { - return getDominantColor(source, true); - } - - public static int getDominantColor(Bitmap source, boolean applyThreshold) { - if (source == null) - return Color.argb(255,255,255,255); - - // Keep track of how many times a hue in a given bin appears in the image. - // Hue values range [0 .. 360), so dividing by 10, we get 36 bins. - int[] colorBins = new int[36]; - - // The bin with the most colors. Initialize to -1 to prevent accidentally - // thinking the first bin holds the dominant color. - int maxBin = -1; - - // Keep track of sum hue/saturation/value per hue bin, which we'll use to - // compute an average to for the dominant color. - float[] sumHue = new float[36]; - float[] sumSat = new float[36]; - float[] sumVal = new float[36]; - float[] hsv = new float[3]; - - int height = source.getHeight(); - int width = source.getWidth(); - int[] pixels = new int[width * height]; - source.getPixels(pixels, 0, width, 0, 0, width, height); - for (int row = 0; row < height; row++) { - for (int col = 0; col < width; col++) { - int c = pixels[col + row * width]; - // Ignore pixels with a certain transparency. - if (Color.alpha(c) < 128) - continue; - - Color.colorToHSV(c, hsv); - - // If a threshold is applied, ignore arbitrarily chosen values for "white" and "black". - if (applyThreshold && (hsv[1] <= 0.35f || hsv[2] <= 0.35f)) - continue; - - // We compute the dominant color by putting colors in bins based on their hue. - int bin = (int) Math.floor(hsv[0] / 10.0f); - - // Update the sum hue/saturation/value for this bin. - sumHue[bin] = sumHue[bin] + hsv[0]; - sumSat[bin] = sumSat[bin] + hsv[1]; - sumVal[bin] = sumVal[bin] + hsv[2]; - - // Increment the number of colors in this bin. - colorBins[bin]++; - - // Keep track of the bin that holds the most colors. - if (maxBin < 0 || colorBins[bin] > colorBins[maxBin]) - maxBin = bin; - } - } - - // maxBin may never get updated if the image holds only transparent and/or black/white pixels. - if (maxBin < 0) - return Color.argb(255,255,255,255); - - // Return a color with the average hue/saturation/value of the bin with the most colors. - hsv[0] = sumHue[maxBin]/colorBins[maxBin]; - hsv[1] = sumSat[maxBin]/colorBins[maxBin]; - hsv[2] = sumVal[maxBin]/colorBins[maxBin]; - return Color.HSVToColor(hsv); - } - - /** - * Decodes a bitmap from a Base64 data URI. - * - * @param dataURI a Base64-encoded data URI string - * @return the decoded bitmap, or null if the data URI is invalid - */ - public static Bitmap getBitmapFromDataURI(String dataURI) { - String base64 = dataURI.substring(dataURI.indexOf(',') + 1); - try { - byte[] raw = Base64.decode(base64, Base64.DEFAULT); - return BitmapUtils.decodeByteArray(raw); - } catch (Exception e) { - Log.e(LOGTAG, "exception decoding bitmap from data URI: " + dataURI, e); - } - return null; - } - - public static Bitmap getBitmapFromDrawable(Drawable drawable) { - if (drawable instanceof BitmapDrawable) { - return ((BitmapDrawable) drawable).getBitmap(); - } - - int width = drawable.getIntrinsicWidth(); - width = width > 0 ? width : 1; - int height = drawable.getIntrinsicHeight(); - height = height > 0 ? height : 1; - - Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - drawable.draw(canvas); - - return bitmap; - } - - public static int getResource(Uri resourceUrl, int defaultIcon) { - int icon = defaultIcon; - - final String scheme = resourceUrl.getScheme(); - if ("drawable".equals(scheme)) { - String resource = resourceUrl.getSchemeSpecificPart(); - resource = resource.substring(resource.lastIndexOf('/') + 1); - - try { - return Integer.parseInt(resource); - } catch(NumberFormatException ex) { - // This isn't a resource id, try looking for a string - } - - try { - final Class<R.drawable> drawableClass = R.drawable.class; - final Field f = drawableClass.getField(resource); - icon = f.getInt(null); - } catch (final NoSuchFieldException e1) { - - // just means the resource doesn't exist for fennec. Check in Android resources - try { - final Class<android.R.drawable> drawableClass = android.R.drawable.class; - final Field f = drawableClass.getField(resource); - icon = f.getInt(null); - } catch (final NoSuchFieldException e2) { - // This drawable doesn't seem to exist... - } catch(Exception e3) { - Log.i(LOGTAG, "Exception getting drawable", e3); - } - - } catch (Exception e4) { - Log.i(LOGTAG, "Exception getting drawable", e4); - } - - resourceUrl = null; - } - return icon; - } -} - diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/BufferedCairoImage.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/BufferedCairoImage.java deleted file mode 100644 index 307f41204256..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/BufferedCairoImage.java +++ /dev/null @@ -1,69 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import org.mozilla.gecko.mozglue.DirectBufferAllocator; - -import android.graphics.Bitmap; -import android.util.Log; - -import java.nio.ByteBuffer; - -/** A Cairo image that simply saves a buffer of pixel data. */ -public class BufferedCairoImage extends CairoImage { - private ByteBuffer mBuffer; - private IntSize mSize; - private int mFormat; - - private static String LOGTAG = "GeckoBufferedCairoImage"; - - /** Creates a buffered Cairo image from a byte buffer. */ - public BufferedCairoImage(ByteBuffer inBuffer, int inWidth, int inHeight, int inFormat) { - setBuffer(inBuffer, inWidth, inHeight, inFormat); - } - - /** Creates a buffered Cairo image from an Android bitmap. */ - public BufferedCairoImage(Bitmap bitmap) { - setBitmap(bitmap); - } - - private synchronized void freeBuffer() { - mBuffer = DirectBufferAllocator.free(mBuffer); - } - - @Override - public void destroy() { - try { - freeBuffer(); - } catch (Exception ex) { - Log.e(LOGTAG, "error clearing buffer: ", ex); - } - } - - @Override - public ByteBuffer getBuffer() { return mBuffer; } - @Override - public IntSize getSize() { return mSize; } - @Override - public int getFormat() { return mFormat; } - - - public void setBuffer(ByteBuffer buffer, int width, int height, int format) { - freeBuffer(); - mBuffer = buffer; - mSize = new IntSize(width, height); - mFormat = format; - } - - public void setBitmap(Bitmap bitmap) { - mFormat = CairoUtils.bitmapConfigToCairoFormat(bitmap.getConfig()); - mSize = new IntSize(bitmap.getWidth(), bitmap.getHeight()); - - int bpp = CairoUtils.bitsPerPixelForCairoFormat(mFormat); - mBuffer = DirectBufferAllocator.allocate(mSize.getArea() * bpp); - bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer()); - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/CairoGLInfo.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/CairoGLInfo.java deleted file mode 100644 index 472c1d29f792..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/CairoGLInfo.java +++ /dev/null @@ -1,35 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import javax.microedition.khronos.opengles.GL10; - -/** Information needed to render Cairo bitmaps using OpenGL ES. */ -public class CairoGLInfo { - public final int internalFormat; - public final int format; - public final int type; - - public CairoGLInfo(int cairoFormat) { - switch (cairoFormat) { - case CairoImage.FORMAT_ARGB32: - internalFormat = format = GL10.GL_RGBA; type = GL10.GL_UNSIGNED_BYTE; - break; - case CairoImage.FORMAT_RGB24: - internalFormat = format = GL10.GL_RGB; type = GL10.GL_UNSIGNED_BYTE; - break; - case CairoImage.FORMAT_RGB16_565: - internalFormat = format = GL10.GL_RGB; type = GL10.GL_UNSIGNED_SHORT_5_6_5; - break; - case CairoImage.FORMAT_A8: - case CairoImage.FORMAT_A1: - throw new RuntimeException("Cairo FORMAT_A1 and FORMAT_A8 unsupported"); - default: - throw new RuntimeException("Unknown Cairo format"); - } - } -} - diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/CairoImage.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/CairoImage.java deleted file mode 100644 index 5a18a4bb1995..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/CairoImage.java +++ /dev/null @@ -1,28 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import java.nio.ByteBuffer; - -/* - * A bitmap with pixel data in one of the formats that Cairo understands. - */ -public abstract class CairoImage { - public abstract ByteBuffer getBuffer(); - - public abstract void destroy(); - - public abstract IntSize getSize(); - public abstract int getFormat(); - - public static final int FORMAT_INVALID = -1; - public static final int FORMAT_ARGB32 = 0; - public static final int FORMAT_RGB24 = 1; - public static final int FORMAT_A8 = 2; - public static final int FORMAT_A1 = 3; - public static final int FORMAT_RGB16_565 = 4; -} - diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/CairoUtils.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/CairoUtils.java deleted file mode 100644 index 48c449f05e5c..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/CairoUtils.java +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import android.graphics.Bitmap; - -/** - * Utility methods useful when displaying Cairo bitmaps using OpenGL ES. - */ -public class CairoUtils { - private CairoUtils() { /* Don't call me. */ } - - public static int bitsPerPixelForCairoFormat(int cairoFormat) { - switch (cairoFormat) { - case CairoImage.FORMAT_A1: return 1; - case CairoImage.FORMAT_A8: return 8; - case CairoImage.FORMAT_RGB16_565: return 16; - case CairoImage.FORMAT_RGB24: return 24; - case CairoImage.FORMAT_ARGB32: return 32; - default: - throw new RuntimeException("Unknown Cairo format"); - } - } - - public static int bitmapConfigToCairoFormat(Bitmap.Config config) { - if (config == null) - return CairoImage.FORMAT_ARGB32; /* Droid Pro fix. */ - - switch (config) { - case ALPHA_8: return CairoImage.FORMAT_A8; - case ARGB_4444: throw new RuntimeException("ARGB_444 unsupported"); - case ARGB_8888: return CairoImage.FORMAT_ARGB32; - case RGB_565: return CairoImage.FORMAT_RGB16_565; - default: throw new RuntimeException("Unknown Skia bitmap config"); - } - } - - public static Bitmap.Config cairoFormatTobitmapConfig(int format) { - switch (format) { - case CairoImage.FORMAT_A8: return Bitmap.Config.ALPHA_8; - case CairoImage.FORMAT_ARGB32: return Bitmap.Config.ARGB_8888; - case CairoImage.FORMAT_RGB16_565: return Bitmap.Config.RGB_565; - default: - throw new RuntimeException("Unknown CairoImage format"); - } - } -} - diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java deleted file mode 100644 index f101dfbdb75f..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/DisplayPortCalculator.java +++ /dev/null @@ -1,777 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -//import org.mozilla.gecko.GeckoAppShell; -//import org.mozilla.gecko.PrefsHelper; -import org.libreoffice.LOKitShell; -import org.mozilla.gecko.util.FloatUtils; - -import org.json.JSONArray; - -import android.graphics.PointF; -import android.graphics.RectF; -import android.util.FloatMath; -import android.util.Log; - -import java.util.HashMap; -import java.util.Map; - -final class DisplayPortCalculator { - private static final String LOGTAG = "GeckoDisplayPort"; - private static final PointF ZERO_VELOCITY = new PointF(0, 0); - - // Keep this in sync with the TILEDLAYERBUFFER_TILE_SIZE defined in gfx/layers/TiledLayerBuffer.h - private static final int TILE_SIZE = 256; - - private static final String PREF_DISPLAYPORT_STRATEGY = "gfx.displayport.strategy"; - private static final String PREF_DISPLAYPORT_FM_MULTIPLIER = "gfx.displayport.strategy_fm.multiplier"; - private static final String PREF_DISPLAYPORT_FM_DANGER_X = "gfx.displayport.strategy_fm.danger_x"; - private static final String PREF_DISPLAYPORT_FM_DANGER_Y = "gfx.displayport.strategy_fm.danger_y"; - private static final String PREF_DISPLAYPORT_VB_MULTIPLIER = "gfx.displayport.strategy_vb.multiplier"; - private static final String PREF_DISPLAYPORT_VB_VELOCITY_THRESHOLD = "gfx.displayport.strategy_vb.threshold"; - private static final String PREF_DISPLAYPORT_VB_REVERSE_BUFFER = "gfx.displayport.strategy_vb.reverse_buffer"; - private static final String PREF_DISPLAYPORT_VB_DANGER_X_BASE = "gfx.displayport.strategy_vb.danger_x_base"; - private static final String PREF_DISPLAYPORT_VB_DANGER_Y_BASE = "gfx.displayport.strategy_vb.danger_y_base"; - private static final String PREF_DISPLAYPORT_VB_DANGER_X_INCR = "gfx.displayport.strategy_vb.danger_x_incr"; - private static final String PREF_DISPLAYPORT_VB_DANGER_Y_INCR = "gfx.displayport.strategy_vb.danger_y_incr"; - private static final String PREF_DISPLAYPORT_PB_VELOCITY_THRESHOLD = "gfx.displayport.strategy_pb.threshold"; - - private static DisplayPortStrategy sStrategy = new VelocityBiasStrategy(null); - - static DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) { - return sStrategy.calculate(metrics, (velocity == null ? ZERO_VELOCITY : velocity)); - } - - static boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) { - if (displayPort == null) { - return true; - } - return sStrategy.aboutToCheckerboard(metrics, (velocity == null ? ZERO_VELOCITY : velocity), displayPort); - } - - static boolean drawTimeUpdate(long millis, int pixels) { - return sStrategy.drawTimeUpdate(millis, pixels); - } - - static void resetPageState() { - sStrategy.resetPageState(); - } - - static void initPrefs() { - final String[] prefs = { PREF_DISPLAYPORT_STRATEGY, - PREF_DISPLAYPORT_FM_MULTIPLIER, - PREF_DISPLAYPORT_FM_DANGER_X, - PREF_DISPLAYPORT_FM_DANGER_Y, - PREF_DISPLAYPORT_VB_MULTIPLIER, - PREF_DISPLAYPORT_VB_VELOCITY_THRESHOLD, - PREF_DISPLAYPORT_VB_REVERSE_BUFFER, - PREF_DISPLAYPORT_VB_DANGER_X_BASE, - PREF_DISPLAYPORT_VB_DANGER_Y_BASE, - PREF_DISPLAYPORT_VB_DANGER_X_INCR, - PREF_DISPLAYPORT_VB_DANGER_Y_INCR, - PREF_DISPLAYPORT_PB_VELOCITY_THRESHOLD }; - - /*PrefsHelper.getPrefs(prefs, new PrefsHelper.PrefHandlerBase() { - private Map<String, Integer> mValues = new HashMap<String, Integer>(); - - @Override public void prefValue(String pref, int value) { - mValues.put(pref, value); - } - - @Override public void finish() { - setStrategy(mValues); - } - });*/ - } - - /** - * Set the active strategy to use. - * See the gfx.displayport.strategy pref in mobile/android/app/mobile.js to see the - * mapping between ints and strategies. - */ - static boolean setStrategy(Map<String, Integer> prefs) { - Integer strategy = prefs.get(PREF_DISPLAYPORT_STRATEGY); - if (strategy == null) { - return false; - } - - switch (strategy) { - case 0: - sStrategy = new FixedMarginStrategy(prefs); - break; - case 1: - sStrategy = new VelocityBiasStrategy(prefs); - break; - case 2: - sStrategy = new DynamicResolutionStrategy(prefs); - break; - case 3: - sStrategy = new NoMarginStrategy(prefs); - break; - case 4: - sStrategy = new PredictionBiasStrategy(prefs); - break; - default: - Log.e(LOGTAG, "Invalid strategy index specified"); - return false; - } - Log.i(LOGTAG, "Set strategy " + sStrategy.toString()); - return true; - } - - private static float getFloatPref(Map<String, Integer> prefs, String prefName, int defaultValue) { - Integer value = (prefs == null ? null : prefs.get(prefName)); - return (float)(value == null || value < 0 ? defaultValue : value) / 1000f; - } - - private static abstract class DisplayPortStrategy { - /** Calculates a displayport given a viewport and panning velocity. */ - public abstract DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity); - /** Returns true if a checkerboard is about to be visible and we should not throttle drawing. */ - public abstract boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort); - /** Notify the strategy of a new recorded draw time. Return false to turn off draw time recording. */ - public boolean drawTimeUpdate(long millis, int pixels) { return false; } - /** Reset any page-specific state stored, as the page being displayed has changed. */ - public void resetPageState() {} - } - - /** - * Return the dimensions for a rect that has area (width*height) that does not exceed the page size in the - * given metrics object. The area in the returned FloatSize may be less than width*height if the page is - * small, but it will never be larger than width*height. - * Note that this process may change the relative aspect ratio of the given dimensions. - */ - private static FloatSize reshapeForPage(float width, float height, ImmutableViewportMetrics metrics) { - // figure out how much of the desired buffer amount we can actually use on the horizontal axis - float usableWidth = Math.min(width, metrics.getPageWidth()); - // if we reduced the buffer amount on the horizontal axis, we should take that saved memory and - // use it on the vertical axis - float extraUsableHeight = (float)Math.floor(((width - usableWidth) * height) / usableWidth); - float usableHeight = Math.min(height + extraUsableHeight, metrics.getPageHeight()); - if (usableHeight < height && usableWidth == width) { - // and the reverse - if we shrunk the buffer on the vertical axis we can add it to the horizontal - float extraUsableWidth = (float)Math.floor(((height - usableHeight) * width) / usableHeight); - usableWidth = Math.min(width + extraUsableWidth, metrics.getPageWidth()); - } - return new FloatSize(usableWidth, usableHeight); - } - - /** - * Expand the given rect in all directions by a "danger zone". The size of the danger zone on an axis - * is the size of the view on that axis multiplied by the given multiplier. The expanded rect is then - * clamped to page bounds and returned. - */ - private static RectF expandByDangerZone(RectF rect, float dangerZoneXMultiplier, float dangerZoneYMultiplier, ImmutableViewportMetrics metrics) { - // calculate the danger zone amounts in pixels - float dangerZoneX = metrics.getWidth() * dangerZoneXMultiplier; - float dangerZoneY = metrics.getHeight() * dangerZoneYMultiplier; - rect = RectUtils.expand(rect, dangerZoneX, dangerZoneY); - // clamp to page bounds - return clampToPageBounds(rect, metrics); - } - - /** - * Expand the given margins such that when they are applied on the viewport, the resulting rect - * does not have any partial tiles, except when it is clipped by the page bounds. This assumes - * the tiles are TILE_SIZE by TILE_SIZE and start at the origin, such that there will always be - * a tile at (0,0)-(TILE_SIZE,TILE_SIZE)). - */ - private static DisplayPortMetrics getTileAlignedDisplayPortMetrics(RectF margins, float zoom, ImmutableViewportMetrics metrics) { - float left = metrics.viewportRectLeft - margins.left; - float top = metrics.viewportRectTop - margins.top; - float right = metrics.viewportRectRight + margins.right; - float bottom = metrics.viewportRectBottom + margins.bottom; - left = Math.max(metrics.pageRectLeft, TILE_SIZE * FloatMath.floor(left / TILE_SIZE)); - top = Math.max(metrics.pageRectTop, TILE_SIZE * FloatMath.floor(top / TILE_SIZE)); - right = Math.min(metrics.pageRectRight, TILE_SIZE * FloatMath.ceil(right / TILE_SIZE)); - bottom = Math.min(metrics.pageRectBottom, TILE_SIZE * FloatMath.ceil(bottom / TILE_SIZE)); - return new DisplayPortMetrics(left, top, right, bottom, zoom); - } - - /** - * Adjust the given margins so if they are applied on the viewport in the metrics, the resulting rect - * does not exceed the page bounds. This code will maintain the total margin amount for a given axis; - * it assumes that margins.left + metrics.getWidth() + margins.right is less than or equal to - * metrics.getPageWidth(); and the same for the y axis. - */ - private static RectF shiftMarginsForPageBounds(RectF margins, ImmutableViewportMetrics metrics) { - // check how much we're overflowing in each direction. note that at most one of leftOverflow - // and rightOverflow can be greater than zero, and at most one of topOverflow and bottomOverflow - // can be greater than zero, because of the assumption described in the method javadoc. - float leftOverflow = metrics.pageRectLeft - (metrics.viewportRectLeft - margins.left); - float rightOverflow = (metrics.viewportRectRight + margins.right) - metrics.pageRectRight; - float topOverflow = metrics.pageRectTop - (metrics.viewportRectTop - margins.top); - float bottomOverflow = (metrics.viewportRectBottom + margins.bottom) - metrics.pageRectBottom; - - // if the margins overflow the page bounds, shift them to other side on the same axis - if (leftOverflow > 0) { - margins.left -= leftOverflow; - margins.right += leftOverflow; - } else if (rightOverflow > 0) { - margins.right -= rightOverflow; - margins.left += rightOverflow; - } - if (topOverflow > 0) { - margins.top -= topOverflow; - margins.bottom += topOverflow; - } else if (bottomOverflow > 0) { - margins.bottom -= bottomOverflow; - margins.top += bottomOverflow; - } - return margins; - } - - /** - * Clamp the given rect to the page bounds and return it. - */ - private static RectF clampToPageBounds(RectF rect, ImmutableViewportMetrics metrics) { - if (rect.top < metrics.pageRectTop) rect.top = metrics.pageRectTop; - if (rect.left < metrics.pageRectLeft) rect.left = metrics.pageRectLeft; - if (rect.right > metrics.pageRectRight) rect.right = metrics.pageRectRight; - if (rect.bottom > metrics.pageRectBottom) rect.bottom = metrics.pageRectBottom; - return rect; - } - - /** - * This class implements the variation where we basically don't bother with a display port. - */ - private static class NoMarginStrategy extends DisplayPortStrategy { - NoMarginStrategy(Map<String, Integer> prefs) { - // no prefs in this strategy - } - - @Override - public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) { - return new DisplayPortMetrics(metrics.viewportRectLeft, - metrics.viewportRectTop, - metrics.viewportRectRight, - metrics.viewportRectBottom, - metrics.zoomFactor); - } - - @Override - public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) { - return true; - } - - @Override - public String toString() { - return "NoMarginStrategy"; - } - } - - /** - * This class implements the variation where we use a fixed-size margin on the display port. - * The margin is always 300 pixels in all directions, except when we are (a) approaching a page - * boundary, and/or (b) if we are limited by the page size. In these cases we try to maintain - * the area of the display port by (a) shifting the buffer to the other side on the same axis, - * and/or (b) increasing the buffer on the other axis to compensate for the reduced buffer on - * one axis. - */ - private static class FixedMarginStrategy extends DisplayPortStrategy { - // The length of each axis of the display port will be the corresponding view length - // multiplied by this factor. - private final float SIZE_MULTIPLIER; - - // If the visible rect is within the danger zone (measured as a fraction of the view size - // from the edge of the displayport) we start redrawing to minimize checkerboarding. - private final float DANGER_ZONE_X_MULTIPLIER; - private final float DANGER_ZONE_Y_MULTIPLIER; - - FixedMarginStrategy(Map<String, Integer> prefs) { - SIZE_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_FM_MULTIPLIER, 2000); - DANGER_ZONE_X_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_FM_DANGER_X, 100); - DANGER_ZONE_Y_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_FM_DANGER_Y, 200); - } - - @Override - public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) { - float displayPortWidth = metrics.getWidth() * SIZE_MULTIPLIER; - float displayPortHeight = metrics.getHeight() * SIZE_MULTIPLIER; - - // we need to avoid having a display port that is larger than the page, or we will end up - // painting things outside the page bounds (bug 729169). we simultaneously need to make - // the display port as large as possible so that we redraw less. reshape the display - // port dimensions to accomplish this. - FloatSize usableSize = reshapeForPage(displayPortWidth, displayPortHeight, metrics); - float horizontalBuffer = usableSize.width - metrics.getWidth(); - float verticalBuffer = usableSize.height - metrics.getHeight(); - - // and now calculate the display port margins based on how much buffer we've decided to use and - // the page bounds, ensuring we use all of the available buffer amounts on one side or the other - // on any given axis. (i.e. if we're scrolled to the top of the page, the vertical buffer is - // entirely below the visible viewport, but if we're halfway down the page, the vertical buffer - // is split). - RectF margins = new RectF(); - margins.left = horizontalBuffer / 2.0f; - margins.right = horizontalBuffer - margins.left; - margins.top = verticalBuffer / 2.0f; - margins.bottom = verticalBuffer - margins.top; - margins = shiftMarginsForPageBounds(margins, metrics); - - return getTileAlignedDisplayPortMetrics(margins, metrics.zoomFactor, metrics); - } - - @Override - public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) { - // Increase the size of the viewport based on the danger zone multiplier (and clamp to page - // boundaries), and intersect it with the current displayport to determine whether we're - // close to checkerboarding. - RectF adjustedViewport = expandByDangerZone(metrics.getViewport(), DANGER_ZONE_X_MULTIPLIER, DANGER_ZONE_Y_MULTIPLIER, metrics); - return !displayPort.contains(adjustedViewport); - } - - @Override - public String toString() { - return "FixedMarginStrategy mult=" + SIZE_MULTIPLIER + ", dangerX=" + DANGER_ZONE_X_MULTIPLIER + ", dangerY=" + DANGER_ZONE_Y_MULTIPLIER; - } - } - - /** - * This class implements the variation with a small fixed-size margin with velocity bias. - * In this variation, the default margins are pretty small relative to the view size, but - * they are affected by the panning velocity. Specifically, if we are panning on one axis, - * we remove the margins on the other axis because we are likely axis-locked. Also once - * we are panning in one direction above a certain threshold velocity, we shift the buffer - * so that it is almost entirely in the direction of the pan, with a little bit in the - * reverse direction. - */ - private static class VelocityBiasStrategy extends DisplayPortStrategy { - // The length of each axis of the display port will be the corresponding view length - // multiplied by this factor. - private final float SIZE_MULTIPLIER; - // The velocity above which we apply the velocity bias - private final float VELOCITY_THRESHOLD; - // How much of the buffer to keep in the reverse direction of the velocity - private final float REVERSE_BUFFER; - // If the visible rect is within the danger zone we start redrawing to minimize - // checkerboarding. the danger zone amount is a linear function of the form: - // viewportsize * (base + velocity * incr) - // where base and incr are configurable values. - private final float DANGER_ZONE_BASE_X_MULTIPLIER; - private final float DANGER_ZONE_BASE_Y_MULTIPLIER; - private final float DANGER_ZONE_INCR_X_MULTIPLIER; - private final float DANGER_ZONE_INCR_Y_MULTIPLIER; - - VelocityBiasStrategy(Map<String, Integer> prefs) { - SIZE_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_MULTIPLIER, 2000); - VELOCITY_THRESHOLD = /*GeckoAppShell.getDpi()*/ LOKitShell.getDpi() * getFloatPref(prefs, PREF_DISPLAYPORT_VB_VELOCITY_THRESHOLD, 32); - REVERSE_BUFFER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_REVERSE_BUFFER, 200); - DANGER_ZONE_BASE_X_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_X_BASE, 1000); - DANGER_ZONE_BASE_Y_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_Y_BASE, 1000); - DANGER_ZONE_INCR_X_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_X_INCR, 0); - DANGER_ZONE_INCR_Y_MULTIPLIER = getFloatPref(prefs, PREF_DISPLAYPORT_VB_DANGER_Y_INCR, 0); - } - - /** - * Split the given amounts into margins based on the VELOCITY_THRESHOLD and REVERSE_BUFFER values. - * If the velocity is above the VELOCITY_THRESHOLD on an axis, split the amount into REVERSE_BUFFER - * and 1.0 - REVERSE_BUFFER fractions. The REVERSE_BUFFER fraction is set as the margin in the - * direction opposite to the velocity, and the remaining fraction is set as the margin in the direction - * of the velocity. If the velocity is lower than VELOCITY_THRESHOLD, split the amount evenly into the - * two margins on that axis. - */ - private RectF velocityBiasedMargins(float xAmount, float yAmount, PointF velocity) { - RectF margins = new RectF(); - - if (velocity.x > VELOCITY_THRESHOLD) { - margins.left = xAmount * REVERSE_BUFFER; - } else if (velocity.x < -VELOCITY_THRESHOLD) { - margins.left = xAmount * (1.0f - REVERSE_BUFFER); - } else { - margins.left = xAmount / 2.0f; - } - margins.right = xAmount - margins.left; - - if (velocity.y > VELOCITY_THRESHOLD) { - margins.top = yAmount * REVERSE_BUFFER; - } else if (velocity.y < -VELOCITY_THRESHOLD) { - margins.top = yAmount * (1.0f - REVERSE_BUFFER); - } else { - margins.top = yAmount / 2.0f; - } - margins.bottom = yAmount - margins.top; - - return margins; - } - - @Override - public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) { - float displayPortWidth = metrics.getWidth() * SIZE_MULTIPLIER; - float displayPortHeight = metrics.getHeight() * SIZE_MULTIPLIER; - - // but if we're panning on one axis, set the margins for the other axis to zero since we are likely - // axis locked and won't be displaying that extra area. - if (Math.abs(velocity.x) > VELOCITY_THRESHOLD && FloatUtils.fuzzyEquals(velocity.y, 0)) { - displayPortHeight = metrics.getHeight(); - } else if (Math.abs(velocity.y) > VELOCITY_THRESHOLD && FloatUtils.fuzzyEquals(velocity.x, 0)) { - displayPortWidth = metrics.getWidth(); - } - - // we need to avoid having a display port that is larger than the page, or we will end up - // painting things outside the page bounds (bug 729169). - displayPortWidth = Math.min(displayPortWidth, metrics.getPageWidth()); - displayPortHeight = Math.min(displayPortHeight, metrics.getPageHeight()); - float horizontalBuffer = displayPortWidth - metrics.getWidth(); - float verticalBuffer = displayPortHeight - metrics.getHeight(); - - // split the buffer amounts into margins based on velocity, and shift it to - // take into account the page bounds - RectF margins = velocityBiasedMargins(horizontalBuffer, verticalBuffer, velocity); - margins = shiftMarginsForPageBounds(margins, metrics); - - return getTileAlignedDisplayPortMetrics(margins, metrics.zoomFactor, metrics); - } - - @Override - public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) { - // calculate the danger zone amounts based on the prefs - float dangerZoneX = metrics.getWidth() * (DANGER_ZONE_BASE_X_MULTIPLIER + (velocity.x * DANGER_ZONE_INCR_X_MULTIPLIER)); - float dangerZoneY = metrics.getHeight() * (DANGER_ZONE_BASE_Y_MULTIPLIER + (velocity.y * DANGER_ZONE_INCR_Y_MULTIPLIER)); - // clamp it such that when added to the viewport, they don't exceed page size. - // this is a prerequisite to calling shiftMarginsForPageBounds as we do below. - dangerZoneX = Math.min(dangerZoneX, metrics.getPageWidth() - metrics.getWidth()); - dangerZoneY = Math.min(dangerZoneY, metrics.getPageHeight() - metrics.getHeight()); - - // split the danger zone into margins based on velocity, and ensure it doesn't exceed - // page bounds. - RectF dangerMargins = velocityBiasedMargins(dangerZoneX, dangerZoneY, velocity); - dangerMargins = shiftMarginsForPageBounds(dangerMargins, metrics); - - // we're about to checkerboard if the current viewport area + the danger zone margins - // fall out of the current displayport anywhere. - RectF adjustedViewport = new RectF( - metrics.viewportRectLeft - dangerMargins.left, - metrics.viewportRectTop - dangerMargins.top, - metrics.viewportRectRight + dangerMargins.right, - metrics.viewportRectBottom + dangerMargins.bottom); - return !displayPort.contains(adjustedViewport); - } - - @Override - public String toString() { - return "VelocityBiasStrategy mult=" + SIZE_MULTIPLIER + ", threshold=" + VELOCITY_THRESHOLD + ", reverse=" + REVERSE_BUFFER - + ", dangerBaseX=" + DANGER_ZONE_BASE_X_MULTIPLIER + ", dangerBaseY=" + DANGER_ZONE_BASE_Y_MULTIPLIER - + ", dangerIncrX=" + DANGER_ZONE_INCR_Y_MULTIPLIER + ", dangerIncrY=" + DANGER_ZONE_INCR_Y_MULTIPLIER; - } - } - - /** - * This class implements the variation where we draw more of the page at low resolution while panning. - * In this variation, as we pan faster, we increase the page area we are drawing, but reduce the draw - * resolution to compensate. This results in the same device-pixel area drawn; the compositor then - * scales this up to the viewport zoom level. This results in a large area of the page drawn but it - * looks blurry. The assumption is that drawing extra that we never display is better than checkerboarding, - * where we draw less but never even show it on the screen. - */ - private static class DynamicResolutionStrategy extends DisplayPortStrategy { - // The length of each axis of the display port will be the corresponding view length - // multiplied by this factor. - private static final float SIZE_MULTIPLIER = 1.5f; - - // The velocity above which we start zooming out the display port to keep up - // with the panning. - private static final float VELOCITY_EXPANSION_THRESHOLD = /*GeckoAppShell.getDpi()*/ LOKitShell.getDpi() / 16f; - - // How much we increase the display port based on velocity. Assuming no friction and - // splitting (see below), this should be the number of frames (@60fps) between us - // calculating the display port and the draw of the *next* display port getting composited - // and displayed on the screen. This is because the timeline looks like this: - // Java: pan pan pan pan pan pan ! pan pan pan pan pan pan ! - // Gecko: \-> draw -> composite / \-> draw -> composite / - // The display port calculated on the first "pan" gets composited to the screen at the - // first exclamation mark, and remains on the screen until the second exclamation mark. - // In order to avoid checkerboarding, that display port must be able to contain all of - // the panning until the second exclamation mark, which encompasses two entire draw/composite - // cycles. - // If we take into account friction, our velocity multiplier should be reduced as the - // amount of pan will decrease each time. If we take into account display port splitting, - // it should be increased as the splitting means some of the display port will be used to - // draw in the opposite direction of the velocity. For now I'm assuming these two cancel - // each other out. - private static final float VELOCITY_MULTIPLIER = 60.0f; - - // The following constants adjust how biased the display port is in the direction of panning. - // When panning fast (above the FAST_THRESHOLD) we use the fast split factor to split the - // display port "buffer" area, otherwise we use the slow split factor. This is based on the - // assumption that if the user is panning fast, they are less likely to reverse directions - // and go backwards, so we should spend more of our display port buffer in the direction of - // panning. - private static final float VELOCITY_FAST_THRESHOLD = VELOCITY_EXPANSION_THRESHOLD * 2.0f; - private static final float FAST_SPLIT_FACTOR = 0.95f; - private static final float SLOW_SPLIT_FACTOR = 0.8f; - - // The following constants are used for viewport prediction; we use them to estimate where - // the viewport will be soon and whether or not we should trigger a draw right now. "soon" - // in the previous sentence really refers to the amount of time it would take to draw and - // composite from the point at which we do the calculation, and that is not really a known - // quantity. The velocity multiplier is how much we multiply the velocity by; it has the - // same caveats as the VELOCITY_MULTIPLIER above except that it only needs to take into account - // one draw/composite cycle instead of two. The danger zone multiplier is a multiplier of the - // viewport size that we use as an extra "danger zone" around the viewport; if this danger - // zone falls outside the display port then we are approaching the point at which we will - // checkerboard, and hence should start drawing. Note that if DANGER_ZONE_MULTIPLIER is - // greater than (SIZE_MULTIPLIER - 1.0f), then at zero velocity we will always be in the - // danger zone, and thus will be constantly drawing. - private static final float PREDICTION_VELOCITY_MULTIPLIER = 30.0f; - private static final float DANGER_ZONE_MULTIPLIER = 0.20f; // must be less than (SIZE_MULTIPLIER - 1.0f) - - DynamicResolutionStrategy(Map<String, Integer> prefs) { - // ignore prefs for now - } - - @Override - public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) { - float displayPortWidth = metrics.getWidth() * SIZE_MULTIPLIER; - float displayPortHeight = metrics.getHeight() * SIZE_MULTIPLIER; - - // for resolution calculation purposes, we need to know what the adjusted display port dimensions - // would be if we had zero velocity, so calculate that here before we increase the display port - // based on velocity. - FloatSize reshapedSize = reshapeForPage(displayPortWidth, displayPortHeight, metrics); - - // increase displayPortWidth and displayPortHeight based on the velocity, but maintaining their - // relative aspect ratio. - if (velocity.length() > VELOCITY_EXPANSION_THRESHOLD) { - float velocityFactor = Math.max(Math.abs(velocity.x) / displayPortWidth, - Math.abs(velocity.y) / displayPortHeight); - velocityFactor *= VELOCITY_MULTIPLIER; - - displayPortWidth += (displayPortWidth * velocityFactor); - displayPortHeight += (displayPortHeight * velocityFactor); - } - - // at this point, displayPortWidth and displayPortHeight are how much of the page (in device pixels) - // we want to be rendered by Gecko. Note here "device pixels" is equivalent to CSS pixels multiplied - // by metrics.zoomFactor - - // we need to avoid having a display port that is larger than the page, or we will end up - // painting things outside the page bounds (bug 729169). we simultaneously need to make - // the display port as large as possible so that we redraw less. reshape the display - // port dimensions to accomplish this. this may change the aspect ratio of the display port, - // but we are assuming that this is desirable because the advantages from pre-drawing will - // outweigh the disadvantages from any buffer reallocations that might occur. - FloatSize usableSize = reshapeForPage(displayPortWidth, displayPortHeight, metrics); - float horizontalBuffer = usableSize.width - metrics.getWidth(); - float verticalBuffer = usableSize.height - metrics.getHeight(); - - // at this point, horizontalBuffer and verticalBuffer are the dimensions of the buffer area we have. - // the buffer area is the off-screen area that is part of the display port and will be pre-drawn in case - // the user scrolls there. we now need to split the buffer area on each axis so that we know - // what the exact margins on each side will be. first we split the buffer amount based on the direction - // we're moving, so that we have a larger buffer in the direction of travel. - RectF margins = new RectF(); - margins.left = splitBufferByVelocity(horizontalBuffer, velocity.x); - margins.right = horizontalBuffer - margins.left; - margins.top = splitBufferByVelocity(verticalBuffer, velocity.y); - margins.bottom = verticalBuffer - margins.top; - - // then, we account for running into the page bounds - so that if we hit the top of the page, we need - // to drop the top margin and move that amount to the bottom margin. - margins = shiftMarginsForPageBounds(margins, metrics); - - // finally, we calculate the resolution we want to render the display port area at. We do this - // so that as we expand the display port area (because of velocity), we reduce the resolution of - // the painted area so as to maintain the size of the buffer Gecko is painting into. we calculate - // the reduction in resolution by comparing the display port size with and without the velocity - // changes applied. - // this effectively means that as we pan faster and faster, the display port grows, but we paint - // at lower resolutions. this paints more area to reduce checkerboard at the cost of increasing - // compositor-scaling and blurriness. Once we stop panning, the blurriness must be entirely gone. - // Note that usable* could be less than base* if we are pinch-zoomed out into overscroll, so we - // clamp it to make sure this doesn't increase our display resolution past metrics.zoomFactor. - float scaleFactor = Math.min(reshapedSize.width / usableSize.width, reshapedSize.height / usableSize.height); - float displayResolution = metrics.zoomFactor * Math.min(1.0f, scaleFactor); - - DisplayPortMetrics dpMetrics = new DisplayPortMetrics( - metrics.viewportRectLeft - margins.left, - metrics.viewportRectTop - margins.top, - metrics.viewportRectRight + margins.right, - metrics.viewportRectBottom + margins.bottom, - displayResolution); - return dpMetrics; - } - - /** - * Split the given buffer amount into two based on the velocity. - * Given an amount of total usable buffer on an axis, this will - * return the amount that should be used on the left/top side of - * the axis (the side which a negative velocity vector corresponds - * to). - */ - private float splitBufferByVelocity(float amount, float velocity) { - // if no velocity, so split evenly - if (FloatUtils.fuzzyEquals(velocity, 0)) { - return amount / 2.0f; - } - // if we're moving quickly, assign more of the amount in that direction - // since is is less likely that we will reverse direction immediately - if (velocity < -VELOCITY_FAST_THRESHOLD) { - return amount * FAST_SPLIT_FACTOR; - } - if (velocity > VELOCITY_FAST_THRESHOLD) { - return amount * (1.0f - FAST_SPLIT_FACTOR); - } - // if we're moving slowly, then assign less of the amount in that direction - if (velocity < 0) { - return amount * SLOW_SPLIT_FACTOR; - } else { - return amount * (1.0f - SLOW_SPLIT_FACTOR); - } - } - - @Override - public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) { - // Expand the viewport based on our velocity (and clamp it to page boundaries). - // Then intersect it with the last-requested displayport to determine whether we're - // close to checkerboarding. - - RectF predictedViewport = metrics.getViewport(); - - // first we expand the viewport in the direction we're moving based on some - // multiple of the current velocity. - if (velocity.length() > 0) { - if (velocity.x < 0) { - predictedViewport.left += velocity.x * PREDICTION_VELOCITY_MULTIPLIER; - } else if (velocity.x > 0) { - predictedViewport.right += velocity.x * PREDICTION_VELOCITY_MULTIPLIER; - } - - if (velocity.y < 0) { - predictedViewport.top += velocity.y * PREDICTION_VELOCITY_MULTIPLIER; - } else if (velocity.y > 0) { - predictedViewport.bottom += velocity.y * PREDICTION_VELOCITY_MULTIPLIER; - } - } - - // then we expand the viewport evenly in all directions just to have an extra - // safety zone. this also clamps it to page bounds. - predictedViewport = expandByDangerZone(predictedViewport, DANGER_ZONE_MULTIPLIER, DANGER_ZONE_MULTIPLIER, metrics); - return !displayPort.contains(predictedViewport); - } - - @Override - public String toString() { - return "DynamicResolutionStrategy"; - } - } - - /** - * This class implements the variation where we use the draw time to predict where we will be when - * a draw completes, and draw that instead of where we are now. In this variation, when our panning - * speed drops below a certain threshold, we draw 9 viewports' worth of content so that the user can - * pan in any direction without encountering checkerboarding. - * Once the user is panning, we modify the displayport to encompass an area range of where we think - * the user will be when the draw completes. This heuristic relies on both the estimated draw time - * the panning velocity; unexpected changes in either of these values will cause the heuristic to - * fail and show checkerboard. - */ - private static class PredictionBiasStrategy extends DisplayPortStrategy { - private static float VELOCITY_THRESHOLD; - - private int mPixelArea; // area of the viewport, used in draw time calculations - private int mMinFramesToDraw; // minimum number of frames we take to draw - private int mMaxFramesToDraw; // maximum number of frames we take to draw - - PredictionBiasStrategy(Map<String, Integer> prefs) { - VELOCITY_THRESHOLD = /*GeckoAppShell.getDpi()*/ LOKitShell.getDpi() * getFloatPref(prefs, PREF_DISPLAYPORT_PB_VELOCITY_THRESHOLD, 16); - resetPageState(); - } - - @Override - public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) { - float width = metrics.getWidth(); - float height = metrics.getHeight(); - mPixelArea = (int)(width * height); - - if (velocity.length() < VELOCITY_THRESHOLD) { - // if we're going slow, expand the displayport to 9x viewport size - RectF margins = new RectF(width, height, width, height); - return getTileAlignedDisplayPortMetrics(margins, metrics.zoomFactor, metrics); - } - - // figure out how far we expect to be - float minDx = velocity.x * mMinFramesToDraw; - float minDy = velocity.y * mMinFramesToDraw; - float maxDx = velocity.x * mMaxFramesToDraw; - float maxDy = velocity.y * mMaxFramesToDraw; - - // figure out how many pixels we will be drawing when we draw the above-calculated range. - // this will be larger than the viewport area. - float pixelsToDraw = (width + Math.abs(maxDx - minDx)) * (height + Math.abs(maxDy - minDy)); - // adjust how far we will get because of the time spent drawing all these extra pixels. this - // will again increase the number of pixels drawn so really we could keep iterating this over - // and over, but once seems enough for now. - maxDx = maxDx * pixelsToDraw / mPixelArea; - maxDy = maxDy * pixelsToDraw / mPixelArea; - - // and finally generate the displayport. the min/max stuff takes care of - // negative velocities as well as positive. - RectF margins = new RectF( - -Math.min(minDx, maxDx), - -Math.min(minDy, maxDy), - Math.max(minDx, maxDx), - Math.max(minDy, maxDy)); - return getTileAlignedDisplayPortMetrics(margins, metrics.zoomFactor, metrics); - } - - @Override - public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) { - // the code below is the same as in calculate() but is awkward to refactor since it has multiple outputs. - // refer to the comments in calculate() to understand what this is doing. - float minDx = velocity.x * mMinFramesToDraw; - float minDy = velocity.y * mMinFramesToDraw; - float maxDx = velocity.x * mMaxFramesToDraw; - float maxDy = velocity.y * mMaxFramesToDraw; - float pixelsToDraw = (metrics.getWidth() + Math.abs(maxDx - minDx)) * (metrics.getHeight() + Math.abs(maxDy - minDy)); - maxDx = maxDx * pixelsToDraw / mPixelArea; - maxDy = maxDy * pixelsToDraw / mPixelArea; - - // now that we have an idea of how far we will be when the draw completes, take the farthest - // end of that range and see if it falls outside the displayport bounds. if it does, allow - // the draw to go through - RectF predictedViewport = metrics.getViewport(); - predictedViewport.left += maxDx; - predictedViewport.top += maxDy; - predictedViewport.right += maxDx; - predictedViewport.bottom += maxDy; - - predictedViewport = clampToPageBounds(predictedViewport, metrics); - return !displayPort.contains(predictedViewport); - } - - @Override - public boolean drawTimeUpdate(long millis, int pixels) { - // calculate the number of frames it took to draw a viewport-sized area - float normalizedTime = (float)mPixelArea * (float)millis / (float)pixels; - int normalizedFrames = (int)FloatMath.ceil(normalizedTime * 60f / 1000f); - // broaden our range on how long it takes to draw if the draw falls outside - // the range. this allows it to grow gradually. this heuristic may need to - // be tweaked into more of a floating window average or something. - if (normalizedFrames <= mMinFramesToDraw) { - mMinFramesToDraw--; - } else if (normalizedFrames > mMaxFramesToDraw) { - mMaxFramesToDraw++; - } else { - return true; - } - Log.d(LOGTAG, "Widened draw range to [" + mMinFramesToDraw + ", " + mMaxFramesToDraw + "]"); - return true; - } - - @Override - public void resetPageState() { - mMinFramesToDraw = 0; - mMaxFramesToDraw = 2; - } - - @Override - public String toString() { - return "PredictionBiasStrategy threshold=" + VELOCITY_THRESHOLD; - } - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java deleted file mode 100644 index 741136f90a53..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/DisplayPortMetrics.java +++ /dev/null @@ -1,78 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -//import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI; -import org.mozilla.gecko.util.FloatUtils; - -import android.graphics.RectF; - -/* - * This class keeps track of the area we request Gecko to paint, as well - * as the resolution of the paint. The area may be different from the visible - * area of the page, and the resolution may be different from the resolution - * used in the compositor to render the page. This is so that we can ask Gecko - * to paint a much larger area without using extra memory, and then render some - * subsection of that with compositor scaling. - */ -public final class DisplayPortMetrics { - //@WrapElementForJNI - public final float resolution; - //@WrapElementForJNI - private final RectF mPosition; - - public DisplayPortMetrics() { - this(0, 0, 0, 0, 1); - } - - //@WrapElementForJNI - public DisplayPortMetrics(float left, float top, float right, float bottom, float resolution) { - this.resolution = resolution; - mPosition = new RectF(left, top, right, bottom); - } - - public float getLeft() { - return mPosition.left; - } - - public float getTop() { - return mPosition.top; - } - - public float getRight() { - return mPosition.right; - } - - public float getBottom() { - return mPosition.bottom; - } - - public boolean contains(RectF rect) { - return mPosition.contains(rect); - } - - public boolean fuzzyEquals(DisplayPortMetrics metrics) { - return RectUtils.fuzzyEquals(mPosition, metrics.mPosition) - && FloatUtils.fuzzyEquals(resolution, metrics.resolution); - } - - public String toJSON() { - StringBuilder sb = new StringBuilder(256); - sb.append("{ \"left\": ").append(mPosition.left) - .append(", \"top\": ").append(mPosition.top) - .append(", \"right\": ").append(mPosition.right) - .append(", \"bottom\": ").append(mPosition.bottom) - .append(", \"resolution\": ").append(resolution) - .append('}'); - return sb.toString(); - } - - @Override - public String toString() { - return "DisplayPortMetrics v=(" + mPosition.left + "," + mPosition.top + "," + mPosition.right + "," - + mPosition.bottom + ") z=" + resolution; - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/DrawTimingQueue.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/DrawTimingQueue.java deleted file mode 100644 index ce868f18ce2f..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/DrawTimingQueue.java +++ /dev/null @@ -1,95 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import android.os.SystemClock; - -/** - * A custom-built data structure to assist with measuring draw times. - * - * This class maintains a fixed-size circular buffer of DisplayPortMetrics - * objects and associated timestamps. It provides only three operations, which - * is all we require for our purposes of measuring draw times. Note - * in particular that the class is designed so that even though it is - * accessed from multiple threads, it does not require synchronization; - * any concurrency errors that result from this are handled gracefully. - * - * Assuming an unrolled buffer so that mTail is greater than mHead, the data - * stored in the buffer at entries [mHead, mTail) will never be modified, and - * so are "safe" to read. If this reading is done on the same thread that - * owns mHead, then reading the range [mHead, mTail) is guaranteed to be safe - * since the range itself will not shrink. - */ -final class DrawTimingQueue { - private static final String LOGTAG = "GeckoDrawTimingQueue"; - private static final int BUFFER_SIZE = 16; - - private final DisplayPortMetrics[] mMetrics; - private final long[] mTimestamps; - - private int mHead; - private int mTail; - - DrawTimingQueue() { - mMetrics = new DisplayPortMetrics[BUFFER_SIZE]; - mTimestamps = new long[BUFFER_SIZE]; - mHead = BUFFER_SIZE - 1; - mTail = 0; - } - - /** - * Add a new entry to the tail of the queue. If the buffer is full, - * do nothing. This must only be called from the Java UI thread. - */ - boolean add(DisplayPortMetrics metrics) { - if (mHead == mTail) { - return false; - } - mMetrics[mTail] = metrics; - mTimestamps[mTail] = SystemClock.uptimeMillis(); - mTail = (mTail + 1) % BUFFER_SIZE; - return true; - } - - /** - * Find the timestamp associated with the given metrics, AND remove - * all metrics objects from the start of the queue up to and including - * the one provided. Note that because of draw coalescing, the metrics - * object passed in here may not be the one at the head of the queue, - * and so we must iterate our way through the list to find it. - * This must only be called from the compositor thread. - */ - long findTimeFor(DisplayPortMetrics metrics) { - // keep a copy of the tail pointer so that we ignore new items - // added to the queue while we are searching. this is fine because - // the one we are looking for will either have been added already - // or will not be in the queue at all. - int tail = mTail; - // walk through the "safe" range from mHead to tail; these entries - // will not be modified unless we change mHead. - int i = (mHead + 1) % BUFFER_SIZE; - while (i != tail) { - if (mMetrics[i].fuzzyEquals(metrics)) { - // found it, copy out the timestamp to a local var BEFORE - // changing mHead or add could clobber the timestamp. - long timestamp = mTimestamps[i]; - mHead = i; - return timestamp; - } - i = (i + 1) % BUFFER_SIZE; - } - return -1; - } - - /** - * Reset the buffer to empty. - * This must only be called from the compositor thread. - */ - void reset() { - // we can only modify mHead on this thread. - mHead = (mTail + BUFFER_SIZE - 1) % BUFFER_SIZE; - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/FloatSize.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/FloatSize.java deleted file mode 100644 index 4b495ab77ecc..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/FloatSize.java +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import org.mozilla.gecko.util.FloatUtils; - -import org.json.JSONException; -import org.json.JSONObject; - -public class FloatSize { - public final float width, height; - - public FloatSize(FloatSize size) { width = size.width; height = size.height; } - public FloatSize(IntSize size) { width = size.width; height = size.height; } - public FloatSize(float aWidth, float aHeight) { width = aWidth; height = aHeight; } - - public FloatSize(JSONObject json) { - try { - width = (float)json.getDouble("width"); - height = (float)json.getDouble("height"); - } catch (JSONException e) { - throw new RuntimeException(e); - } - } - - @Override - public String toString() { return "(" + width + "," + height + ")"; } - - public boolean isPositive() { - return (width > 0 && height > 0); - } - - public boolean fuzzyEquals(FloatSize size) { - return (FloatUtils.fuzzyEquals(size.width, width) && - FloatUtils.fuzzyEquals(size.height, height)); - } - - public FloatSize scale(float factor) { - return new FloatSize(width * factor, height * factor); - } - - /* - * Returns the size that represents a linear transition between this size and `to` at time `t`, - * which is on the scale [0, 1). - */ - public FloatSize interpolate(FloatSize to, float t) { - return new FloatSize(FloatUtils.interpolate(width, to.width, t), - FloatUtils.interpolate(height, to.height, t)); - } -} - diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/GLController.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/GLController.java deleted file mode 100644 index be6118894e6a..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/GLController.java +++ /dev/null @@ -1,354 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -//import org.mozilla.gecko.GeckoAppShell; -//import org.mozilla.gecko.GeckoEvent; -//import org.mozilla.gecko.GeckoThread; -//import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI; -import org.libreoffice.LOKitShell; -import org.mozilla.gecko.util.ThreadUtils; - -import android.util.Log; - -import javax.microedition.khronos.egl.EGL10; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.egl.EGLContext; -import javax.microedition.khronos.egl.EGLDisplay; -import javax.microedition.khronos.egl.EGLSurface; - -/** - * EGLPreloadingThread is purely a preloading optimization, not something - * we rely on for anything else than performance. We will be initializing - * EGL in GLController::initEGL() when we need it, but having EGL initialization - * already previously done by EGLPreloadingThread::run() will make it much - * faster for GLController to do again. - * - * For example, here are some timings recorded on two devices: - * - * Device | EGLPreloadingThread::run() | GLController::initEGL() - * -----------------------+----------------------------+------------------------ - * Nexus S (Android 2.3) | ~ 80 ms | < 0.1 ms - * Nexus 10 (Android 4.3) | ~ 35 ms | < 0.1 ms - */ -class EGLPreloadingThread extends Thread -{ - private static final String LOGTAG = "EGLPreloadingThread"; - private EGL10 mEGL; - private EGLDisplay mEGLDisplay; - - public EGLPreloadingThread() - { - } - - @Override - public void run() - { - mEGL = (EGL10)EGLContext.getEGL(); - mEGLDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); - if (mEGLDisplay == EGL10.EGL_NO_DISPLAY) { - Log.w(LOGTAG, "Can't get EGL display!"); - return; - } - - int[] returnedVersion = new int[2]; - if (!mEGL.eglInitialize(mEGLDisplay, returnedVersion)) { - Log.w(LOGTAG, "eglInitialize failed"); - return; - } - } -} - -/** - * This class is a singleton that tracks EGL and compositor things over - * the lifetime of Fennec running. - * We only ever create one C++ compositor over Fennec's lifetime, but - * most of the Java-side objects (e.g. LayerView, GeckoLayerClient, - * LayerRenderer) can all get destroyed and re-created if the GeckoApp - * activity is destroyed. This GLController is never destroyed, so that - * the mCompositorCreated field and other state variables are always - * accurate. - */ -public class GLController { - private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; - private static final String LOGTAG = "GeckoGLController"; - - private static GLController sInstance; - - private LayerView mView; - private boolean mServerSurfaceValid; - private int mWidth, mHeight; - - /* This is written by the compositor thread (while the UI thread - * is blocked on it) and read by the UI thread. */ - private volatile boolean mCompositorCreated; - - private EGL10 mEGL; - private EGLDisplay mEGLDisplay; - private EGLConfig mEGLConfig; - private EGLPreloadingThread mEGLPreloadingThread; - private EGLSurface mEGLSurfaceForCompositor; - - private static final int LOCAL_EGL_OPENGL_ES2_BIT = 4; - - private static final int[] CONFIG_SPEC_16BPP = { - EGL10.EGL_RED_SIZE, 5, - EGL10.EGL_GREEN_SIZE, 6, - EGL10.EGL_BLUE_SIZE, 5, - EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT, - EGL10.EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT, - EGL10.EGL_NONE - }; - - private static final int[] CONFIG_SPEC_24BPP = { - EGL10.EGL_RED_SIZE, 8, - EGL10.EGL_GREEN_SIZE, 8, - EGL10.EGL_BLUE_SIZE, 8, - EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT, - EGL10.EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT, - EGL10.EGL_NONE - }; - - private GLController() { - mEGLPreloadingThread = new EGLPreloadingThread(); - mEGLPreloadingThread.start(); - } - - static GLController getInstance(LayerView view) { - if (sInstance == null) { - sInstance = new GLController(); - } - sInstance.mView = view; - return sInstance; - } - - synchronized void serverSurfaceDestroyed() { - ThreadUtils.assertOnUiThread(); - Log.w(LOGTAG, "GLController::serverSurfaceDestroyed() with mCompositorCreated=" + mCompositorCreated); - - mServerSurfaceValid = false; - - if (mEGLSurfaceForCompositor != null) { - mEGL.eglDestroySurface(mEGLDisplay, mEGLSurfaceForCompositor); - mEGLSurfaceForCompositor = null; - } - - // We need to coordinate with Gecko when pausing composition, to ensure - // that Gecko never executes a draw event while the compositor is paused. - // This is sent synchronously to make sure that we don't attempt to use - // any outstanding Surfaces after we call this (such as from a - // serverSurfaceDestroyed notification), and to make sure that any in-flight - // Gecko draw events have been processed. When this returns, composition is - // definitely paused -- it'll synchronize with the Gecko event loop, which - // in turn will synchronize with the compositor thread. - if (mCompositorCreated) { - //GeckoAppShell.sendEventToGeckoSync(GeckoEvent.createCompositorPauseEvent()); - } - Log.w(LOGTAG, "done GLController::serverSurfaceDestroyed()"); - } - - synchronized void serverSurfaceChanged(int newWidth, int newHeight) { - ThreadUtils.assertOnUiThread(); - Log.w(LOGTAG, "GLController::serverSurfaceChanged(" + newWidth + ", " + newHeight + ")"); - - mWidth = newWidth; - mHeight = newHeight; - mServerSurfaceValid = true; - - // we defer to a runnable the task of updating the compositor, because this is going to - // call back into createEGLSurfaceForCompositor, which will try to create an EGLSurface - // against mView, which we suspect might fail if called too early. By posting this to - // mView, we hope to ensure that it is deferred until mView is actually "ready" for some - // sense of "ready". - mView.post(new Runnable() { - @Override - public void run() { - updateCompositor(); - } - }); - } - - void updateCompositor() { - ThreadUtils.assertOnUiThread(); - Log.w(LOGTAG, "GLController::updateCompositor with mCompositorCreated=" + mCompositorCreated); - - if (mCompositorCreated) { - // If the compositor has already been created, just resume it instead. We don't need - // to block here because if the surface is destroyed before the compositor grabs it, - // we can handle that gracefully (i.e. the compositor will remain paused). - resumeCompositor(mWidth, mHeight); - Log.w(LOGTAG, "done GLController::updateCompositor with compositor resume"); - return; - } - - if (!AttemptPreallocateEGLSurfaceForCompositor()) { - return; - } - - // Only try to create the compositor if we have a valid surface and gecko is up. When these - // two conditions are satisfied, we can be relatively sure that the compositor creation will - // happen without needing to block anywhere. Do it with a sync gecko event so that the - // android doesn't have a chance to destroy our surface in between. - /*if (GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) { - GeckoAppShell.sendEventToGeckoSync(GeckoEvent.createCompositorCreateEvent(mWidth, mHeight)); - }*/ - Log.w(LOGTAG, "done GLController::updateCompositor"); - } - - void compositorCreated() { - Log.w(LOGTAG, "GLController::compositorCreated"); - // This is invoked on the compositor thread, while the java UI thread - // is blocked on the gecko sync event in updateCompositor() above - mCompositorCreated = true; - } - - public boolean isServerSurfaceValid() { - return mServerSurfaceValid; - } - - public boolean isCompositorCreated() { - return mCompositorCreated; - } - - private void initEGL() { - if (mEGL != null) { - return; - } - - // This join() should not be necessary, but makes this code a bit easier to think about. - // The EGLPreloadingThread should long be done by now, and even if it's not, - // it shouldn't be a problem to be initalizing EGL from two different threads. - // Still, having this join() here means that we don't have to wonder about what - // kind of caveats might exist with EGL initialization reentrancy on various drivers. - try { - mEGLPreloadingThread.join(); - } catch (InterruptedException e) { - Log.w(LOGTAG, "EGLPreloadingThread interrupted", e); - } - - mEGL = (EGL10)EGLContext.getEGL(); - - mEGLDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); - if (mEGLDisplay == EGL10.EGL_NO_DISPLAY) { - Log.w(LOGTAG, "Can't get EGL display!"); - return; - } - - // while calling eglInitialize here should not be necessary as it was already called - // by the EGLPreloadingThread, it really doesn't cost much to call it again here, - // and makes this code easier to think about: EGLPreloadingThread is only a - // preloading optimization, not something we rely on for anything else. - // - // Also note that while calling eglInitialize isn't necessary on Android 4.x - // (at least Android's HardwareRenderer does it for us already), it is necessary - // on Android 2.x. - int[] returnedVersion = new int[2]; - if (!mEGL.eglInitialize(mEGLDisplay, returnedVersion)) { - Log.w(LOGTAG, "eglInitialize failed"); - return; - } - - mEGLConfig = chooseConfig(); - } - - private EGLConfig chooseConfig() { - int[] desiredConfig; - int rSize, gSize, bSize; - int[] numConfigs = new int[1]; - - switch (/*GeckoAppShell*/LOKitShell.getScreenDepth()) { - case 24: - desiredConfig = CONFIG_SPEC_24BPP; - rSize = gSize = bSize = 8; - break; - case 16: - default: - desiredConfig = CONFIG_SPEC_16BPP; - rSize = 5; gSize = 6; bSize = 5; - break; - } - - if (!mEGL.eglChooseConfig(mEGLDisplay, desiredConfig, null, 0, numConfigs) || - numConfigs[0] <= 0) { - throw new GLControllerException("No available EGL configurations " + - getEGLError()); - } - - EGLConfig[] configs = new EGLConfig[numConfigs[0]]; - if (!mEGL.eglChooseConfig(mEGLDisplay, desiredConfig, configs, numConfigs[0], numConfigs)) { - throw new GLControllerException("No EGL configuration for that specification " + - getEGLError()); - } - - // Select the first configuration that matches the screen depth. - int[] red = new int[1], green = new int[1], blue = new int[1]; - for (EGLConfig config : configs) { - mEGL.eglGetConfigAttrib(mEGLDisplay, config, EGL10.EGL_RED_SIZE, red); - mEGL.eglGetConfigAttrib(mEGLDisplay, config, EGL10.EGL_GREEN_SIZE, green); - mEGL.eglGetConfigAttrib(mEGLDisplay, config, EGL10.EGL_BLUE_SIZE, blue); - if (red[0] == rSize && green[0] == gSize && blue[0] == bSize) { - return config; - } - } - - throw new GLControllerException("No suitable EGL configuration found"); - } - - private synchronized boolean AttemptPreallocateEGLSurfaceForCompositor() { - if (mEGLSurfaceForCompositor == null) { - initEGL(); - try { - mEGLSurfaceForCompositor = mEGL.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mView.getNativeWindow(), null); - // In failure cases, eglCreateWindowSurface should return EGL_NO_SURFACE. - // We currently normalize this to null, and compare to null in all our checks. - if (mEGLSurfaceForCompositor == EGL10.EGL_NO_SURFACE) { - mEGLSurfaceForCompositor = null; - } - } catch (Exception e) { - Log.e(LOGTAG, "eglCreateWindowSurface threw", e); - } - } - if (mEGLSurfaceForCompositor == null) { - Log.w(LOGTAG, "eglCreateWindowSurface returned no surface!"); - } - return mEGLSurfaceForCompositor != null; - } - - // @WrapElementForJNI(allowMultithread = true, stubName = "CreateEGLSurfaceForCompositorWrapper") - private synchronized EGLSurface createEGLSurfaceForCompositor() { - AttemptPreallocateEGLSurfaceForCompositor(); - EGLSurface result = mEGLSurfaceForCompositor; - mEGLSurfaceForCompositor = null; - return result; - } - - private String getEGLError() { - return "Error " + (mEGL == null ? "(no mEGL)" : mEGL.eglGetError()); - } - - void resumeCompositor(int width, int height) { - Log.w(LOGTAG, "GLController::resumeCompositor(" + width + ", " + height + ") and mCompositorCreated=" + mCompositorCreated); - // Asking Gecko to resume the compositor takes too long (see - // https://bugzilla.mozilla.org/show_bug.cgi?id=735230#c23), so we - // resume the compositor directly. We still need to inform Gecko about - // the compositor resuming, so that Gecko knows that it can now draw. - // It is important to not notify Gecko until after the compositor has - // been resumed, otherwise Gecko may send updates that get dropped. - if (mCompositorCreated) { - //GeckoAppShell.scheduleResumeComposition(width, height); - //GeckoAppShell.sendEventToGecko(GeckoEvent.createCompositorResumeEvent()); - } - Log.w(LOGTAG, "done GLController::resumeCompositor"); - } - - public static class GLControllerException extends RuntimeException { - public static final long serialVersionUID = 1L; - - GLControllerException(String e) { - super(e); - } - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java deleted file mode 100644 index 049b1fe59564..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java +++ /dev/null @@ -1,1000 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -//import org.mozilla.gecko.GeckoAppShell; -//import org.mozilla.gecko.GeckoEvent; -//import org.mozilla.gecko.Tab; -//import org.mozilla.gecko.Tabs; -import org.mozilla.gecko.ZoomConstraints; -//import org.mozilla.gecko.mozglue.RobocopTarget; -//import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI; -import org.mozilla.gecko.util.EventDispatcher; -import org.mozilla.gecko.util.FloatUtils; - -import android.content.Context; -import android.graphics.PointF; -import android.graphics.RectF; -import android.os.SystemClock; -import android.util.DisplayMetrics; -import android.util.Log; - -public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget -{ - private static final String LOGTAG = "GeckoLayerClient"; - - private LayerRenderer mLayerRenderer; - private boolean mLayerRendererInitialized; - - private Context mContext; - private IntSize mScreenSize; - private IntSize mWindowSize; - private DisplayPortMetrics mDisplayPort; - - private boolean mRecordDrawTimes; - private final DrawTimingQueue mDrawTimingQueue; - - private VirtualLayer mRootLayer; - - /* The Gecko viewport as per the UI thread. Must be touched only on the UI thread. - * If any events being sent to Gecko that are relative to the Gecko viewport position, - * they must (a) be relative to this viewport, and (b) be sent on the UI thread to - * avoid races. As long as these two conditions are satisfied, and the events being - * sent to Gecko are processed in FIFO order, the events will properly be relative - * to the Gecko viewport position. Note that if Gecko updates its viewport independently, - * we get notified synchronously and also update this on the UI thread. - */ - private ImmutableViewportMetrics mGeckoViewport; - - /* - * The viewport metrics being used to draw the current frame. This is only - * accessed by the compositor thread, and so needs no synchronisation. - */ - private ImmutableViewportMetrics mFrameMetrics; - - private DrawListener mDrawListener; - - /* Used as temporaries by syncViewportInfo */ - private final ViewTransform mCurrentViewTransform; - private final RectF mCurrentViewTransformMargins; - - /* Used as the return value of progressiveUpdateCallback */ - private final ProgressiveUpdateData mProgressiveUpdateData; - private DisplayPortMetrics mProgressiveUpdateDisplayPort; - private boolean mLastProgressiveUpdateWasLowPrecision; - private boolean mProgressiveUpdateWasInDanger; - - private boolean mForceRedraw; - - /* The current viewport metrics. - * This is volatile so that we can read and write to it from different threads. - * We avoid synchronization to make getting the viewport metrics from - * the compositor as cheap as possible. The viewport is immutable so - * we don't need to worry about anyone mutating it while we're reading from it. - * Specifically: - * 1) reading mViewportMetrics from any thread is fine without synchronization - * 2) writing to mViewportMetrics requires synchronizing on the layer controller object - * 3) whenver reading multiple fields from mViewportMetrics without synchronization (i.e. in - * case 1 above) you should always frist grab a local copy of the reference, and then use - * that because mViewportMetrics might get reassigned in between reading the different - * fields. */ - private volatile ImmutableViewportMetrics mViewportMetrics; - private OnMetricsChangedListener mViewportChangeListener; - - private ZoomConstraints mZoomConstraints; - - private boolean mGeckoIsReady; - - private final PanZoomController mPanZoomController; - private final LayerMarginsAnimator mMarginsAnimator; - private LayerView mView; - - /* This flag is true from the time that browser.js detects a first-paint is about to start, - * to the time that we receive the first-paint composite notification from the compositor. - * Note that there is a small race condition with this; if there are two paints that both - * have the first-paint flag set, and the second paint happens concurrently with the - * composite for the first paint, then this flag may be set to true prematurely. Fixing this - * is possible but risky; see https://bugzilla.mozilla.org/show_bug.cgi?id=797615#c751 - */ - private volatile boolean mContentDocumentIsDisplayed; - - public GeckoLayerClient(Context context, LayerView view, EventDispatcher eventDispatcher) { - // we can fill these in with dummy values because they are always written - // to before being read - mContext = context; - mScreenSize = new IntSize(0, 0); - mWindowSize = new IntSize(0, 0); - mDisplayPort = new DisplayPortMetrics(); - mRecordDrawTimes = true; - mDrawTimingQueue = new DrawTimingQueue(); - mCurrentViewTransform = new ViewTransform(0, 0, 1); - mCurrentViewTransformMargins = new RectF(); - mProgressiveUpdateData = new ProgressiveUpdateData(); - mProgressiveUpdateDisplayPort = new DisplayPortMetrics(); - mLastProgressiveUpdateWasLowPrecision = false; - mProgressiveUpdateWasInDanger = false; - - mForceRedraw = true; - DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); - mViewportMetrics = new ImmutableViewportMetrics(displayMetrics) - .setViewportSize(view.getWidth(), view.getHeight()); - mZoomConstraints = new ZoomConstraints(false); - - /*Tab tab = Tabs.getInstance().getSelectedTab(); - if (tab != null) { - mZoomConstraints = tab.getZoomConstraints(); - mViewportMetrics = mViewportMetrics.setIsRTL(tab.getIsRTL()); - }*/ - - mFrameMetrics = mViewportMetrics; - - mPanZoomController = PanZoomController.Factory.create(this, view, eventDispatcher); - mMarginsAnimator = new LayerMarginsAnimator(this, view); - mView = view; - mView.setListener(this); - mContentDocumentIsDisplayed = true; - } - - public void setOverscrollHandler(final Overscroll listener) { - mPanZoomController.setOverscrollHandler(listener); - } - - /** Attaches to root layer so that Gecko appears. */ - public void notifyGeckoReady() { - mGeckoIsReady = true; - - mRootLayer = new VirtualLayer(new IntSize(mView.getWidth(), mView.getHeight())); - mLayerRenderer = mView.getRenderer(); - - sendResizeEventIfNecessary(true); - - DisplayPortCalculator.initPrefs(); - - // Gecko being ready is one of the two conditions (along with having an available - // surface) that cause us to create the compositor. So here, now that we know gecko - // is ready, call updateCompositor() to see if we can actually do the creation. - // This needs to run on the UI thread so that the surface validity can't change on - // us while we're in the middle of creating the compositor. - mView.post(new Runnable() { - @Override - public void run() { - mView.getGLController().updateCompositor(); - } - }); - } - - public void destroy() { - mPanZoomController.destroy(); - mMarginsAnimator.destroy(); - } - - /** - * Returns true if this client is fine with performing a redraw operation or false if it - * would prefer that the action didn't take place. - */ - private boolean getRedrawHint() { - if (mForceRedraw) { - mForceRedraw = false; - return true; - } - - if (!mPanZoomController.getRedrawHint()) { - return false; - } - - return DisplayPortCalculator.aboutToCheckerboard(mViewportMetrics, - mPanZoomController.getVelocityVector(), mDisplayPort); - } - - Layer getRoot() { - return mGeckoIsReady ? mRootLayer : null; - } - - public LayerView getView() { - return mView; - } - - public FloatSize getViewportSize() { - return mViewportMetrics.getSize(); - } - - /** - * The view calls this function to indicate that the viewport changed size. It must hold the - * monitor while calling it. - * - * TODO: Refactor this to use an interface. Expose that interface only to the view and not - * to the layer client. That way, the layer client won't be tempted to call this, which might - * result in an infinite loop. - */ - void setViewportSize(int width, int height) { - mViewportMetrics = mViewportMetrics.setViewportSize(width, height); - - if (mGeckoIsReady) { - // here we send gecko a resize message. The code in browser.js is responsible for - // picking up on that resize event, modifying the viewport as necessary, and informing - // us of the new viewport. - sendResizeEventIfNecessary(true); - // the following call also sends gecko a message, which will be processed after the resize - // message above has updated the viewport. this message ensures that if we have just put - // focus in a text field, we scroll the content so that the text field is in view. - - //GeckoAppShell.viewSizeChanged(); - } - } - - PanZoomController getPanZoomController() { - return mPanZoomController; - } - - LayerMarginsAnimator getLayerMarginsAnimator() { - return mMarginsAnimator; - } - - /* Informs Gecko that the screen size has changed. */ - private void sendResizeEventIfNecessary(boolean force) { - DisplayMetrics metrics = mContext.getResources().getDisplayMetrics(); - - IntSize newScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels); - IntSize newWindowSize = new IntSize(mView.getWidth(), mView.getHeight()); - - boolean screenSizeChanged = !mScreenSize.equals(newScreenSize); - boolean windowSizeChanged = !mWindowSize.equals(newWindowSize); - - if (!force && !screenSizeChanged && !windowSizeChanged) { - return; - } - - mScreenSize = newScreenSize; - mWindowSize = newWindowSize; - - if (screenSizeChanged) { - Log.d(LOGTAG, "Screen-size changed to " + mScreenSize); - } - - if (windowSizeChanged) { - Log.d(LOGTAG, "Window-size changed to " + mWindowSize); - } - - /*GeckoEvent event = GeckoEvent.createSizeChangedEvent(mWindowSize.width, mWindowSize.height, - mScreenSize.width, mScreenSize.height); - GeckoAppShell.sendEventToGecko(event); - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Window:Resize", ""));*/ - } - - /** Sets the current page rect. You must hold the monitor while calling this. */ - private void setPageRect(RectF rect, RectF cssRect) { - // Since the "rect" is always just a multiple of "cssRect" we don't need to - // check both; this function assumes that both "rect" and "cssRect" are relative - // the zoom factor in mViewportMetrics. - if (mViewportMetrics.getCssPageRect().equals(cssRect)) - return; - - mViewportMetrics = mViewportMetrics.setPageRect(rect, cssRect); - - // Page size is owned by the layer client, so no need to notify it of - // this change. - - post(new Runnable() { - @Override - public void run() { - mPanZoomController.pageRectUpdated(); - mView.requestRender(); - } - }); - } - - /** - * Derives content document fixed position margins/fixed layer margins from - * the view margins in the given metrics object. - */ - private void getFixedMargins(ImmutableViewportMetrics metrics, RectF fixedMargins) { - fixedMargins.left = 0; - fixedMargins.top = 0; - fixedMargins.right = 0; - fixedMargins.bottom = 0; - - // The maximum margins are determined by the scrollable area of the page. - float maxMarginWidth = Math.max(0, metrics.getPageWidth() - metrics.getWidthWithoutMargins()); - float maxMarginHeight = Math.max(0, metrics.getPageHeight() - metrics.getHeightWithoutMargins()); - - // If the margins can't fully hide, they're pinned on - in which case, - // fixed margins should always be zero. - if (maxMarginWidth < metrics.marginLeft + metrics.marginRight) { - maxMarginWidth = 0; - } - if (maxMarginHeight < metrics.marginTop + metrics.marginBottom) { - maxMarginHeight = 0; - } - - PointF offset = metrics.getMarginOffset(); - RectF overscroll = metrics.getOverscroll(); - if (offset.x >= 0) { - fixedMargins.right = Math.max(0, Math.min(offset.x - overscroll.right, maxMarginWidth)); - } else { - fixedMargins.left = Math.max(0, Math.min(-offset.x - overscroll.left, maxMarginWidth)); - } - if (offset.y >= 0) { - fixedMargins.bottom = Math.max(0, Math.min(offset.y - overscroll.bottom, maxMarginHeight)); - } else { - fixedMargins.top = Math.max(0, Math.min(-offset.y - overscroll.top, maxMarginHeight)); - } - - // Adjust for overscroll. If we're overscrolled on one side, add that - // distance to the margins of the other side (limiting to the maximum - // margin size calculated above). - if (overscroll.left > 0) { - fixedMargins.right = Math.min(maxMarginWidth - fixedMargins.left, - fixedMargins.right + overscroll.left); - } else if (overscroll.right > 0) { - fixedMargins.left = Math.min(maxMarginWidth - fixedMargins.right, - fixedMargins.left + overscroll.right); - } - if (overscroll.top > 0) { - fixedMargins.bottom = Math.min(maxMarginHeight - fixedMargins.top, - fixedMargins.bottom + overscroll.top); - } else if (overscroll.bottom > 0) { - fixedMargins.top = Math.min(maxMarginHeight - fixedMargins.bottom, - fixedMargins.top + overscroll.bottom); - } - } - - private void adjustViewport(DisplayPortMetrics displayPort) { - ImmutableViewportMetrics metrics = getViewportMetrics(); - ImmutableViewportMetrics clampedMetrics = metrics.clamp(); - - RectF margins = new RectF(); - getFixedMargins(metrics, margins); - clampedMetrics = clampedMetrics.setMargins( - margins.left, margins.top, margins.right, margins.bottom); - - if (displayPort == null) { - displayPort = DisplayPortCalculator.calculate(metrics, mPanZoomController.getVelocityVector()); - } - - mDisplayPort = displayPort; - mGeckoViewport = clampedMetrics; - - if (mRecordDrawTimes) { - mDrawTimingQueue.add(displayPort); - } - - //GeckoAppShell.sendEventToGecko(GeckoEvent.createViewportEvent(clampedMetrics, displayPort)); - } - - /** Aborts any pan/zoom animation that is currently in progress. */ - private void abortPanZoomAnimation() { - if (mPanZoomController != null) { - post(new Runnable() { - @Override - public void run() { - mPanZoomController.abortAnimation(); - } - }); - } - } - - /** - * The different types of Viewport messages handled. All viewport events - * expect a display-port to be returned, but can handle one not being - * returned. - */ - private enum ViewportMessageType { - UPDATE, // The viewport has changed and should be entirely updated - PAGE_SIZE // The viewport's page-size has changed - } - - /** Viewport message handler. */ - private DisplayPortMetrics handleViewportMessage(ImmutableViewportMetrics messageMetrics, ViewportMessageType type) { - synchronized (getLock()) { - ImmutableViewportMetrics newMetrics; - ImmutableViewportMetrics oldMetrics = getViewportMetrics(); - - switch (type) { - default: - case UPDATE: - // Keep the old viewport size - newMetrics = messageMetrics.setViewportSize(oldMetrics.getWidth(), oldMetrics.getHeight()); - if (!oldMetrics.fuzzyEquals(newMetrics)) { - abortPanZoomAnimation(); - } - break; - case PAGE_SIZE: - // adjust the page dimensions to account for differences in zoom - // between the rendered content (which is what Gecko tells us) - // and our zoom level (which may have diverged). - float scaleFactor = oldMetrics.zoomFactor / messageMetrics.zoomFactor; - newMetrics = oldMetrics.setPageRect(RectUtils.scale(messageMetrics.getPageRect(), scaleFactor), messageMetrics.getCssPageRect()); - break; - } - - // Update the Gecko-side viewport metrics. Make sure to do this - // before modifying the metrics below. - final ImmutableViewportMetrics geckoMetrics = newMetrics.clamp(); - post(new Runnable() { - @Override - public void run() { - mGeckoViewport = geckoMetrics; - } - }); - - setViewportMetrics(newMetrics, type == ViewportMessageType.UPDATE); - mDisplayPort = DisplayPortCalculator.calculate(getViewportMetrics(), null); - } - return mDisplayPort; - } - - //@WrapElementForJNI - DisplayPortMetrics getDisplayPort(boolean pageSizeUpdate, boolean isBrowserContentDisplayed, int tabId, ImmutableViewportMetrics metrics) { - /**Tabs tabs = Tabs.getInstance(); - if (isBrowserContentDisplayed && tabs.isSelectedTabId(tabId)) { - // for foreground tabs, send the viewport update unless the document - // displayed is different from the content document. In that case, just - // calculate the display port. - return handleViewportMessage(metrics, pageSizeUpdate ? ViewportMessageType.PAGE_SIZE : ViewportMessageType.UPDATE); - } else*/ { - // for background tabs, request a new display port calculation, so that - // when we do switch to that tab, we have the correct display port and - // don't need to draw twice (once to allow the first-paint viewport to - // get to java, and again once java figures out the display port). - return DisplayPortCalculator.calculate(metrics, null); - } - } - - //@WrapElementForJNI - void contentDocumentChanged() { - mContentDocumentIsDisplayed = false; - } - - //@WrapElementForJNI - boolean isContentDocumentDisplayed() { - return mContentDocumentIsDisplayed; - } - - // This is called on the Gecko thread to determine if we're still interested - // in the update of this display-port to continue. We can return true here - // to abort the current update and continue with any subsequent ones. This - // is useful for slow-to-render pages when the display-port starts lagging - // behind enough that continuing to draw it is wasted effort. - //@WrapElementForJNI(allowMultithread = true) - public ProgressiveUpdateData progressiveUpdateCallback(boolean aHasPendingNewThebesContent, - float x, float y, float width, float height, - float resolution, boolean lowPrecision) { - // Reset the checkerboard risk flag when switching to low precision - // rendering. - if (lowPrecision && !mLastProgressiveUpdateWasLowPrecision) { - // Skip low precision rendering until we're at risk of checkerboarding. - if (!mProgressiveUpdateWasInDanger) { - mProgressiveUpdateData.abort = true; - return mProgressiveUpdateData; - } - mProgressiveUpdateWasInDanger = false; - } - mLastProgressiveUpdateWasLowPrecision = lowPrecision; - - // Grab a local copy of the last display-port sent to Gecko and the - // current viewport metrics to avoid races when accessing them. - DisplayPortMetrics displayPort = mDisplayPort; - ImmutableViewportMetrics viewportMetrics = mViewportMetrics; - mProgressiveUpdateData.setViewport(viewportMetrics); - mProgressiveUpdateData.abort = false; - - // Always abort updates if the resolution has changed. There's no use - // in drawing at the incorrect resolution. - if (!FloatUtils.fuzzyEquals(resolution, viewportMetrics.zoomFactor)) { - Log.d(LOGTAG, "Aborting draw due to resolution change: " + resolution + " != " + viewportMetrics.zoomFactor); - mProgressiveUpdateData.abort = true; - return mProgressiveUpdateData; - } - - // Store the high precision displayport for comparison when doing low - // precision updates. - if (!lowPrecision) { - if (!FloatUtils.fuzzyEquals(resolution, mProgressiveUpdateDisplayPort.resolution) || - !FloatUtils.fuzzyEquals(x, mProgressiveUpdateDisplayPort.getLeft()) || - !FloatUtils.fuzzyEquals(y, mProgressiveUpdateDisplayPort.getTop()) || - !FloatUtils.fuzzyEquals(x + width, mProgressiveUpdateDisplayPort.getRight()) || - !FloatUtils.fuzzyEquals(y + height, mProgressiveUpdateDisplayPort.getBottom())) { - mProgressiveUpdateDisplayPort = - new DisplayPortMetrics(x, y, x+width, y+height, resolution); - } - } - - // If we're not doing low precision draws and we're about to - // checkerboard, enable low precision drawing. - if (!lowPrecision && !mProgressiveUpdateWasInDanger) { - if (DisplayPortCalculator.aboutToCheckerboard(viewportMetrics, - mPanZoomController.getVelocityVector(), mProgressiveUpdateDisplayPort)) { - mProgressiveUpdateWasInDanger = true; - } - } - - // XXX All sorts of rounding happens inside Gecko that becomes hard to - // account exactly for. Given we align the display-port to tile - // boundaries (and so they rarely vary by sub-pixel amounts), just - // check that values are within a couple of pixels of the - // display-port bounds. - - // Never abort drawing if we can't be sure we've sent a more recent - // display-port. If we abort updating when we shouldn't, we can end up - // with blank regions on the screen and we open up the risk of entering - // an endless updating cycle. - if (Math.abs(displayPort.getLeft() - mProgressiveUpdateDisplayPort.getLeft()) <= 2 && - Math.abs(displayPort.getTop() - mProgressiveUpdateDisplayPort.getTop()) <= 2 && - Math.abs(displayPort.getBottom() - mProgressiveUpdateDisplayPort.getBottom()) <= 2 && - Math.abs(displayPort.getRight() - mProgressiveUpdateDisplayPort.getRight()) <= 2) { - return mProgressiveUpdateData; - } - - // Abort updates when the display-port no longer contains the visible - // area of the page (that is, the viewport cropped by the page - // boundaries). - // XXX This makes the assumption that we never let the visible area of - // the page fall outside of the display-port. - if (Math.max(viewportMetrics.viewportRectLeft, viewportMetrics.pageRectLeft) + 1 < x || - Math.max(viewportMetrics.viewportRectTop, viewportMetrics.pageRectTop) + 1 < y || - Math.min(viewportMetrics.viewportRectRight, viewportMetrics.pageRectRight) - 1 > x + width || - Math.min(viewportMetrics.viewportRectBottom, viewportMetrics.pageRectBottom) - 1 > y + height) { - Log.d(LOGTAG, "Aborting update due to viewport not in display-port"); - mProgressiveUpdateData.abort = true; - - // Enable low-precision drawing, as we're likely to be in danger if - // this situation has been encountered. - mProgressiveUpdateWasInDanger = true; - - return mProgressiveUpdateData; - } - - // Abort drawing stale low-precision content if there's a more recent - // display-port in the pipeline. - if (lowPrecision && !aHasPendingNewThebesContent) { - mProgressiveUpdateData.abort = true; - } - return mProgressiveUpdateData; - } - - void setZoomConstraints(ZoomConstraints constraints) { - mZoomConstraints = constraints; - } - - void setIsRTL(boolean aIsRTL) { - synchronized (getLock()) { - ImmutableViewportMetrics newMetrics = getViewportMetrics().setIsRTL(aIsRTL); - setViewportMetrics(newMetrics, false); - } - } - - /** The compositor invokes this function just before compositing a frame where the document - * is different from the document composited on the last frame. In these cases, the viewport - * information we have in Java is no longer valid and needs to be replaced with the new - * viewport information provided. setPageRect will never be invoked on the same frame that - * this function is invoked on; and this function will always be called prior to syncViewportInfo. - */ - //@WrapElementForJNI(allowMultithread = true) - public void setFirstPaintViewport(float offsetX, float offsetY, float zoom, - float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom) { - synchronized (getLock()) { - ImmutableViewportMetrics currentMetrics = getViewportMetrics(); - - //Tab tab = Tabs.getInstance().getSelectedTab(); - - RectF cssPageRect = new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom); - RectF pageRect = RectUtils.scaleAndRound(cssPageRect, zoom); - - final ImmutableViewportMetrics newMetrics = currentMetrics - .setViewportOrigin(offsetX, offsetY) - .setZoomFactor(zoom) - .setPageRect(pageRect, cssPageRect) - /*.setIsRTL(tab.getIsRTL())*/; - // Since we have switched to displaying a different document, we need to update any - // viewport-related state we have lying around. This includes mGeckoViewport and - // mViewportMetrics. Usually this information is updated via handleViewportMessage - // while we remain on the same document. - post(new Runnable() { - @Override - public void run() { - mGeckoViewport = newMetrics; - } - }); - - setViewportMetrics(newMetrics); - - //mView.setBackgroundColor(tab.getBackgroundColor()); - //setZoomConstraints(tab.getZoomConstraints()); - - // At this point, we have just switched to displaying a different document than we - // we previously displaying. This means we need to abort any panning/zooming animations - // that are in progress and send an updated display port request to browser.js as soon - // as possible. The call to PanZoomController.abortAnimation accomplishes this by calling the - // forceRedraw function, which sends the viewport to gecko. The display port request is - // actually a full viewport update, which is fine because if browser.js has somehow moved to - // be out of sync with this first-paint viewport, then we force them back in sync. - abortPanZoomAnimation(); - - // Indicate that the document is about to be composited so the - // LayerView background can be removed. - if (mView.getPaintState() == LayerView.PAINT_START) { - mView.setPaintState(LayerView.PAINT_BEFORE_FIRST); - } - } - DisplayPortCalculator.resetPageState(); - mDrawTimingQueue.reset(); - - mContentDocumentIsDisplayed = true; - } - - /** The compositor invokes this function whenever it determines that the page rect - * has changed (based on the information it gets from layout). If setFirstPaintViewport - * is invoked on a frame, then this function will not be. For any given frame, this - * function will be invoked before syncViewportInfo. - */ - //@WrapElementForJNI(allowMultithread = true) - public void setPageRect(float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom) { - synchronized (getLock()) { - RectF cssPageRect = new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom); - float ourZoom = getViewportMetrics().zoomFactor; - setPageRect(RectUtils.scale(cssPageRect, ourZoom), cssPageRect); - // Here the page size of the document has changed, but the document being displayed - // is still the same. Therefore, we don't need to send anything to browser.js; any - // changes we need to make to the display port will get sent the next time we call - // adjustViewport(). - } - } - - /** The compositor invokes this function on every frame to figure out what part of the - * page to display, and to inform Java of the current display port. Since it is called - * on every frame, it needs to be ultra-fast. - * It avoids taking any locks or allocating any objects. We keep around a - * mCurrentViewTransform so we don't need to allocate a new ViewTransform - * every time we're called. NOTE: we might be able to return a ImmutableViewportMetrics - * which would avoid the copy into mCurrentViewTransform. - */ - //@WrapElementForJNI(allowMultithread = true) - public ViewTransform syncViewportInfo(int x, int y, int width, int height, float resolution, boolean layersUpdated) { - // getViewportMetrics is thread safe so we don't need to synchronize. - // We save the viewport metrics here, so we later use it later in - // createFrame (which will be called by nsWindow::DrawWindowUnderlay on - // the native side, by the compositor). The viewport - // metrics can change between here and there, as it's accessed outside - // of the compositor thread. - mFrameMetrics = getViewportMetrics(); - - mCurrentViewTransform.x = mFrameMetrics.viewportRectLeft; - mCurrentViewTransform.y = mFrameMetrics.viewportRectTop; - mCurrentViewTransform.scale = mFrameMetrics.zoomFactor; - - // Adjust the fixed layer margins so that overscroll subtracts from them. - getFixedMargins(mFrameMetrics, mCurrentViewTransformMargins); - mCurrentViewTransform.fixedLayerMarginLeft = mCurrentViewTransformMargins.left; - mCurrentViewTransform.fixedLayerMarginTop = mCurrentViewTransformMargins.top; - mCurrentViewTransform.fixedLayerMarginRight = mCurrentViewTransformMargins.right; - mCurrentViewTransform.fixedLayerMarginBottom = mCurrentViewTransformMargins.bottom; - - // Offset the view transform so that it renders in the correct place. - PointF offset = mFrameMetrics.getMarginOffset(); - mCurrentViewTransform.offsetX = offset.x; - mCurrentViewTransform.offsetY = offset.y; - - mRootLayer.setPositionAndResolution( - Math.round(x + mCurrentViewTransform.offsetX), - Math.round(y + mCurrentViewTransform.offsetY), - Math.round(x + width + mCurrentViewTransform.offsetX), - Math.round(y + height + mCurrentViewTransform.offsetY), - resolution); - - if (layersUpdated && mRecordDrawTimes) { - // If we got a layers update, that means a draw finished. Check to see if the area drawn matches - // one of our requested displayports; if it does calculate the draw time and notify the - // DisplayPortCalculator - DisplayPortMetrics drawn = new DisplayPortMetrics(x, y, x + width, y + height, resolution); - long time = mDrawTimingQueue.findTimeFor(drawn); - if (time >= 0) { - long now = SystemClock.uptimeMillis(); - time = now - time; - mRecordDrawTimes = DisplayPortCalculator.drawTimeUpdate(time, width * height); - } - } - - if (layersUpdated && mDrawListener != null) { - /* Used by robocop for testing purposes */ - mDrawListener.drawFinished(); - } - - return mCurrentViewTransform; - } - - //@WrapElementForJNI(allowMultithread = true) - public ViewTransform syncFrameMetrics(float offsetX, float offsetY, float zoom, - float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom, - boolean layersUpdated, int x, int y, int width, int height, float resolution, - boolean isFirstPaint) - { - if (isFirstPaint) { - setFirstPaintViewport(offsetX, offsetY, zoom, - cssPageLeft, cssPageTop, cssPageRight, cssPageBottom); - } - - return syncViewportInfo(x, y, width, height, resolution, layersUpdated); - } - - //@WrapElementForJNI(allowMultithread = true) - public LayerRenderer.Frame createFrame() { - // Create the shaders and textures if necessary. - if (!mLayerRendererInitialized) { - mLayerRenderer.checkMonitoringEnabled(); - mLayerRenderer.createDefaultProgram(); - mLayerRendererInitialized = true; - } - - return mLayerRenderer.createFrame(mFrameMetrics); - } - - //@WrapElementForJNI(allowMultithread = true) - public void activateProgram() { - mLayerRenderer.activateDefaultProgram(); - } - - //@WrapElementForJNI(allowMultithread = true) - public void deactivateProgram() { - mLayerRenderer.deactivateDefaultProgram(); - } - - private void geometryChanged(DisplayPortMetrics displayPort) { - /* Let Gecko know if the screensize has changed */ - sendResizeEventIfNecessary(false); - if (getRedrawHint()) { - adjustViewport(displayPort); - } - } - - /** Implementation of LayerView.Listener */ - @Override - public void renderRequested() { - try { - //GeckoAppShell.scheduleComposite(); - } catch (UnsupportedOperationException uoe) { - // In some very rare cases this gets called before libxul is loaded, - // so catch and ignore the exception that will throw. See bug 837821 - Log.d(LOGTAG, "Dropping renderRequested call before libxul load."); - } - } - - /** Implementation of LayerView.Listener */ - @Override - public void sizeChanged(int width, int height) { - // We need to make sure a draw happens synchronously at this point, - // but resizing the surface before the SurfaceView has resized will - // cause a visible jump. - mView.getGLController().resumeCompositor(mWindowSize.width, mWindowSize.height); - } - - /** Implementation of LayerView.Listener */ - @Override - public void surfaceChanged(int width, int height) { - setViewportSize(width, height); - } - - /** Implementation of PanZoomTarget */ - @Override - public ImmutableViewportMetrics getViewportMetrics() { - return mViewportMetrics; - } - - /** Implementation of PanZoomTarget */ - @Override - public ZoomConstraints getZoomConstraints() { - return mZoomConstraints; - } - - /** Implementation of PanZoomTarget */ - @Override - public boolean isFullScreen() { - return mView.isFullScreen(); - } - - /** Implementation of PanZoomTarget */ - @Override - public RectF getMaxMargins() { - return mMarginsAnimator.getMaxMargins(); - } - - /** Implementation of PanZoomTarget */ - @Override - public void setAnimationTarget(ImmutableViewportMetrics metrics) { - if (mGeckoIsReady) { - // We know what the final viewport of the animation is going to be, so - // immediately request a draw of that area by setting the display port - // accordingly. This way we should have the content pre-rendered by the - // time the animation is done. - DisplayPortMetrics displayPort = DisplayPortCalculator.calculate(metrics, null); - adjustViewport(displayPort); - } - } - - /** Implementation of PanZoomTarget - * You must hold the monitor while calling this. - */ - @Override - public void setViewportMetrics(ImmutableViewportMetrics metrics) { - setViewportMetrics(metrics, true); - } - - /* - * You must hold the monitor while calling this. - */ - private void setViewportMetrics(ImmutableViewportMetrics metrics, boolean notifyGecko) { - // This class owns the viewport size and the fixed layer margins; don't let other pieces - // of code clobber either of them. The only place the viewport size should ever be - // updated is in GeckoLayerClient.setViewportSize, and the only place the margins should - // ever be updated is in GeckoLayerClient.setFixedLayerMargins; both of these assign to - // mViewportMetrics directly. - metrics = metrics.setViewportSize(mViewportMetrics.getWidth(), mViewportMetrics.getHeight()); - metrics = metrics.setMarginsFrom(mViewportMetrics); - mViewportMetrics = metrics; - - viewportMetricsChanged(notifyGecko); - } - - /* - * You must hold the monitor while calling this. - */ - private void viewportMetricsChanged(boolean notifyGecko) { - if (mViewportChangeListener != null) { - mViewportChangeListener.onMetricsChanged(mViewportMetrics); - } - - mView.requestRender(); - if (notifyGecko && mGeckoIsReady) { - geometryChanged(null); - } - } - - /* - * Updates the viewport metrics, overriding the viewport size and margins - * which are normally retained when calling setViewportMetrics. - * You must hold the monitor while calling this. - */ - void forceViewportMetrics(ImmutableViewportMetrics metrics, boolean notifyGecko, boolean forceRedraw) { - if (forceRedraw) { - mForceRedraw = true; - } - mViewportMetrics = metrics; - viewportMetricsChanged(notifyGecko); - } - - /** Implementation of PanZoomTarget - * Scroll the viewport by a certain amount. This will take viewport margins - * and margin animation into account. If margins are currently animating, - * this will just go ahead and modify the viewport origin, otherwise the - * delta will be applied to the margins and the remainder will be applied to - * the viewport origin. - * - * You must hold the monitor while calling this. - */ - @Override - public void scrollBy(float dx, float dy) { - // Set mViewportMetrics manually so the margin changes take. - mViewportMetrics = mMarginsAnimator.scrollBy(mViewportMetrics, dx, dy); - viewportMetricsChanged(true); - } - - /** Implementation of PanZoomTarget - * Notification that a subdocument has been scrolled by a certain amount. - * This is used here to make sure that the margins are still accessible - * during subdocument scrolling. - * - * You must hold the monitor while calling this. - */ - @Override - public void scrollMarginsBy(float dx, float dy) { - ImmutableViewportMetrics newMarginsMetrics = - mMarginsAnimator.scrollBy(mViewportMetrics, dx, dy); - mViewportMetrics = mViewportMetrics.setMarginsFrom(newMarginsMetrics); - viewportMetricsChanged(true); - } - - /** Implementation of PanZoomTarget */ - @Override - public void panZoomStopped() { - if (mViewportChangeListener != null) { - mViewportChangeListener.onPanZoomStopped(); - } - } - - public interface OnMetricsChangedListener { - public void onMetricsChanged(ImmutableViewportMetrics viewport); - public void onPanZoomStopped(); - } - - /** Implementation of PanZoomTarget */ - @Override - public void forceRedraw(DisplayPortMetrics displayPort) { - mForceRedraw = true; - if (mGeckoIsReady) { - geometryChanged(displayPort); - } - } - - /** Implementation of PanZoomTarget */ - @Override - public boolean post(Runnable action) { - return mView.post(action); - } - - /** Implementation of PanZoomTarget */ - @Override - public void postRenderTask(RenderTask task) { - mView.postRenderTask(task); - } - - /** Implementation of PanZoomTarget */ - @Override - public void removeRenderTask(RenderTask task) { - mView.removeRenderTask(task); - } - - - /** Implementation of PanZoomTarget */ - @Override - public boolean postDelayed(Runnable action, long delayMillis) { - return mView.postDelayed(action, delayMillis); - } - - /** Implementation of PanZoomTarget */ - @Override - public Object getLock() { - return this; - } - - /** Implementation of PanZoomTarget - * Converts a point from layer view coordinates to layer coordinates. In other words, given a - * point measured in pixels from the top left corner of the layer view, returns the point in - * pixels measured from the last scroll position we sent to Gecko, in CSS pixels. Assuming the - * events being sent to Gecko are processed in FIFO order, this calculation should always be - * correct. - */ - @Override - public PointF convertViewPointToLayerPoint(PointF viewPoint) { - if (!mGeckoIsReady) { - return null; - } - - ImmutableViewportMetrics viewportMetrics = mViewportMetrics; - PointF origin = viewportMetrics.getOrigin(); - PointF offset = viewportMetrics.getMarginOffset(); - origin.offset(-offset.x, -offset.y); - float zoom = viewportMetrics.zoomFactor; - ImmutableViewportMetrics geckoViewport = mGeckoViewport; - PointF geckoOrigin = geckoViewport.getOrigin(); - float geckoZoom = geckoViewport.zoomFactor; - - // viewPoint + origin - offset gives the coordinate in device pixels from the top-left corner of the page. - // Divided by zoom, this gives us the coordinate in CSS pixels from the top-left corner of the page. - // geckoOrigin / geckoZoom is where Gecko thinks it is (scrollTo position) in CSS pixels from - // the top-left corner of the page. Subtracting the two gives us the offset of the viewPoint from - // the current Gecko coordinate in CSS pixels. - PointF layerPoint = new PointF( - ((viewPoint.x + origin.x) / zoom) - (geckoOrigin.x / geckoZoom), - ((viewPoint.y + origin.y) / zoom) - (geckoOrigin.y / geckoZoom)); - - return layerPoint; - } - - public void setOnMetricsChangedListener(OnMetricsChangedListener listener) { - mViewportChangeListener = listener; - } - - /** Used by robocop for testing purposes. Not for production use! */ - //@RobocopTarget - public void setDrawListener(DrawListener listener) { - mDrawListener = listener; - } - - /** Used by robocop for testing purposes. Not for production use! */ - //@RobocopTarget - public static interface DrawListener { - public void drawFinished(); - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/ImmutableViewportMetrics.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/ImmutableViewportMetrics.java deleted file mode 100644 index 463bc3c4a4a5..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/ImmutableViewportMetrics.java +++ /dev/null @@ -1,374 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -//import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI; -import org.mozilla.gecko.util.FloatUtils; - -import android.graphics.PointF; -import android.graphics.RectF; -import android.util.DisplayMetrics; - -/** - * ImmutableViewportMetrics are used to store the viewport metrics - * in way that we can access a version of them from multiple threads - * without having to take a lock - */ -public class ImmutableViewportMetrics { - - // We need to flatten the RectF and FloatSize structures - // because Java doesn't have the concept of const classes - public final float pageRectLeft; - public final float pageRectTop; - public final float pageRectRight; - public final float pageRectBottom; - public final float cssPageRectLeft; - public final float cssPageRectTop; - public final float cssPageRectRight; - public final float cssPageRectBottom; - public final float viewportRectLeft; - public final float viewportRectTop; - public final float viewportRectRight; - public final float viewportRectBottom; - public final float marginLeft; - public final float marginTop; - public final float marginRight; - public final float marginBottom; - public final float zoomFactor; - public final boolean isRTL; - - public ImmutableViewportMetrics(DisplayMetrics metrics) { - viewportRectLeft = pageRectLeft = cssPageRectLeft = 0; - viewportRectTop = pageRectTop = cssPageRectTop = 0; - viewportRectRight = pageRectRight = cssPageRectRight = metrics.widthPixels; - viewportRectBottom = pageRectBottom = cssPageRectBottom = metrics.heightPixels; - marginLeft = marginTop = marginRight = marginBottom = 0; - zoomFactor = 1.0f; - isRTL = false; - } - - /** This constructor is used by native code in AndroidJavaWrappers.cpp, be - * careful when modifying the signature. - */ - //@WrapElementForJNI(allowMultithread = true) - public ImmutableViewportMetrics(float aPageRectLeft, float aPageRectTop, - float aPageRectRight, float aPageRectBottom, float aCssPageRectLeft, - float aCssPageRectTop, float aCssPageRectRight, float aCssPageRectBottom, - float aViewportRectLeft, float aViewportRectTop, float aViewportRectRight, - float aViewportRectBottom, float aZoomFactor) - { - this(aPageRectLeft, aPageRectTop, - aPageRectRight, aPageRectBottom, aCssPageRectLeft, - aCssPageRectTop, aCssPageRectRight, aCssPageRectBottom, - aViewportRectLeft, aViewportRectTop, aViewportRectRight, - aViewportRectBottom, 0.0f, 0.0f, 0.0f, 0.0f, aZoomFactor, false); - } - - private ImmutableViewportMetrics(float aPageRectLeft, float aPageRectTop, - float aPageRectRight, float aPageRectBottom, float aCssPageRectLeft, - float aCssPageRectTop, float aCssPageRectRight, float aCssPageRectBottom, - float aViewportRectLeft, float aViewportRectTop, float aViewportRectRight, - float aViewportRectBottom, float aMarginLeft, - float aMarginTop, float aMarginRight, - float aMarginBottom, float aZoomFactor, boolean aIsRTL) - { - pageRectLeft = aPageRectLeft; - pageRectTop = aPageRectTop; - pageRectRight = aPageRectRight; - pageRectBottom = aPageRectBottom; - cssPageRectLeft = aCssPageRectLeft; - cssPageRectTop = aCssPageRectTop; - cssPageRectRight = aCssPageRectRight; - cssPageRectBottom = aCssPageRectBottom; - viewportRectLeft = aViewportRectLeft; - viewportRectTop = aViewportRectTop; - viewportRectRight = aViewportRectRight; - viewportRectBottom = aViewportRectBottom; - marginLeft = aMarginLeft; - marginTop = aMarginTop; - marginRight = aMarginRight; - marginBottom = aMarginBottom; - zoomFactor = aZoomFactor; - isRTL = aIsRTL; - } - - public float getWidth() { - return viewportRectRight - viewportRectLeft; - } - - public float getHeight() { - return viewportRectBottom - viewportRectTop; - } - - public float getWidthWithoutMargins() { - return viewportRectRight - viewportRectLeft - marginLeft - marginRight; - } - - public float getHeightWithoutMargins() { - return viewportRectBottom - viewportRectTop - marginTop - marginBottom; - } - - public PointF getOrigin() { - return new PointF(viewportRectLeft, viewportRectTop); - } - - public PointF getMarginOffset() { - if (isRTL) { - return new PointF(marginLeft - marginRight, marginTop); - } - return new PointF(marginLeft, marginTop); - } - - public FloatSize getSize() { - return new FloatSize(viewportRectRight - viewportRectLeft, viewportRectBottom - viewportRectTop); - } - - public RectF getViewport() { - return new RectF(viewportRectLeft, - viewportRectTop, - viewportRectRight, - viewportRectBottom); - } - - public RectF getCssViewport() { - return RectUtils.scale(getViewport(), 1/zoomFactor); - } - - public RectF getPageRect() { - return new RectF(pageRectLeft, pageRectTop, pageRectRight, pageRectBottom); - } - - public float getPageWidth() { - return pageRectRight - pageRectLeft; - } - - public float getPageWidthWithMargins() { - return (pageRectRight - pageRectLeft) + marginLeft + marginRight; - } - - public float getPageHeight() { - return pageRectBottom - pageRectTop; - } - - public float getPageHeightWithMargins() { - return (pageRectBottom - pageRectTop) + marginTop + marginBottom; - } - - public RectF getCssPageRect() { - return new RectF(cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom); - } - - public RectF getOverscroll() { - return new RectF(Math.max(0, pageRectLeft - viewportRectLeft), - Math.max(0, pageRectTop - viewportRectTop), - Math.max(0, viewportRectRight - pageRectRight), - Math.max(0, viewportRectBottom - pageRectBottom)); - } - - /* - * Returns the viewport metrics that represent a linear transition between "this" and "to" at - * time "t", which is on the scale [0, 1). This function interpolates all values stored in - * the viewport metrics. - */ - public ImmutableViewportMetrics interpolate(ImmutableViewportMetrics to, float t) { - return new ImmutableViewportMetrics( - FloatUtils.interpolate(pageRectLeft, to.pageRectLeft, t), - FloatUtils.interpolate(pageRectTop, to.pageRectTop, t), - FloatUtils.interpolate(pageRectRight, to.pageRectRight, t), - FloatUtils.interpolate(pageRectBottom, to.pageRectBottom, t), - FloatUtils.interpolate(cssPageRectLeft, to.cssPageRectLeft, t), - FloatUtils.interpolate(cssPageRectTop, to.cssPageRectTop, t), - FloatUtils.interpolate(cssPageRectRight, to.cssPageRectRight, t), - FloatUtils.interpolate(cssPageRectBottom, to.cssPageRectBottom, t), - FloatUtils.interpolate(viewportRectLeft, to.viewportRectLeft, t), - FloatUtils.interpolate(viewportRectTop, to.viewportRectTop, t), - FloatUtils.interpolate(viewportRectRight, to.viewportRectRight, t), - FloatUtils.interpolate(viewportRectBottom, to.viewportRectBottom, t), - FloatUtils.interpolate(marginLeft, to.marginLeft, t), - FloatUtils.interpolate(marginTop, to.marginTop, t), - FloatUtils.interpolate(marginRight, to.marginRight, t), - FloatUtils.interpolate(marginBottom, to.marginBottom, t), - FloatUtils.interpolate(zoomFactor, to.zoomFactor, t), - t >= 0.5 ? to.isRTL : isRTL); - } - - public ImmutableViewportMetrics setViewportSize(float width, float height) { - if (FloatUtils.fuzzyEquals(width, getWidth()) && FloatUtils.fuzzyEquals(height, getHeight())) { - return this; - } - - return new ImmutableViewportMetrics( - pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, - cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, - viewportRectLeft, viewportRectTop, viewportRectLeft + width, viewportRectTop + height, - marginLeft, marginTop, marginRight, marginBottom, - zoomFactor, isRTL); - } - - public ImmutableViewportMetrics setViewportOrigin(float newOriginX, float newOriginY) { - return new ImmutableViewportMetrics( - pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, - cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, - newOriginX, newOriginY, newOriginX + getWidth(), newOriginY + getHeight(), - marginLeft, marginTop, marginRight, marginBottom, - zoomFactor, isRTL); - } - - public ImmutableViewportMetrics setZoomFactor(float newZoomFactor) { - return new ImmutableViewportMetrics( - pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, - cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, - viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom, - marginLeft, marginTop, marginRight, marginBottom, - newZoomFactor, isRTL); - } - - public ImmutableViewportMetrics offsetViewportBy(float dx, float dy) { - return setViewportOrigin(viewportRectLeft + dx, viewportRectTop + dy); - } - - public ImmutableViewportMetrics offsetViewportByAndClamp(float dx, float dy) { - if (isRTL) { - return setViewportOrigin( - Math.min(pageRectRight - getWidthWithoutMargins(), Math.max(viewportRectLeft + dx, pageRectLeft)), - Math.max(pageRectTop, Math.min(viewportRectTop + dy, pageRectBottom - getHeightWithoutMargins()))); - } - return setViewportOrigin( - Math.max(pageRectLeft, Math.min(viewportRectLeft + dx, pageRectRight - getWidthWithoutMargins())), - Math.max(pageRectTop, Math.min(viewportRectTop + dy, pageRectBottom - getHeightWithoutMargins()))); - } - - public ImmutableViewportMetrics setPageRect(RectF pageRect, RectF cssPageRect) { - return new ImmutableViewportMetrics( - pageRect.left, pageRect.top, pageRect.right, pageRect.bottom, - cssPageRect.left, cssPageRect.top, cssPageRect.right, cssPageRect.bottom, - viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom, - marginLeft, marginTop, marginRight, marginBottom, - zoomFactor, isRTL); - } - - public ImmutableViewportMetrics setMargins(float left, float top, float right, float bottom) { - if (FloatUtils.fuzzyEquals(left, marginLeft) - && FloatUtils.fuzzyEquals(top, marginTop) - && FloatUtils.fuzzyEquals(right, marginRight) - && FloatUtils.fuzzyEquals(bottom, marginBottom)) { - return this; - } - - return new ImmutableViewportMetrics( - pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, - cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, - viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom, - left, top, right, bottom, zoomFactor, isRTL); - } - - public ImmutableViewportMetrics setMarginsFrom(ImmutableViewportMetrics fromMetrics) { - return setMargins(fromMetrics.marginLeft, - fromMetrics.marginTop, - fromMetrics.marginRight, - fromMetrics.marginBottom); - } - - public ImmutableViewportMetrics setIsRTL(boolean aIsRTL) { - if (isRTL == aIsRTL) { - return this; - } - - return new ImmutableViewportMetrics( - pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, - cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, - viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom, - marginLeft, marginTop, marginRight, marginBottom, zoomFactor, aIsRTL); - } - - /* This will set the zoom factor and re-scale page-size and viewport offset - * accordingly. The given focus will remain at the same point on the screen - * after scaling. - */ - public ImmutableViewportMetrics scaleTo(float newZoomFactor, PointF focus) { - // cssPageRect* is invariant, since we're setting the scale factor - // here. The page rect is based on the CSS page rect. - float newPageRectLeft = cssPageRectLeft * newZoomFactor; - float newPageRectTop = cssPageRectTop * newZoomFactor; - float newPageRectRight = cssPageRectLeft + ((cssPageRectRight - cssPageRectLeft) * newZoomFactor); - float newPageRectBottom = cssPageRectTop + ((cssPageRectBottom - cssPageRectTop) * newZoomFactor); - - PointF origin = getOrigin(); - origin.offset(focus.x, focus.y); - origin = PointUtils.scale(origin, newZoomFactor / zoomFactor); - origin.offset(-focus.x, -focus.y); - - return new ImmutableViewportMetrics( - newPageRectLeft, newPageRectTop, newPageRectRight, newPageRectBottom, - cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, - origin.x, origin.y, origin.x + getWidth(), origin.y + getHeight(), - marginLeft, marginTop, marginRight, marginBottom, - newZoomFactor, isRTL); - } - - /** Clamps the viewport to remain within the page rect. */ - private ImmutableViewportMetrics clamp(float marginLeft, float marginTop, - float marginRight, float marginBottom) { - RectF newViewport = getViewport(); - PointF offset = getMarginOffset(); - - // The viewport bounds ought to never exceed the page bounds. - if (newViewport.right > pageRectRight + marginLeft + marginRight) - newViewport.offset((pageRectRight + marginLeft + marginRight) - newViewport.right, 0); - if (newViewport.left < pageRectLeft) - newViewport.offset(pageRectLeft - newViewport.left, 0); - - if (newViewport.bottom > pageRectBottom + marginTop + marginBottom) - newViewport.offset(0, (pageRectBottom + marginTop + marginBottom) - newViewport.bottom); - if (newViewport.top < pageRectTop) - newViewport.offset(0, pageRectTop - newViewport.top); - - return new ImmutableViewportMetrics( - pageRectLeft, pageRectTop, pageRectRight, pageRectBottom, - cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom, - newViewport.left, newViewport.top, newViewport.right, newViewport.bottom, - marginLeft, marginTop, marginRight, marginBottom, - zoomFactor, isRTL); - } - - public ImmutableViewportMetrics clamp() { - return clamp(0, 0, 0, 0); - } - - public ImmutableViewportMetrics clampWithMargins() { - return clamp(marginLeft, marginTop, - marginRight, marginBottom); - } - - public boolean fuzzyEquals(ImmutableViewportMetrics other) { - // Don't bother checking the pageRectXXX values because they are a product - // of the cssPageRectXXX values and the zoomFactor, except with more rounding - // error. Checking those is both inefficient and can lead to false negatives. - // - // This doesn't return false if the margins differ as none of the users - // of this function are interested in the margins in that way. - return FloatUtils.fuzzyEquals(cssPageRectLeft, other.cssPageRectLeft) - && FloatUtils.fuzzyEquals(cssPageRectTop, other.cssPageRectTop) - && FloatUtils.fuzzyEquals(cssPageRectRight, other.cssPageRectRight) - && FloatUtils.fuzzyEquals(cssPageRectBottom, other.cssPageRectBottom) - && FloatUtils.fuzzyEquals(viewportRectLeft, other.viewportRectLeft) - && FloatUtils.fuzzyEquals(viewportRectTop, other.viewportRectTop) - && FloatUtils.fuzzyEquals(viewportRectRight, other.viewportRectRight) - && FloatUtils.fuzzyEquals(viewportRectBottom, other.viewportRectBottom) - && FloatUtils.fuzzyEquals(zoomFactor, other.zoomFactor); - } - - @Override - public String toString() { - return "ImmutableViewportMetrics v=(" + viewportRectLeft + "," + viewportRectTop + "," - + viewportRectRight + "," + viewportRectBottom + ") p=(" + pageRectLeft + "," - + pageRectTop + "," + pageRectRight + "," + pageRectBottom + ") c=(" - + cssPageRectLeft + "," + cssPageRectTop + "," + cssPageRectRight + "," - + cssPageRectBottom + ") m=(" + marginLeft + "," - + marginTop + "," + marginRight + "," - + marginBottom + ") z=" + zoomFactor + ", rtl=" + isRTL; - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/InputConnectionHandler.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/InputConnectionHandler.java deleted file mode 100644 index 9b3ca381b5a4..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/InputConnectionHandler.java +++ /dev/null @@ -1,22 +0,0 @@ -/* 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.gecko.gfx; - -import android.os.Handler; -import android.view.KeyEvent; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputConnection; - -public interface InputConnectionHandler -{ - Handler getHandler(Handler defHandler); - InputConnection onCreateInputConnection(EditorInfo outAttrs); - boolean onKeyPreIme(int keyCode, KeyEvent event); - boolean onKeyDown(int keyCode, KeyEvent event); - boolean onKeyLongPress(int keyCode, KeyEvent event); - boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event); - boolean onKeyUp(int keyCode, KeyEvent event); - boolean isIMEEnabled(); -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/IntSize.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/IntSize.java deleted file mode 100644 index b758d732c2ed..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/IntSize.java +++ /dev/null @@ -1,91 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import org.json.JSONException; -import org.json.JSONObject; - -import android.util.FloatMath; - -public class IntSize { - public final int width, height; - - public IntSize(IntSize size) { width = size.width; height = size.height; } - public IntSize(int inWidth, int inHeight) { width = inWidth; height = inHeight; } - - public IntSize(FloatSize size) { - width = Math.round(size.width); - height = Math.round(size.height); - } - - public IntSize(JSONObject json) { - try { - width = json.getInt("width"); - height = json.getInt("height"); - } catch (JSONException e) { - throw new RuntimeException(e); - } - } - - public int getArea() { - return width * height; - } - - public boolean equals(IntSize size) { - return ((size.width == width) && (size.height == height)); - } - - public boolean isPositive() { - return (width > 0 && height > 0); - } - - @Override - public String toString() { return "(" + width + "," + height + ")"; } - - public IntSize scale(float factor) { - return new IntSize(Math.round(width * factor), - Math.round(height * factor)); - } - - /* Returns the power of two that is greater than or equal to value */ - public static int nextPowerOfTwo(int value) { - // code taken from http://acius2.blogspot.com/2007/11/calculating-next-power-of-2.html - if (0 == value--) { - return 1; - } - value = (value >> 1) | value; - value = (value >> 2) | value; - value = (value >> 4) | value; - value = (value >> 8) | value; - value = (value >> 16) | value; - return value + 1; - } - - public IntSize nextPowerOfTwo() { - return new IntSize(nextPowerOfTwo(width), nextPowerOfTwo(height)); - } - - public static boolean isPowerOfTwo(int value) { - if (value == 0) - return false; - return (value & (value - 1)) == 0; - } - - public static int largestPowerOfTwoLessThan(float value) { - int val = (int)FloatMath.floor(value); - if (val <= 0) { - throw new IllegalArgumentException("Error: value must be > 0"); - } - // keep dropping the least-significant set bits until only one is left - int bestVal = val; - while (val != 0) { - bestVal = val; - val &= (val - 1); - } - return bestVal; - } -} - diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/JavaPanZoomController.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/JavaPanZoomController.java deleted file mode 100644 index ac1bf0d3d451..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/JavaPanZoomController.java +++ /dev/null @@ -1,1461 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import org.libreoffice.LOKitShell; -//import org.mozilla.gecko.GeckoAppShell; -//import org.mozilla.gecko.GeckoEvent; -//import org.mozilla.gecko.PrefsHelper; -//import org.mozilla.gecko.Tab; -//import org.mozilla.gecko.Tabs; -import org.mozilla.gecko.ZoomConstraints; -import org.mozilla.gecko.util.EventDispatcher; -import org.mozilla.gecko.util.FloatUtils; -//import org.mozilla.gecko.util.GamepadUtils; -import org.mozilla.gecko.util.GeckoEventListener; -import org.mozilla.gecko.util.ThreadUtils; - -import org.json.JSONObject; - -import android.graphics.PointF; -import android.graphics.RectF; -import android.os.Build; -import android.util.FloatMath; -import android.util.Log; -import android.view.GestureDetector; -import android.view.InputDevice; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; - -/* - * Handles the kinetic scrolling and zooming physics for a layer controller. - * - * Many ideas are from Joe Hewitt's Scrollability: - * https://github.com/joehewitt/scrollability/ - */ -class JavaPanZoomController - extends GestureDetector.SimpleOnGestureListener - implements PanZoomController, SimpleScaleGestureDetector.SimpleScaleGestureListener, GeckoEventListener -{ - private static final String LOGTAG = "GeckoPanZoomController"; - - private static String MESSAGE_ZOOM_RECT = "Browser:ZoomToRect"; - private static String MESSAGE_ZOOM_PAGE = "Browser:ZoomToPageWidth"; - private static String MESSAGE_TOUCH_LISTENER = "Tab:HasTouchListener"; - - // Animation stops if the velocity is below this value when overscrolled or panning. - private static final float STOPPED_THRESHOLD = 4.0f; - - // Animation stops is the velocity is below this threshold when flinging. - private static final float FLING_STOPPED_THRESHOLD = 0.1f; - - // The distance the user has to pan before we recognize it as such (e.g. to avoid 1-pixel pans - // between the touch-down and touch-up of a click). In units of density-independent pixels. - public static final float PAN_THRESHOLD = 1/16f * LOKitShell.getDpi(); //GeckoAppShell.getDpi(); - - // Angle from axis within which we stay axis-locked - private static final double AXIS_LOCK_ANGLE = Math.PI / 6.0; // 30 degrees - - // Axis-lock breakout angle - private static final double AXIS_BREAKOUT_ANGLE = Math.PI / 8.0; - - // The distance the user has to pan before we consider breaking out of a locked axis - public static final float AXIS_BREAKOUT_THRESHOLD = 1/32f * LOKitShell.getDpi(); //GeckoAppShell.getDpi(); - - // The maximum amount we allow you to zoom into a page - private static final float MAX_ZOOM = 4.0f; - - // The maximum amount we would like to scroll with the mouse - private static final float MAX_SCROLL = 0.075f * LOKitShell.getDpi(); - - // The maximum zoom factor adjustment per frame of the AUTONAV animation - private static final float MAX_ZOOM_DELTA = 0.125f; - - // The duration of the bounce animation in ns - private static final int BOUNCE_ANIMATION_DURATION = 250000000; - - private enum PanZoomState { - NOTHING, /* no touch-start events received */ - FLING, /* all touches removed, but we're still scrolling page */ - TOUCHING, /* one touch-start event received */ - PANNING_LOCKED_X, /* touch-start followed by move (i.e. panning with axis lock) X axis */ - PANNING_LOCKED_Y, /* as above for Y axis */ - PANNING, /* panning without axis lock */ - PANNING_HOLD, /* in panning, but not moving. - * similar to TOUCHING but after starting a pan */ - PANNING_HOLD_LOCKED_X, /* like PANNING_HOLD, but axis lock still in effect for X axis */ - PANNING_HOLD_LOCKED_Y, /* as above but for Y axis */ - PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */ - ANIMATED_ZOOM, /* animated zoom to a new rect */ - BOUNCE, /* in a bounce animation */ - WAITING_LISTENERS, /* a state halfway between NOTHING and TOUCHING - the user has - put a finger down, but we don't yet know if a touch listener has - prevented the default actions yet. we still need to abort animations. */ - AUTONAV, /* We are scrolling using an AutonavRunnable animation. This is similar - to the FLING state except that it must be stopped manually by the code that - started it, and it's velocity can be updated while it's running. */ - } - - private enum AxisLockMode { - STANDARD, /* Default axis locking mode that doesn't break out until finger release */ - FREE, /* No locking at all */ - STICKY /* Break out with hysteresis so that it feels as free as possible whilst locking */ - } - - private final PanZoomTarget mTarget; - private final SubdocumentScrollHelper mSubscroller; - private final Axis mX; - private final Axis mY; - private final TouchEventHandler mTouchEventHandler; - private final EventDispatcher mEventDispatcher; - - /* The task that handles flings, autonav or bounces. */ - private PanZoomRenderTask mAnimationRenderTask; - /* The zoom focus at the first zoom event (in page coordinates). */ - private PointF mLastZoomFocus; - /* The time the last motion event took place. */ - private long mLastEventTime; - /* Current state the pan/zoom UI is in. */ - private PanZoomState mState; - /* The per-frame zoom delta for the currently-running AUTONAV animation. */ - private float mAutonavZoomDelta; - /* The user selected panning mode */ - private AxisLockMode mMode; - /* A medium-length tap/press is happening */ - private boolean mMediumPress; - /* Used to change the scrollY direction */ - private boolean mNegateWheelScrollY; - /* Whether the current event has been default-prevented. */ - private boolean mDefaultPrevented; - - // Handler to be notified when overscroll occurs - private Overscroll mOverscroll; - - public JavaPanZoomController(PanZoomTarget target, View view, EventDispatcher eventDispatcher) { - mTarget = target; - mSubscroller = new SubdocumentScrollHelper(eventDispatcher); - mX = new AxisX(mSubscroller); - mY = new AxisY(mSubscroller); - mTouchEventHandler = new TouchEventHandler(view.getContext(), view, this); - - checkMainThread(); - - setState(PanZoomState.NOTHING); - - mEventDispatcher = eventDispatcher; - registerEventListener(MESSAGE_ZOOM_RECT); - registerEventListener(MESSAGE_ZOOM_PAGE); - registerEventListener(MESSAGE_TOUCH_LISTENER); - - mMode = AxisLockMode.STANDARD; - - String[] prefs = { "ui.scrolling.axis_lock_mode", - "ui.scrolling.negate_wheel_scrollY", - "ui.scrolling.gamepad_dead_zone" }; - mNegateWheelScrollY = false; - - /*PrefsHelper.getPrefs(prefs, new PrefsHelper.PrefHandlerBase() { - @Override public void prefValue(String pref, String value) { - if (pref.equals("ui.scrolling.axis_lock_mode")) { - if (value.equals("standard")) { - mMode = AxisLockMode.STANDARD; - } else if (value.equals("free")) { - mMode = AxisLockMode.FREE; - } else { - mMode = AxisLockMode.STICKY; - } - } - } - - @Override public void prefValue(String pref, int value) { - if (pref.equals("ui.scrolling.gamepad_dead_zone")) { - GamepadUtils.overrideDeadZoneThreshold((float)value / 1000f); - } - } - - @Override public void prefValue(String pref, boolean value) { - if (pref.equals("ui.scrolling.negate_wheel_scrollY")) { - mNegateWheelScrollY = value; - } - } - - @Override - public boolean isObserver() { - return true; - } - });*/ - - Axis.initPrefs(); - } - - @Override - public void destroy() { - unregisterEventListener(MESSAGE_ZOOM_RECT); - unregisterEventListener(MESSAGE_ZOOM_PAGE); - unregisterEventListener(MESSAGE_TOUCH_LISTENER); - mSubscroller.destroy(); - mTouchEventHandler.destroy(); - } - - private final static float easeOut(float t) { - // ease-out approx. - // -(t-1)^2+1 - t = t-1; - return -t*t+1; - } - - private void registerEventListener(String event) { - mEventDispatcher.registerEventListener(event, this); - } - - private void unregisterEventListener(String event) { - mEventDispatcher.unregisterEventListener(event, this); - } - - private void setState(PanZoomState state) { - if (state != mState) { - //GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("PanZoom:StateChange", state.toString())); - mState = state; - - // Let the target know we've finished with it (for now) - if (state == PanZoomState.NOTHING) { - mTarget.panZoomStopped(); - } - } - } - - private ImmutableViewportMetrics getMetrics() { - return mTarget.getViewportMetrics(); - } - - private void checkMainThread() { - if (!ThreadUtils.isOnUiThread()) { - // log with full stack trace - Log.e(LOGTAG, "Uh-oh, we're running on the wrong thread!", new Exception()); - } - } - - @Override - public void handleMessage(String event, JSONObject message) { - try { - if (MESSAGE_ZOOM_RECT.equals(event)) { - float x = (float)message.getDouble("x"); - float y = (float)message.getDouble("y"); - final RectF zoomRect = new RectF(x, y, - x + (float)message.getDouble("w"), - y + (float)message.getDouble("h")); - if (message.optBoolean("animate", true)) { - mTarget.post(new Runnable() { - @Override - public void run() { - animatedZoomTo(zoomRect); - } - }); - } else { - mTarget.setViewportMetrics(getMetricsToZoomTo(zoomRect)); - } - } else if (MESSAGE_ZOOM_PAGE.equals(event)) { - ImmutableViewportMetrics metrics = getMetrics(); - RectF cssPageRect = metrics.getCssPageRect(); - - RectF viewableRect = metrics.getCssViewport(); - float y = viewableRect.top; - // attempt to keep zoom keep focused on the center of the viewport - float newHeight = viewableRect.height() * cssPageRect.width() / viewableRect.width(); - float dh = viewableRect.height() - newHeight; // increase in the height - final RectF r = new RectF(0.0f, - y + dh/2, - cssPageRect.width(), - y + dh/2 + newHeight); - if (message.optBoolean("animate", true)) { - mTarget.post(new Runnable() { - @Override - public void run() { - animatedZoomTo(r); - } - }); - } else { - mTarget.setViewportMetrics(getMetricsToZoomTo(r)); - } - } else if (MESSAGE_TOUCH_LISTENER.equals(event)) { - /*int tabId = message.getInt("tabID"); - final Tab tab = Tabs.getInstance().getTab(tabId); - tab.setHasTouchListeners(true); - mTarget.post(new Runnable() { - @Override - public void run() { - if (Tabs.getInstance().isSelectedTab(tab)) - mTouchEventHandler.setWaitForTouchListeners(true); - } - });*/ - } - } catch (Exception e) { - Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); - } - } - - /** This function MUST be called on the UI thread */ - @Override - public boolean onKeyEvent(KeyEvent event) { - if (Build.VERSION.SDK_INT <= 11) { - return false; - } - - if ((event.getSource() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD - && event.getAction() == KeyEvent.ACTION_DOWN) { - - switch (event.getKeyCode()) { - case KeyEvent.KEYCODE_ZOOM_IN: - return animatedScale(0.2f); - case KeyEvent.KEYCODE_ZOOM_OUT: - return animatedScale(-0.2f); - } - } - return false; - } - - /** This function MUST be called on the UI thread */ - @Override - public boolean onMotionEvent(MotionEvent event) { - if (Build.VERSION.SDK_INT <= 11) { - return false; - } - - switch (event.getSource() & InputDevice.SOURCE_CLASS_MASK) { - case InputDevice.SOURCE_CLASS_POINTER: - switch (event.getAction() & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_SCROLL: return handlePointerScroll(event); - } - break; - case InputDevice.SOURCE_CLASS_JOYSTICK: - switch (event.getAction() & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_MOVE: return handleJoystickNav(event); - } - break; - } - return false; - } - - /** This function MUST be called on the UI thread */ - @Override - public boolean onTouchEvent(MotionEvent event) { - return mTouchEventHandler.handleEvent(event); - } - - boolean handleEvent(MotionEvent event, boolean defaultPrevented) { - mDefaultPrevented = defaultPrevented; - - switch (event.getAction() & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_DOWN: return handleTouchStart(event); - case MotionEvent.ACTION_MOVE: return handleTouchMove(event); - case MotionEvent.ACTION_UP: return handleTouchEnd(event); - case MotionEvent.ACTION_CANCEL: return handleTouchCancel(event); - } - return false; - } - - /** This function MUST be called on the UI thread */ - @Override - public void notifyDefaultActionPrevented(boolean prevented) { - mTouchEventHandler.handleEventListenerAction(!prevented); - } - - /** This function must be called from the UI thread. */ - @Override - public void abortAnimation() { - checkMainThread(); - // this happens when gecko changes the viewport on us or if the device is rotated. - // if that's the case, abort any animation in progress and re-zoom so that the page - // snaps to edges. for other cases (where the user's finger(s) are down) don't do - // anything special. - switch (mState) { - case FLING: - mX.stopFling(); - mY.stopFling(); - // fall through - case BOUNCE: - case ANIMATED_ZOOM: - // the zoom that's in progress likely makes no sense any more (such as if - // the screen orientation changed) so abort it - setState(PanZoomState.NOTHING); - // fall through - case NOTHING: - // Don't do animations here; they're distracting and can cause flashes on page - // transitions. - synchronized (mTarget.getLock()) { - mTarget.setViewportMetrics(getValidViewportMetrics()); - mTarget.forceRedraw(null); - } - break; - } - } - - /** This function must be called on the UI thread. */ - public void startingNewEventBlock(MotionEvent event, boolean waitingForTouchListeners) { - checkMainThread(); - mSubscroller.cancel(); - if (waitingForTouchListeners && (event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) { - // this is the first touch point going down, so we enter the pending state - // seting the state will kill any animations in progress, possibly leaving - // the page in overscroll - setState(PanZoomState.WAITING_LISTENERS); - } - } - - /** This must be called on the UI thread. */ - @Override - public void pageRectUpdated() { - if (mState == PanZoomState.NOTHING) { - synchronized (mTarget.getLock()) { - ImmutableViewportMetrics validated = getValidViewportMetrics(); - if (!getMetrics().fuzzyEquals(validated)) { - // page size changed such that we are now in overscroll. snap to the - // the nearest valid viewport - mTarget.setViewportMetrics(validated); - } - } - } - } - - /* - * Panning/scrolling - */ - - private boolean handleTouchStart(MotionEvent event) { - // user is taking control of movement, so stop - // any auto-movement we have going - stopAnimationTask(); - - switch (mState) { - case ANIMATED_ZOOM: - // We just interrupted a double-tap animation, so force a redraw in - // case this touchstart is just a tap that doesn't end up triggering - // a redraw - mTarget.forceRedraw(null); - // fall through - case FLING: - case AUTONAV: - case BOUNCE: - case NOTHING: - case WAITING_LISTENERS: - startTouch(event.getX(0), event.getY(0), event.getEventTime()); - return false; - case TOUCHING: - case PANNING: - case PANNING_LOCKED_X: - case PANNING_LOCKED_Y: - case PANNING_HOLD: - case PANNING_HOLD_LOCKED_X: - case PANNING_HOLD_LOCKED_Y: - case PINCHING: - Log.e(LOGTAG, "Received impossible touch down while in " + mState); - return false; - } - Log.e(LOGTAG, "Unhandled case " + mState + " in handleTouchStart"); - return false; - } - - private boolean handleTouchMove(MotionEvent event) { - - switch (mState) { - case FLING: - case AUTONAV: - case BOUNCE: - case WAITING_LISTENERS: - // should never happen - Log.e(LOGTAG, "Received impossible touch move while in " + mState); - // fall through - case ANIMATED_ZOOM: - case NOTHING: - // may happen if user double-taps and drags without lifting after the - // second tap. ignore the move if this happens. - return false; - - case TOUCHING: - // Don't allow panning if there is an element in full-screen mode. See bug 775511. - if ((mTarget.isFullScreen() && !mSubscroller.scrolling()) || panDistance(event) < PAN_THRESHOLD) { - return false; - } - cancelTouch(); - startPanning(event.getX(0), event.getY(0), event.getEventTime()); - track(event); - return true; - - case PANNING_HOLD_LOCKED_X: - setState(PanZoomState.PANNING_LOCKED_X); - track(event); - return true; - case PANNING_HOLD_LOCKED_Y: - setState(PanZoomState.PANNING_LOCKED_Y); - // fall through - case PANNING_LOCKED_X: - case PANNING_LOCKED_Y: - track(event); - return true; - - case PANNING_HOLD: - setState(PanZoomState.PANNING); - // fall through - case PANNING: - track(event); - return true; - - case PINCHING: - // scale gesture listener will handle this - return false; - } - Log.e(LOGTAG, "Unhandled case " + mState + " in handleTouchMove"); - return false; - } - - private boolean handleTouchEnd(MotionEvent event) { - - switch (mState) { - case FLING: - case AUTONAV: - case BOUNCE: - case ANIMATED_ZOOM: - case NOTHING: - // may happen if user double-taps and drags without lifting after the - // second tap. ignore if this happens. - return false; - - case WAITING_LISTENERS: - if (!mDefaultPrevented) { - // should never happen - Log.e(LOGTAG, "Received impossible touch end while in " + mState); - } - // fall through - case TOUCHING: - // the switch into TOUCHING might have happened while the page was - // snapping back after overscroll. we need to finish the snap if that - // was the case - bounce(); - return false; - - case PANNING: - case PANNING_LOCKED_X: - case PANNING_LOCKED_Y: - case PANNING_HOLD: - case PANNING_HOLD_LOCKED_X: - case PANNING_HOLD_LOCKED_Y: - setState(PanZoomState.FLING); - fling(); - return true; - - case PINCHING: - setState(PanZoomState.NOTHING); - return true; - } - Log.e(LOGTAG, "Unhandled case " + mState + " in handleTouchEnd"); - return false; - } - - private boolean handleTouchCancel(MotionEvent event) { - cancelTouch(); - - // ensure we snap back if we're overscrolled - bounce(); - return false; - } - - private boolean handlePointerScroll(MotionEvent event) { - if (mState == PanZoomState.NOTHING || mState == PanZoomState.FLING) { - float scrollX = event.getAxisValue(MotionEvent.AXIS_HSCROLL); - float scrollY = event.getAxisValue(MotionEvent.AXIS_VSCROLL); - if (mNegateWheelScrollY) { - scrollY *= -1.0; - } - scrollBy(scrollX * MAX_SCROLL, scrollY * MAX_SCROLL); - bounce(); - return true; - } - return false; - } - - private float filterDeadZone(MotionEvent event, int axis) { - return 0; //(GamepadUtils.isValueInDeadZone(event, axis) ? 0 : event.getAxisValue(axis)); - } - - private float normalizeJoystickScroll(MotionEvent event, int axis) { - return filterDeadZone(event, axis) * MAX_SCROLL; - } - - private float normalizeJoystickZoom(MotionEvent event, int axis) { - // negate MAX_ZOOM_DELTA so that pushing up on the stick zooms in - return filterDeadZone(event, axis) * -MAX_ZOOM_DELTA; - } - - // Since this event is a position-based event rather than a motion-based event, we need to - // set up an AUTONAV animation to keep scrolling even while we don't get events. - private boolean handleJoystickNav(MotionEvent event) { - float velocityX = normalizeJoystickScroll(event, MotionEvent.AXIS_X); - float velocityY = normalizeJoystickScroll(event, MotionEvent.AXIS_Y); - float zoomDelta = normalizeJoystickZoom(event, MotionEvent.AXIS_RZ); - - if (velocityX == 0 && velocityY == 0 && zoomDelta == 0) { - if (mState == PanZoomState.AUTONAV) { - bounce(); // if not needed, this will automatically go to state NOTHING - return true; - } - return false; - } - - if (mState == PanZoomState.NOTHING) { - setState(PanZoomState.AUTONAV); - startAnimationRenderTask(new AutonavRenderTask()); - } - if (mState == PanZoomState.AUTONAV) { - mX.setAutoscrollVelocity(velocityX); - mY.setAutoscrollVelocity(velocityY); - mAutonavZoomDelta = zoomDelta; - return true; - } - return false; - } - - private void startTouch(float x, float y, long time) { - mX.startTouch(x); - mY.startTouch(y); - setState(PanZoomState.TOUCHING); - mLastEventTime = time; - } - - private void startPanning(float x, float y, long time) { - float dx = mX.panDistance(x); - float dy = mY.panDistance(y); - double angle = Math.atan2(dy, dx); // range [-pi, pi] - angle = Math.abs(angle); // range [0, pi] - - // When the touch move breaks through the pan threshold, reposition the touch down origin - // so the page won't jump when we start panning. - mX.startTouch(x); - mY.startTouch(y); - mLastEventTime = time; - - if (mMode == AxisLockMode.STANDARD || mMode == AxisLockMode.STICKY) { - if (!mX.scrollable() || !mY.scrollable()) { - setState(PanZoomState.PANNING); - } else if (angle < AXIS_LOCK_ANGLE || angle > (Math.PI - AXIS_LOCK_ANGLE)) { - mY.setScrollingDisabled(true); - setState(PanZoomState.PANNING_LOCKED_X); - } else if (Math.abs(angle - (Math.PI / 2)) < AXIS_LOCK_ANGLE) { - mX.setScrollingDisabled(true); - setState(PanZoomState.PANNING_LOCKED_Y); - } else { - setState(PanZoomState.PANNING); - } - } else if (mMode == AxisLockMode.FREE) { - setState(PanZoomState.PANNING); - } - } - - private float panDistance(MotionEvent move) { - float dx = mX.panDistance(move.getX(0)); - float dy = mY.panDistance(move.getY(0)); - return FloatMath.sqrt(dx * dx + dy * dy); - } - - private void track(float x, float y, long time) { - float timeDelta = (float)(time - mLastEventTime); - if (FloatUtils.fuzzyEquals(timeDelta, 0)) { - // probably a duplicate event, ignore it. using a zero timeDelta will mess - // up our velocity - return; - } - mLastEventTime = time; - - - // if we're axis-locked check if the user is trying to scroll away from the lock - if (mMode == AxisLockMode.STICKY) { - float dx = mX.panDistance(x); - float dy = mY.panDistance(y); - double angle = Math.atan2(dy, dx); // range [-pi, pi] - angle = Math.abs(angle); // range [0, pi] - - if (Math.abs(dx) > AXIS_BREAKOUT_THRESHOLD || Math.abs(dy) > AXIS_BREAKOUT_THRESHOLD) { - if (mState == PanZoomState.PANNING_LOCKED_X) { - if (angle > AXIS_BREAKOUT_ANGLE && angle < (Math.PI - AXIS_BREAKOUT_ANGLE)) { - mY.setScrollingDisabled(false); - setState(PanZoomState.PANNING); - } - } else if (mState == PanZoomState.PANNING_LOCKED_Y) { - if (Math.abs(angle - (Math.PI / 2)) > AXIS_BREAKOUT_ANGLE) { - mX.setScrollingDisabled(false); - setState(PanZoomState.PANNING); - } - } - } - } - - mX.updateWithTouchAt(x, timeDelta); - mY.updateWithTouchAt(y, timeDelta); - } - - private void track(MotionEvent event) { - mX.saveTouchPos(); - mY.saveTouchPos(); - - for (int i = 0; i < event.getHistorySize(); i++) { - track(event.getHistoricalX(0, i), - event.getHistoricalY(0, i), - event.getHistoricalEventTime(i)); - } - track(event.getX(0), event.getY(0), event.getEventTime()); - - if (stopped()) { - if (mState == PanZoomState.PANNING) { - setState(PanZoomState.PANNING_HOLD); - } else if (mState == PanZoomState.PANNING_LOCKED_X) { - setState(PanZoomState.PANNING_HOLD_LOCKED_X); - } else if (mState == PanZoomState.PANNING_LOCKED_Y) { - setState(PanZoomState.PANNING_HOLD_LOCKED_Y); - } else { - // should never happen, but handle anyway for robustness - Log.e(LOGTAG, "Impossible case " + mState + " when stopped in track"); - setState(PanZoomState.PANNING_HOLD); - } - } - - mX.startPan(); - mY.startPan(); - updatePosition(); - } - - private void scrollBy(float dx, float dy) { - mTarget.scrollBy(dx, dy); - } - - private void fling() { - updatePosition(); - - stopAnimationTask(); - - boolean stopped = stopped(); - mX.startFling(stopped); - mY.startFling(stopped); - - startAnimationRenderTask(new FlingRenderTask()); - } - - /* Performs a bounce-back animation to the given viewport metrics. */ - private void bounce(ImmutableViewportMetrics metrics, PanZoomState state) { - stopAnimationTask(); - - ImmutableViewportMetrics bounceStartMetrics = getMetrics(); - if (bounceStartMetrics.fuzzyEquals(metrics)) { - setState(PanZoomState.NOTHING); - return; - } - - setState(state); - - // At this point we have already set mState to BOUNCE or ANIMATED_ZOOM, so - // getRedrawHint() is returning false. This means we can safely call - // setAnimationTarget to set the new final display port and not have it get - // clobbered by display ports from intermediate animation frames. - mTarget.setAnimationTarget(metrics); - startAnimationRenderTask(new BounceRenderTask(bounceStartMetrics, metrics)); - } - - /* Performs a bounce-back animation to the nearest valid viewport metrics. */ - private void bounce() { - bounce(getValidViewportMetrics(), PanZoomState.BOUNCE); - } - - /* Starts the fling or bounce animation. */ - private void startAnimationRenderTask(final PanZoomRenderTask task) { - if (mAnimationRenderTask != null) { - Log.e(LOGTAG, "Attempted to start a new task without canceling the old one!"); - stopAnimationTask(); - } - - mAnimationRenderTask = task; - mTarget.postRenderTask(mAnimationRenderTask); - } - - /* Stops the fling or bounce animation. */ - private void stopAnimationTask() { - if (mAnimationRenderTask != null) { - mAnimationRenderTask.terminate(); - mTarget.removeRenderTask(mAnimationRenderTask); - mAnimationRenderTask = null; - } - } - - private float getVelocity() { - float xvel = mX.getRealVelocity(); - float yvel = mY.getRealVelocity(); - return FloatMath.sqrt(xvel * xvel + yvel * yvel); - } - - @Override - public PointF getVelocityVector() { - return new PointF(mX.getRealVelocity(), mY.getRealVelocity()); - } - - private boolean stopped() { - return getVelocity() < STOPPED_THRESHOLD; - } - - PointF resetDisplacement() { - return new PointF(mX.resetDisplacement(), mY.resetDisplacement()); - } - - private void updatePosition() { - mX.displace(); - mY.displace(); - PointF displacement = resetDisplacement(); - if (FloatUtils.fuzzyEquals(displacement.x, 0.0f) && FloatUtils.fuzzyEquals(displacement.y, 0.0f)) { - return; - } - if (mDefaultPrevented || mSubscroller.scrollBy(displacement)) { - synchronized (mTarget.getLock()) { - mTarget.scrollMarginsBy(displacement.x, displacement.y); - } - } else { - synchronized (mTarget.getLock()) { - scrollBy(displacement.x, displacement.y); - } - } - } - - /** - * This class is an implementation of RenderTask which enforces its implementor to run in the UI thread. - * - */ - private abstract class PanZoomRenderTask extends RenderTask { - - /** - * the time when the current frame was started in ns. - */ - protected long mCurrentFrameStartTime; - /** - * The current frame duration in ns. - */ - protected long mLastFrameTimeDelta; - - private final Runnable mRunnable = new Runnable() { - @Override - public final void run() { - if (mContinueAnimation) { - animateFrame(); - } - } - }; - - private boolean mContinueAnimation = true; - - public PanZoomRenderTask() { - super(false); - } - - @Override - protected final boolean internalRun(long timeDelta, long currentFrameStartTime) { - - mCurrentFrameStartTime = currentFrameStartTime; - mLastFrameTimeDelta = timeDelta; - - mTarget.post(mRunnable); - return mContinueAnimation; - } - - /** - * The method subclasses must override. This method is run on the UI thread thanks to internalRun - */ - protected abstract void animateFrame(); - - /** - * Terminate the animation. - */ - public void terminate() { - mContinueAnimation = false; - } - } - - private class AutonavRenderTask extends PanZoomRenderTask { - public AutonavRenderTask() { - super(); - } - - @Override - protected void animateFrame() { - if (mState != PanZoomState.AUTONAV) { - finishAnimation(); - return; - } - - updatePosition(); - synchronized (mTarget.getLock()) { - mTarget.setViewportMetrics(applyZoomDelta(getMetrics(), mAutonavZoomDelta)); - } - } - } - - /* The task that performs the bounce animation. */ - private class BounceRenderTask extends PanZoomRenderTask { - - /* - * The viewport metrics that represent the start and end of the bounce-back animation, - * respectively. - */ - private ImmutableViewportMetrics mBounceStartMetrics; - private ImmutableViewportMetrics mBounceEndMetrics; - // How long ago this bounce was started in ns. - private long mBounceDuration; - - BounceRenderTask(ImmutableViewportMetrics startMetrics, ImmutableViewportMetrics endMetrics) { - super(); - mBounceStartMetrics = startMetrics; - mBounceEndMetrics = endMetrics; - } - - @Override - protected void animateFrame() { - /* - * The pan/zoom controller might have signaled to us that it wants to abort the - * animation by setting the state to PanZoomState.NOTHING. Handle this case and bail - * out. - */ - if (!(mState == PanZoomState.BOUNCE || mState == PanZoomState.ANIMATED_ZOOM)) { - finishAnimation(); - return; - } - - /* Perform the next frame of the bounce-back animation. */ - mBounceDuration = mCurrentFrameStartTime - getStartTime(); - if (mBounceDuration < BOUNCE_ANIMATION_DURATION) { - advanceBounce(); - return; - } - - /* Finally, if there's nothing else to do, complete the animation and go to sleep. */ - finishBounce(); - finishAnimation(); - setState(PanZoomState.NOTHING); - } - - /* Performs one frame of a bounce animation. */ - private void advanceBounce() { - synchronized (mTarget.getLock()) { - float t = easeOut((float)mBounceDuration / BOUNCE_ANIMATION_DURATION); - ImmutableViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t); - mTarget.setViewportMetrics(newMetrics); - } - } - - /* Concludes a bounce animation and snaps the viewport into place. */ - private void finishBounce() { - synchronized (mTarget.getLock()) { - mTarget.setViewportMetrics(mBounceEndMetrics); - } - } - } - - // The callback that performs the fling animation. - private class FlingRenderTask extends PanZoomRenderTask { - - public FlingRenderTask() { - super(); - } - - @Override - protected void animateFrame() { - /* - * The pan/zoom controller might have signaled to us that it wants to abort the - * animation by setting the state to PanZoomState.NOTHING. Handle this case and bail - * out. - */ - if (mState != PanZoomState.FLING) { - finishAnimation(); - return; - } - - /* Advance flings, if necessary. */ - boolean flingingX = mX.advanceFling(mLastFrameTimeDelta); - boolean flingingY = mY.advanceFling(mLastFrameTimeDelta); - - boolean overscrolled = (mX.overscrolled() || mY.overscrolled()); - - /* If we're still flinging in any direction, update the origin. */ - if (flingingX || flingingY) { - updatePosition(); - - /* - * Check to see if we're still flinging with an appreciable velocity. The threshold is - * higher in the case of overscroll, so we bounce back eagerly when overscrolling but - * coast smoothly to a stop when not. In other words, require a greater velocity to - * maintain the fling once we enter overscroll. - */ - float threshold = (overscrolled && !mSubscroller.scrolling() ? STOPPED_THRESHOLD : FLING_STOPPED_THRESHOLD); - if (getVelocity() >= threshold) { - // we're still flinging - return; - } - - mX.stopFling(); - mY.stopFling(); - } - - /* Perform a bounce-back animation if overscrolled. */ - if (overscrolled) { - bounce(); - } else { - finishAnimation(); - setState(PanZoomState.NOTHING); - } - } - } - - private void finishAnimation() { - checkMainThread(); - - stopAnimationTask(); - - // Force a viewport synchronisation - mTarget.forceRedraw(null); - } - - /* Returns the nearest viewport metrics with no overscroll visible. */ - private ImmutableViewportMetrics getValidViewportMetrics() { - return getValidViewportMetrics(getMetrics()); - } - - private ImmutableViewportMetrics getValidViewportMetrics(ImmutableViewportMetrics viewportMetrics) { - /* First, we adjust the zoom factor so that we can make no overscrolled area visible. */ - float zoomFactor = viewportMetrics.zoomFactor; - RectF pageRect = viewportMetrics.getPageRect(); - RectF viewport = viewportMetrics.getViewport(); - - float focusX = viewport.width() / 2.0f; - float focusY = viewport.height() / 2.0f; - - float minZoomFactor = 0.0f; - float maxZoomFactor = MAX_ZOOM; - - ZoomConstraints constraints = mTarget.getZoomConstraints(); - - if (constraints.getMinZoom() > 0) - minZoomFactor = constraints.getMinZoom(); - if (constraints.getMaxZoom() > 0) - maxZoomFactor = constraints.getMaxZoom(); - - if (!constraints.getAllowZoom()) { - // If allowZoom is false, clamp to the default zoom level. - maxZoomFactor = minZoomFactor = constraints.getDefaultZoom(); - } - - // Ensure minZoomFactor keeps the page at least as big as the viewport. - if (pageRect.width() > 0) { - float pageWidth = pageRect.width() + - viewportMetrics.marginLeft + - viewportMetrics.marginRight; - float scaleFactor = viewport.width() / pageWidth; - minZoomFactor = Math.max(minZoomFactor, zoomFactor * scaleFactor); - if (viewport.width() > pageWidth) - focusX = 0.0f; - } - if (pageRect.height() > 0) { - float pageHeight = pageRect.height() + - viewportMetrics.marginTop + - viewportMetrics.marginBottom; - float scaleFactor = viewport.height() / pageHeight; - minZoomFactor = Math.max(minZoomFactor, zoomFactor * scaleFactor); - if (viewport.height() > pageHeight) - focusY = 0.0f; - } - - maxZoomFactor = Math.max(maxZoomFactor, minZoomFactor); - - if (zoomFactor < minZoomFactor) { - // if one (or both) of the page dimensions is smaller than the viewport, - // zoom using the top/left as the focus on that axis. this prevents the - // scenario where, if both dimensions are smaller than the viewport, but - // by different scale factors, we end up scrolled to the end on one axis - // after applying the scale - PointF center = new PointF(focusX, focusY); - viewportMetrics = viewportMetrics.scaleTo(minZoomFactor, center); - } else if (zoomFactor > maxZoomFactor) { - PointF center = new PointF(viewport.width() / 2.0f, viewport.height() / 2.0f); - viewportMetrics = viewportMetrics.scaleTo(maxZoomFactor, center); - } - - /* Now we pan to the right origin. */ - viewportMetrics = viewportMetrics.clampWithMargins(); - - return viewportMetrics; - } - - private class AxisX extends Axis { - AxisX(SubdocumentScrollHelper subscroller) { super(subscroller); } - @Override - public float getOrigin() { return getMetrics().viewportRectLeft; } - @Override - protected float getViewportLength() { return getMetrics().getWidth(); } - @Override - protected float getPageStart() { return getMetrics().pageRectLeft; } - @Override - protected float getMarginStart() { return mTarget.getMaxMargins().left - getMetrics().marginLeft; } - @Override - protected float getMarginEnd() { return mTarget.getMaxMargins().right - getMetrics().marginRight; } - @Override - protected float getPageLength() { return getMetrics().getPageWidthWithMargins(); } - @Override - protected boolean marginsHidden() { - ImmutableViewportMetrics metrics = getMetrics(); - RectF maxMargins = mTarget.getMaxMargins(); - return (metrics.marginLeft < maxMargins.left || metrics.marginRight < maxMargins.right); - } - @Override - protected void overscrollFling(final float velocity) { - if (mOverscroll != null) { - mOverscroll.setVelocity(velocity, Overscroll.Axis.X); - } - } - @Override - protected void overscrollPan(final float distance) { - if (mOverscroll != null) { - mOverscroll.setDistance(distance, Overscroll.Axis.X); - } - } - } - - private class AxisY extends Axis { - AxisY(SubdocumentScrollHelper subscroller) { super(subscroller); } - @Override - public float getOrigin() { return getMetrics().viewportRectTop; } - @Override - protected float getViewportLength() { return getMetrics().getHeight(); } - @Override - protected float getPageStart() { return getMetrics().pageRectTop; } - @Override - protected float getPageLength() { return getMetrics().getPageHeightWithMargins(); } - @Override - protected float getMarginStart() { return mTarget.getMaxMargins().top - getMetrics().marginTop; } - @Override - protected float getMarginEnd() { return mTarget.getMaxMargins().bottom - getMetrics().marginBottom; } - @Override - protected boolean marginsHidden() { - ImmutableViewportMetrics metrics = getMetrics(); - RectF maxMargins = mTarget.getMaxMargins(); - return (metrics.marginTop < maxMargins.top || metrics.marginBottom < maxMargins.bottom); - } - @Override - protected void overscrollFling(final float velocity) { - if (mOverscroll != null) { - mOverscroll.setVelocity(velocity, Overscroll.Axis.Y); - } - } - @Override - protected void overscrollPan(final float distance) { - if (mOverscroll != null) { - mOverscroll.setDistance(distance, Overscroll.Axis.Y); - } - } - } - - /* - * Zooming - */ - @Override - public boolean onScaleBegin(SimpleScaleGestureDetector detector) { - if (mState == PanZoomState.ANIMATED_ZOOM) - return false; - - if (!mTarget.getZoomConstraints().getAllowZoom()) - return false; - - setState(PanZoomState.PINCHING); - mLastZoomFocus = new PointF(detector.getFocusX(), detector.getFocusY()); - cancelTouch(); - - //GeckoAppShell.sendEventToGecko(GeckoEvent.createNativeGestureEvent(GeckoEvent.ACTION_MAGNIFY_START, mLastZoomFocus, getMetrics().zoomFactor)); - - return true; - } - - @Override - public boolean onScale(SimpleScaleGestureDetector detector) { - if (mTarget.isFullScreen()) - return false; - - if (mState != PanZoomState.PINCHING) - return false; - - float prevSpan = detector.getPreviousSpan(); - if (FloatUtils.fuzzyEquals(prevSpan, 0.0f)) { - // let's eat this one to avoid setting the new zoom to infinity (bug 711453) - return true; - } - - synchronized (mTarget.getLock()) { - float zoomFactor = getAdjustedZoomFactor(detector.getCurrentSpan() / prevSpan); - scrollBy(mLastZoomFocus.x - detector.getFocusX(), - mLastZoomFocus.y - detector.getFocusY()); - mLastZoomFocus.set(detector.getFocusX(), detector.getFocusY()); - ImmutableViewportMetrics target = getMetrics().scaleTo(zoomFactor, mLastZoomFocus); - - // If overscroll is diabled, prevent zooming outside the normal document pans. - if (mX.getOverScrollMode() == View.OVER_SCROLL_NEVER || mY.getOverScrollMode() == View.OVER_SCROLL_NEVER) { - target = getValidViewportMetrics(target); - } - mTarget.setViewportMetrics(target); - } - - //GeckoEvent event = GeckoEvent.createNativeGestureEvent(GeckoEvent.ACTION_MAGNIFY, mLastZoomFocus, getMetrics().zoomFactor); - //GeckoAppShell.sendEventToGecko(event); - - return true; - } - - private ImmutableViewportMetrics applyZoomDelta(ImmutableViewportMetrics metrics, float zoomDelta) { - float oldZoom = metrics.zoomFactor; - float newZoom = oldZoom + zoomDelta; - float adjustedZoom = getAdjustedZoomFactor(newZoom / oldZoom); - // since we don't have a particular focus to zoom to, just use the center - PointF center = new PointF(metrics.getWidth() / 2.0f, metrics.getHeight() / 2.0f); - metrics = metrics.scaleTo(adjustedZoom, center); - return metrics; - } - - private boolean animatedScale(float zoomDelta) { - if (mState != PanZoomState.NOTHING && mState != PanZoomState.BOUNCE) { - return false; - } - synchronized (mTarget.getLock()) { - ImmutableViewportMetrics metrics = applyZoomDelta(getMetrics(), zoomDelta); - bounce(getValidViewportMetrics(metrics), PanZoomState.BOUNCE); - } - return true; - } - - private float getAdjustedZoomFactor(float zoomRatio) { - /* - * Apply edge resistance if we're zoomed out smaller than the page size by scaling the zoom - * factor toward 1.0. - */ - float resistance = Math.min(mX.getEdgeResistance(true), mY.getEdgeResistance(true)); - if (zoomRatio > 1.0f) - zoomRatio = 1.0f + (zoomRatio - 1.0f) * resistance; - else - zoomRatio = 1.0f - (1.0f - zoomRatio) * resistance; - - float newZoomFactor = getMetrics().zoomFactor * zoomRatio; - float minZoomFactor = 0.0f; - float maxZoomFactor = MAX_ZOOM; - - ZoomConstraints constraints = mTarget.getZoomConstraints(); - - if (constraints.getMinZoom() > 0) - minZoomFactor = constraints.getMinZoom(); - if (constraints.getMaxZoom() > 0) - maxZoomFactor = constraints.getMaxZoom(); - - if (newZoomFactor < minZoomFactor) { - // apply resistance when zooming past minZoomFactor, - // such that it asymptotically reaches minZoomFactor / 2.0 - // but never exceeds that - final float rate = 0.5f; // controls how quickly we approach the limit - float excessZoom = minZoomFactor - newZoomFactor; - excessZoom = 1.0f - (float)Math.exp(-excessZoom * rate); - newZoomFactor = minZoomFactor * (1.0f - excessZoom / 2.0f); - } - - if (newZoomFactor > maxZoomFactor) { - // apply resistance when zooming past maxZoomFactor, - // such that it asymptotically reaches maxZoomFactor + 1.0 - // but never exceeds that - float excessZoom = newZoomFactor - maxZoomFactor; - excessZoom = 1.0f - (float)Math.exp(-excessZoom); - newZoomFactor = maxZoomFactor + excessZoom; - } - - return newZoomFactor; - } - - @Override - public void onScaleEnd(SimpleScaleGestureDetector detector) { - if (mState == PanZoomState.ANIMATED_ZOOM) - return; - - // switch back to the touching state - startTouch(detector.getFocusX(), detector.getFocusY(), detector.getEventTime()); - - // Force a viewport synchronisation - mTarget.forceRedraw(null); - - PointF point = new PointF(detector.getFocusX(), detector.getFocusY()); - //GeckoEvent event = GeckoEvent.createNativeGestureEvent(GeckoEvent.ACTION_MAGNIFY_END, point, getMetrics().zoomFactor); - - //if (event == null) { - // return; - //} - - //GeckoAppShell.sendEventToGecko(event); - } - - @Override - public boolean getRedrawHint() { - switch (mState) { - case PINCHING: - case ANIMATED_ZOOM: - case BOUNCE: - // don't redraw during these because the zoom is (or might be, in the case - // of BOUNCE) be changing rapidly and gecko will have to redraw the entire - // display port area. we trigger a force-redraw upon exiting these states. - return false; - default: - // allow redrawing in other states - return true; - } - } - - private void sendPointToGecko(String event, MotionEvent motionEvent) { - String json; - try { - PointF point = new PointF(motionEvent.getX(), motionEvent.getY()); - point = mTarget.convertViewPointToLayerPoint(point); - if (point == null) { - return; - } - json = PointUtils.toJSON(point).toString(); - } catch (Exception e) { - Log.e(LOGTAG, "Unable to convert point to JSON for " + event, e); - return; - } - - //GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(event, json)); - } - - @Override - public boolean onDown(MotionEvent motionEvent) { - mMediumPress = false; - return false; - } - - @Override - public void onShowPress(MotionEvent motionEvent) { - // If we get this, it will be followed either by a call to - // onSingleTapUp (if the user lifts their finger before the - // long-press timeout) or a call to onLongPress (if the user - // does not). In the former case, we want to make sure it is - // treated as a click. (Note that if this is called, we will - // not get a call to onDoubleTap). - mMediumPress = true; - } - - @Override - public void onLongPress(MotionEvent motionEvent) { - sendPointToGecko("Gesture:LongPress", motionEvent); - } - - @Override - public boolean onSingleTapUp(MotionEvent motionEvent) { - // When zooming is enabled, we wait to see if there's a double-tap. - // However, if mMediumPress is true then we know there will be no - // double-tap so we treat this as a click. - if (mMediumPress || !mTarget.getZoomConstraints().getAllowZoom()) { - sendPointToGecko("Gesture:SingleTap", motionEvent); - } - // return false because we still want to get the ACTION_UP event that triggers this - return false; - } - - @Override - public boolean onSingleTapConfirmed(MotionEvent motionEvent) { - // When zooming is disabled, we handle this in onSingleTapUp. - if (mTarget.getZoomConstraints().getAllowZoom()) { - sendPointToGecko("Gesture:SingleTap", motionEvent); - } - return true; - } - - @Override - public boolean onDoubleTap(MotionEvent motionEvent) { - if (mTarget.getZoomConstraints().getAllowZoom()) { - sendPointToGecko("Gesture:DoubleTap", motionEvent); - } - return true; - } - - private void cancelTouch() { - //GeckoEvent e = GeckoEvent.createBroadcastEvent("Gesture:CancelTouch", ""); - //GeckoAppShell.sendEventToGecko(e); - } - - /** - * Zoom to a specified rect IN CSS PIXELS. - * - * While we usually use device pixels, @zoomToRect must be specified in CSS - * pixels. - */ - private ImmutableViewportMetrics getMetricsToZoomTo(RectF zoomToRect) { - final float startZoom = getMetrics().zoomFactor; - - RectF viewport = getMetrics().getViewport(); - // 1. adjust the aspect ratio of zoomToRect to match that of the current viewport, - // enlarging as necessary (if it gets too big, it will get shrunk in the next step). - // while enlarging make sure we enlarge equally on both sides to keep the target rect - // centered. - float targetRatio = viewport.width() / viewport.height(); - float rectRatio = zoomToRect.width() / zoomToRect.height(); - if (FloatUtils.fuzzyEquals(targetRatio, rectRatio)) { - // all good, do nothing - } else if (targetRatio < rectRatio) { - // need to increase zoomToRect height - float newHeight = zoomToRect.width() / targetRatio; - zoomToRect.top -= (newHeight - zoomToRect.height()) / 2; - zoomToRect.bottom = zoomToRect.top + newHeight; - } else { // targetRatio > rectRatio) { - // need to increase zoomToRect width - float newWidth = targetRatio * zoomToRect.height(); - zoomToRect.left -= (newWidth - zoomToRect.width()) / 2; - zoomToRect.right = zoomToRect.left + newWidth; - } - - float finalZoom = viewport.width() / zoomToRect.width(); - - ImmutableViewportMetrics finalMetrics = getMetrics(); - finalMetrics = finalMetrics.setViewportOrigin( - zoomToRect.left * finalMetrics.zoomFactor, - zoomToRect.top * finalMetrics.zoomFactor); - finalMetrics = finalMetrics.scaleTo(finalZoom, new PointF(0.0f, 0.0f)); - - // 2. now run getValidViewportMetrics on it, so that the target viewport is - // clamped down to prevent overscroll, over-zoom, and other bad conditions. - finalMetrics = getValidViewportMetrics(finalMetrics); - return finalMetrics; - } - - private boolean animatedZoomTo(RectF zoomToRect) { - bounce(getMetricsToZoomTo(zoomToRect), PanZoomState.ANIMATED_ZOOM); - return true; - } - - /** This function must be called from the UI thread. */ - @Override - public void abortPanning() { - checkMainThread(); - bounce(); - } - - @Override - public void setOverScrollMode(int overscrollMode) { - mX.setOverScrollMode(overscrollMode); - mY.setOverScrollMode(overscrollMode); - } - - @Override - public int getOverScrollMode() { - return mX.getOverScrollMode(); - } - - @Override - public void setOverscrollHandler(final Overscroll handler) { - mOverscroll = handler; - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/Layer.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/Layer.java deleted file mode 100644 index cae7377d7a29..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/Layer.java +++ /dev/null @@ -1,207 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import org.mozilla.gecko.util.FloatUtils; - -import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.RectF; - -import java.nio.FloatBuffer; -import java.util.concurrent.locks.ReentrantLock; - -public abstract class Layer { - private final ReentrantLock mTransactionLock; - private boolean mInTransaction; - private Rect mNewPosition; - private float mNewResolution; - - protected Rect mPosition; - protected float mResolution; - - public Layer() { - this(null); - } - - public Layer(IntSize size) { - mTransactionLock = new ReentrantLock(); - if (size == null) { - mPosition = new Rect(); - } else { - mPosition = new Rect(0, 0, size.width, size.height); - } - mResolution = 1.0f; - } - - /** - * Updates the layer. This returns false if there is still work to be done - * after this update. - */ - public final boolean update(RenderContext context) { - if (mTransactionLock.isHeldByCurrentThread()) { - throw new RuntimeException("draw() called while transaction lock held by this " + - "thread?!"); - } - - if (mTransactionLock.tryLock()) { - try { - performUpdates(context); - return true; - } finally { - mTransactionLock.unlock(); - } - } - - return false; - } - - /** Subclasses override this function to draw the layer. */ - public abstract void draw(RenderContext context); - - /** Given the intrinsic size of the layer, returns the pixel boundaries of the layer rect. */ - protected RectF getBounds(RenderContext context) { - return RectUtils.scale(new RectF(mPosition), context.zoomFactor / mResolution); - } - - /** - * Call this before modifying the layer. Note that, for TileLayers, "modifying the layer" - * includes altering the underlying CairoImage in any way. Thus you must call this function - * before modifying the byte buffer associated with this layer. - * - * This function may block, so you should never call this on the main UI thread. - */ - public void beginTransaction() { - if (mTransactionLock.isHeldByCurrentThread()) - throw new RuntimeException("Nested transactions are not supported"); - mTransactionLock.lock(); - mInTransaction = true; - mNewResolution = mResolution; - } - - /** Call this when you're done modifying the layer. */ - public void endTransaction() { - if (!mInTransaction) - throw new RuntimeException("endTransaction() called outside a transaction"); - mInTransaction = false; - mTransactionLock.unlock(); - } - - /** Returns true if the layer is currently in a transaction and false otherwise. */ - protected boolean inTransaction() { - return mInTransaction; - } - - /** Returns the current layer position. */ - public Rect getPosition() { - return mPosition; - } - - /** Sets the position. Only valid inside a transaction. */ - public void setPosition(Rect newPosition) { - if (!mInTransaction) - throw new RuntimeException("setPosition() is only valid inside a transaction"); - mNewPosition = newPosition; - } - - /** Returns the current layer's resolution. */ - public float getResolution() { - return mResolution; - } - - /** - * Sets the layer resolution. This value is used to determine how many pixels per - * device pixel this layer was rendered at. This will be reflected by scaling by - * the reciprocal of the resolution in the layer's transform() function. - * Only valid inside a transaction. */ - public void setResolution(float newResolution) { - if (!mInTransaction) - throw new RuntimeException("setResolution() is only valid inside a transaction"); - mNewResolution = newResolution; - } - - /** - * Subclasses may override this method to perform custom layer updates. This will be called - * with the transaction lock held. Subclass implementations of this method must call the - * superclass implementation. Returns false if there is still work to be done after this - * update is complete. - */ - protected void performUpdates(RenderContext context) { - if (mNewPosition != null) { - mPosition = mNewPosition; - mNewPosition = null; - } - if (mNewResolution != 0.0f) { - mResolution = mNewResolution; - mNewResolution = 0.0f; - } - } - - /** - * This function fills in the provided <tt>dest</tt> array with values to render a texture. - * The array is filled with 4 sets of {x, y, z, texture_x, texture_y} values (so 20 values - * in total) corresponding to the corners of the rect. - */ - protected final void fillRectCoordBuffer(float[] dest, RectF rect, float viewWidth, float viewHeight, - Rect cropRect, float texWidth, float texHeight) { - //x, y, z, texture_x, texture_y - dest[0] = rect.left / viewWidth; - dest[1] = rect.bottom / viewHeight; - dest[2] = 0; - dest[3] = cropRect.left / texWidth; - dest[4] = cropRect.top / texHeight; - - dest[5] = rect.left / viewWidth; - dest[6] = rect.top / viewHeight; - dest[7] = 0; - dest[8] = cropRect.left / texWidth; - dest[9] = cropRect.bottom / texHeight; - - dest[10] = rect.right / viewWidth; - dest[11] = rect.bottom / viewHeight; - dest[12] = 0; - dest[13] = cropRect.right / texWidth; - dest[14] = cropRect.top / texHeight; - - dest[15] = rect.right / viewWidth; - dest[16] = rect.top / viewHeight; - dest[17] = 0; - dest[18] = cropRect.right / texWidth; - dest[19] = cropRect.bottom / texHeight; - } - - public static class RenderContext { - public final RectF viewport; - public final RectF pageRect; - public final float zoomFactor; - public final PointF offset; - public final int positionHandle; - public final int textureHandle; - public final FloatBuffer coordBuffer; - - public RenderContext(RectF aViewport, RectF aPageRect, float aZoomFactor, PointF aOffset, - int aPositionHandle, int aTextureHandle, FloatBuffer aCoordBuffer) { - viewport = aViewport; - pageRect = aPageRect; - zoomFactor = aZoomFactor; - offset = aOffset; - positionHandle = aPositionHandle; - textureHandle = aTextureHandle; - coordBuffer = aCoordBuffer; - } - - public boolean fuzzyEquals(RenderContext other) { - if (other == null) { - return false; - } - return RectUtils.fuzzyEquals(viewport, other.viewport) - && RectUtils.fuzzyEquals(pageRect, other.pageRect) - && FloatUtils.fuzzyEquals(zoomFactor, other.zoomFactor) - && FloatUtils.fuzzyEquals(offset, other.offset); - } - } -} - diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/LayerMarginsAnimator.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/LayerMarginsAnimator.java deleted file mode 100644 index c2b719b914dc..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/LayerMarginsAnimator.java +++ /dev/null @@ -1,324 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -//import org.mozilla.gecko.GeckoAppShell; -//import org.mozilla.gecko.GeckoEvent; -//import org.mozilla.gecko.PrefsHelper; -import org.mozilla.gecko.TouchEventInterceptor; -import org.mozilla.gecko.util.FloatUtils; -import org.mozilla.gecko.util.ThreadUtils; - -import android.graphics.PointF; -import android.graphics.RectF; -import android.os.SystemClock; -import android.util.Log; -import android.view.animation.DecelerateInterpolator; -import android.view.MotionEvent; -import android.view.View; - -public class LayerMarginsAnimator implements TouchEventInterceptor { - private static final String LOGTAG = "GeckoLayerMarginsAnimator"; - // The duration of the animation in ns - private static final long MARGIN_ANIMATION_DURATION = 250000000; - private static final String PREF_SHOW_MARGINS_THRESHOLD = "browser.ui.show-margins-threshold"; - - /* This is the proportion of the viewport rect, minus maximum margins, - * that needs to be travelled before margins will be exposed. - */ - private float SHOW_MARGINS_THRESHOLD = 0.20f; - - /* This rect stores the maximum value margins can grow to when scrolling. When writing - * to this member variable, or when reading from this member variable on a non-UI thread, - * you must synchronize on the LayerMarginsAnimator instance. */ - private final RectF mMaxMargins; - /* If this boolean is true, scroll changes will not affect margins */ - private boolean mMarginsPinned; - /* The task that handles showing/hiding margins */ - private LayerMarginsAnimationTask mAnimationTask; - /* This interpolator is used for the above mentioned animation */ - private final DecelerateInterpolator mInterpolator; - /* The GeckoLayerClient whose margins will be animated */ - private final GeckoLayerClient mTarget; - /* The distance that has been scrolled since either the first touch event, - * or since the margins were last fully hidden */ - private final PointF mTouchTravelDistance; - /* The ID of the prefs listener for the show-marginss threshold */ - private Integer mPrefObserverId; - - public LayerMarginsAnimator(GeckoLayerClient aTarget, LayerView aView) { - // Assign member variables from parameters - mTarget = aTarget; - - // Create other member variables - mMaxMargins = new RectF(); - mInterpolator = new DecelerateInterpolator(); - mTouchTravelDistance = new PointF(); - - // Listen to the dynamic toolbar pref - /*mPrefObserverId = PrefsHelper.getPref(PREF_SHOW_MARGINS_THRESHOLD, new PrefsHelper.PrefHandlerBase() { - @Override - public void prefValue(String pref, int value) { - SHOW_MARGINS_THRESHOLD = (float)value / 100.0f; - } - - @Override - public boolean isObserver() { - return true; - } - });*/ - - // Listen to touch events, for auto-pinning - aView.addTouchInterceptor(this); - } - - public void destroy() { - if (mPrefObserverId != null) { - //PrefsHelper.removeObserver(mPrefObserverId); - mPrefObserverId = null; - } - } - - /** - * Sets the maximum values for margins to grow to, in pixels. - */ - public synchronized void setMaxMargins(float left, float top, float right, float bottom) { - ThreadUtils.assertOnUiThread(); - - mMaxMargins.set(left, top, right, bottom); - - // Update the Gecko-side global for fixed viewport margins. - /*GeckoAppShell.sendEventToGecko( - GeckoEvent.createBroadcastEvent("Viewport:FixedMarginsChanged", - "{ \"top\" : " + top + ", \"right\" : " + right - + ", \"bottom\" : " + bottom + ", \"left\" : " + left + " }"));*/ - } - - RectF getMaxMargins() { - return mMaxMargins; - } - - private void animateMargins(final float left, final float top, final float right, final float bottom, boolean immediately) { - if (mAnimationTask != null) { - mTarget.getView().removeRenderTask(mAnimationTask); - mAnimationTask = null; - } - - if (immediately) { - ImmutableViewportMetrics newMetrics = mTarget.getViewportMetrics().setMargins(left, top, right, bottom); - mTarget.forceViewportMetrics(newMetrics, true, true); - return; - } - - ImmutableViewportMetrics metrics = mTarget.getViewportMetrics(); - - mAnimationTask = new LayerMarginsAnimationTask(false, metrics, left, top, right, bottom); - mTarget.getView().postRenderTask(mAnimationTask); - } - - /** - * Exposes the margin area by growing the margin components of the current - * metrics to the values set in setMaxMargins. - */ - public synchronized void showMargins(boolean immediately) { - animateMargins(mMaxMargins.left, mMaxMargins.top, mMaxMargins.right, mMaxMargins.bottom, immediately); - } - - public synchronized void hideMargins(boolean immediately) { - animateMargins(0, 0, 0, 0, immediately); - } - - public void setMarginsPinned(boolean pin) { - if (pin == mMarginsPinned) { - return; - } - - mMarginsPinned = pin; - } - - public boolean areMarginsShown() { - final ImmutableViewportMetrics metrics = mTarget.getViewportMetrics(); - return metrics.marginLeft != 0 || - metrics.marginRight != 0 || - metrics.marginTop != 0 || - metrics.marginBottom != 0; - } - - /** - * This function will scroll a margin down to zero, or up to the maximum - * specified margin size and return the left-over delta. - * aMargins are in/out parameters. In specifies the current margin size, - * and out specifies the modified margin size. They are specified in the - * order of start-margin, then end-margin. - * This function will also take into account how far the touch point has - * moved and react accordingly. If a touch point hasn't moved beyond a - * certain threshold, margins can only be hidden and not shown. - * aNegativeOffset can be used if the remaining delta should be determined - * by the end-margin instead of the start-margin (for example, in rtl - * pages). - */ - private float scrollMargin(float[] aMargins, float aDelta, - float aOverscrollStart, float aOverscrollEnd, - float aTouchTravelDistance, - float aViewportStart, float aViewportEnd, - float aPageStart, float aPageEnd, - float aMaxMarginStart, float aMaxMarginEnd, - boolean aNegativeOffset) { - float marginStart = aMargins[0]; - float marginEnd = aMargins[1]; - float viewportSize = aViewportEnd - aViewportStart; - float exposeThreshold = viewportSize * SHOW_MARGINS_THRESHOLD; - - if (aDelta >= 0) { - float marginDelta = Math.max(0, aDelta - aOverscrollStart); - aMargins[0] = marginStart - Math.min(marginDelta, marginStart); - if (aTouchTravelDistance < exposeThreshold && marginEnd == 0) { - // We only want the margin to be newly exposed after the touch - // has moved a certain distance. - marginDelta = Math.max(0, marginDelta - (aPageEnd - aViewportEnd)); - } - aMargins[1] = marginEnd + Math.min(marginDelta, aMaxMarginEnd - marginEnd); - } else { - float marginDelta = Math.max(0, -aDelta - aOverscrollEnd); - aMargins[1] = marginEnd - Math.min(marginDelta, marginEnd); - if (-aTouchTravelDistance < exposeThreshold && marginStart == 0) { - marginDelta = Math.max(0, marginDelta - (aViewportStart - aPageStart)); - } - aMargins[0] = marginStart + Math.min(marginDelta, aMaxMarginStart - marginStart); - } - - if (aNegativeOffset) { - return aDelta - (marginEnd - aMargins[1]); - } - return aDelta - (marginStart - aMargins[0]); - } - - /* - * Taking maximum margins into account, offsets the margins and then the - * viewport origin and returns the modified metrics. - */ - ImmutableViewportMetrics scrollBy(ImmutableViewportMetrics aMetrics, float aDx, float aDy) { - float[] newMarginsX = { aMetrics.marginLeft, aMetrics.marginRight }; - float[] newMarginsY = { aMetrics.marginTop, aMetrics.marginBottom }; - - // Only alter margins if the toolbar isn't pinned - if (!mMarginsPinned) { - // Make sure to cancel any margin animations when margin-scrolling begins - if (mAnimationTask != null) { - mTarget.getView().removeRenderTask(mAnimationTask); - mAnimationTask = null; - } - - // Reset the touch travel when changing direction - if ((aDx >= 0) != (mTouchTravelDistance.x >= 0)) { - mTouchTravelDistance.x = 0; - } - if ((aDy >= 0) != (mTouchTravelDistance.y >= 0)) { - mTouchTravelDistance.y = 0; - } - - mTouchTravelDistance.offset(aDx, aDy); - RectF overscroll = aMetrics.getOverscroll(); - - // Only allow margins to scroll if the page can fill the viewport. - if (aMetrics.getPageWidth() >= aMetrics.getWidth()) { - aDx = scrollMargin(newMarginsX, aDx, - overscroll.left, overscroll.right, - mTouchTravelDistance.x, - aMetrics.viewportRectLeft, aMetrics.viewportRectRight, - aMetrics.pageRectLeft, aMetrics.pageRectRight, - mMaxMargins.left, mMaxMargins.right, - aMetrics.isRTL); - } - if (aMetrics.getPageHeight() >= aMetrics.getHeight()) { - aDy = scrollMargin(newMarginsY, aDy, - overscroll.top, overscroll.bottom, - mTouchTravelDistance.y, - aMetrics.viewportRectTop, aMetrics.viewportRectBottom, - aMetrics.pageRectTop, aMetrics.pageRectBottom, - mMaxMargins.top, mMaxMargins.bottom, - false); - } - } - - return aMetrics.setMargins(newMarginsX[0], newMarginsY[0], newMarginsX[1], newMarginsY[1]).offsetViewportBy(aDx, aDy); - } - - /** Implementation of TouchEventInterceptor */ - @Override - public boolean onTouch(View view, MotionEvent event) { - return false; - } - - /** Implementation of TouchEventInterceptor */ - @Override - public boolean onInterceptTouchEvent(View view, MotionEvent event) { - int action = event.getActionMasked(); - if (action == MotionEvent.ACTION_DOWN && event.getPointerCount() == 1) { - mTouchTravelDistance.set(0.0f, 0.0f); - } - - return false; - } - - class LayerMarginsAnimationTask extends RenderTask { - private float mStartLeft, mStartTop, mStartRight, mStartBottom; - private float mTop, mBottom, mLeft, mRight; - private boolean mContinueAnimation; - - public LayerMarginsAnimationTask(boolean runAfter, ImmutableViewportMetrics metrics, - float left, float top, float right, float bottom) { - super(runAfter); - mContinueAnimation = true; - this.mStartLeft = metrics.marginLeft; - this.mStartTop = metrics.marginTop; - this.mStartRight = metrics.marginRight; - this.mStartBottom = metrics.marginBottom; - this.mLeft = left; - this.mRight = right; - this.mTop = top; - this.mBottom = bottom; - } - - @Override - public boolean internalRun(long timeDelta, long currentFrameStartTime) { - if (!mContinueAnimation) { - return false; - } - - // Calculate the progress (between 0 and 1) - float progress = mInterpolator.getInterpolation( - Math.min(1.0f, (System.nanoTime() - getStartTime()) - / (float)MARGIN_ANIMATION_DURATION)); - - // Calculate the new metrics accordingly - synchronized (mTarget.getLock()) { - ImmutableViewportMetrics oldMetrics = mTarget.getViewportMetrics(); - ImmutableViewportMetrics newMetrics = oldMetrics.setMargins( - FloatUtils.interpolate(mStartLeft, mLeft, progress), - FloatUtils.interpolate(mStartTop, mTop, progress), - FloatUtils.interpolate(mStartRight, mRight, progress), - FloatUtils.interpolate(mStartBottom, mBottom, progress)); - PointF oldOffset = oldMetrics.getMarginOffset(); - PointF newOffset = newMetrics.getMarginOffset(); - newMetrics = - newMetrics.offsetViewportByAndClamp(newOffset.x - oldOffset.x, - newOffset.y - oldOffset.y); - - if (progress >= 1.0f) { - mContinueAnimation = false; - - // Force a redraw and update Gecko - mTarget.forceViewportMetrics(newMetrics, true, true); - } else { - mTarget.forceViewportMetrics(newMetrics, false, false); - } - } - return mContinueAnimation; - } - } - -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/LayerRenderer.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/LayerRenderer.java deleted file mode 100644 index e8759fd3e462..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/LayerRenderer.java +++ /dev/null @@ -1,722 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import org.libreoffice.LOKitShell; -import org.libreoffice.R; - -//import org.mozilla.gecko.GeckoAppShell; -//import org.mozilla.gecko.R; -//import org.mozilla.gecko.Tab; -//import org.mozilla.gecko.Tabs; -import org.mozilla.gecko.gfx.Layer.RenderContext; -import org.mozilla.gecko.gfx.RenderTask; -import org.mozilla.gecko.mozglue.DirectBufferAllocator; - -import android.content.Context; -import android.content.SharedPreferences; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Matrix; -import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.RectF; -import android.opengl.GLES20; -import android.os.SystemClock; -import android.util.Log; -//import org.mozilla.gecko.mozglue.JNITarget; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -import java.util.concurrent.CopyOnWriteArrayList; - -import javax.microedition.khronos.egl.EGLConfig; - -/** - * The layer renderer implements the rendering logic for a layer view. - */ -public class LayerRenderer /*implements Tabs.OnTabsChangedListener*/ { - private static final String LOGTAG = "GeckoLayerRenderer"; - private static final String PROFTAG = "GeckoLayerRendererProf"; - - /* - * The amount of time a frame is allowed to take to render before we declare it a dropped - * frame. - */ - private static final int MAX_FRAME_TIME = 16; /* 1000 ms / 60 FPS */ - - private static final int FRAME_RATE_METER_WIDTH = 128; - private static final int FRAME_RATE_METER_HEIGHT = 32; - - private static final long NANOS_PER_MS = 1000000; - private static final int NANOS_PER_SECOND = 1000000000; - - private final LayerView mView; - private TextLayer mFrameRateLayer; - private final ScrollbarLayer mHorizScrollLayer; - private final ScrollbarLayer mVertScrollLayer; - private final FadeRunnable mFadeRunnable; - private ByteBuffer mCoordByteBuffer; - private FloatBuffer mCoordBuffer; - private RenderContext mLastPageContext; - private int mMaxTextureSize; - private int mBackgroundColor; - private int mOverscrollColor; - - private long mLastFrameTime; - private final CopyOnWriteArrayList<RenderTask> mTasks; - - private CopyOnWriteArrayList<Layer> mExtraLayers = new CopyOnWriteArrayList<Layer>(); - - // Dropped frames display - private int[] mFrameTimings; - private int mCurrentFrame, mFrameTimingsSum, mDroppedFrames; - - // Render profiling output - private int mFramesRendered; - private float mCompleteFramesRendered; - private boolean mProfileRender; - private long mProfileOutputTime; - - private IntBuffer mPixelBuffer; - - // Used by GLES 2.0 - private int mProgram; - private int mPositionHandle; - private int mTextureHandle; - private int mSampleHandle; - private int mTMatrixHandle; - - // column-major matrix applied to each vertex to shift the viewport from - // one ranging from (-1, -1),(1,1) to (0,0),(1,1) and to scale all sizes by - // a factor of 2 to fill up the screen - public static final float[] DEFAULT_TEXTURE_MATRIX = { - 2.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 2.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 2.0f, 0.0f, - -1.0f, -1.0f, 0.0f, 1.0f - }; - - private static final int COORD_BUFFER_SIZE = 20; - - // The shaders run on the GPU directly, the vertex shader is only applying the - // matrix transform detailed above - - // Note we flip the y-coordinate in the vertex shader from a - // coordinate system with (0,0) in the top left to one with (0,0) in - // the bottom left. - - public static final String DEFAULT_VERTEX_SHADER = - "uniform mat4 uTMatrix;\n" + - "attribute vec4 vPosition;\n" + - "attribute vec2 aTexCoord;\n" + - "varying vec2 vTexCoord;\n" + - "void main() {\n" + - " gl_Position = uTMatrix * vPosition;\n" + - " vTexCoord.x = aTexCoord.x;\n" + - " vTexCoord.y = 1.0 - aTexCoord.y;\n" + - "}\n"; - - // We use highp because the screenshot textures - // we use are large and we stretch them a lot - // so we need all the precision we can get. - // Unfortunately, highp is not required by ES 2.0 - // so on GPU's like Mali we end up getting mediump - public static final String DEFAULT_FRAGMENT_SHADER = - "precision highp float;\n" + - "varying vec2 vTexCoord;\n" + - "uniform sampler2D sTexture;\n" + - "void main() {\n" + - " gl_FragColor = texture2D(sTexture, vTexCoord);\n" + - "}\n"; - - public LayerRenderer(LayerView view) { - mView = view; - mOverscrollColor = view.getContext().getResources().getColor(R.color.background_normal); - - Bitmap scrollbarImage = view.getScrollbarImage(); - IntSize size = new IntSize(scrollbarImage.getWidth(), scrollbarImage.getHeight()); - scrollbarImage = expandCanvasToPowerOfTwo(scrollbarImage, size); - - mTasks = new CopyOnWriteArrayList<RenderTask>(); - mLastFrameTime = System.nanoTime(); - - mVertScrollLayer = new ScrollbarLayer(this, scrollbarImage, size, true); - mHorizScrollLayer = new ScrollbarLayer(this, diagonalFlip(scrollbarImage), new IntSize(size.height, size.width), false); - mFadeRunnable = new FadeRunnable(); - - mFrameTimings = new int[60]; - mCurrentFrame = mFrameTimingsSum = mDroppedFrames = 0; - - // Initialize the FloatBuffer that will be used to store all vertices and texture - // coordinates in draw() commands. - mCoordByteBuffer = DirectBufferAllocator.allocate(COORD_BUFFER_SIZE * 4); - mCoordByteBuffer.order(ByteOrder.nativeOrder()); - mCoordBuffer = mCoordByteBuffer.asFloatBuffer(); - - //Tabs.registerOnTabsChangedListener(this); - } - - private Bitmap expandCanvasToPowerOfTwo(Bitmap image, IntSize size) { - IntSize potSize = size.nextPowerOfTwo(); - if (size.equals(potSize)) { - return image; - } - // make the bitmap size a power-of-two in both dimensions if it's not already. - Bitmap potImage = Bitmap.createBitmap(potSize.width, potSize.height, image.getConfig()); - new Canvas(potImage).drawBitmap(image, new Matrix(), null); - return potImage; - } - - private Bitmap diagonalFlip(Bitmap image) { - Matrix rotation = new Matrix(); - rotation.setValues(new float[] { 0, 1, 0, 1, 0, 0, 0, 0, 1 }); // transform (x,y) into (y,x) - Bitmap rotated = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), rotation, true); - return rotated; - } - - public void destroy() { - DirectBufferAllocator.free(mCoordByteBuffer); - mCoordByteBuffer = null; - mCoordBuffer = null; - mHorizScrollLayer.destroy(); - mVertScrollLayer.destroy(); - if (mFrameRateLayer != null) { - mFrameRateLayer.destroy(); - } - //Tabs.unregisterOnTabsChangedListener(this); - } - - void onSurfaceCreated(EGLConfig config) { - checkMonitoringEnabled(); - createDefaultProgram(); - activateDefaultProgram(); - } - - public void createDefaultProgram() { - int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DEFAULT_VERTEX_SHADER); - int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DEFAULT_FRAGMENT_SHADER); - - mProgram = GLES20.glCreateProgram(); - GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program - GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program - GLES20.glLinkProgram(mProgram); // creates OpenGL program executables - - // Get handles to the vertex shader's vPosition, aTexCoord, sTexture, and uTMatrix members. - mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); - mTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord"); - mSampleHandle = GLES20.glGetUniformLocation(mProgram, "sTexture"); - mTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uTMatrix"); - - int maxTextureSizeResult[] = new int[1]; - GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSizeResult, 0); - mMaxTextureSize = maxTextureSizeResult[0]; - } - - // Activates the shader program. - public void activateDefaultProgram() { - // Add the program to the OpenGL environment - GLES20.glUseProgram(mProgram); - - // Set the transformation matrix - GLES20.glUniformMatrix4fv(mTMatrixHandle, 1, false, DEFAULT_TEXTURE_MATRIX, 0); - - // Enable the arrays from which we get the vertex and texture coordinates - GLES20.glEnableVertexAttribArray(mPositionHandle); - GLES20.glEnableVertexAttribArray(mTextureHandle); - - GLES20.glUniform1i(mSampleHandle, 0); - - // TODO: Move these calls into a separate deactivate() call that is called after the - // underlay and overlay are rendered. - } - - // Deactivates the shader program. This must be done to avoid crashes after returning to the - // Gecko C++ compositor from Java. - public void deactivateDefaultProgram() { - GLES20.glDisableVertexAttribArray(mTextureHandle); - GLES20.glDisableVertexAttribArray(mPositionHandle); - GLES20.glUseProgram(0); - } - - public int getMaxTextureSize() { - return mMaxTextureSize; - } - - public void postRenderTask(RenderTask aTask) { - mTasks.add(aTask); - mView.requestRender(); - } - - public void removeRenderTask(RenderTask aTask) { - mTasks.remove(aTask); - } - - private void runRenderTasks(CopyOnWriteArrayList<RenderTask> tasks, boolean after, long frameStartTime) { - for (RenderTask task : tasks) { - if (task.runAfter != after) { - continue; - } - - boolean stillRunning = task.run(frameStartTime - mLastFrameTime, frameStartTime); - - // Remove the task from the list if its finished - if (!stillRunning) { - tasks.remove(task); - } - } - } - - public void addLayer(Layer layer) { - synchronized (mExtraLayers) { - if (mExtraLayers.contains(layer)) { - mExtraLayers.remove(layer); - } - - mExtraLayers.add(layer); - } - } - - public void removeLayer(Layer layer) { - synchronized (mExtraLayers) { - mExtraLayers.remove(layer); - } - } - - private void printCheckerboardStats() { - Log.d(PROFTAG, "Frames rendered over last 1000ms: " + mCompleteFramesRendered + "/" + mFramesRendered); - mFramesRendered = 0; - mCompleteFramesRendered = 0; - } - - /** Used by robocop for testing purposes. Not for production use! */ - IntBuffer getPixels() { - IntBuffer pixelBuffer = IntBuffer.allocate(mView.getWidth() * mView.getHeight()); - synchronized (pixelBuffer) { - mPixelBuffer = pixelBuffer; - mView.requestRender(); - try { - pixelBuffer.wait(); - } catch (InterruptedException ie) { - } - mPixelBuffer = null; - } - return pixelBuffer; - } - - private RenderContext createScreenContext(ImmutableViewportMetrics metrics, PointF offset) { - RectF viewport = new RectF(0.0f, 0.0f, metrics.getWidth(), metrics.getHeight()); - RectF pageRect = metrics.getPageRect(); - - return createContext(viewport, pageRect, 1.0f, offset); - } - - private RenderContext createPageContext(ImmutableViewportMetrics metrics, PointF offset) { - RectF viewport = metrics.getViewport(); - RectF pageRect = metrics.getPageRect(); - float zoomFactor = metrics.zoomFactor; - - return createContext(new RectF(RectUtils.round(viewport)), pageRect, zoomFactor, offset); - } - - private RenderContext createContext(RectF viewport, RectF pageRect, float zoomFactor, PointF offset) { - return new RenderContext(viewport, pageRect, zoomFactor, offset, mPositionHandle, mTextureHandle, - mCoordBuffer); - } - - private void updateDroppedFrames(long frameStartTime) { - int frameElapsedTime = (int)((System.nanoTime() - frameStartTime) / NANOS_PER_MS); - - /* Update the running statistics. */ - mFrameTimingsSum -= mFrameTimings[mCurrentFrame]; - mFrameTimingsSum += frameElapsedTime; - mDroppedFrames -= (mFrameTimings[mCurrentFrame] + 1) / MAX_FRAME_TIME; - mDroppedFrames += (frameElapsedTime + 1) / MAX_FRAME_TIME; - - mFrameTimings[mCurrentFrame] = frameElapsedTime; - mCurrentFrame = (mCurrentFrame + 1) % mFrameTimings.length; - - int averageTime = mFrameTimingsSum / mFrameTimings.length; - mFrameRateLayer.beginTransaction(); // called on compositor thread - try { - mFrameRateLayer.setText(averageTime + " ms/" + mDroppedFrames); - } finally { - mFrameRateLayer.endTransaction(); - } - } - - /* Given the new dimensions for the surface, moves the frame rate layer appropriately. */ - private void moveFrameRateLayer(int width, int height) { - mFrameRateLayer.beginTransaction(); // called on compositor thread - try { - Rect position = new Rect(width - FRAME_RATE_METER_WIDTH - 8, - height - FRAME_RATE_METER_HEIGHT + 8, - width - 8, - height + 8); - mFrameRateLayer.setPosition(position); - } finally { - mFrameRateLayer.endTransaction(); - } - } - - void checkMonitoringEnabled() { - /* Do this I/O off the main thread to minimize its impact on startup time. */ - new Thread(new Runnable() { - @Override - public void run() { - Context context = mView.getContext(); - SharedPreferences preferences = context.getSharedPreferences("GeckoApp", 0); - if (preferences.getBoolean("showFrameRate", false)) { - IntSize frameRateLayerSize = new IntSize(FRAME_RATE_METER_WIDTH, FRAME_RATE_METER_HEIGHT); - mFrameRateLayer = TextLayer.create(frameRateLayerSize, "-- ms/--"); - moveFrameRateLayer(mView.getWidth(), mView.getHeight()); - } - mProfileRender = Log.isLoggable(PROFTAG, Log.DEBUG); - } - }).start(); - } - - /* - * create a vertex shader type (GLES20.GL_VERTEX_SHADER) - * or a fragment shader type (GLES20.GL_FRAGMENT_SHADER) - */ - public static int loadShader(int type, String shaderCode) { - int shader = GLES20.glCreateShader(type); - GLES20.glShaderSource(shader, shaderCode); - GLES20.glCompileShader(shader); - return shader; - } - - public Frame createFrame(ImmutableViewportMetrics metrics) { - return new Frame(metrics); - } - - class FadeRunnable implements Runnable { - private boolean mStarted; - private long mRunAt; - - void scheduleStartFade(long delay) { - mRunAt = SystemClock.elapsedRealtime() + delay; - if (!mStarted) { - mView.postDelayed(this, delay); - mStarted = true; - } - } - - void scheduleNextFadeFrame() { - if (mStarted) { - Log.e(LOGTAG, "scheduleNextFadeFrame() called while scheduled for starting fade"); - } - mView.postDelayed(this, 1000L / 60L); // request another frame at 60fps - } - - boolean timeToFade() { - return !mStarted; - } - - @Override - public void run() { - long timeDelta = mRunAt - SystemClock.elapsedRealtime(); - if (timeDelta > 0) { - // the run-at time was pushed back, so reschedule - mView.postDelayed(this, timeDelta); - } else { - // reached the run-at time, execute - mStarted = false; - mView.requestRender(); - } - } - } - - public class Frame { - // The timestamp recording the start of this frame. - private long mFrameStartTime; - // A fixed snapshot of the viewport metrics that this frame is using to render content. - private ImmutableViewportMetrics mFrameMetrics; - // A rendering context for page-positioned layers, and one for screen-positioned layers. - private RenderContext mPageContext, mScreenContext; - // Whether a layer was updated. - private boolean mUpdated; - private final Rect mPageRect; - private final Rect mAbsolutePageRect; - private final PointF mRenderOffset; - - public Frame(ImmutableViewportMetrics metrics) { - mFrameMetrics = metrics; - - // Work out the offset due to margins - Layer rootLayer = mView.getLayerClient().getRoot(); - mRenderOffset = mFrameMetrics.getMarginOffset(); - mPageContext = createPageContext(metrics, mRenderOffset); - mScreenContext = createScreenContext(metrics, mRenderOffset); - - RectF pageRect = mFrameMetrics.getPageRect(); - mAbsolutePageRect = RectUtils.round(pageRect); - - PointF origin = mFrameMetrics.getOrigin(); - pageRect.offset(-origin.x, -origin.y); - mPageRect = RectUtils.round(pageRect); - } - - private void setScissorRect() { - Rect scissorRect = transformToScissorRect(mPageRect); - GLES20.glEnable(GLES20.GL_SCISSOR_TEST); - GLES20.glScissor(scissorRect.left, scissorRect.top, - scissorRect.width(), scissorRect.height()); - } - - private Rect transformToScissorRect(Rect rect) { - IntSize screenSize = new IntSize(mFrameMetrics.getSize()); - - int left = Math.max(0, rect.left); - int top = Math.max(0, rect.top); - int right = Math.min(screenSize.width, rect.right); - int bottom = Math.min(screenSize.height, rect.bottom); - - Rect scissorRect = new Rect(left, screenSize.height - bottom, right, - (screenSize.height - bottom) + (bottom - top)); - scissorRect.offset(Math.round(-mRenderOffset.x), Math.round(-mRenderOffset.y)); - - return scissorRect; - } - - /** This function is invoked via JNI; be careful when modifying signature. */ - //@JNITarget - public void beginDrawing() { - mFrameStartTime = System.nanoTime(); - - TextureReaper.get().reap(); - TextureGenerator.get().fill(); - - mUpdated = true; - - Layer rootLayer = mView.getLayerClient().getRoot(); - - // Run through pre-render tasks - runRenderTasks(mTasks, false, mFrameStartTime); - - if (!mPageContext.fuzzyEquals(mLastPageContext) && !mView.isFullScreen()) { - // The viewport or page changed, so show the scrollbars again - // as per UX decision. Don't do this if we're in full-screen mode though. - mVertScrollLayer.unfade(); - mHorizScrollLayer.unfade(); - mFadeRunnable.scheduleStartFade(ScrollbarLayer.FADE_DELAY); - } else if (mFadeRunnable.timeToFade()) { - boolean stillFading = mVertScrollLayer.fade() | mHorizScrollLayer.fade(); - if (stillFading) { - mFadeRunnable.scheduleNextFadeFrame(); - } - } - mLastPageContext = mPageContext; - - /* Update layers. */ - if (rootLayer != null) { - // Called on compositor thread. - mUpdated &= rootLayer.update(mPageContext); - } - - if (mFrameRateLayer != null) { - // Called on compositor thread. - mUpdated &= mFrameRateLayer.update(mScreenContext); - } - - mUpdated &= mVertScrollLayer.update(mPageContext); // called on compositor thread - mUpdated &= mHorizScrollLayer.update(mPageContext); // called on compositor thread - - for (Layer layer : mExtraLayers) { - mUpdated &= layer.update(mPageContext); // called on compositor thread - } - } - - /** Retrieves the bounds for the layer, rounded in such a way that it - * can be used as a mask for something that will render underneath it. - * This will round the bounds inwards, but stretch the mask towards any - * near page edge, where near is considered to be 'within 2 pixels'. - * Returns null if the given layer is null. - */ - private Rect getMaskForLayer(Layer layer) { - if (layer == null) { - return null; - } - - RectF bounds = RectUtils.contract(layer.getBounds(mPageContext), 1.0f, 1.0f); - Rect mask = RectUtils.roundIn(bounds); - - // If the mask is within two pixels of any page edge, stretch it over - // that edge. This is to avoid drawing thin slivers when masking - // layers. - if (mask.top <= 2) { - mask.top = -1; - } - if (mask.left <= 2) { - mask.left = -1; - } - - // Because we're drawing relative to the page-rect, we only need to - // take into account its width and height (and not its origin) - int pageRight = mPageRect.width(); - int pageBottom = mPageRect.height(); - - if (mask.right >= pageRight - 2) { - mask.right = pageRight + 1; - } - if (mask.bottom >= pageBottom - 2) { - mask.bottom = pageBottom + 1; - } - - return mask; - } - - private void clear(int color) { - GLES20.glClearColor(((color >> 16) & 0xFF) / 255.0f, - ((color >> 8) & 0xFF) / 255.0f, - (color & 0xFF) / 255.0f, - 0.0f); - // The bits set here need to match up with those used - // in gfx/layers/opengl/LayerManagerOGL.cpp. - GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | - GLES20.GL_DEPTH_BUFFER_BIT); - } - - /** This function is invoked via JNI; be careful when modifying signature. */ - //@JNITarget - public void drawBackground() { - // Any GL state which is changed here must be restored in - // CompositorOGL::RestoreState - - GLES20.glDisable(GLES20.GL_SCISSOR_TEST); - - // Draw the overscroll background area as a solid color - clear(mOverscrollColor); - - // Update background color. - mBackgroundColor = mView.getBackgroundColor(); - - // Clear the page area to the page background colour. - setScissorRect(); - clear(mBackgroundColor); - GLES20.glDisable(GLES20.GL_SCISSOR_TEST); - } - - // Draws the layer the client added to us. - void drawRootLayer() { - Layer rootLayer = mView.getLayerClient().getRoot(); - if (rootLayer == null) { - return; - } - - rootLayer.draw(mPageContext); - } - - //@JNITarget - public void drawForeground() { - // Any GL state which is changed here must be restored in - // CompositorOGL::RestoreState - - /* Draw any extra layers that were added (likely plugins) */ - if (mExtraLayers.size() > 0) { - for (Layer layer : mExtraLayers) { - layer.draw(mPageContext); - } - } - - /* Draw the vertical scrollbar. */ - if (mPageRect.height() > mFrameMetrics.getHeight()) - mVertScrollLayer.draw(mPageContext); - - /* Draw the horizontal scrollbar. */ - if (mPageRect.width() > mFrameMetrics.getWidth()) - mHorizScrollLayer.draw(mPageContext); - - /* Measure how much of the screen is checkerboarding */ - Layer rootLayer = mView.getLayerClient().getRoot(); - if ((rootLayer != null) && - (mProfileRender || PanningPerfAPI.isRecordingCheckerboard())) { - // Calculate the incompletely rendered area of the page - float checkerboard = 1.0f - /*GeckoAppShell*/LOKitShell.computeRenderIntegrity(); - - PanningPerfAPI.recordCheckerboard(checkerboard); - if (checkerboard < 0.0f || checkerboard > 1.0f) { - Log.e(LOGTAG, "Checkerboard value out of bounds: " + checkerboard); - } - - mCompleteFramesRendered += 1.0f - checkerboard; - mFramesRendered ++; - - if (mFrameStartTime - mProfileOutputTime > NANOS_PER_SECOND) { - mProfileOutputTime = mFrameStartTime; - printCheckerboardStats(); - } - } - - runRenderTasks(mTasks, true, mFrameStartTime); - - /* Draw the FPS. */ - if (mFrameRateLayer != null) { - updateDroppedFrames(mFrameStartTime); - - GLES20.glEnable(GLES20.GL_BLEND); - GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); - mFrameRateLayer.draw(mScreenContext); - } - } - - /** This function is invoked via JNI; be careful when modifying signature. */ - //@JNITarget - public void endDrawing() { - // If a layer update requires further work, schedule another redraw - if (!mUpdated) - mView.requestRender(); - - PanningPerfAPI.recordFrameTime(); - - /* Used by robocop for testing purposes */ - IntBuffer pixelBuffer = mPixelBuffer; - if (mUpdated && pixelBuffer != null) { - synchronized (pixelBuffer) { - pixelBuffer.position(0); - GLES20.glReadPixels(0, 0, (int)mScreenContext.viewport.width(), - (int)mScreenContext.viewport.height(), GLES20.GL_RGBA, - GLES20.GL_UNSIGNED_BYTE, pixelBuffer); - pixelBuffer.notify(); - } - } - - // Remove background color once we've painted. GeckoLayerClient is - // responsible for setting this flag before current document is - // composited. - if (mView.getPaintState() == LayerView.PAINT_BEFORE_FIRST) { - mView.post(new Runnable() { - @Override - public void run() { - mView.getChildAt(0).setBackgroundColor(Color.TRANSPARENT); - } - }); - mView.setPaintState(LayerView.PAINT_AFTER_FIRST); - } - mLastFrameTime = mFrameStartTime; - } - } - - /*@Override - public void onTabChanged(final Tab tab, Tabs.TabEvents msg, Object data) { - // Sets the background of the newly selected tab. This background color - // gets cleared in endDrawing(). This function runs on the UI thread, - // but other code that touches the paint state is run on the compositor - // thread, so this may need to be changed if any problems appear. - if (msg == Tabs.TabEvents.SELECTED) { - if (mView != null) { - if (mView.getChildAt(0) != null) { - mView.getChildAt(0).setBackgroundColor(tab.getBackgroundColor()); - } - mView.setPaintState(LayerView.PAINT_START); - } - } - }*/ -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/LayerView.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/LayerView.java deleted file mode 100644 index c489e7f044bc..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/LayerView.java +++ /dev/null @@ -1,692 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -//import org.mozilla.gecko.GeckoAccessibility; -//import org.mozilla.gecko.GeckoAppShell; -//import org.mozilla.gecko.GeckoEvent; -//import org.mozilla.gecko.PrefsHelper; -//import org.mozilla.gecko.R; -//import org.mozilla.gecko.Tab; -//import org.mozilla.gecko.Tabs; -import org.libreoffice.LOKitShell; -import org.mozilla.gecko.TouchEventInterceptor; -import org.mozilla.gecko.ZoomConstraints; -//import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI; -//import org.mozilla.gecko.mozglue.RobocopTarget; -import org.mozilla.gecko.util.EventDispatcher; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.PixelFormat; -import android.graphics.Point; -import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.SurfaceTexture; -import android.os.Build; -import android.os.Handler; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.TextureView; -import android.view.View; -import android.view.ViewGroup; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputConnection; -import android.widget.FrameLayout; - -import java.nio.IntBuffer; -import java.util.ArrayList; - -/** - * A view rendered by the layer compositor. - * - * Note that LayerView is accessed by Robocop via reflection. - */ -public class LayerView extends FrameLayout /*implements Tabs.OnTabsChangedListener */ { - private static String LOGTAG = "GeckoLayerView"; - - private GeckoLayerClient mLayerClient; - private PanZoomController mPanZoomController; - private LayerMarginsAnimator mMarginsAnimator; - private GLController mGLController; - private InputConnectionHandler mInputConnectionHandler; - private LayerRenderer mRenderer; - /* Must be a PAINT_xxx constant */ - private int mPaintState; - private int mBackgroundColor; - private boolean mFullScreen; - - private SurfaceView mSurfaceView; - private TextureView mTextureView; - - private Listener mListener; - - /* This should only be modified on the Java UI thread. */ - private final ArrayList<TouchEventInterceptor> mTouchInterceptors; - private final Overscroll mOverscroll; - - /* Flags used to determine when to show the painted surface. */ - public static final int PAINT_START = 0; - public static final int PAINT_BEFORE_FIRST = 1; - public static final int PAINT_AFTER_FIRST = 2; - - public boolean shouldUseTextureView() { - // Disable TextureView support for now as it causes panning/zooming - // performance regressions (see bug 792259). Uncomment the code below - // once this bug is fixed. - return false; - - /* - // we can only use TextureView on ICS or higher - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - Log.i(LOGTAG, "Not using TextureView: not on ICS+"); - return false; - } - - try { - // and then we can only use it if we have a hardware accelerated window - Method m = View.class.getMethod("isHardwareAccelerated", (Class[]) null); - return (Boolean) m.invoke(this); - } catch (Exception e) { - Log.i(LOGTAG, "Not using TextureView: caught exception checking for hw accel: " + e.toString()); - return false; - } */ - } - - public LayerView(Context context, AttributeSet attrs) { - super(context, attrs); - - mGLController = GLController.getInstance(this); - mPaintState = PAINT_START; - mBackgroundColor = Color.WHITE; - - mTouchInterceptors = new ArrayList<TouchEventInterceptor>(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - mOverscroll = new OverscrollEdgeEffect(this); - } else { - mOverscroll = null; - } - //Tabs.registerOnTabsChangedListener(this); - } - - public LayerView(Context context) { - this(context, null); - } - - public void initializeView(EventDispatcher eventDispatcher) { - mLayerClient = new GeckoLayerClient(getContext(), this, eventDispatcher); - if (mOverscroll != null) { - mLayerClient.setOverscrollHandler(mOverscroll); - } - - mPanZoomController = mLayerClient.getPanZoomController(); - mMarginsAnimator = mLayerClient.getLayerMarginsAnimator(); - - mRenderer = new LayerRenderer(this); - mInputConnectionHandler = null; - - setFocusable(true); - setFocusableInTouchMode(true); - - //GeckoAccessibility.setDelegate(this); - } - - private Point getEventRadius(MotionEvent event) { - if (Build.VERSION.SDK_INT >= 9) { - return new Point((int)event.getToolMajor()/2, - (int)event.getToolMinor()/2); - } - - float size = event.getSize(); - DisplayMetrics displaymetrics = getContext().getResources().getDisplayMetrics(); - size = size * Math.min(displaymetrics.heightPixels, displaymetrics.widthPixels); - return new Point((int)size, (int)size); - } - - public void geckoConnected() { - // See if we want to force 16-bit colour before doing anything - /*PrefsHelper.getPref("gfx.android.rgb16.force", new PrefsHelper.PrefHandlerBase() { - @Override public void prefValue(String pref, boolean force16bit) { - if (force16bit) { - GeckoAppShell.setScreenDepthOverride(16); - } - } - });*/ - - mLayerClient.notifyGeckoReady(); - addTouchInterceptor(new TouchEventInterceptor() { - private PointF mInitialTouchPoint = null; - - @Override - public boolean onInterceptTouchEvent(View view, MotionEvent event) { - return false; - } - - @Override - public boolean onTouch(View view, MotionEvent event) { - if (event == null) { - return true; - } - - int action = event.getActionMasked(); - PointF point = new PointF(event.getX(), event.getY()); - if (action == MotionEvent.ACTION_DOWN) { - mInitialTouchPoint = point; - } - - if (mInitialTouchPoint != null && action == MotionEvent.ACTION_MOVE) { - Point p = getEventRadius(event); - - if (PointUtils.subtract(point, mInitialTouchPoint).length() < - Math.max(PanZoomController.CLICK_THRESHOLD, Math.min(Math.min(p.x, p.y), PanZoomController.PAN_THRESHOLD))) { - // Don't send the touchmove event if the users finger hasn't moved far. - // Necessary for Google Maps to work correctly. See bug 771099. - return true; - } else { - mInitialTouchPoint = null; - } - } - - //GeckoAppShell.sendEventToGecko(GeckoEvent.createMotionEvent(event, false)); - return true; - } - }); - } - - public void showSurface() { - // Fix this if TextureView support is turned back on above - mSurfaceView.setVisibility(View.VISIBLE); - } - - public void hideSurface() { - // Fix this if TextureView support is turned back on above - mSurfaceView.setVisibility(View.INVISIBLE); - } - - public void destroy() { - if (mLayerClient != null) { - mLayerClient.destroy(); - } - if (mRenderer != null) { - mRenderer.destroy(); - } - //Tabs.unregisterOnTabsChangedListener(this); - } - - public void addTouchInterceptor(final TouchEventInterceptor aTouchInterceptor) { - post(new Runnable() { - @Override - public void run() { - mTouchInterceptors.add(aTouchInterceptor); - } - }); - } - - public void removeTouchInterceptor(final TouchEventInterceptor aTouchInterceptor) { - post(new Runnable() { - @Override - public void run() { - mTouchInterceptors.remove(aTouchInterceptor); - } - }); - } - - private boolean runTouchInterceptors(MotionEvent event, boolean aOnTouch) { - boolean result = false; - for (TouchEventInterceptor i : mTouchInterceptors) { - if (aOnTouch) { - result |= i.onTouch(this, event); - } else { - result |= i.onInterceptTouchEvent(this, event); - } - } - - return result; - } - - @Override - public void dispatchDraw(final Canvas canvas) { - super.dispatchDraw(canvas); - - // We must have a layer client to get valid viewport metrics - if (mLayerClient != null && mOverscroll != null) { - mOverscroll.draw(canvas, getViewportMetrics()); - } - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { - requestFocus(); - } - - if (runTouchInterceptors(event, false)) { - return true; - } - if (mPanZoomController != null && mPanZoomController.onTouchEvent(event)) { - return true; - } - if (runTouchInterceptors(event, true)) { - return true; - } - return false; - } - - @Override - public boolean onHoverEvent(MotionEvent event) { - if (runTouchInterceptors(event, true)) { - return true; - } - return false; - } - - @Override - public boolean onGenericMotionEvent(MotionEvent event) { - if (mPanZoomController != null && mPanZoomController.onMotionEvent(event)) { - return true; - } - return false; - } - - @Override - protected void onAttachedToWindow() { - // This check should not be done before the view is attached to a window - // as hardware acceleration will not be enabled at that point. - // We must create and add the SurfaceView instance before the view tree - // is fully created to avoid flickering (see bug 801477). - if (shouldUseTextureView()) { - mTextureView = new TextureView(getContext()); - mTextureView.setSurfaceTextureListener(new SurfaceTextureListener()); - - // The background is set to this color when the LayerView is - // created, and it will be shown immediately at startup. Shortly - // after, the tab's background color will be used before any content - // is shown. - mTextureView.setBackgroundColor(Color.WHITE); - addView(mTextureView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - } else { - // This will stop PropertyAnimator from creating a drawing cache (i.e. a bitmap) - // from a SurfaceView, which is just not possible (the bitmap will be transparent). - setWillNotCacheDrawing(false); - - mSurfaceView = new LayerSurfaceView(getContext(), this); - mSurfaceView.setBackgroundColor(Color.WHITE); - addView(mSurfaceView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - - SurfaceHolder holder = mSurfaceView.getHolder(); - holder.addCallback(new SurfaceListener()); - holder.setFormat(PixelFormat.RGB_565); - } - } - - //@RobocopTarget - public GeckoLayerClient getLayerClient() { return mLayerClient; } - public PanZoomController getPanZoomController() { return mPanZoomController; } - public LayerMarginsAnimator getLayerMarginsAnimator() { return mMarginsAnimator; } - - public ImmutableViewportMetrics getViewportMetrics() { - return mLayerClient.getViewportMetrics(); - } - - public void abortPanning() { - if (mPanZoomController != null) { - mPanZoomController.abortPanning(); - } - } - - public PointF convertViewPointToLayerPoint(PointF viewPoint) { - return mLayerClient.convertViewPointToLayerPoint(viewPoint); - } - - int getBackgroundColor() { - return mBackgroundColor; - } - - @Override - public void setBackgroundColor(int newColor) { - mBackgroundColor = newColor; - requestRender(); - } - - public void setZoomConstraints(ZoomConstraints constraints) { - mLayerClient.setZoomConstraints(constraints); - } - - public void setIsRTL(boolean aIsRTL) { - mLayerClient.setIsRTL(aIsRTL); - } - - public void setInputConnectionHandler(InputConnectionHandler inputConnectionHandler) { - mInputConnectionHandler = inputConnectionHandler; - mLayerClient.forceRedraw(null); - } - - @Override - public Handler getHandler() { - if (mInputConnectionHandler != null) - return mInputConnectionHandler.getHandler(super.getHandler()); - return super.getHandler(); - } - - @Override - public InputConnection onCreateInputConnection(EditorInfo outAttrs) { - if (mInputConnectionHandler != null) - return mInputConnectionHandler.onCreateInputConnection(outAttrs); - return null; - } - - @Override - public boolean onKeyPreIme(int keyCode, KeyEvent event) { - if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyPreIme(keyCode, event)) { - return true; - } - return false; - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (mPanZoomController != null && mPanZoomController.onKeyEvent(event)) { - return true; - } - if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyDown(keyCode, event)) { - return true; - } - return false; - } - - @Override - public boolean onKeyLongPress(int keyCode, KeyEvent event) { - if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyLongPress(keyCode, event)) { - return true; - } - return false; - } - - @Override - public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { - if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyMultiple(keyCode, repeatCount, event)) { - return true; - } - return false; - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (mInputConnectionHandler != null && mInputConnectionHandler.onKeyUp(keyCode, event)) { - return true; - } - return false; - } - - public boolean isIMEEnabled() { - if (mInputConnectionHandler != null) { - return mInputConnectionHandler.isIMEEnabled(); - } - return false; - } - - public void requestRender() { - if (mListener != null) { - mListener.renderRequested(); - } - } - - public void addLayer(Layer layer) { - mRenderer.addLayer(layer); - } - - public void removeLayer(Layer layer) { - mRenderer.removeLayer(layer); - } - - public void postRenderTask(RenderTask task) { - mRenderer.postRenderTask(task); - } - - public void removeRenderTask(RenderTask task) { - mRenderer.removeRenderTask(task); - } - - public int getMaxTextureSize() { - return mRenderer.getMaxTextureSize(); - } - - /** Used by robocop for testing purposes. Not for production use! */ - //@RobocopTarget - public IntBuffer getPixels() { - return mRenderer.getPixels(); - } - - /* paintState must be a PAINT_xxx constant. */ - public void setPaintState(int paintState) { - mPaintState = paintState; - } - - public int getPaintState() { - return mPaintState; - } - - public LayerRenderer getRenderer() { - return mRenderer; - } - - public void setListener(Listener listener) { - mListener = listener; - } - - Listener getListener() { - return mListener; - } - - public GLController getGLController() { - return mGLController; - } - - private Bitmap getDrawable(String name) { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inScaled = false; - Context context = getContext(); - int resId = context.getResources().getIdentifier(name, "drawable", context.getPackageName()); - return BitmapUtils.decodeResource(context, resId, options); - } - - Bitmap getScrollbarImage() { - return getDrawable("scrollbar"); - } - - /* When using a SurfaceView (mSurfaceView != null), resizing happens in two - * phases. First, the LayerView changes size, then, often some frames later, - * the SurfaceView changes size. Because of this, we need to split the - * resize into two phases to avoid jittering. - * - * The first phase is the LayerView size change. mListener is notified so - * that a synchronous draw can be performed (otherwise a blank frame will - * appear). - * - * The second phase is the SurfaceView size change. At this point, the - * backing GL surface is resized and another synchronous draw is performed. - * Gecko is also sent the new window size, and this will likely cause an - * extra draw a few frames later, after it's re-rendered and caught up. - * - * In the case that there is no valid GL surface (for example, when - * resuming, or when coming back from the awesomescreen), or we're using a - * TextureView instead of a SurfaceView, the first phase is skipped. - */ - private void onSizeChanged(int width, int height) { - if (!mGLController.isCompositorCreated()) { - return; - } - - surfaceChanged(width, height); - - if (mSurfaceView == null) { - return; - } - - if (mListener != null) { - mListener.sizeChanged(width, height); - } - - if (mOverscroll != null) { - mOverscroll.setSize(width, height); - } - } - - private void surfaceChanged(int width, int height) { - mGLController.serverSurfaceChanged(width, height); - - if (mListener != null) { - mListener.surfaceChanged(width, height); - } - - if (mOverscroll != null) { - mOverscroll.setSize(width, height); - } - } - - private void onDestroyed() { - mGLController.serverSurfaceDestroyed(); - } - - public Object getNativeWindow() { - if (mSurfaceView != null) - return mSurfaceView.getHolder(); - - return mTextureView.getSurfaceTexture(); - } - - //@WrapElementForJNI(allowMultithread = true, stubName = "RegisterCompositorWrapper") - public static GLController registerCxxCompositor() { - try { - LayerView layerView = /*GeckoAppShell*/LOKitShell.getLayerView(); - GLController controller = layerView.getGLController(); - controller.compositorCreated(); - return controller; - } catch (Exception e) { - Log.e(LOGTAG, "Error registering compositor!", e); - return null; - } - } - - public interface Listener { - void renderRequested(); - void sizeChanged(int width, int height); - void surfaceChanged(int width, int height); - } - - private class SurfaceListener implements SurfaceHolder.Callback { - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, - int height) { - onSizeChanged(width, height); - } - - @Override - public void surfaceCreated(SurfaceHolder holder) { - } - - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - onDestroyed(); - } - } - - /* A subclass of SurfaceView to listen to layout changes, as - * View.OnLayoutChangeListener requires API level 11. - */ - private class LayerSurfaceView extends SurfaceView { - LayerView mParent; - - public LayerSurfaceView(Context aContext, LayerView aParent) { - super(aContext); - mParent = aParent; - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - if (changed) { - mParent.surfaceChanged(right - left, bottom - top); - } - } - } - - private class SurfaceTextureListener implements TextureView.SurfaceTextureListener { - @Override - public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { - // We don't do this for surfaceCreated above because it is always followed by a surfaceChanged, - // but that is not the case here. - onSizeChanged(width, height); - } - - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { - onDestroyed(); - return true; // allow Android to call release() on the SurfaceTexture, we are done drawing to it - } - - @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { - onSizeChanged(width, height); - } - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surface) { - - } - } - - @Override - public void setOverScrollMode(int overscrollMode) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { - super.setOverScrollMode(overscrollMode); - } - if (mPanZoomController != null) { - mPanZoomController.setOverScrollMode(overscrollMode); - } - } - - @Override - public int getOverScrollMode() { - if (mPanZoomController != null) { - return mPanZoomController.getOverScrollMode(); - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { - return super.getOverScrollMode(); - } - return View.OVER_SCROLL_ALWAYS; - } - - @Override - public void onFocusChanged (boolean gainFocus, int direction, Rect previouslyFocusedRect) { - super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); - //GeckoAccessibility.onLayerViewFocusChanged(this, gainFocus); - } - - public void setFullScreen(boolean fullScreen) { - mFullScreen = fullScreen; - } - - public boolean isFullScreen() { - return mFullScreen; - } - - /*@Override - public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) { - if (msg == Tabs.TabEvents.VIEWPORT_CHANGE && Tabs.getInstance().isSelectedTab(tab) && mLayerClient != null) { - setZoomConstraints(tab.getZoomConstraints()); - setIsRTL(tab.getIsRTL()); - } - }*/ -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/Overscroll.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/Overscroll.java deleted file mode 100644 index e442444d5a7b..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/Overscroll.java +++ /dev/null @@ -1,21 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import android.graphics.Canvas; - -public interface Overscroll { - // The axis to show overscroll on. - public enum Axis { - X, - Y, - }; - - public void draw(final Canvas canvas, final ImmutableViewportMetrics metrics); - public void setSize(final int width, final int height); - public void setVelocity(final float velocity, final Axis axis); - public void setDistance(final float distance, final Axis axis); -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/OverscrollEdgeEffect.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/OverscrollEdgeEffect.java deleted file mode 100644 index 9ab64d5f3e51..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/OverscrollEdgeEffect.java +++ /dev/null @@ -1,130 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import android.content.Context; -import android.graphics.Canvas; -import android.os.Build; -import android.widget.EdgeEffect; -import android.view.View; - - -public class OverscrollEdgeEffect implements Overscroll { - // Used to index particular edges in the edges array - private static final int TOP = 0; - private static final int BOTTOM = 1; - private static final int LEFT = 2; - private static final int RIGHT = 3; - - // All four edges of the screen - private final EdgeEffect[] mEdges = new EdgeEffect[4]; - - // The view we're showing this overscroll on. - private final View mView; - - public OverscrollEdgeEffect(final View v) { - mView = v; - Context context = v.getContext(); - for (int i = 0; i < 4; i++) { - mEdges[i] = new EdgeEffect(context); - } - } - - public void setSize(final int width, final int height) { - mEdges[LEFT].setSize(height, width); - mEdges[RIGHT].setSize(height, width); - mEdges[TOP].setSize(width, height); - mEdges[BOTTOM].setSize(width, height); - } - - private EdgeEffect getEdgeForAxisAndSide(final Axis axis, final float side) { - if (axis == Axis.Y) { - if (side < 0) { - return mEdges[TOP]; - } else { - return mEdges[BOTTOM]; - } - } else { - if (side < 0) { - return mEdges[LEFT]; - } else { - return mEdges[RIGHT]; - } - } - } - - private void invalidate() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - mView.postInvalidateOnAnimation(); - } else { - mView.postInvalidateDelayed(10); - } - } - - public void setVelocity(final float velocity, final Axis axis) { - final EdgeEffect edge = getEdgeForAxisAndSide(axis, velocity); - - // If we're showing overscroll already, start fading it out. - if (!edge.isFinished()) { - edge.onRelease(); - } else { - // Otherwise, show an absorb effect - edge.onAbsorb((int)velocity); - } - - invalidate(); - } - - public void setDistance(final float distance, final Axis axis) { - // The first overscroll event often has zero distance. Throw it out - if (distance == 0.0f) { - return; - } - - final EdgeEffect edge = getEdgeForAxisAndSide(axis, (int)distance); - edge.onPull(distance / (axis == Axis.X ? mView.getWidth() : mView.getHeight())); - invalidate(); - } - - public void draw(final Canvas canvas, final ImmutableViewportMetrics metrics) { - if (metrics == null) { - return; - } - - // If we're pulling an edge, or fading it out, draw! - boolean invalidate = false; - if (!mEdges[TOP].isFinished()) { - invalidate |= draw(mEdges[TOP], canvas, metrics.marginLeft, metrics.marginTop, 0); - } - - if (!mEdges[BOTTOM].isFinished()) { - invalidate |= draw(mEdges[BOTTOM], canvas, mView.getWidth(), mView.getHeight(), 180); - } - - if (!mEdges[LEFT].isFinished()) { - invalidate |= draw(mEdges[LEFT], canvas, metrics.marginLeft, mView.getHeight(), 270); - } - - if (!mEdges[RIGHT].isFinished()) { - invalidate |= draw(mEdges[RIGHT], canvas, mView.getWidth(), metrics.marginTop, 90); - } - - // If the edge effect is animating off screen, invalidate. - if (invalidate) { - invalidate(); - } - } - - public boolean draw(final EdgeEffect edge, final Canvas canvas, final float translateX, final float translateY, final float rotation) { - final int state = canvas.save(); - canvas.translate(translateX, translateY); - canvas.rotate(rotation); - boolean invalidate = edge.draw(canvas); - canvas.restoreToCount(state); - - return invalidate; - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/PanZoomController.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/PanZoomController.java deleted file mode 100644 index 5ef25a64628c..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/PanZoomController.java +++ /dev/null @@ -1,49 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -//import org.mozilla.gecko.GeckoAppShell; -import org.libreoffice.LOKitShell; -import org.mozilla.gecko.util.EventDispatcher; - -import android.graphics.PointF; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; - -public interface PanZoomController { - // The distance the user has to pan before we recognize it as such (e.g. to avoid 1-pixel pans - // between the touch-down and touch-up of a click). In units of density-independent pixels. - public static final float PAN_THRESHOLD = 1/16f * /*GeckoAppShell*/LOKitShell.getDpi(); - - // Threshold for sending touch move events to content - public static final float CLICK_THRESHOLD = 1/50f * /*GeckoAppShell*/LOKitShell.getDpi(); - - static class Factory { - static PanZoomController create(PanZoomTarget target, View view, EventDispatcher dispatcher) { - return new JavaPanZoomController(target, view, dispatcher); - } - } - - public void destroy(); - - public boolean onTouchEvent(MotionEvent event); - public boolean onMotionEvent(MotionEvent event); - public boolean onKeyEvent(KeyEvent event); - public void notifyDefaultActionPrevented(boolean prevented); - - public boolean getRedrawHint(); - public PointF getVelocityVector(); - - public void pageRectUpdated(); - public void abortPanning(); - public void abortAnimation(); - - public void setOverScrollMode(int overscrollMode); - public int getOverScrollMode(); - - public void setOverscrollHandler(final Overscroll controller); -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/PanZoomTarget.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/PanZoomTarget.java deleted file mode 100644 index c32f213937f2..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/PanZoomTarget.java +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import org.mozilla.gecko.ZoomConstraints; - -import android.graphics.PointF; -import android.graphics.RectF; - -public interface PanZoomTarget { - public ImmutableViewportMetrics getViewportMetrics(); - public ZoomConstraints getZoomConstraints(); - public boolean isFullScreen(); - public RectF getMaxMargins(); - - public void setAnimationTarget(ImmutableViewportMetrics viewport); - public void setViewportMetrics(ImmutableViewportMetrics viewport); - public void scrollBy(float dx, float dy); - public void scrollMarginsBy(float dx, float dy); - public void panZoomStopped(); - /** This triggers an (asynchronous) viewport update/redraw. */ - public void forceRedraw(DisplayPortMetrics displayPort); - - public boolean post(Runnable action); - public boolean postDelayed(Runnable action, long delayMillis); - public void postRenderTask(RenderTask task); - public void removeRenderTask(RenderTask task); - public Object getLock(); - public PointF convertViewPointToLayerPoint(PointF viewPoint); -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/PanningPerfAPI.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/PanningPerfAPI.java deleted file mode 100644 index 7c2ca2b9d030..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/PanningPerfAPI.java +++ /dev/null @@ -1,123 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -//import org.mozilla.gecko.mozglue.RobocopTarget; - -import android.os.SystemClock; -import android.util.Log; - -import java.util.ArrayList; -import java.util.List; - -public class PanningPerfAPI { - private static final String LOGTAG = "GeckoPanningPerfAPI"; - - // make this large enough to avoid having to resize the frame time - // list, as that may be expensive and impact the thing we're trying - // to measure. - private static final int EXPECTED_FRAME_COUNT = 2048; - - private static boolean mRecordingFrames = false; - private static List<Long> mFrameTimes; - private static long mFrameStartTime; - - private static boolean mRecordingCheckerboard = false; - private static List<Float> mCheckerboardAmounts; - private static long mCheckerboardStartTime; - - private static void initialiseRecordingArrays() { - if (mFrameTimes == null) { - mFrameTimes = new ArrayList<Long>(EXPECTED_FRAME_COUNT); - } else { - mFrameTimes.clear(); - } - if (mCheckerboardAmounts == null) { - mCheckerboardAmounts = new ArrayList<Float>(EXPECTED_FRAME_COUNT); - } else { - mCheckerboardAmounts.clear(); - } - } - - //@RobocopTarget - public static void startFrameTimeRecording() { - if (mRecordingFrames || mRecordingCheckerboard) { - Log.e(LOGTAG, "Error: startFrameTimeRecording() called while already recording!"); - return; - } - mRecordingFrames = true; - initialiseRecordingArrays(); - mFrameStartTime = SystemClock.uptimeMillis(); - } - - //@RobocopTarget - public static List<Long> stopFrameTimeRecording() { - if (!mRecordingFrames) { - Log.e(LOGTAG, "Error: stopFrameTimeRecording() called when not recording!"); - return null; - } - mRecordingFrames = false; - return mFrameTimes; - } - - public static void recordFrameTime() { - // this will be called often, so try to make it as quick as possible - if (mRecordingFrames) { - mFrameTimes.add(SystemClock.uptimeMillis() - mFrameStartTime); - } - } - - public static boolean isRecordingCheckerboard() { - return mRecordingCheckerboard; - } - - //@RobocopTarget - public static void startCheckerboardRecording() { - if (mRecordingCheckerboard || mRecordingFrames) { - Log.e(LOGTAG, "Error: startCheckerboardRecording() called while already recording!"); - return; - } - mRecordingCheckerboard = true; - initialiseRecordingArrays(); - mCheckerboardStartTime = SystemClock.uptimeMillis(); - } - - //@RobocopTarget - public static List<Float> stopCheckerboardRecording() { - if (!mRecordingCheckerboard) { - Log.e(LOGTAG, "Error: stopCheckerboardRecording() called when not recording!"); - return null; - } - mRecordingCheckerboard = false; - - // We take the number of values in mCheckerboardAmounts here, as there's - // the possibility that this function is called while recordCheckerboard - // is still executing. As values are added to this list last, we use - // this number as the canonical number of recordings. - int values = mCheckerboardAmounts.size(); - - // The score will be the sum of all the values in mCheckerboardAmounts, - // so weight the checkerboard values by time so that frame-rate and - // run-length don't affect score. - long lastTime = 0; - float totalTime = mFrameTimes.get(values - 1); - for (int i = 0; i < values; i++) { - long elapsedTime = mFrameTimes.get(i) - lastTime; - mCheckerboardAmounts.set(i, mCheckerboardAmounts.get(i) * elapsedTime / totalTime); - lastTime += elapsedTime; - } - - return mCheckerboardAmounts; - } - - public static void recordCheckerboard(float amount) { - // this will be called often, so try to make it as quick as possible - if (mRecordingCheckerboard) { - mFrameTimes.add(SystemClock.uptimeMillis() - mCheckerboardStartTime); - mCheckerboardAmounts.add(amount); - } - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/PointUtils.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/PointUtils.java deleted file mode 100644 index 8db329c9fe8d..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/PointUtils.java +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import org.json.JSONException; -import org.json.JSONObject; - -import android.graphics.Point; -import android.graphics.PointF; - -public final class PointUtils { - public static PointF add(PointF one, PointF two) { - return new PointF(one.x + two.x, one.y + two.y); - } - - public static PointF subtract(PointF one, PointF two) { - return new PointF(one.x - two.x, one.y - two.y); - } - - public static PointF scale(PointF point, float factor) { - return new PointF(point.x * factor, point.y * factor); - } - - public static Point round(PointF point) { - return new Point(Math.round(point.x), Math.round(point.y)); - } - - /* Computes the magnitude of the given vector. */ - public static float distance(PointF point) { - return (float)Math.sqrt(point.x * point.x + point.y * point.y); - } - - /** Computes the scalar distance between two points. */ - public static float distance(PointF one, PointF two) { - return PointF.length(one.x - two.x, one.y - two.y); - } - - public static JSONObject toJSON(PointF point) throws JSONException { - // Ensure we put ints, not longs, because Gecko message handlers call getInt(). - int x = Math.round(point.x); - int y = Math.round(point.y); - JSONObject json = new JSONObject(); - json.put("x", x); - json.put("y", y); - return json; - } -} - diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/ProgressiveUpdateData.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/ProgressiveUpdateData.java deleted file mode 100644 index b7c381c688b0..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/ProgressiveUpdateData.java +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -//import org.mozilla.gecko.mozglue.generatorannotations.WrapEntireClassForJNI; - -/** - * This is the data structure that's returned by the progressive tile update - * callback function. It encompasses the current viewport and a boolean value - * representing whether the front-end is interested in the current progressive - * update continuing. - */ -//@WrapEntireClassForJNI -public class ProgressiveUpdateData { - public float x; - public float y; - public float width; - public float height; - public float scale; - public boolean abort; - - public void setViewport(ImmutableViewportMetrics viewport) { - this.x = viewport.viewportRectLeft; - this.y = viewport.viewportRectTop; - this.width = viewport.viewportRectRight - this.x; - this.height = viewport.viewportRectBottom - this.y; - this.scale = viewport.zoomFactor; - } -} - diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/RectUtils.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/RectUtils.java deleted file mode 100644 index 22151db76921..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/RectUtils.java +++ /dev/null @@ -1,126 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import org.mozilla.gecko.util.FloatUtils; - -import org.json.JSONException; -import org.json.JSONObject; - -import android.graphics.Point; -import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.RectF; - -public final class RectUtils { - private RectUtils() {} - - public static Rect create(JSONObject json) { - try { - int x = json.getInt("x"); - int y = json.getInt("y"); - int width = json.getInt("width"); - int height = json.getInt("height"); - return new Rect(x, y, x + width, y + height); - } catch (JSONException e) { - throw new RuntimeException(e); - } - } - - public static String toJSON(RectF rect) { - StringBuilder sb = new StringBuilder(256); - sb.append("{ \"left\": ").append(rect.left) - .append(", \"top\": ").append(rect.top) - .append(", \"right\": ").append(rect.right) - .append(", \"bottom\": ").append(rect.bottom) - .append('}'); - return sb.toString(); - } - - public static RectF expand(RectF rect, float moreWidth, float moreHeight) { - float halfMoreWidth = moreWidth / 2; - float halfMoreHeight = moreHeight / 2; - return new RectF(rect.left - halfMoreWidth, - rect.top - halfMoreHeight, - rect.right + halfMoreWidth, - rect.bottom + halfMoreHeight); - } - - public static RectF contract(RectF rect, float lessWidth, float lessHeight) { - float halfLessWidth = lessWidth / 2.0f; - float halfLessHeight = lessHeight / 2.0f; - return new RectF(rect.left + halfLessWidth, - rect.top + halfLessHeight, - rect.right - halfLessWidth, - rect.bottom - halfLessHeight); - } - - public static RectF intersect(RectF one, RectF two) { - float left = Math.max(one.left, two.left); - float top = Math.max(one.top, two.top); - float right = Math.min(one.right, two.right); - float bottom = Math.min(one.bottom, two.bottom); - return new RectF(left, top, Math.max(right, left), Math.max(bottom, top)); - } - - public static RectF scale(RectF rect, float scale) { - float x = rect.left * scale; - float y = rect.top * scale; - return new RectF(x, y, - x + (rect.width() * scale), - y + (rect.height() * scale)); - } - - public static RectF scaleAndRound(RectF rect, float scale) { - float left = rect.left * scale; - float top = rect.top * scale; - return new RectF(Math.round(left), - Math.round(top), - Math.round(left + (rect.width() * scale)), - Math.round(top + (rect.height() * scale))); - } - - /** Returns the nearest integer rect of the given rect. */ - public static Rect round(RectF rect) { - Rect r = new Rect(); - round(rect, r); - return r; - } - - public static void round(RectF rect, Rect dest) { - dest.set(Math.round(rect.left), Math.round(rect.top), - Math.round(rect.right), Math.round(rect.bottom)); - } - - public static Rect roundIn(RectF rect) { - return new Rect((int)Math.ceil(rect.left), (int)Math.ceil(rect.top), - (int)Math.floor(rect.right), (int)Math.floor(rect.bottom)); - } - - public static IntSize getSize(Rect rect) { - return new IntSize(rect.width(), rect.height()); - } - - public static Point getOrigin(Rect rect) { - return new Point(rect.left, rect.top); - } - - public static PointF getOrigin(RectF rect) { - return new PointF(rect.left, rect.top); - } - - public static boolean fuzzyEquals(RectF a, RectF b) { - if (a == null && b == null) - return true; - else if ((a == null && b != null) || (a != null && b == null)) - return false; - else - return FloatUtils.fuzzyEquals(a.top, b.top) - && FloatUtils.fuzzyEquals(a.left, b.left) - && FloatUtils.fuzzyEquals(a.right, b.right) - && FloatUtils.fuzzyEquals(a.bottom, b.bottom); - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/RenderTask.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/RenderTask.java deleted file mode 100644 index 39c6eacb59f8..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/RenderTask.java +++ /dev/null @@ -1,80 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -/** - * A class used to schedule a callback to occur when the next frame is drawn. - * Subclasses must redefine the internalRun method, not the run method. - */ -public abstract class RenderTask { - /** - * Whether to run the task after the render, or before. - */ - public final boolean runAfter; - - /** - * Time when this task has first run, in ns. Useful for tasks which run for a specific duration. - */ - private long mStartTime; - - /** - * Whether we should initialise mStartTime on the next frame run. - */ - private boolean mResetStartTime = true; - - /** - * The callback to run on each frame. timeDelta is the time elapsed since - * the last call, in nanoseconds. Returns true if it should continue - * running, or false if it should be removed from the task queue. Returning - * true implicitly schedules a redraw. - * - * This method first initializes the start time if resetStartTime has been invoked, - * then calls internalRun. - * - * Note : subclasses should override internalRun. - * - * @param timeDelta the time between the beginning of last frame and the beginning of this frame, in ns. - * @param currentFrameStartTime the startTime of the current frame, in ns. - * @return true if animation should be run at the next frame, false otherwise - * @see org.mozilla.gecko.gfx.RenderTask#internalRun(long, long) - */ - public final boolean run(long timeDelta, long currentFrameStartTime) { - if (mResetStartTime) { - mStartTime = currentFrameStartTime; - mResetStartTime = false; - } - return internalRun(timeDelta, currentFrameStartTime); - } - - /** - * Abstract method to be overridden by subclasses. - * @param timeDelta the time between the beginning of last frame and the beginning of this frame, in ns - * @param currentFrameStartTime the startTime of the current frame, in ns. - * @return true if animation should be run at the next frame, false otherwise - */ - protected abstract boolean internalRun(long timeDelta, long currentFrameStartTime); - - public RenderTask(boolean aRunAfter) { - runAfter = aRunAfter; - } - - /** - * Get the start time of this task. - * It is the start time of the first frame this task was run on. - * @return the start time in ns - */ - public long getStartTime() { - return mStartTime; - } - - /** - * Schedule a reset of the recorded start time next time {@link org.mozilla.gecko.gfx.RenderTask#run(long, long)} is run. - * @see org.mozilla.gecko.gfx.RenderTask#getStartTime() - */ - public void resetStartTime() { - mResetStartTime = true; - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/ScrollbarLayer.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/ScrollbarLayer.java deleted file mode 100644 index 043c82775467..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/ScrollbarLayer.java +++ /dev/null @@ -1,297 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import org.mozilla.gecko.util.FloatUtils; - -import android.graphics.Bitmap; -import android.graphics.Rect; -import android.graphics.RectF; -import android.opengl.GLES20; - -import java.nio.FloatBuffer; - -public class ScrollbarLayer extends TileLayer { - public static final long FADE_DELAY = 500; // milliseconds before fade-out starts - private static final float FADE_AMOUNT = 0.03f; // how much (as a percent) the scrollbar should fade per frame - - private final boolean mVertical; - private float mOpacity; - - // To avoid excessive GC, declare some objects here that would otherwise - // be created and destroyed frequently during draw(). - private final RectF mBarRectF; - private final Rect mBarRect; - private final float[] mCoords; - private final RectF mCapRectF; - - private LayerRenderer mRenderer; - private int mProgram; - private int mPositionHandle; - private int mTextureHandle; - private int mSampleHandle; - private int mTMatrixHandle; - private int mOpacityHandle; - - // Fragment shader used to draw the scroll-bar with opacity - private static final String FRAGMENT_SHADER = - "precision mediump float;\n" + - "varying vec2 vTexCoord;\n" + - "uniform sampler2D sTexture;\n" + - "uniform float uOpacity;\n" + - "void main() {\n" + - " gl_FragColor = texture2D(sTexture, vTexCoord);\n" + - " gl_FragColor.a *= uOpacity;\n" + - "}\n"; - - // Dimensions of the texture bitmap (will always be power-of-two) - private final int mTexWidth; - private final int mTexHeight; - // Some useful dimensions of the actual content in the bitmap - private final int mBarWidth; - private final int mCapLength; - - private final Rect mStartCapTexCoords; // top/left endcap coordinates - private final Rect mBodyTexCoords; // 1-pixel slice of the texture to be stretched - private final Rect mEndCapTexCoords; // bottom/right endcap coordinates - - ScrollbarLayer(LayerRenderer renderer, Bitmap scrollbarImage, IntSize imageSize, boolean vertical) { - super(new BufferedCairoImage(scrollbarImage), TileLayer.PaintMode.NORMAL); - mRenderer = renderer; - mVertical = vertical; - - mBarRectF = new RectF(); - mBarRect = new Rect(); - mCoords = new float[20]; - mCapRectF = new RectF(); - - mTexHeight = scrollbarImage.getHeight(); - mTexWidth = scrollbarImage.getWidth(); - - if (mVertical) { - mBarWidth = imageSize.width; - mCapLength = imageSize.height / 2; - mStartCapTexCoords = new Rect(0, mTexHeight - mCapLength, imageSize.width, mTexHeight); - mBodyTexCoords = new Rect(0, mTexHeight - (mCapLength + 1), imageSize.width, mTexHeight - mCapLength); - mEndCapTexCoords = new Rect(0, mTexHeight - imageSize.height, imageSize.width, mTexHeight - (mCapLength + 1)); - } else { - mBarWidth = imageSize.height; - mCapLength = imageSize.width / 2; - mStartCapTexCoords = new Rect(0, mTexHeight - imageSize.height, mCapLength, mTexHeight); - mBodyTexCoords = new Rect(mCapLength, mTexHeight - imageSize.height, mCapLength + 1, mTexHeight); - mEndCapTexCoords = new Rect(mCapLength + 1, mTexHeight - imageSize.height, imageSize.width, mTexHeight); - } - } - - private void createProgram() { - int vertexShader = LayerRenderer.loadShader(GLES20.GL_VERTEX_SHADER, - LayerRenderer.DEFAULT_VERTEX_SHADER); - int fragmentShader = LayerRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, - FRAGMENT_SHADER); - - mProgram = GLES20.glCreateProgram(); - GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program - GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program - GLES20.glLinkProgram(mProgram); // creates OpenGL program executables - - // Get handles to the shaders' vPosition, aTexCoord, sTexture, and uTMatrix members. - mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); - mTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord"); - mSampleHandle = GLES20.glGetUniformLocation(mProgram, "sTexture"); - mTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uTMatrix"); - mOpacityHandle = GLES20.glGetUniformLocation(mProgram, "uOpacity"); - } - - private void activateProgram() { - // Add the program to the OpenGL environment - GLES20.glUseProgram(mProgram); - - // Set the transformation matrix - GLES20.glUniformMatrix4fv(mTMatrixHandle, 1, false, - LayerRenderer.DEFAULT_TEXTURE_MATRIX, 0); - - // Enable the arrays from which we get the vertex and texture coordinates - GLES20.glEnableVertexAttribArray(mPositionHandle); - GLES20.glEnableVertexAttribArray(mTextureHandle); - - GLES20.glUniform1i(mSampleHandle, 0); - GLES20.glUniform1f(mOpacityHandle, mOpacity); - } - - private void deactivateProgram() { - GLES20.glDisableVertexAttribArray(mTextureHandle); - GLES20.glDisableVertexAttribArray(mPositionHandle); - GLES20.glUseProgram(0); - } - - /** - * Decrease the opacity of the scrollbar by one frame's worth. - * Return true if the opacity was decreased, or false if the scrollbars - * are already fully faded out. - */ - public boolean fade() { - if (FloatUtils.fuzzyEquals(mOpacity, 0.0f)) { - return false; - } - beginTransaction(); // called on compositor thread - mOpacity = Math.max(mOpacity - FADE_AMOUNT, 0.0f); - endTransaction(); - return true; - } - - /** - * Restore the opacity of the scrollbar to fully opaque. - * Return true if the opacity was changed, or false if the scrollbars - * are already fully opaque. - */ - public boolean unfade() { - if (FloatUtils.fuzzyEquals(mOpacity, 1.0f)) { - return false; - } - beginTransaction(); // called on compositor thread - mOpacity = 1.0f; - endTransaction(); - return true; - } - - @Override - public void draw(RenderContext context) { - if (!initialized()) - return; - - // Create the shader program, if necessary - if (mProgram == 0) { - createProgram(); - } - - // Enable the shader program - mRenderer.deactivateDefaultProgram(); - activateProgram(); - - GLES20.glEnable(GLES20.GL_BLEND); - GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); - - if (mVertical) { - getVerticalRect(context, mBarRectF); - } else { - getHorizontalRect(context, mBarRectF); - } - RectUtils.round(mBarRectF, mBarRect); - - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID()); - - float viewWidth = context.viewport.width(); - float viewHeight = context.viewport.height(); - - mBarRectF.set(mBarRect.left, viewHeight - mBarRect.top, mBarRect.right, viewHeight - mBarRect.bottom); - mBarRectF.offset(context.offset.x, -context.offset.y); - - // We take a 1-pixel slice from the center of the image and scale it to become the bar - fillRectCoordBuffer(mCoords, mBarRectF, viewWidth, viewHeight, mBodyTexCoords, mTexWidth, mTexHeight); - - // Get the buffer and handles from the context - FloatBuffer coordBuffer = context.coordBuffer; - int positionHandle = mPositionHandle; - int textureHandle = mTextureHandle; - - // Make sure we are at position zero in the buffer in case other draw methods did not - // clean up after themselves - coordBuffer.position(0); - coordBuffer.put(mCoords); - - // Unbind any the current array buffer so we can use client side buffers - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); - - // Vertex coordinates are x,y,z starting at position 0 into the buffer. - coordBuffer.position(0); - GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer); - - // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer. - coordBuffer.position(3); - GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer); - - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - - // Reset the position in the buffer for the next set of vertex and texture coordinates. - coordBuffer.position(0); - if (mVertical) { - // top endcap - mCapRectF.set(mBarRectF.left, mBarRectF.top + mCapLength, mBarRectF.right, mBarRectF.top); - } else { - // left endcap - mCapRectF.set(mBarRectF.left - mCapLength, mBarRectF.bottom + mBarWidth, mBarRectF.left, mBarRectF.bottom); - } - - fillRectCoordBuffer(mCoords, mCapRectF, viewWidth, viewHeight, mStartCapTexCoords, mTexWidth, mTexHeight); - coordBuffer.put(mCoords); - - // Vertex coordinates are x,y,z starting at position 0 into the buffer. - coordBuffer.position(0); - GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer); - - // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer. - coordBuffer.position(3); - GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer); - - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - - // Reset the position in the buffer for the next set of vertex and texture coordinates. - coordBuffer.position(0); - if (mVertical) { - // bottom endcap - mCapRectF.set(mBarRectF.left, mBarRectF.bottom, mBarRectF.right, mBarRectF.bottom - mCapLength); - } else { - // right endcap - mCapRectF.set(mBarRectF.right, mBarRectF.bottom + mBarWidth, mBarRectF.right + mCapLength, mBarRectF.bottom); - } - fillRectCoordBuffer(mCoords, mCapRectF, viewWidth, viewHeight, mEndCapTexCoords, mTexWidth, mTexHeight); - coordBuffer.put(mCoords); - - // Vertex coordinates are x,y,z starting at position 0 into the buffer. - coordBuffer.position(0); - GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer); - - // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer. - coordBuffer.position(3); - GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer); - - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - - // Reset the position in the buffer for the next set of vertex and texture coordinates. - coordBuffer.position(0); - - // Enable the default shader program again - deactivateProgram(); - mRenderer.activateDefaultProgram(); - } - - private void getVerticalRect(RenderContext context, RectF dest) { - RectF viewport = context.viewport; - RectF pageRect = context.pageRect; - float viewportHeight = viewport.height() - context.offset.y; - float barStart = ((viewport.top - context.offset.y - pageRect.top) * (viewportHeight / pageRect.height())) + mCapLength; - float barEnd = ((viewport.bottom - context.offset.y - pageRect.top) * (viewportHeight / pageRect.height())) - mCapLength; - if (barStart > barEnd) { - float middle = (barStart + barEnd) / 2.0f; - barStart = barEnd = middle; - } - dest.set(viewport.width() - mBarWidth, barStart, viewport.width(), barEnd); - } - - private void getHorizontalRect(RenderContext context, RectF dest) { - RectF viewport = context.viewport; - RectF pageRect = context.pageRect; - float viewportWidth = viewport.width() - context.offset.x; - float barStart = ((viewport.left - context.offset.x - pageRect.left) * (viewport.width() / pageRect.width())) + mCapLength; - float barEnd = ((viewport.right - context.offset.x - pageRect.left) * (viewport.width() / pageRect.width())) - mCapLength; - if (barStart > barEnd) { - float middle = (barStart + barEnd) / 2.0f; - barStart = barEnd = middle; - } - dest.set(barStart, viewport.height() - mBarWidth, barEnd, viewport.height()); - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/SimpleScaleGestureDetector.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/SimpleScaleGestureDetector.java deleted file mode 100644 index b3f6fcbc55e9..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/SimpleScaleGestureDetector.java +++ /dev/null @@ -1,322 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import org.json.JSONException; - -import android.graphics.PointF; -import android.util.Log; -import android.view.MotionEvent; - -import java.util.LinkedList; -import java.util.ListIterator; -import java.util.Stack; - -/** - * A less buggy, and smoother, replacement for the built-in Android ScaleGestureDetector. - * - * This gesture detector is more reliable than the built-in ScaleGestureDetector because: - * - * - It doesn't assume that pointer IDs are numbered 0 and 1. - * - * - It doesn't attempt to correct for "slop" when resting one's hand on the device. On some - * devices (e.g. the Droid X) this can cause the ScaleGestureDetector to lose track of how many - * pointers are down, with disastrous results (bug 706684). - * - * - Cancelling a zoom into a pan is handled correctly. - * - * - Starting with three or more fingers down, releasing fingers so that only two are down, and - * then performing a scale gesture is handled correctly. - * - * - It doesn't take pressure into account, which results in smoother scaling. - */ -class SimpleScaleGestureDetector { - private static final String LOGTAG = "GeckoSimpleScaleGestureDetector"; - - private SimpleScaleGestureListener mListener; - private long mLastEventTime; - private boolean mScaleResult; - - /* Information about all pointers that are down. */ - private LinkedList<PointerInfo> mPointerInfo; - - /** Creates a new gesture detector with the given listener. */ - SimpleScaleGestureDetector(SimpleScaleGestureListener listener) { - mListener = listener; - mPointerInfo = new LinkedList<PointerInfo>(); - } - - /** Forward touch events to this function. */ - public void onTouchEvent(MotionEvent event) { - switch (event.getAction() & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_DOWN: - // If we get ACTION_DOWN while still tracking any pointers, - // something is wrong. Cancel the current gesture and start over. - if (getPointersDown() > 0) - onTouchEnd(event); - onTouchStart(event); - break; - case MotionEvent.ACTION_POINTER_DOWN: - onTouchStart(event); - break; - case MotionEvent.ACTION_MOVE: - onTouchMove(event); - break; - case MotionEvent.ACTION_POINTER_UP: - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - onTouchEnd(event); - break; - } - } - - private int getPointersDown() { - return mPointerInfo.size(); - } - - private int getActionIndex(MotionEvent event) { - return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) - >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; - } - - private void onTouchStart(MotionEvent event) { - mLastEventTime = event.getEventTime(); - mPointerInfo.addFirst(PointerInfo.create(event, getActionIndex(event))); - if (getPointersDown() == 2) { - sendScaleGesture(EventType.BEGIN); - } - } - - private void onTouchMove(MotionEvent event) { - mLastEventTime = event.getEventTime(); - for (int i = 0; i < event.getPointerCount(); i++) { - PointerInfo pointerInfo = pointerInfoForEventIndex(event, i); - if (pointerInfo != null) { - pointerInfo.populate(event, i); - } - } - - if (getPointersDown() == 2) { - sendScaleGesture(EventType.CONTINUE); - } - } - - private void onTouchEnd(MotionEvent event) { - mLastEventTime = event.getEventTime(); - - int action = event.getAction() & MotionEvent.ACTION_MASK; - boolean isCancel = (action == MotionEvent.ACTION_CANCEL || - action == MotionEvent.ACTION_DOWN); - - int id = event.getPointerId(getActionIndex(event)); - ListIterator<PointerInfo> iterator = mPointerInfo.listIterator(); - while (iterator.hasNext()) { - PointerInfo pointerInfo = iterator.next(); - if (!(isCancel || pointerInfo.getId() == id)) { - continue; - } - - // One of the pointers we were tracking was lifted. Remove its info object from the - // list, recycle it to avoid GC pauses, and send an onScaleEnd() notification if this - // ended the gesture. - iterator.remove(); - pointerInfo.recycle(); - if (getPointersDown() == 1) { - sendScaleGesture(EventType.END); - } - } - } - - /** - * Returns the X coordinate of the focus location (the midpoint of the two fingers). If only - * one finger is down, returns the location of that finger. - */ - public float getFocusX() { - switch (getPointersDown()) { - case 1: - return mPointerInfo.getFirst().getCurrent().x; - case 2: - PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast(); - return (pointerA.getCurrent().x + pointerB.getCurrent().x) / 2.0f; - } - - Log.e(LOGTAG, "No gesture taking place in getFocusX()!"); - return 0.0f; - } - - /** - * Returns the Y coordinate of the focus location (the midpoint of the two fingers). If only - * one finger is down, returns the location of that finger. - */ - public float getFocusY() { - switch (getPointersDown()) { - case 1: - return mPointerInfo.getFirst().getCurrent().y; - case 2: - PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast(); - return (pointerA.getCurrent().y + pointerB.getCurrent().y) / 2.0f; - } - - Log.e(LOGTAG, "No gesture taking place in getFocusY()!"); - return 0.0f; - } - - /** Returns the most recent distance between the two pointers. */ - public float getCurrentSpan() { - if (getPointersDown() != 2) { - Log.e(LOGTAG, "No gesture taking place in getCurrentSpan()!"); - return 0.0f; - } - - PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast(); - return PointUtils.distance(pointerA.getCurrent(), pointerB.getCurrent()); - } - - /** Returns the second most recent distance between the two pointers. */ - public float getPreviousSpan() { - if (getPointersDown() != 2) { - Log.e(LOGTAG, "No gesture taking place in getPreviousSpan()!"); - return 0.0f; - } - - PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast(); - PointF a = pointerA.getPrevious(), b = pointerB.getPrevious(); - if (a == null || b == null) { - a = pointerA.getCurrent(); - b = pointerB.getCurrent(); - } - - return PointUtils.distance(a, b); - } - - /** Returns the time of the last event related to the gesture. */ - public long getEventTime() { - return mLastEventTime; - } - - /** Returns true if the scale gesture is in progress and false otherwise. */ - public boolean isInProgress() { - return getPointersDown() == 2; - } - - /* Sends the requested scale gesture notification to the listener. */ - private void sendScaleGesture(EventType eventType) { - switch (eventType) { - case BEGIN: - mScaleResult = mListener.onScaleBegin(this); - break; - case CONTINUE: - if (mScaleResult) { - mListener.onScale(this); - } - break; - case END: - if (mScaleResult) { - mListener.onScaleEnd(this); - } - break; - } - } - - /* - * Returns the pointer info corresponding to the given pointer index, or null if the pointer - * isn't one that's being tracked. - */ - private PointerInfo pointerInfoForEventIndex(MotionEvent event, int index) { - int id = event.getPointerId(index); - for (PointerInfo pointerInfo : mPointerInfo) { - if (pointerInfo.getId() == id) { - return pointerInfo; - } - } - return null; - } - - private enum EventType { - BEGIN, - CONTINUE, - END, - } - - /* Encapsulates information about one of the two fingers involved in the gesture. */ - private static class PointerInfo { - /* A free list that recycles pointer info objects, to reduce GC pauses. */ - private static Stack<PointerInfo> sPointerInfoFreeList; - - private int mId; - private PointF mCurrent, mPrevious; - - private PointerInfo() { - // External users should use create() instead. - } - - /* Creates or recycles a new PointerInfo instance from an event and a pointer index. */ - public static PointerInfo create(MotionEvent event, int index) { - if (sPointerInfoFreeList == null) { - sPointerInfoFreeList = new Stack<PointerInfo>(); - } - - PointerInfo pointerInfo; - if (sPointerInfoFreeList.empty()) { - pointerInfo = new PointerInfo(); - } else { - pointerInfo = sPointerInfoFreeList.pop(); - } - - pointerInfo.populate(event, index); - return pointerInfo; - } - - /* - * Fills in the fields of this instance from the given motion event and pointer index - * within that event. - */ - public void populate(MotionEvent event, int index) { - mId = event.getPointerId(index); - mPrevious = mCurrent; - mCurrent = new PointF(event.getX(index), event.getY(index)); - } - - public void recycle() { - mId = -1; - mPrevious = mCurrent = null; - sPointerInfoFreeList.push(this); - } - - public int getId() { return mId; } - public PointF getCurrent() { return mCurrent; } - public PointF getPrevious() { return mPrevious; } - - @Override - public String toString() { - if (mId == -1) { - return "(up)"; - } - - try { - String prevString; - if (mPrevious == null) { - prevString = "n/a"; - } else { - prevString = PointUtils.toJSON(mPrevious).toString(); - } - - // The current position should always be non-null. - String currentString = PointUtils.toJSON(mCurrent).toString(); - return "id=" + mId + " cur=" + currentString + " prev=" + prevString; - } catch (JSONException e) { - throw new RuntimeException(e); - } - } - } - - public static interface SimpleScaleGestureListener { - public boolean onScale(SimpleScaleGestureDetector detector); - public boolean onScaleBegin(SimpleScaleGestureDetector detector); - public void onScaleEnd(SimpleScaleGestureDetector detector); - } -} - diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/SingleTileLayer.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/SingleTileLayer.java deleted file mode 100644 index 4b29c515271a..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/SingleTileLayer.java +++ /dev/null @@ -1,153 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.Region; -import android.graphics.RegionIterator; -import android.opengl.GLES20; - -import java.nio.FloatBuffer; - -/** - * Encapsulates the logic needed to draw a single textured tile. - * - * TODO: Repeating textures really should be their own type of layer. - */ -public class SingleTileLayer extends TileLayer { - private static final String LOGTAG = "GeckoSingleTileLayer"; - - private Rect mMask; - - // To avoid excessive GC, declare some objects here that would otherwise - // be created and destroyed frequently during draw(). - private final RectF mBounds; - private final RectF mTextureBounds; - private final RectF mViewport; - private final Rect mIntBounds; - private final Rect mSubRect; - private final RectF mSubRectF; - private final Region mMaskedBounds; - private final Rect mCropRect; - private final RectF mObjRectF; - private final float[] mCoords; - - public SingleTileLayer(CairoImage image) { - this(false, image); - } - - public SingleTileLayer(boolean repeat, CairoImage image) { - this(image, repeat ? PaintMode.REPEAT : PaintMode.NORMAL); - } - - public SingleTileLayer(CairoImage image, PaintMode paintMode) { - super(image, paintMode); - - mBounds = new RectF(); - mTextureBounds = new RectF(); - mViewport = new RectF(); - mIntBounds = new Rect(); - mSubRect = new Rect(); - mSubRectF = new RectF(); - mMaskedBounds = new Region(); - mCropRect = new Rect(); - mObjRectF = new RectF(); - mCoords = new float[20]; - } - - /** - * Set an area to mask out when rendering. - */ - public void setMask(Rect aMaskRect) { - mMask = aMaskRect; - } - - @Override - public void draw(RenderContext context) { - // mTextureIDs may be null here during startup if Layer.java's draw method - // failed to acquire the transaction lock and call performUpdates. - if (!initialized()) - return; - - mViewport.set(context.viewport); - - if (repeats()) { - // If we're repeating, we want to adjust the texture bounds so that - // the texture repeats the correct number of times when drawn at - // the size of the viewport. - mBounds.set(getBounds(context)); - mTextureBounds.set(0.0f, 0.0f, mBounds.width(), mBounds.height()); - mBounds.set(0.0f, 0.0f, mViewport.width(), mViewport.height()); - } else if (stretches()) { - // If we're stretching, we just want the bounds and texture bounds - // to fit to the page. - mBounds.set(context.pageRect); - mTextureBounds.set(mBounds); - } else { - mBounds.set(getBounds(context)); - mTextureBounds.set(mBounds); - } - - mBounds.roundOut(mIntBounds); - mMaskedBounds.set(mIntBounds); - if (mMask != null) { - mMaskedBounds.op(mMask, Region.Op.DIFFERENCE); - if (mMaskedBounds.isEmpty()) - return; - } - - // XXX Possible optimisation here, form this array so we can draw it in - // a single call. - RegionIterator i = new RegionIterator(mMaskedBounds); - while (i.next(mSubRect)) { - // Compensate for rounding errors at the edge of the tile caused by - // the roundOut above - mSubRectF.set(Math.max(mBounds.left, (float)mSubRect.left), - Math.max(mBounds.top, (float)mSubRect.top), - Math.min(mBounds.right, (float)mSubRect.right), - Math.min(mBounds.bottom, (float)mSubRect.bottom)); - - // This is the left/top/right/bottom of the rect, relative to the - // bottom-left of the layer, to use for texture coordinates. - mCropRect.set(Math.round(mSubRectF.left - mBounds.left), - Math.round(mBounds.bottom - mSubRectF.top), - Math.round(mSubRectF.right - mBounds.left), - Math.round(mBounds.bottom - mSubRectF.bottom)); - - mObjRectF.set(mSubRectF.left - mViewport.left, - mViewport.bottom - mSubRectF.bottom, - mSubRectF.right - mViewport.left, - mViewport.bottom - mSubRectF.top); - - fillRectCoordBuffer(mCoords, mObjRectF, mViewport.width(), mViewport.height(), - mCropRect, mTextureBounds.width(), mTextureBounds.height()); - - FloatBuffer coordBuffer = context.coordBuffer; - int positionHandle = context.positionHandle; - int textureHandle = context.textureHandle; - - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID()); - - // Make sure we are at position zero in the buffer - coordBuffer.position(0); - coordBuffer.put(mCoords); - - // Unbind any the current array buffer so we can use client side buffers - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); - - // Vertex coordinates are x,y,z starting at position 0 into the buffer. - coordBuffer.position(0); - GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer); - - // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer. - coordBuffer.position(3); - GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer); - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); - } - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/SubdocumentScrollHelper.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/SubdocumentScrollHelper.java deleted file mode 100644 index b581d3147ec1..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/SubdocumentScrollHelper.java +++ /dev/null @@ -1,148 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -//import org.mozilla.gecko.GeckoAppShell; -//import org.mozilla.gecko.GeckoEvent; -import org.mozilla.gecko.util.EventDispatcher; -import org.mozilla.gecko.util.GeckoEventListener; - -import org.json.JSONException; -import org.json.JSONObject; - -import android.graphics.PointF; -import android.os.Handler; -import android.util.Log; - -class SubdocumentScrollHelper implements GeckoEventListener { - private static final String LOGTAG = "GeckoSubdocScroll"; - - private static String MESSAGE_PANNING_OVERRIDE = "Panning:Override"; - private static String MESSAGE_CANCEL_OVERRIDE = "Panning:CancelOverride"; - private static String MESSAGE_SCROLL = "Gesture:Scroll"; - private static String MESSAGE_SCROLL_ACK = "Gesture:ScrollAck"; - - private final Handler mUiHandler; - private final EventDispatcher mEventDispatcher; - - /* This is the amount of displacement we have accepted but not yet sent to JS; this is - * only valid when mOverrideScrollPending is true. */ - private final PointF mPendingDisplacement; - - /* When this is true, we're sending scroll events to JS to scroll the active subdocument. */ - private boolean mOverridePanning; - - /* When this is true, we have received an ack for the last scroll event we sent to JS, and - * are ready to send the next scroll event. Note we only ever have one scroll event inflight - * at a time. */ - private boolean mOverrideScrollAck; - - /* When this is true, we have a pending scroll that we need to send to JS; we were unable - * to send it when it was initially requested because mOverrideScrollAck was not true. */ - private boolean mOverrideScrollPending; - - /* When this is true, the last scroll event we sent actually did some amount of scrolling on - * the subdocument; we use this to decide when we have reached the end of the subdocument. */ - private boolean mScrollSucceeded; - - SubdocumentScrollHelper(EventDispatcher eventDispatcher) { - // mUiHandler will be bound to the UI thread since that's where this constructor runs - mUiHandler = new Handler(); - mPendingDisplacement = new PointF(); - - mEventDispatcher = eventDispatcher; - registerEventListener(MESSAGE_PANNING_OVERRIDE); - registerEventListener(MESSAGE_CANCEL_OVERRIDE); - registerEventListener(MESSAGE_SCROLL_ACK); - } - - void destroy() { - unregisterEventListener(MESSAGE_PANNING_OVERRIDE); - unregisterEventListener(MESSAGE_CANCEL_OVERRIDE); - unregisterEventListener(MESSAGE_SCROLL_ACK); - } - - private void registerEventListener(String event) { - mEventDispatcher.registerEventListener(event, this); - } - - private void unregisterEventListener(String event) { - mEventDispatcher.unregisterEventListener(event, this); - } - - boolean scrollBy(PointF displacement) { - if (! mOverridePanning) { - return false; - } - - if (! mOverrideScrollAck) { - mOverrideScrollPending = true; - mPendingDisplacement.x += displacement.x; - mPendingDisplacement.y += displacement.y; - return true; - } - - JSONObject json = new JSONObject(); - try { - json.put("x", displacement.x); - json.put("y", displacement.y); - } catch (JSONException e) { - Log.e(LOGTAG, "Error forming subwindow scroll message: ", e); - } - //GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(MESSAGE_SCROLL, json.toString())); - - mOverrideScrollAck = false; - mOverrideScrollPending = false; - // clear the |mPendingDisplacement| after serializing |displacement| to - // JSON because they might be the same object - mPendingDisplacement.x = 0; - mPendingDisplacement.y = 0; - - return true; - } - - void cancel() { - mOverridePanning = false; - } - - boolean scrolling() { - return mOverridePanning; - } - - boolean lastScrollSucceeded() { - return mScrollSucceeded; - } - - // GeckoEventListener implementation - - @Override - public void handleMessage(final String event, final JSONObject message) { - // This comes in on the Gecko thread; hand off the handling to the UI thread. - mUiHandler.post(new Runnable() { - @Override - public void run() { - try { - if (MESSAGE_PANNING_OVERRIDE.equals(event)) { - mOverridePanning = true; - mOverrideScrollAck = true; - mOverrideScrollPending = false; - mScrollSucceeded = true; - } else if (MESSAGE_CANCEL_OVERRIDE.equals(event)) { - mOverridePanning = false; - } else if (MESSAGE_SCROLL_ACK.equals(event)) { - mOverrideScrollAck = true; - mScrollSucceeded = message.getBoolean("scrolled"); - if (mOverridePanning && mOverrideScrollPending) { - scrollBy(mPendingDisplacement); - } - } - } catch (Exception e) { - Log.e(LOGTAG, "Exception handling message", e); - } - } - }); - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/TextLayer.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/TextLayer.java deleted file mode 100644 index c8eb99cb4a88..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/TextLayer.java +++ /dev/null @@ -1,69 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import org.mozilla.gecko.mozglue.DirectBufferAllocator; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Typeface; - -import java.nio.ByteBuffer; - -/** - * Draws text on a layer. This is used for the frame rate meter. - */ -public class TextLayer extends SingleTileLayer { - private final ByteBuffer mBuffer; // this buffer is owned by the BufferedCairoImage - private final IntSize mSize; - - /* - * This awkward pattern is necessary due to Java's restrictions on when one can call superclass - * constructors. - */ - private TextLayer(ByteBuffer buffer, BufferedCairoImage image, IntSize size, String text) { - super(false, image); - mBuffer = buffer; - mSize = size; - renderText(text); - } - - public static TextLayer create(IntSize size, String text) { - ByteBuffer buffer = DirectBufferAllocator.allocate(size.width * size.height * 4); - BufferedCairoImage image = new BufferedCairoImage(buffer, size.width, size.height, - CairoImage.FORMAT_ARGB32); - return new TextLayer(buffer, image, size, text); - } - - public void setText(String text) { - renderText(text); - invalidate(); - } - - private void renderText(String text) { - Bitmap bitmap = Bitmap.createBitmap(mSize.width, mSize.height, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - - Paint textPaint = new Paint(); - textPaint.setAntiAlias(true); - textPaint.setColor(Color.WHITE); - textPaint.setFakeBoldText(true); - textPaint.setTextSize(18.0f); - textPaint.setTypeface(Typeface.DEFAULT_BOLD); - float width = textPaint.measureText(text) + 18.0f; - - Paint backgroundPaint = new Paint(); - backgroundPaint.setColor(Color.argb(127, 0, 0, 0)); - canvas.drawRect(0.0f, 0.0f, width, 18.0f + 6.0f, backgroundPaint); - - canvas.drawText(text, 6.0f, 18.0f, textPaint); - - bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer()); - } -} - diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/TextureGenerator.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/TextureGenerator.java deleted file mode 100644 index 239ba7bf7694..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/TextureGenerator.java +++ /dev/null @@ -1,75 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import android.opengl.GLES20; -import android.util.Log; - -import java.util.concurrent.ArrayBlockingQueue; - -import javax.microedition.khronos.egl.EGL10; -import javax.microedition.khronos.egl.EGLContext; - -public class TextureGenerator { - private static final String LOGTAG = "TextureGenerator"; - private static final int POOL_SIZE = 5; - - private static TextureGenerator sSharedInstance; - - private ArrayBlockingQueue<Integer> mTextureIds; - private EGLContext mContext; - - private TextureGenerator() { mTextureIds = new ArrayBlockingQueue<Integer>(POOL_SIZE); } - - public static TextureGenerator get() { - if (sSharedInstance == null) - sSharedInstance = new TextureGenerator(); - return sSharedInstance; - } - - public synchronized int take() { - try { - // Will block until one becomes available - return (int)mTextureIds.take(); - } catch (InterruptedException e) { - return 0; - } - } - - public synchronized void fill() { - EGL10 egl = (EGL10)EGLContext.getEGL(); - EGLContext context = egl.eglGetCurrentContext(); - - if (mContext != null && mContext != context) { - mTextureIds.clear(); - } - - mContext = context; - - int numNeeded = mTextureIds.remainingCapacity(); - if (numNeeded == 0) - return; - - // Clear existing GL errors - int error; - while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { - Log.w(LOGTAG, String.format("Clearing GL error: %#x", error)); - } - - int[] textures = new int[numNeeded]; - GLES20.glGenTextures(numNeeded, textures, 0); - - error = GLES20.glGetError(); - if (error != GLES20.GL_NO_ERROR) { - Log.e(LOGTAG, String.format("Failed to generate textures: %#x", error), new Exception()); - return; - } - - for (int i = 0; i < numNeeded; i++) { - mTextureIds.offer(textures[i]); - } - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/TextureReaper.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/TextureReaper.java deleted file mode 100644 index 71b4690eb9f1..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/TextureReaper.java +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import android.opengl.GLES20; - -import java.util.ArrayList; - -/** Manages a list of dead tiles, so we don't leak resources. */ -public class TextureReaper { - private static TextureReaper sSharedInstance; - private ArrayList<Integer> mDeadTextureIDs; - - private TextureReaper() { mDeadTextureIDs = new ArrayList<Integer>(); } - - public static TextureReaper get() { - if (sSharedInstance == null) - sSharedInstance = new TextureReaper(); - return sSharedInstance; - } - - public void add(int[] textureIDs) { - for (int textureID : textureIDs) - add(textureID); - } - - public void add(int textureID) { - mDeadTextureIDs.add(textureID); - } - - public void reap() { - int numTextures = mDeadTextureIDs.size(); - // Adreno 200 will generate INVALID_VALUE if len == 0 is passed to glDeleteTextures, - // even though it's not supposed to. - if (numTextures == 0) - return; - - int[] deadTextureIDs = new int[numTextures]; - for (int i = 0; i < numTextures; i++) { - deadTextureIDs[i] = mDeadTextureIDs.get(i); - } - mDeadTextureIDs.clear(); - - GLES20.glDeleteTextures(deadTextureIDs.length, deadTextureIDs, 0); - } -} - - diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/TileLayer.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/TileLayer.java deleted file mode 100644 index 3c3bc2887109..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/TileLayer.java +++ /dev/null @@ -1,177 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -import android.graphics.Rect; -import android.opengl.GLES20; -import android.util.Log; - -import java.nio.ByteBuffer; - -/** - * Base class for tile layers, which encapsulate the logic needed to draw textured tiles in OpenGL - * ES. - */ -public abstract class TileLayer extends Layer { - private static final String LOGTAG = "GeckoTileLayer"; - - private final Rect mDirtyRect; - private IntSize mSize; - private int[] mTextureIDs; - - protected final CairoImage mImage; - - public enum PaintMode { NORMAL, REPEAT, STRETCH }; - private PaintMode mPaintMode; - - public TileLayer(CairoImage image, PaintMode paintMode) { - super(image.getSize()); - - mPaintMode = paintMode; - mImage = image; - mSize = new IntSize(0, 0); - mDirtyRect = new Rect(); - } - - protected boolean repeats() { return mPaintMode == PaintMode.REPEAT; } - protected boolean stretches() { return mPaintMode == PaintMode.STRETCH; } - protected int getTextureID() { return mTextureIDs[0]; } - protected boolean initialized() { return mImage != null && mTextureIDs != null; } - - @Override - protected void finalize() throws Throwable { - try { - if (mTextureIDs != null) - TextureReaper.get().add(mTextureIDs); - } finally { - super.finalize(); - } - } - - public void destroy() { - try { - if (mImage != null) { - mImage.destroy(); - } - } catch (Exception ex) { - Log.e(LOGTAG, "error clearing buffers: ", ex); - } - } - - public void setPaintMode(PaintMode mode) { - mPaintMode = mode; - } - - /** - * Invalidates the entire buffer so that it will be uploaded again. Only valid inside a - * transaction. - */ - - public void invalidate() { - if (!inTransaction()) - throw new RuntimeException("invalidate() is only valid inside a transaction"); - IntSize bufferSize = mImage.getSize(); - mDirtyRect.set(0, 0, bufferSize.width, bufferSize.height); - } - - private void validateTexture() { - /* Calculate the ideal texture size. This must be a power of two if - * the texture is repeated or OpenGL ES 2.0 isn't supported, as - * OpenGL ES 2.0 is required for NPOT texture support (without - * extensions), but doesn't support repeating NPOT textures. - * - * XXX Currently, we don't pick a GLES 2.0 context, so always round. - */ - IntSize textureSize = mImage.getSize().nextPowerOfTwo(); - - if (!textureSize.equals(mSize)) { - mSize = textureSize; - - // Delete the old texture - if (mTextureIDs != null) { - TextureReaper.get().add(mTextureIDs); - mTextureIDs = null; - - // Free the texture immediately, so we don't incur a - // temporarily increased memory usage. - TextureReaper.get().reap(); - } - } - } - - @Override - protected void performUpdates(RenderContext context) { - super.performUpdates(context); - - // Reallocate the texture if the size has changed - validateTexture(); - - // Don't do any work if the image has an invalid size. - if (!mImage.getSize().isPositive()) - return; - - // If we haven't allocated a texture, assume the whole region is dirty - if (mTextureIDs == null) { - uploadFullTexture(); - } else { - uploadDirtyRect(mDirtyRect); - } - - mDirtyRect.setEmpty(); - } - - private void uploadFullTexture() { - IntSize bufferSize = mImage.getSize(); - uploadDirtyRect(new Rect(0, 0, bufferSize.width, bufferSize.height)); - } - - private void uploadDirtyRect(Rect dirtyRect) { - // If we have nothing to upload, just return for now - if (dirtyRect.isEmpty()) - return; - - // It's possible that the buffer will be null, check for that and return - ByteBuffer imageBuffer = mImage.getBuffer(); - if (imageBuffer == null) - return; - - if (mTextureIDs == null) { - mTextureIDs = new int[1]; - GLES20.glGenTextures(mTextureIDs.length, mTextureIDs, 0); - } - - int cairoFormat = mImage.getFormat(); - CairoGLInfo glInfo = new CairoGLInfo(cairoFormat); - - bindAndSetGLParameters(); - - // XXX TexSubImage2D is too broken to rely on Adreno, and very slow - // on other chipsets, so we always upload the entire buffer. - IntSize bufferSize = mImage.getSize(); - if (mSize.equals(bufferSize)) { - GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width, - mSize.height, 0, glInfo.format, glInfo.type, imageBuffer); - } else { - // Our texture has been expanded to the next power of two. - // XXX We probably never want to take this path, so throw an exception. - throw new RuntimeException("Buffer/image size mismatch in TileLayer!"); - } - } - - private void bindAndSetGLParameters() { - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIDs[0]); - GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, - GLES20.GL_LINEAR); - GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, - GLES20.GL_LINEAR); - - int repeatMode = repeats() ? GLES20.GL_REPEAT : GLES20.GL_CLAMP_TO_EDGE; - GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, repeatMode); - GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, repeatMode); - } -} - diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/TouchEventHandler.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/TouchEventHandler.java deleted file mode 100644 index 9710bd41df5f..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/TouchEventHandler.java +++ /dev/null @@ -1,306 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -//import org.mozilla.gecko.Tab; -//import org.mozilla.gecko.Tabs; - -import android.content.Context; -import android.os.SystemClock; -import android.util.Log; -import android.view.GestureDetector; -import android.view.MotionEvent; -import android.view.View; - -import java.util.LinkedList; -import java.util.Queue; - -/** - * This class handles incoming touch events from the user and sends them to - * listeners in Gecko and/or performs the "default action" (asynchronous pan/zoom - * behaviour. EVERYTHING IN THIS CLASS MUST RUN ON THE UI THREAD. - * - * In the following code/comments, a "block" of events refers to a contiguous - * sequence of events that starts with a DOWN or POINTER_DOWN and goes up to - * but not including the next DOWN or POINTER_DOWN event. - * - * "Dispatching" an event refers to performing the default actions for the event, - * which at our level of abstraction just means sending it off to the gesture - * detectors and the pan/zoom controller. - * - * If an event is "default-prevented" that means one or more listeners in Gecko - * has called preventDefault() on the event, which means that the default action - * for that event should not occur. Usually we care about a "block" of events being - * default-prevented, which means that the DOWN/POINTER_DOWN event that started - * the block, or the first MOVE event following that, were prevent-defaulted. - * - * A "default-prevented notification" is when we here in Java-land receive a notification - * from gecko as to whether or not a block of events was default-prevented. This happens - * at some point after the first or second event in the block is processed in Gecko. - * This code assumes we get EXACTLY ONE default-prevented notification for each block - * of events. - * - * Note that even if all events are default-prevented, we still send specific types - * of notifications to the pan/zoom controller. The notifications are needed - * to respond to user actions a timely manner regardless of default-prevention, - * and fix issues like bug 749384. - */ -final class TouchEventHandler /*implements Tabs.OnTabsChangedListener*/ { - private static final String LOGTAG = "GeckoTouchEventHandler"; - - // The time limit for listeners to respond with preventDefault on touchevents - // before we begin panning the page - private final int EVENT_LISTENER_TIMEOUT = 200; - - private final View mView; - private final GestureDetector mGestureDetector; - private final SimpleScaleGestureDetector mScaleGestureDetector; - private final JavaPanZoomController mPanZoomController; - - // the queue of events that we are holding on to while waiting for a preventDefault - // notification - private final Queue<MotionEvent> mEventQueue; - private final ListenerTimeoutProcessor mListenerTimeoutProcessor; - - // whether or not we should wait for touch listeners to respond (this state is - // per-tab and is updated when we switch tabs). - private boolean mWaitForTouchListeners; - - // true if we should hold incoming events in our queue. this is re-set for every - // block of events, this is cleared once we find out if the block has been - // default-prevented or not (or we time out waiting for that). - private boolean mHoldInQueue; - - // false if the current event block has been default-prevented. In this case, - // we still pass the event to both Gecko and the pan/zoom controller, but the - // latter will not use it to scroll content. It may still use the events for - // other things, such as making the dynamic toolbar visible. - private boolean mAllowDefaultAction; - - // this next variable requires some explanation. strap yourself in. - // - // for each block of events, we do two things: (1) send the events to gecko and expect - // exactly one default-prevented notification in return, and (2) kick off a delayed - // ListenerTimeoutProcessor that triggers in case we don't hear from the listener in - // a timely fashion. - // since events are constantly coming in, we need to be able to handle more than one - // block of events in the queue. - // - // this means that there are ordering restrictions on these that we can take advantage of, - // and need to abide by. blocks of events in the queue will always be in the order that - // the user generated them. default-prevented notifications we get from gecko will be in - // the same order as the blocks of events in the queue. the ListenerTimeoutProcessors that - // have been posted will also fire in the same order as the blocks of events in the queue. - // HOWEVER, we may get multiple default-prevented notifications interleaved with multiple - // ListenerTimeoutProcessor firings, and that interleaving is not predictable. - // - // therefore, we need to make sure that for each block of events, we process the queued - // events exactly once, either when we get the default-prevented notification, or when the - // timeout expires (whichever happens first). there is no way to associate the - // default-prevented notification with a particular block of events other than via ordering, - // - // so what we do to accomplish this is to track a "processing balance", which is the number - // of default-prevented notifications that we have received, minus the number of ListenerTimeoutProcessors - // that have fired. (think "balance" as in teeter-totter balance). this value is: - // - zero when we are in a state where the next default-prevented notification we expect - // to receive and the next ListenerTimeoutProcessor we expect to fire both correspond to - // the next block of events in the queue. - // - positive when we are in a state where we have received more default-prevented notifications - // than ListenerTimeoutProcessors. This means that the next default-prevented notification - // does correspond to the block at the head of the queue, but the next n ListenerTimeoutProcessors - // need to be ignored as they are for blocks we have already processed. (n is the absolute value - // of the balance.) - // - negative when we are in a state where we have received more ListenerTimeoutProcessors than - // default-prevented notifications. This means that the next ListenerTimeoutProcessor that - // we receive does correspond to the block at the head of the queue, but the next n - // default-prevented notifications need to be ignored as they are for blocks we have already - // processed. (n is the absolute value of the balance.) - private int mProcessingBalance; - - TouchEventHandler(Context context, View view, JavaPanZoomController panZoomController) { - mView = view; - - mEventQueue = new LinkedList<MotionEvent>(); - mPanZoomController = panZoomController; - mGestureDetector = new GestureDetector(context, mPanZoomController); - mScaleGestureDetector = new SimpleScaleGestureDetector(mPanZoomController); - mListenerTimeoutProcessor = new ListenerTimeoutProcessor(); - mAllowDefaultAction = true; - - mGestureDetector.setOnDoubleTapListener(mPanZoomController); - - //Tabs.registerOnTabsChangedListener(this); - } - - public void destroy() { - //Tabs.unregisterOnTabsChangedListener(this); - } - - /* This function MUST be called on the UI thread */ - public boolean handleEvent(MotionEvent event) { - if (isDownEvent(event)) { - // this is the start of a new block of events! whee! - mHoldInQueue = mWaitForTouchListeners; - - // Set mAllowDefaultAction to true so that in the event we dispatch events, the - // PanZoomController doesn't treat them as if they've been prevent-defaulted - // when they haven't. - mAllowDefaultAction = true; - if (mHoldInQueue) { - // if the new block we are starting is the current block (i.e. there are no - // other blocks waiting in the queue, then we should let the pan/zoom controller - // know we are waiting for the touch listeners to run - if (mEventQueue.isEmpty()) { - mPanZoomController.startingNewEventBlock(event, true); - } - } else { - // we're not going to be holding this block of events in the queue, but we need - // a marker of some sort so that the processEventBlock loop deals with the blocks - // in the right order as notifications come in. we use a single null event in - // the queue as a placeholder for a block of events that has already been dispatched. - mEventQueue.add(null); - mPanZoomController.startingNewEventBlock(event, false); - } - - // set the timeout so that we dispatch these events and update mProcessingBalance - // if we don't get a default-prevented notification - mView.postDelayed(mListenerTimeoutProcessor, EVENT_LISTENER_TIMEOUT); - } - - // if we need to hold the events, add it to the queue, otherwise dispatch - // it directly. - if (mHoldInQueue) { - mEventQueue.add(MotionEvent.obtain(event)); - } else { - dispatchEvent(event, mAllowDefaultAction); - } - - return false; - } - - /** - * This function is how gecko sends us a default-prevented notification. It is called - * once gecko knows definitively whether the block of events has had preventDefault - * called on it (either on the initial down event that starts the block, or on - * the first event following that down event). - * - * This function MUST be called on the UI thread. - */ - public void handleEventListenerAction(boolean allowDefaultAction) { - if (mProcessingBalance > 0) { - // this event listener that triggered this took too long, and the corresponding - // ListenerTimeoutProcessor runnable already ran for the event in question. the - // block of events this is for has already been processed, so we don't need to - // do anything here. - } else { - processEventBlock(allowDefaultAction); - } - mProcessingBalance--; - } - - /* This function MUST be called on the UI thread. */ - public void setWaitForTouchListeners(boolean aValue) { - mWaitForTouchListeners = aValue; - } - - private boolean isDownEvent(MotionEvent event) { - int action = (event.getAction() & MotionEvent.ACTION_MASK); - return (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN); - } - - private boolean touchFinished(MotionEvent event) { - int action = (event.getAction() & MotionEvent.ACTION_MASK); - return (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL); - } - - /** - * Dispatch the event to the gesture detectors and the pan/zoom controller. - */ - private void dispatchEvent(MotionEvent event, boolean allowDefaultAction) { - if (allowDefaultAction) { - if (mGestureDetector.onTouchEvent(event)) { - return; - } - mScaleGestureDetector.onTouchEvent(event); - if (mScaleGestureDetector.isInProgress()) { - return; - } - } - mPanZoomController.handleEvent(event, !allowDefaultAction); - } - - /** - * Process the block of events at the head of the queue now that we know - * whether it has been default-prevented or not. - */ - private void processEventBlock(boolean allowDefaultAction) { - if (mEventQueue.isEmpty()) { - Log.e(LOGTAG, "Unexpected empty event queue in processEventBlock!", new Exception()); - return; - } - - // the odd loop condition is because the first event in the queue will - // always be a DOWN or POINTER_DOWN event, and we want to process all - // the events in the queue starting at that one, up to but not including - // the next DOWN or POINTER_DOWN event. - - MotionEvent event = mEventQueue.poll(); - while (true) { - // event being null here is valid and represents a block of events - // that has already been dispatched. - - if (event != null) { - dispatchEvent(event, allowDefaultAction); - } - if (mEventQueue.isEmpty()) { - // we have processed the backlog of events, and are all caught up. - // now we can set clear the hold flag and set the dispatch flag so - // that the handleEvent() function can do the right thing for all - // remaining events in this block (which is still ongoing) without - // having to put them in the queue. - mHoldInQueue = false; - mAllowDefaultAction = allowDefaultAction; - break; - } - event = mEventQueue.peek(); - if (event == null || isDownEvent(event)) { - // we have finished processing the block we were interested in. - // now we wait for the next call to processEventBlock - if (event != null) { - mPanZoomController.startingNewEventBlock(event, true); - } - break; - } - // pop the event we peeked above, as it is still part of the block and - // we want to keep processing - mEventQueue.remove(); - } - } - - private class ListenerTimeoutProcessor implements Runnable { - /* This MUST be run on the UI thread */ - @Override - public void run() { - if (mProcessingBalance < 0) { - // gecko already responded with default-prevented notification, and so - // the block of events this ListenerTimeoutProcessor corresponds to have - // already been removed from the queue. - } else { - processEventBlock(true); - } - mProcessingBalance++; - } - } - - // Tabs.OnTabsChangedListener implementation - - /*@Override - public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) { - if ((Tabs.getInstance().isSelectedTab(tab) && msg == Tabs.TabEvents.STOP) || msg == Tabs.TabEvents.SELECTED) { - mWaitForTouchListeners = tab.getHasTouchListeners(); - } - }*/ -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/ViewTransform.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/ViewTransform.java deleted file mode 100644 index 97ca109f118d..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/ViewTransform.java +++ /dev/null @@ -1,34 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -//import org.mozilla.gecko.mozglue.generatorannotations.WrapEntireClassForJNI; - -//@WrapEntireClassForJNI -public class ViewTransform { - public float x; - public float y; - public float scale; - public float fixedLayerMarginLeft; - public float fixedLayerMarginTop; - public float fixedLayerMarginRight; - public float fixedLayerMarginBottom; - public float offsetX; - public float offsetY; - - public ViewTransform(float inX, float inY, float inScale) { - x = inX; - y = inY; - scale = inScale; - fixedLayerMarginLeft = 0; - fixedLayerMarginTop = 0; - fixedLayerMarginRight = 0; - fixedLayerMarginBottom = 0; - offsetX = 0; - offsetY = 0; - } -} - diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/VirtualLayer.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/VirtualLayer.java deleted file mode 100644 index 83d012876176..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/gfx/VirtualLayer.java +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.gfx; - -public class VirtualLayer extends Layer { - public VirtualLayer(IntSize size) { - super(size); - } - - @Override - public void draw(RenderContext context) { - // No-op. - } - - void setPositionAndResolution(int left, int top, int right, int bottom, float newResolution) { - // This is an optimized version of the following code: - // beginTransaction(); - // try { - // setPosition(new Rect(left, top, right, bottom)); - // setResolution(newResolution); - // performUpdates(null); - // } finally { - // endTransaction(); - // } - - // it is safe to drop the transaction lock in this instance (i.e. for the - // VirtualLayer that is just a shadow of what gecko is painting) because - // the position and resolution of this layer are always touched on the compositor - // thread, and therefore do not require synchronization. - mPosition.set(left, top, right, bottom); - mResolution = newResolution; - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/mozglue/DirectBufferAllocator.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/mozglue/DirectBufferAllocator.java deleted file mode 100644 index 61444e6cfc47..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/mozglue/DirectBufferAllocator.java +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.mozglue; - -import java.nio.ByteBuffer; - -// -// We must manually allocate direct buffers in JNI to work around a bug where Honeycomb's -// ByteBuffer.allocateDirect() grossly overallocates the direct buffer size. -// https://code.google.com/p/android/issues/detail?id=16941 -// - -public final class DirectBufferAllocator { - private DirectBufferAllocator() {} - - public static ByteBuffer allocate(int size) { - if (size <= 0) { - throw new IllegalArgumentException("Invalid size " + size); - } - - ByteBuffer directBuffer = ByteBuffer.allocateDirect(size); - //ByteBuffer directBuffer = nativeAllocateDirectBuffer(size); - if (directBuffer == null) { - throw new OutOfMemoryError("allocateDirectBuffer() returned null"); - } else if (!directBuffer.isDirect()) { - throw new AssertionError("allocateDirectBuffer() did not return a direct buffer"); - } - - return directBuffer; - } - - public static ByteBuffer free(ByteBuffer buffer) { - if (buffer == null) { - return null; - } - - if (!buffer.isDirect()) { - throw new IllegalArgumentException("buffer must be direct"); - } - - //nativeFreeDirectBuffer(buffer); - return null; - } - - // These JNI methods are implemented in mozglue/android/nsGeckoUtils.cpp. - //private static native ByteBuffer nativeAllocateDirectBuffer(long size); - //private static native void nativeFreeDirectBuffer(ByteBuffer buf); -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/EventDispatcher.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/EventDispatcher.java deleted file mode 100644 index 5b6d50883b42..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/EventDispatcher.java +++ /dev/null @@ -1,115 +0,0 @@ -/* 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.gecko.util; - -import org.json.JSONObject; - -import android.util.Log; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; - -public final class EventDispatcher { - private static final String LOGTAG = "GeckoEventDispatcher"; - - private final Map<String, CopyOnWriteArrayList<GeckoEventListener>> mEventListeners - = new HashMap<String, CopyOnWriteArrayList<GeckoEventListener>>(); - - public void registerEventListener(String event, GeckoEventListener listener) { - synchronized (mEventListeners) { - CopyOnWriteArrayList<GeckoEventListener> listeners = mEventListeners.get(event); - if (listeners == null) { - // create a CopyOnWriteArrayList so that we can modify it - // concurrently with iterating through it in handleGeckoMessage. - // Otherwise we could end up throwing a ConcurrentModificationException. - listeners = new CopyOnWriteArrayList<GeckoEventListener>(); - } else if (listeners.contains(listener)) { - Log.w(LOGTAG, "EventListener already registered for event '" + event + "'", - new IllegalArgumentException()); - } - listeners.add(listener); - mEventListeners.put(event, listeners); - } - } - - public void unregisterEventListener(String event, GeckoEventListener listener) { - synchronized (mEventListeners) { - CopyOnWriteArrayList<GeckoEventListener> listeners = mEventListeners.get(event); - if (listeners == null) { - Log.w(LOGTAG, "unregisterEventListener: event '" + event + "' has no listeners"); - return; - } - if (!listeners.remove(listener)) { - Log.w(LOGTAG, "unregisterEventListener: tried to remove an unregistered listener " + - "for event '" + event + "'"); - } - if (listeners.size() == 0) { - mEventListeners.remove(event); - } - } - } - - public String dispatchEvent(String message) { - try { - JSONObject json = new JSONObject(message); - return dispatchEvent(json); - } catch (Exception e) { - Log.e(LOGTAG, "dispatchEvent: malformed JSON.", e); - } - - return ""; - } - - public String dispatchEvent(JSONObject json) { - // { - // "type": "value", - // "event_specific": "value", - // ... - try { - JSONObject gecko = json.has("gecko") ? json.getJSONObject("gecko") : null; - if (gecko != null) { - json = gecko; - } - - String type = json.getString("type"); - - if (gecko != null) { - Log.w(LOGTAG, "Message '" + type + "' has deprecated 'gecko' property!"); - } - - CopyOnWriteArrayList<GeckoEventListener> listeners; - synchronized (mEventListeners) { - listeners = mEventListeners.get(type); - } - - if (listeners == null || listeners.size() == 0) { - Log.d(LOGTAG, "dispatchEvent: no listeners registered for event '" + type + "'"); - return ""; - } - - String response = null; - - for (GeckoEventListener listener : listeners) { - listener.handleMessage(type, json); - if (listener instanceof GeckoEventResponder) { - String newResponse = ((GeckoEventResponder)listener).getResponse(json); - if (response != null && newResponse != null) { - Log.e(LOGTAG, "Received two responses for message of type " + type); - } - response = newResponse; - } - } - - if (response != null) - return response; - - } catch (Exception e) { - Log.e(LOGTAG, "handleGeckoMessage throws " + e, e); - } - - return ""; - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/FloatUtils.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/FloatUtils.java deleted file mode 100644 index fbcd7254f62b..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/FloatUtils.java +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.util; - -import android.graphics.PointF; - -import java.lang.IllegalArgumentException; - -public final class FloatUtils { - private FloatUtils() {} - - public static boolean fuzzyEquals(float a, float b) { - return (Math.abs(a - b) < 1e-6); - } - - public static boolean fuzzyEquals(PointF a, PointF b) { - return fuzzyEquals(a.x, b.x) && fuzzyEquals(a.y, b.y); - } - - /* - * Returns the value that represents a linear transition between `from` and `to` at time `t`, - * which is on the scale [0, 1). Thus with t = 0.0f, this returns `from`; with t = 1.0f, this - * returns `to`; with t = 0.5f, this returns the value halfway from `from` to `to`. - */ - public static float interpolate(float from, float to, float t) { - return from + (to - from) * t; - } - - /** - * Returns 'value', clamped so that it isn't any lower than 'low', and it - * isn't any higher than 'high'. - */ - public static float clamp(float value, float low, float high) { - if (high < low) { - throw new IllegalArgumentException( - "clamp called with invalid parameters (" + high + " < " + low + ")" ); - } - return Math.max(low, Math.min(high, value)); - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/GeckoBackgroundThread.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/GeckoBackgroundThread.java deleted file mode 100644 index f7873fe733d3..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/GeckoBackgroundThread.java +++ /dev/null @@ -1,55 +0,0 @@ -/* 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.gecko.util; - -import android.os.Handler; -import android.os.Looper; - -import java.util.concurrent.SynchronousQueue; - -final class GeckoBackgroundThread extends Thread { - private static final String LOOPER_NAME = "GeckoBackgroundThread"; - - // Guarded by 'this'. - private static Handler sHandler = null; - private SynchronousQueue<Handler> mHandlerQueue = new SynchronousQueue<Handler>(); - - // Singleton, so private constructor. - private GeckoBackgroundThread() { - super(); - } - - @Override - public void run() { - setName(LOOPER_NAME); - Looper.prepare(); - try { - mHandlerQueue.put(new Handler()); - } catch (InterruptedException ie) {} - - Looper.loop(); - } - - // Get a Handler for a looper thread, or create one if it doesn't yet exist. - /*package*/ static synchronized Handler getHandler() { - if (sHandler == null) { - GeckoBackgroundThread lt = new GeckoBackgroundThread(); - ThreadUtils.setBackgroundThread(lt); - lt.start(); - try { - sHandler = lt.mHandlerQueue.take(); - } catch (InterruptedException ie) {} - } - return sHandler; - } - - /*package*/ static void post(Runnable runnable) { - Handler handler = getHandler(); - if (handler == null) { - throw new IllegalStateException("No handler! Must have been interrupted. Not posting."); - } - handler.post(runnable); - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/GeckoEventListener.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/GeckoEventListener.java deleted file mode 100644 index 4d0c313b0c6a..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/GeckoEventListener.java +++ /dev/null @@ -1,14 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.util; - -import org.json.JSONObject; -//import org.mozilla.gecko.mozglue.RobocopTarget; - -//@RobocopTarget -public interface GeckoEventListener { - void handleMessage(String event, JSONObject message); -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/GeckoEventResponder.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/GeckoEventResponder.java deleted file mode 100644 index dc4561561c8a..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/GeckoEventResponder.java +++ /dev/null @@ -1,16 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * ***** BEGIN LICENSE BLOCK ***** - * - * 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/. - * - * ***** END LICENSE BLOCK ***** */ - -package org.mozilla.gecko.util; - -import org.json.JSONObject; - -public interface GeckoEventResponder extends GeckoEventListener { - String getResponse(JSONObject response); -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/ThreadUtils.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/ThreadUtils.java deleted file mode 100644 index a646f1ae4e9d..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/ThreadUtils.java +++ /dev/null @@ -1,169 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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.gecko.util; - -import android.os.Handler; -import android.os.MessageQueue; -import android.util.Log; - -import java.util.Map; - -public final class ThreadUtils { - private static final String LOGTAG = "ThreadUtils"; - - private static Thread sUiThread; - private static Thread sBackgroundThread; - - private static Handler sUiHandler; - - // Referenced directly from GeckoAppShell in highly performance-sensitive code (The extra - // function call of the getter was harming performance. (Bug 897123)) - // Once Bug 709230 is resolved we should reconsider this as ProGuard should be able to optimise - // this out at compile time. - public static Handler sGeckoHandler; - public static MessageQueue sGeckoQueue; - public static Thread sGeckoThread; - - // Delayed Runnable that resets the Gecko thread priority. - private static final Runnable sPriorityResetRunnable = new Runnable() { - @Override - public void run() { - resetGeckoPriority(); - } - }; - - private static boolean sIsGeckoPriorityReduced; - - @SuppressWarnings("serial") - public static class UiThreadBlockedException extends RuntimeException { - public UiThreadBlockedException() { - super(); - } - - public UiThreadBlockedException(String msg) { - super(msg); - } - - public UiThreadBlockedException(String msg, Throwable e) { - super(msg, e); - } - - public UiThreadBlockedException(Throwable e) { - super(e); - } - } - - public static void dumpAllStackTraces() { - Log.w(LOGTAG, "Dumping ALL the threads!"); - Map<Thread, StackTraceElement[]> allStacks = Thread.getAllStackTraces(); - for (Thread t : allStacks.keySet()) { - Log.w(LOGTAG, t.toString()); - for (StackTraceElement ste : allStacks.get(t)) { - Log.w(LOGTAG, ste.toString()); - } - Log.w(LOGTAG, "----"); - } - } - - public static void setUiThread(Thread thread, Handler handler) { - sUiThread = thread; - sUiHandler = handler; - } - - public static void setBackgroundThread(Thread thread) { - sBackgroundThread = thread; - } - - public static Thread getUiThread() { - return sUiThread; - } - - public static Handler getUiHandler() { - return sUiHandler; - } - - public static void postToUiThread(Runnable runnable) { - sUiHandler.post(runnable); - } - - public static Thread getBackgroundThread() { - return sBackgroundThread; - } - - public static Handler getBackgroundHandler() { - return GeckoBackgroundThread.getHandler(); - } - - public static void postToBackgroundThread(Runnable runnable) { - GeckoBackgroundThread.post(runnable); - } - - public static void assertOnUiThread() { - assertOnThread(getUiThread()); - } - - public static void assertOnGeckoThread() { - assertOnThread(sGeckoThread); - } - - public static void assertOnBackgroundThread() { - assertOnThread(getBackgroundThread()); - } - - public static void assertOnThread(Thread expectedThread) { - Thread currentThread = Thread.currentThread(); - long currentThreadId = currentThread.getId(); - long expectedThreadId = expectedThread.getId(); - - if (currentThreadId != expectedThreadId) { - throw new IllegalThreadStateException("Expected thread " + expectedThreadId + " (\"" - + expectedThread.getName() - + "\"), but running on thread " + currentThreadId - + " (\"" + currentThread.getName() + ")"); - } - } - - public static boolean isOnUiThread() { - return isOnThread(getUiThread()); - } - - public static boolean isOnBackgroundThread() { - return isOnThread(sBackgroundThread); - } - - public static boolean isOnThread(Thread thread) { - return (Thread.currentThread().getId() == thread.getId()); - } - - /** - * Reduces the priority of the Gecko thread, allowing other operations - * (such as those related to the UI and database) to take precedence. - * - * Note that there are no guards in place to prevent multiple calls - * to this method from conflicting with each other. - * - * @param timeout Timeout in ms after which the priority will be reset - */ - public static void reduceGeckoPriority(long timeout) { - if (!sIsGeckoPriorityReduced) { - sIsGeckoPriorityReduced = true; - sGeckoThread.setPriority(Thread.MIN_PRIORITY); - getUiHandler().postDelayed(sPriorityResetRunnable, timeout); - } - } - - /** - * Resets the priority of a thread whose priority has been reduced - * by reduceGeckoPriority. - */ - public static void resetGeckoPriority() { - if (sIsGeckoPriorityReduced) { - sIsGeckoPriorityReduced = false; - sGeckoThread.setPriority(Thread.NORM_PRIORITY); - getUiHandler().removeCallbacks(sPriorityResetRunnable); - } - } -} diff --git a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/UiAsyncTask.java b/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/UiAsyncTask.java deleted file mode 100644 index aee875c33dc9..000000000000 --- a/android/experimental/LOAndroid/app/src/main/java/org/mozilla/gecko/util/UiAsyncTask.java +++ /dev/null @@ -1,86 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * 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.gecko.util; - -import android.os.Handler; -import android.os.Looper; - -/** - * Executes a background task and publishes the result on the UI thread. - * - * The standard {@link android.os.AsyncTask} only runs onPostExecute on the - * thread it is constructed on, so this is a convenience class for creating - * tasks off the UI thread. - */ -public abstract class UiAsyncTask<Params, Progress, Result> { - private volatile boolean mCancelled = false; - private final Handler mBackgroundThreadHandler; - private static Handler sHandler; - - /** - * Creates a new asynchronous task. - * - * @param backgroundThreadHandler the handler to execute the background task on - */ - public UiAsyncTask(Handler backgroundThreadHandler) { - mBackgroundThreadHandler = backgroundThreadHandler; - } - - private static synchronized Handler getUiHandler() { - if (sHandler == null) { - sHandler = new Handler(Looper.getMainLooper()); - } - return sHandler; - } - - private final class BackgroundTaskRunnable implements Runnable { - private Params[] mParams; - - public BackgroundTaskRunnable(Params... params) { - mParams = params; - } - - @Override - public void run() { - final Result result = doInBackground(mParams); - - getUiHandler().post(new Runnable() { - @Override - public void run() { - if (mCancelled) - onCancelled(); - else - onPostExecute(result); - } - }); - } - } - - public final void execute(final Params... params) { - getUiHandler().post(new Runnable() { - @Override - public void run() { - onPreExecute(); - mBackgroundThreadHandler.post(new BackgroundTaskRunnable(params)); - } - }); - } - - @SuppressWarnings({"UnusedParameters"}) - public final boolean cancel(boolean mayInterruptIfRunning) { - mCancelled = true; - return mCancelled; - } - - public final boolean isCancelled() { - return mCancelled; - } - - protected void onPreExecute() { } - protected void onPostExecute(Result result) { } - protected void onCancelled() { } - protected abstract Result doInBackground(Params... params); -} diff --git a/android/experimental/LOAndroid/app/src/main/res/drawable-hdpi/ic_launcher.png b/android/experimental/LOAndroid/app/src/main/res/drawable-hdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 96a442e5b8e9..000000000000 --- a/android/experimental/LOAndroid/app/src/main/res/drawable-hdpi/ic_launcher.png +++ /dev/null diff --git a/android/experimental/LOAndroid/app/src/main/res/drawable-hdpi/ic_status_logo.png b/android/experimental/LOAndroid/app/src/main/res/drawable-hdpi/ic_status_logo.png Binary files differdeleted file mode 100644 index d5f16694f342..000000000000 --- a/android/experimental/LOAndroid/app/src/main/res/drawable-hdpi/ic_status_logo.png +++ /dev/null diff --git a/android/experimental/LOAndroid/app/src/main/res/drawable-mdpi/ic_launcher.png b/android/experimental/LOAndroid/app/src/main/res/drawable-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 359047dfa4ed..000000000000 --- a/android/experimental/LOAndroid/app/src/main/res/drawable-mdpi/ic_launcher.png +++ /dev/null diff --git a/android/experimental/LOAndroid/app/src/main/res/drawable-mdpi/ic_status_logo.png b/android/experimental/LOAndroid/app/src/main/res/drawable-mdpi/ic_status_logo.png Binary files differdeleted file mode 100644 index 835fc9290727..000000000000 --- a/android/experimental/LOAndroid/app/src/main/res/drawable-mdpi/ic_status_logo.png +++ /dev/null diff --git a/android/experimental/LOAndroid/app/src/main/res/drawable-xhdpi/ic_launcher.png b/android/experimental/LOAndroid/app/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 71c6d760f051..000000000000 --- a/android/experimental/LOAndroid/app/src/main/res/drawable-xhdpi/ic_launcher.png +++ /dev/null diff --git a/android/experimental/LOAndroid/app/src/main/res/drawable-xhdpi/ic_status_logo.png b/android/experimental/LOAndroid/app/src/main/res/drawable-xhdpi/ic_status_logo.png Binary files differdeleted file mode 100644 index c8005425416a..000000000000 --- a/android/experimental/LOAndroid/app/src/main/res/drawable-xhdpi/ic_status_logo.png +++ /dev/null diff --git a/android/experimental/LOAndroid/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/android/experimental/LOAndroid/app/src/main/res/drawable-xxhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 4df18946442e..000000000000 --- a/android/experimental/LOAndroid/app/src/main/res/drawable-xxhdpi/ic_launcher.png +++ /dev/null diff --git a/android/experimental/LOAndroid/app/src/main/res/layout/activity_main.xml b/android/experimental/LOAndroid/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index 600acdd4b40b..000000000000 --- a/android/experimental/LOAndroid/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,15 +0,0 @@ -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingLeft="@dimen/activity_horizontal_margin" - android:paddingRight="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/activity_vertical_margin" - android:paddingBottom="@dimen/activity_vertical_margin" - tools:context="org.libreoffice.MainActivity"> - - <org.libreoffice.MainLayerView android:id="@+id/layer_view" - android:layout_width="fill_parent" - android:layout_height="fill_parent"/> - -</RelativeLayout> diff --git a/android/experimental/LOAndroid/app/src/main/res/menu/main.xml b/android/experimental/LOAndroid/app/src/main/res/menu/main.xml deleted file mode 100644 index 6768fd32a890..000000000000 --- a/android/experimental/LOAndroid/app/src/main/res/menu/main.xml +++ /dev/null @@ -1,9 +0,0 @@ -<menu xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" - tools:context="org.libreoffice.MainActivity" > - <item android:id="@+id/action_settings" - android:title="@string/action_settings" - android:orderInCategory="100" - app:showAsAction="never" /> -</menu> diff --git a/android/experimental/LOAndroid/app/src/main/res/values-w820dp/dimens.xml b/android/experimental/LOAndroid/app/src/main/res/values-w820dp/dimens.xml deleted file mode 100644 index 63fc81644461..000000000000 --- a/android/experimental/LOAndroid/app/src/main/res/values-w820dp/dimens.xml +++ /dev/null @@ -1,6 +0,0 @@ -<resources> - <!-- Example customization of dimensions originally defined in res/values/dimens.xml - (such as screen margins) for screens with more than 820dp of available width. This - would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). --> - <dimen name="activity_horizontal_margin">64dp</dimen> -</resources> diff --git a/android/experimental/LOAndroid/app/src/main/res/values/colors.xml b/android/experimental/LOAndroid/app/src/main/res/values/colors.xml deleted file mode 100644 index f8e207d4e00e..000000000000 --- a/android/experimental/LOAndroid/app/src/main/res/values/colors.xml +++ /dev/null @@ -1,95 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- 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/. --> - -<resources> - <color name="background_light">#FFECF0F3</color> - <color name="background_normal">#FFCED7DE</color> - <color name="background_private">#FF292C29</color> - <color name="background_tabs">#FF363B40</color> - <color name="highlight">#33000000</color> - <color name="highlight_focused">#1A000000</color> - <color name="highlight_dark">#33FFFFFF</color> - <color name="highlight_dark_focused">#1AFFFFFF</color> - - <!-- highlight on shaped button: 20% white over background_tabs --> - <color name="highlight_shaped">#FF696D71</color> - - <!-- highlight-focused on shaped button: 10% white over background_tabs --> - <color name="highlight_shaped_focused">#FF565B60</color> - - <!-- highlight on nav button: 20% black over background_normal --> - <color name="highlight_nav">#FFA5ACB2</color> - - <!-- highlight-focused on nav button: 10% black over background_normal --> - <color name="highlight_nav_focused">#FFB9C1C7</color> - - <!-- highlight on private nav button: 20% white over background_private --> - <color name="highlight_nav_pb">#FF545654</color> - - <!-- highlight-focused on private nav button: 10% white over background_private --> - <color name="highlight_nav_focused_pb">#FF3F423F</color> - - <!-- - Application theme colors - --> - <!-- Default colors --> - <color name="text_color_primary">#222222</color> - <color name="text_color_secondary">#777777</color> - <color name="text_color_tertiary">#9198A1</color> - - <!-- Default inverse colors --> - <color name="text_color_primary_inverse">#FFFFFF</color> - <color name="text_color_secondary_inverse">#DDDDDD</color> - <color name="text_color_tertiary_inverse">#A4A7A9</color> - - <!-- Disabled colors --> - <color name="text_color_primary_disable_only">#999999</color> - - <!-- Hint colors --> - <color name="text_color_hint">#666666</color> - <color name="text_color_hint_inverse">#7F828A</color> - - <!-- Highlight colors --> - <color name="text_color_highlight">#FF9500</color> - <color name="text_color_highlight_inverse">#D06BFF</color> - - <!-- Link colors --> - <color name="text_color_link">#22629E</color> - - <color name="splash_background">#000000</color> - <color name="splash_msgfont">#ffffff</color> - <color name="splash_urlfont">#000000</color> - <color name="splash_content">#ffffff</color> - - <color name="doorhanger_text">#FF222222</color> - <color name="doorhanger_link">#FF2AA1FE</color> - <color name="doorhanger_divider_light">#FFD1D5DA</color> - <color name="doorhanger_divider_dark">#FFB3C2CE</color> - <color name="doorhanger_background_dark">#FFDDE4EA</color> - - <color name="validation_message_text">#ffffff</color> - <color name="url_bar_text_highlight">#FFFF9500</color> - <color name="url_bar_text_highlight_pb">#FFD06BFF</color> - <color name="suggestion_primary">#dddddd</color> - <color name="suggestion_pressed">#bbbbbb</color> - <color name="tab_row_pressed">#4D000000</color> - <color name="dialogtitle_textcolor">#ffffff</color> - - <color name="textbox_background">#FFF</color> - <color name="textbox_background_disabled">#DDD</color> - <color name="textbox_stroke">#000</color> - <color name="textbox_stroke_disabled">#666</color> - - <color name="url_bar_urltext">#A6A6A6</color> - <color name="url_bar_domaintext">#000</color> - <color name="url_bar_domaintext_private">#FFF</color> - <color name="url_bar_blockedtext">#b14646</color> - <color name="url_bar_shadow">#12000000</color> - - <color name="home_last_tab_bar_bg">#FFF5F7F9</color> - - <color name="panel_grid_item_image_background">#D1D9E1</color> -</resources> - diff --git a/android/experimental/LOAndroid/app/src/main/res/values/dimens.xml b/android/experimental/LOAndroid/app/src/main/res/values/dimens.xml deleted file mode 100644 index 47c82246738c..000000000000 --- a/android/experimental/LOAndroid/app/src/main/res/values/dimens.xml +++ /dev/null @@ -1,5 +0,0 @@ -<resources> - <!-- Default screen margins, per the Android Design guidelines. --> - <dimen name="activity_horizontal_margin">16dp</dimen> - <dimen name="activity_vertical_margin">16dp</dimen> -</resources> diff --git a/android/experimental/LOAndroid/app/src/main/res/values/strings.xml b/android/experimental/LOAndroid/app/src/main/res/values/strings.xml deleted file mode 100644 index 8864167560f8..000000000000 --- a/android/experimental/LOAndroid/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - - <string name="app_name">LOAndroid</string> - <string name="hello_world">Hello world!</string> - <string name="action_settings">Settings</string> - -</resources> diff --git a/android/experimental/LOAndroid/app/src/main/res/values/styles.xml b/android/experimental/LOAndroid/app/src/main/res/values/styles.xml deleted file mode 100644 index ff6c9d2c0fb9..000000000000 --- a/android/experimental/LOAndroid/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,8 +0,0 @@ -<resources> - - <!-- Base application theme. --> - <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar"> - <!-- Customize your theme here. --> - </style> - -</resources> |