mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
backing out the backout of the landing of the patrick patch queue. lets try this again in the morning with fresh eyes and fresh coffee
This commit is contained in:
parent
b9688a7a56
commit
9d4fd110ef
@ -40,14 +40,6 @@
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import org.mozilla.fennec.gfx.GeckoSoftwareLayerClient;
|
||||
import org.mozilla.fennec.gfx.IntRect;
|
||||
import org.mozilla.fennec.gfx.IntSize;
|
||||
import org.mozilla.fennec.gfx.LayerController;
|
||||
import org.mozilla.fennec.gfx.LayerView;
|
||||
import org.mozilla.fennec.gfx.PlaceholderLayerClient;
|
||||
import org.mozilla.gecko.Tab.HistoryEntry;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.zip.*;
|
||||
@ -72,7 +64,6 @@ import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.widget.*;
|
||||
import android.hardware.*;
|
||||
import android.location.*;
|
||||
|
||||
import android.util.*;
|
||||
import android.net.*;
|
||||
@ -85,7 +76,7 @@ import android.content.SharedPreferences.*;
|
||||
import dalvik.system.*;
|
||||
|
||||
abstract public class GeckoApp
|
||||
extends Activity implements GeckoEventListener, SensorEventListener, LocationListener
|
||||
extends Activity implements GeckoEventListener
|
||||
{
|
||||
private static final String LOG_NAME = "GeckoApp";
|
||||
|
||||
@ -97,6 +88,7 @@ abstract public class GeckoApp
|
||||
|
||||
private LinearLayout mMainLayout;
|
||||
private RelativeLayout mGeckoLayout;
|
||||
public static GeckoSurfaceView surfaceView;
|
||||
public static SurfaceView cameraView;
|
||||
public static GeckoApp mAppContext;
|
||||
public static boolean mFullscreen = false;
|
||||
@ -111,10 +103,6 @@ abstract public class GeckoApp
|
||||
private static boolean sIsGeckoReady = false;
|
||||
private IntentFilter mBatteryFilter;
|
||||
private BroadcastReceiver mBatteryReceiver;
|
||||
private Geocoder mGeocoder;
|
||||
private Address mLastGeoAddress;
|
||||
private static LayerController mLayerController;
|
||||
private static GeckoSoftwareLayerClient mSoftwareLayerClient;
|
||||
|
||||
public interface OnTabsChangedListener {
|
||||
public void onTabsChanged();
|
||||
@ -135,8 +123,8 @@ abstract public class GeckoApp
|
||||
|
||||
static Vector<ExtraMenuItem> sExtraMenuItems = new Vector<ExtraMenuItem>();
|
||||
|
||||
public enum LaunchState {Launching, WaitButton,
|
||||
Launched, GeckoRunning, GeckoExiting};
|
||||
enum LaunchState {Launching, WaitButton,
|
||||
Launched, GeckoRunning, GeckoExiting};
|
||||
private static LaunchState sLaunchState = LaunchState.Launching;
|
||||
private static boolean sTryCatchAttached = false;
|
||||
|
||||
@ -144,7 +132,7 @@ abstract public class GeckoApp
|
||||
private static final int AWESOMEBAR_REQUEST = 2;
|
||||
private static final int CAMERA_CAPTURE_REQUEST = 3;
|
||||
|
||||
public static boolean checkLaunchState(LaunchState checkState) {
|
||||
static boolean checkLaunchState(LaunchState checkState) {
|
||||
synchronized(sLaunchState) {
|
||||
return sLaunchState == checkState;
|
||||
}
|
||||
@ -545,40 +533,25 @@ abstract public class GeckoApp
|
||||
}
|
||||
}
|
||||
|
||||
public String getStartupBitmapFilePath() {
|
||||
File file = new File(Environment.getExternalStorageDirectory(),
|
||||
"lastScreen.png");
|
||||
return file.toString();
|
||||
}
|
||||
|
||||
private void rememberLastScreen(boolean sync) {
|
||||
if (surfaceView == null)
|
||||
return;
|
||||
Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
if (tab == null)
|
||||
return;
|
||||
|
||||
HistoryEntry lastHistoryEntry = tab.getLastHistoryEntry();
|
||||
if (lastHistoryEntry == null)
|
||||
return;
|
||||
Tab.HistoryEntry he = tab.getLastHistoryEntry();
|
||||
if (he != null) {
|
||||
SharedPreferences prefs = getSharedPreferences("GeckoApp", 0);
|
||||
Editor editor = prefs.edit();
|
||||
|
||||
editor.putString("last-uri", he.mUri);
|
||||
editor.putString("last-title", he.mTitle);
|
||||
|
||||
SharedPreferences prefs = getSharedPreferences("GeckoApp", 0);
|
||||
Editor editor = prefs.edit();
|
||||
|
||||
String uri = lastHistoryEntry.mUri;
|
||||
String title = lastHistoryEntry.mTitle;
|
||||
|
||||
editor.putString("last-uri", uri);
|
||||
editor.putString("last-title", title);
|
||||
|
||||
Log.i(LOG_NAME, "Saving:: " + uri + " " + title);
|
||||
editor.commit();
|
||||
|
||||
GeckoEvent event = new GeckoEvent();
|
||||
event.mType = GeckoEvent.SAVE_STATE;
|
||||
event.mCharacters = getStartupBitmapFilePath();
|
||||
if (sync)
|
||||
GeckoAppShell.sendEventToGeckoSync(event);
|
||||
else
|
||||
GeckoAppShell.sendEventToGecko(event);
|
||||
Log.i(LOG_NAME, "Saving:: " + he.mUri + " " + he.mTitle);
|
||||
editor.commit();
|
||||
surfaceView.saveLast(sync);
|
||||
}
|
||||
}
|
||||
|
||||
private String getLastUri() {
|
||||
@ -749,7 +722,6 @@ abstract public class GeckoApp
|
||||
final int tabId = message.getInt("tabID");
|
||||
final String uri = message.getString("uri");
|
||||
final String title = message.getString("title");
|
||||
final JSONObject jsonPageSize = message.getJSONObject("pageSize");
|
||||
final CharSequence titleText = title;
|
||||
handleContentLoaded(tabId, uri, title);
|
||||
Log.i(LOG_NAME, "URI - " + uri + ", title - " + title);
|
||||
@ -822,12 +794,6 @@ abstract public class GeckoApp
|
||||
sMenu.findItem(R.id.preferences).setEnabled(true);
|
||||
}
|
||||
});
|
||||
} else if (event.equals("PanZoom:Ack")) {
|
||||
final IntRect rect = new IntRect(message.getJSONObject("rect"));
|
||||
mSoftwareLayerClient.jsPanZoomCompleted(rect);
|
||||
} else if (event.equals("PanZoom:Resize")) {
|
||||
final IntSize size = new IntSize(message.getJSONObject("size"));
|
||||
mSoftwareLayerClient.setPageSize(size);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.i(LOG_NAME, "handleMessage throws " + e + " for message: " + event);
|
||||
@ -943,6 +909,7 @@ abstract public class GeckoApp
|
||||
public void run() {
|
||||
if (Tabs.getInstance().isSelectedTab(tab))
|
||||
mBrowserToolbar.setProgressVisibility(false);
|
||||
surfaceView.hideStartupBitmap();
|
||||
onTabsChanged();
|
||||
}
|
||||
});
|
||||
@ -1099,34 +1066,16 @@ abstract public class GeckoApp
|
||||
cameraView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
|
||||
}
|
||||
|
||||
if (mLayerController == null) {
|
||||
/*
|
||||
* Create a layer client so that Gecko will have a buffer to draw into, but don't hook
|
||||
* it up to the layer controller yet.
|
||||
*/
|
||||
mSoftwareLayerClient = new GeckoSoftwareLayerClient(this);
|
||||
if (surfaceView == null) {
|
||||
surfaceView = new GeckoSurfaceView(this);
|
||||
mGeckoLayout.addView(surfaceView);
|
||||
} else if (mGeckoLayout.getChildCount() == 0) {
|
||||
//surfaceView still holds to the old one during rotation. re-add it to new activity
|
||||
((ViewGroup) surfaceView.getParent()).removeAllViews();
|
||||
mGeckoLayout.addView(surfaceView);
|
||||
}
|
||||
|
||||
/*
|
||||
* Hook a placeholder layer client up to the layer controller so that the user can pan
|
||||
* and zoom a cached screenshot of the previous page. This call will return null if
|
||||
* there is no cached screenshot; in that case, we have no choice but to display a
|
||||
* checkerboard.
|
||||
*
|
||||
* TODO: Fall back to a built-in screenshot of the Fennec Start page for a nice first-
|
||||
* run experience, perhaps?
|
||||
*/
|
||||
PlaceholderLayerClient placeholderClient = PlaceholderLayerClient.createInstance(this);
|
||||
if (placeholderClient != null) {
|
||||
mLayerController = new LayerController(this, placeholderClient);
|
||||
placeholderClient.init();
|
||||
} else {
|
||||
mLayerController = new LayerController(this, null);
|
||||
}
|
||||
|
||||
mGeckoLayout.addView(mLayerController.getView());
|
||||
}
|
||||
|
||||
/* TODO: surfaceView.loadStartupBitmap(); */
|
||||
surfaceView.loadStartupBitmap();
|
||||
|
||||
Log.w(LOGTAG, "zerdatime " + new Date().getTime() + " - UI almost up");
|
||||
|
||||
@ -1171,8 +1120,6 @@ abstract public class GeckoApp
|
||||
GeckoAppShell.registerGeckoEventListener("Preferences:Data", GeckoApp.mAppContext);
|
||||
GeckoAppShell.registerGeckoEventListener("Gecko:Ready", GeckoApp.mAppContext);
|
||||
GeckoAppShell.registerGeckoEventListener("Toast:Show", GeckoApp.mAppContext);
|
||||
GeckoAppShell.registerGeckoEventListener("PanZoom:Ack", GeckoApp.mAppContext);
|
||||
GeckoAppShell.registerGeckoEventListener("PanZoom:Resize", GeckoApp.mAppContext);
|
||||
|
||||
|
||||
mConnectivityFilter = new IntentFilter();
|
||||
@ -1418,7 +1365,7 @@ abstract public class GeckoApp
|
||||
Intent intent = new Intent(action);
|
||||
intent.setClassName(getPackageName(),
|
||||
getPackageName() + ".Restarter");
|
||||
/* TODO: addEnvToIntent(intent); */
|
||||
addEnvToIntent(intent);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
|
||||
Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||
Log.i(LOG_NAME, intent.toString());
|
||||
@ -1685,89 +1632,4 @@ abstract public class GeckoApp
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Load", url));
|
||||
}
|
||||
}
|
||||
|
||||
public GeckoSoftwareLayerClient getSoftwareLayerClient() { return mSoftwareLayerClient; }
|
||||
public LayerController getLayerController() { return mLayerController; }
|
||||
|
||||
// accelerometer
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy)
|
||||
{
|
||||
}
|
||||
|
||||
public void onSensorChanged(SensorEvent event)
|
||||
{
|
||||
Log.w(LOGTAG, "onSensorChanged "+event);
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
|
||||
}
|
||||
|
||||
private class GeocoderTask extends AsyncTask<Location, Void, Void> {
|
||||
protected Void doInBackground(Location... location) {
|
||||
try {
|
||||
List<Address> addresses = mGeocoder.getFromLocation(location[0].getLatitude(),
|
||||
location[0].getLongitude(), 1);
|
||||
// grab the first address. in the future,
|
||||
// may want to expose multiple, or filter
|
||||
// for best.
|
||||
mLastGeoAddress = addresses.get(0);
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(location[0], mLastGeoAddress));
|
||||
} catch (Exception e) {
|
||||
Log.w(LOGTAG, "GeocoderTask "+e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// geolocation
|
||||
public void onLocationChanged(Location location)
|
||||
{
|
||||
Log.w(LOGTAG, "onLocationChanged "+location);
|
||||
if (mGeocoder == null)
|
||||
mGeocoder = new Geocoder(mLayerController.getView().getContext(), Locale.getDefault());
|
||||
|
||||
if (mLastGeoAddress == null) {
|
||||
new GeocoderTask().execute(location);
|
||||
}
|
||||
else {
|
||||
float[] results = new float[1];
|
||||
Location.distanceBetween(location.getLatitude(),
|
||||
location.getLongitude(),
|
||||
mLastGeoAddress.getLatitude(),
|
||||
mLastGeoAddress.getLongitude(),
|
||||
results);
|
||||
// pfm value. don't want to slam the
|
||||
// geocoder with very similar values, so
|
||||
// only call after about 100m
|
||||
if (results[0] > 100)
|
||||
new GeocoderTask().execute(location);
|
||||
}
|
||||
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(location, mLastGeoAddress));
|
||||
}
|
||||
|
||||
public void onProviderDisabled(String provider)
|
||||
{
|
||||
}
|
||||
|
||||
public void onProviderEnabled(String provider)
|
||||
{
|
||||
}
|
||||
|
||||
public void onStatusChanged(String provider, int status, Bundle extras)
|
||||
{
|
||||
}
|
||||
|
||||
public void connectGeckoLayerClient() {
|
||||
new Timer("Gecko Wait").schedule(new TimerTask() {
|
||||
public void run() {
|
||||
GeckoApp.mAppContext.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
LayerController layerController = getLayerController();
|
||||
layerController.setLayerClient(mSoftwareLayerClient);
|
||||
mSoftwareLayerClient.init(); /* Attaches the new root layer. */
|
||||
GeckoAppShell.scheduleRedraw();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
@ -38,11 +38,6 @@
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import org.mozilla.fennec.gfx.GeckoSoftwareLayerClient;
|
||||
import org.mozilla.fennec.gfx.IntPoint;
|
||||
import org.mozilla.fennec.gfx.LayerController;
|
||||
import org.mozilla.fennec.gfx.LayerView;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.nio.*;
|
||||
@ -95,11 +90,15 @@ public class GeckoAppShell
|
||||
static private boolean gRestartScheduled = false;
|
||||
static private PromptService gPromptService = null;
|
||||
|
||||
static private GeckoInputConnection mInputConnection = null;
|
||||
|
||||
static private final Timer mIMETimer = new Timer();
|
||||
static private final HashMap<Integer, AlertNotification>
|
||||
mAlertNotifications = new HashMap<Integer, AlertNotification>();
|
||||
|
||||
static private final int NOTIFY_IME_RESETINPUTSTATE = 0;
|
||||
static private final int NOTIFY_IME_SETOPENSTATE = 1;
|
||||
static private final int NOTIFY_IME_CANCELCOMPOSITION = 2;
|
||||
static private final int NOTIFY_IME_FOCUSCHANGE = 3;
|
||||
|
||||
/* Keep in sync with constants found here:
|
||||
http://mxr.mozilla.org/mozilla-central/source/uriloader/base/nsIWebProgressListener.idl
|
||||
*/
|
||||
@ -119,8 +118,7 @@ public class GeckoAppShell
|
||||
public static native void nativeRun(String args);
|
||||
|
||||
// helper methods
|
||||
// public static native void setSurfaceView(GeckoSurfaceView sv);
|
||||
public static native void setSoftwareLayerClient(GeckoSoftwareLayerClient client);
|
||||
public static native void setSurfaceView(GeckoSurfaceView sv);
|
||||
public static native void putenv(String map);
|
||||
public static native void onResume();
|
||||
public static native void onLowMemory();
|
||||
@ -416,8 +414,8 @@ public class GeckoAppShell
|
||||
// run gecko -- it will spawn its own thread
|
||||
GeckoAppShell.nativeInit();
|
||||
|
||||
// Tell Gecko where the target byte buffer is for rendering
|
||||
GeckoAppShell.setSoftwareLayerClient(GeckoApp.mAppContext.getSoftwareLayerClient());
|
||||
// Tell Gecko where the target surface view is for rendering
|
||||
GeckoAppShell.setSurfaceView(GeckoApp.surfaceView);
|
||||
|
||||
// First argument is the .apk path
|
||||
String combinedArgs = apkPath + " -greomni " + apkPath;
|
||||
@ -426,54 +424,10 @@ public class GeckoAppShell
|
||||
if (url != null)
|
||||
combinedArgs += " -remote " + url;
|
||||
|
||||
/* TODO: Is this complexity necessary? */
|
||||
new Timer("Gecko Setup").schedule(new TimerTask() {
|
||||
public void run() {
|
||||
GeckoApp.mAppContext.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
geckoLoaded();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 0);
|
||||
|
||||
// and go
|
||||
GeckoAppShell.nativeRun(combinedArgs);
|
||||
}
|
||||
|
||||
// Called on the UI thread after Gecko loads.
|
||||
private static void geckoLoaded() {
|
||||
GeckoApp.mAppContext.connectGeckoLayerClient();
|
||||
|
||||
final LayerController layerController = GeckoApp.mAppContext.getLayerController();
|
||||
LayerView v = layerController.getView();
|
||||
mInputConnection = new GeckoInputConnection(v);
|
||||
v.setInputConnectionHandler(mInputConnection);
|
||||
|
||||
layerController.setOnTouchListener(new View.OnTouchListener() {
|
||||
public boolean onTouch(View view, MotionEvent event) {
|
||||
float origX = event.getX();
|
||||
float origY = event.getY();
|
||||
/* Transform the point to the layer offset. */
|
||||
IntPoint eventPoint = new IntPoint((int)Math.round(origX),
|
||||
(int)Math.round(origY));
|
||||
IntPoint geckoPoint = layerController.convertViewPointToLayerPoint(eventPoint);
|
||||
event.setLocation(geckoPoint.x, geckoPoint.y);
|
||||
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
|
||||
|
||||
/* Restore the view coordinates in case the caller further processes this event */
|
||||
event.setLocation(origX, origY);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
GeckoEvent event = new GeckoEvent(GeckoEvent.SIZE_CHANGED,
|
||||
LayerController.TILE_WIDTH, LayerController.TILE_HEIGHT,
|
||||
LayerController.TILE_WIDTH, LayerController.TILE_HEIGHT);
|
||||
GeckoAppShell.sendEventToGecko(event);
|
||||
}
|
||||
|
||||
private static GeckoEvent mLastDrawEvent;
|
||||
|
||||
private static void sendPendingEventsToGecko() {
|
||||
@ -486,7 +440,7 @@ public class GeckoAppShell
|
||||
}
|
||||
|
||||
public static void sendEventToGecko(GeckoEvent e) {
|
||||
if (GeckoApp.mAppContext.checkLaunchState(GeckoApp.LaunchState.GeckoRunning)) {
|
||||
if (GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning)) {
|
||||
notifyGeckoOfEvent(e);
|
||||
} else {
|
||||
gPendingEvents.addLast(e);
|
||||
@ -506,24 +460,145 @@ public class GeckoAppShell
|
||||
*/
|
||||
public static void scheduleRedraw() {
|
||||
// Redraw everything
|
||||
Rect rect = new Rect(0, 0, LayerController.TILE_WIDTH, LayerController.TILE_HEIGHT);
|
||||
GeckoEvent event = new GeckoEvent(GeckoEvent.DRAW, rect);
|
||||
event.mNativeWindow = 0;
|
||||
sendEventToGecko(event);
|
||||
scheduleRedraw(0, -1, -1, -1, -1);
|
||||
}
|
||||
|
||||
public static void scheduleRedraw(int nativeWindow, int x, int y, int w, int h) {
|
||||
GeckoEvent e;
|
||||
|
||||
if (x == -1) {
|
||||
e = new GeckoEvent(GeckoEvent.DRAW, null);
|
||||
} else {
|
||||
e = new GeckoEvent(GeckoEvent.DRAW, new Rect(x, y, w, h));
|
||||
}
|
||||
|
||||
e.mNativeWindow = nativeWindow;
|
||||
|
||||
sendEventToGecko(e);
|
||||
}
|
||||
|
||||
/* Delay updating IME states (see bug 573800) */
|
||||
private static final class IMEStateUpdater extends TimerTask
|
||||
{
|
||||
static private IMEStateUpdater instance;
|
||||
private boolean mEnable, mReset;
|
||||
|
||||
static private IMEStateUpdater getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new IMEStateUpdater();
|
||||
mIMETimer.schedule(instance, 200);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
static public synchronized void enableIME() {
|
||||
getInstance().mEnable = true;
|
||||
}
|
||||
|
||||
static public synchronized void resetIME() {
|
||||
getInstance().mReset = true;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
synchronized(IMEStateUpdater.class) {
|
||||
instance = null;
|
||||
}
|
||||
|
||||
InputMethodManager imm = (InputMethodManager)
|
||||
GeckoApp.surfaceView.getContext().getSystemService(
|
||||
Context.INPUT_METHOD_SERVICE);
|
||||
if (imm == null)
|
||||
return;
|
||||
|
||||
if (mReset)
|
||||
imm.restartInput(GeckoApp.surfaceView);
|
||||
|
||||
if (!mEnable)
|
||||
return;
|
||||
|
||||
int state = GeckoApp.surfaceView.mIMEState;
|
||||
if (state != GeckoSurfaceView.IME_STATE_DISABLED &&
|
||||
state != GeckoSurfaceView.IME_STATE_PLUGIN)
|
||||
imm.showSoftInput(GeckoApp.surfaceView, 0);
|
||||
else
|
||||
imm.hideSoftInputFromWindow(
|
||||
GeckoApp.surfaceView.getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void notifyIME(int type, int state) {
|
||||
mInputConnection.notifyIME(type, state);
|
||||
if (GeckoApp.surfaceView == null)
|
||||
return;
|
||||
|
||||
switch (type) {
|
||||
case NOTIFY_IME_RESETINPUTSTATE:
|
||||
// Composition event is already fired from widget.
|
||||
// So reset IME flags.
|
||||
GeckoApp.surfaceView.inputConnection.reset();
|
||||
|
||||
// Don't use IMEStateUpdater for reset.
|
||||
// Because IME may not work showSoftInput()
|
||||
// after calling restartInput() immediately.
|
||||
// So we have to call showSoftInput() delay.
|
||||
InputMethodManager imm = (InputMethodManager)
|
||||
GeckoApp.surfaceView.getContext().getSystemService(
|
||||
Context.INPUT_METHOD_SERVICE);
|
||||
if (imm == null) {
|
||||
// no way to reset IME status directly
|
||||
IMEStateUpdater.resetIME();
|
||||
} else {
|
||||
imm.restartInput(GeckoApp.surfaceView);
|
||||
}
|
||||
|
||||
// keep current enabled state
|
||||
IMEStateUpdater.enableIME();
|
||||
break;
|
||||
|
||||
case NOTIFY_IME_CANCELCOMPOSITION:
|
||||
IMEStateUpdater.resetIME();
|
||||
break;
|
||||
|
||||
case NOTIFY_IME_FOCUSCHANGE:
|
||||
IMEStateUpdater.resetIME();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void notifyIMEEnabled(int state, String typeHint,
|
||||
String actionHint, boolean landscapeFS) {
|
||||
mInputConnection.notifyIMEEnabled(state, typeHint, actionHint, landscapeFS);
|
||||
String actionHint, boolean landscapeFS)
|
||||
{
|
||||
if (GeckoApp.surfaceView == null)
|
||||
return;
|
||||
|
||||
/* When IME is 'disabled', IME processing is disabled.
|
||||
In addition, the IME UI is hidden */
|
||||
GeckoApp.surfaceView.mIMEState = state;
|
||||
GeckoApp.surfaceView.mIMETypeHint = typeHint;
|
||||
GeckoApp.surfaceView.mIMEActionHint = actionHint;
|
||||
GeckoApp.surfaceView.mIMELandscapeFS = landscapeFS;
|
||||
IMEStateUpdater.enableIME();
|
||||
}
|
||||
|
||||
public static void notifyIMEChange(String text, int start, int end, int newEnd) {
|
||||
mInputConnection.notifyIMEChange(text, start, end, newEnd);
|
||||
if (GeckoApp.surfaceView == null ||
|
||||
GeckoApp.surfaceView.inputConnection == null)
|
||||
return;
|
||||
|
||||
InputMethodManager imm = (InputMethodManager)
|
||||
GeckoApp.surfaceView.getContext().getSystemService(
|
||||
Context.INPUT_METHOD_SERVICE);
|
||||
if (imm == null)
|
||||
return;
|
||||
|
||||
// Log.d("GeckoAppJava", String.format("IME: notifyIMEChange: t=%s s=%d ne=%d oe=%d",
|
||||
// text, start, newEnd, end));
|
||||
|
||||
if (newEnd < 0)
|
||||
GeckoApp.surfaceView.inputConnection.notifySelectionChange(
|
||||
imm, start, end);
|
||||
else
|
||||
GeckoApp.surfaceView.inputConnection.notifyTextChange(
|
||||
imm, text, start, end, newEnd);
|
||||
}
|
||||
|
||||
private static CountDownLatch sGeckoPendingAcks = null;
|
||||
@ -552,8 +627,8 @@ public class GeckoAppShell
|
||||
static Sensor gOrientationSensor = null;
|
||||
|
||||
public static void enableDeviceMotion(boolean enable) {
|
||||
LayerView v = GeckoApp.mAppContext.getLayerController().getView();
|
||||
SensorManager sm = (SensorManager) v.getContext().getSystemService(Context.SENSOR_SERVICE);
|
||||
SensorManager sm = (SensorManager)
|
||||
GeckoApp.surfaceView.getContext().getSystemService(Context.SENSOR_SERVICE);
|
||||
|
||||
if (gAccelerometerSensor == null || gOrientationSensor == null) {
|
||||
gAccelerometerSensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
||||
@ -562,24 +637,23 @@ public class GeckoAppShell
|
||||
|
||||
if (enable) {
|
||||
if (gAccelerometerSensor != null)
|
||||
sm.registerListener(GeckoApp.mAppContext, gAccelerometerSensor, SensorManager.SENSOR_DELAY_GAME);
|
||||
sm.registerListener(GeckoApp.surfaceView, gAccelerometerSensor, SensorManager.SENSOR_DELAY_GAME);
|
||||
if (gOrientationSensor != null)
|
||||
sm.registerListener(GeckoApp.mAppContext, gOrientationSensor, SensorManager.SENSOR_DELAY_GAME);
|
||||
sm.registerListener(GeckoApp.surfaceView, gOrientationSensor, SensorManager.SENSOR_DELAY_GAME);
|
||||
} else {
|
||||
if (gAccelerometerSensor != null)
|
||||
sm.unregisterListener(GeckoApp.mAppContext, gAccelerometerSensor);
|
||||
sm.unregisterListener(GeckoApp.surfaceView, gAccelerometerSensor);
|
||||
if (gOrientationSensor != null)
|
||||
sm.unregisterListener(GeckoApp.mAppContext, gOrientationSensor);
|
||||
sm.unregisterListener(GeckoApp.surfaceView, gOrientationSensor);
|
||||
}
|
||||
}
|
||||
|
||||
public static void enableLocation(final boolean enable) {
|
||||
getMainHandler().post(new Runnable() {
|
||||
public void run() {
|
||||
LayerView v = GeckoApp.mAppContext.getLayerController().getView();
|
||||
|
||||
GeckoSurfaceView view = GeckoApp.surfaceView;
|
||||
LocationManager lm = (LocationManager)
|
||||
GeckoApp.mAppContext.getSystemService(Context.LOCATION_SERVICE);
|
||||
view.getContext().getSystemService(Context.LOCATION_SERVICE);
|
||||
|
||||
if (enable) {
|
||||
Criteria crit = new Criteria();
|
||||
@ -591,11 +665,11 @@ public class GeckoAppShell
|
||||
Looper l = Looper.getMainLooper();
|
||||
Location loc = lm.getLastKnownLocation(provider);
|
||||
if (loc != null) {
|
||||
GeckoApp.mAppContext.onLocationChanged(loc);
|
||||
view.onLocationChanged(loc);
|
||||
}
|
||||
lm.requestLocationUpdates(provider, 100, (float).5, GeckoApp.mAppContext, l);
|
||||
lm.requestLocationUpdates(provider, 100, (float).5, view, l);
|
||||
} else {
|
||||
lm.removeUpdates(GeckoApp.mAppContext);
|
||||
lm.removeUpdates(view);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -606,19 +680,24 @@ public class GeckoAppShell
|
||||
}
|
||||
|
||||
public static void returnIMEQueryResult(String result, int selectionStart, int selectionLength) {
|
||||
mInputConnection.returnIMEQueryResult(result, selectionStart, selectionLength);
|
||||
GeckoApp.surfaceView.inputConnection.mSelectionStart = selectionStart;
|
||||
GeckoApp.surfaceView.inputConnection.mSelectionLength = selectionLength;
|
||||
try {
|
||||
GeckoApp.surfaceView.inputConnection.mQueryResult.put(result);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
static void onAppShellReady()
|
||||
{
|
||||
// mLaunchState can only be Launched at this point
|
||||
GeckoApp.mAppContext.setLaunchState(GeckoApp.LaunchState.GeckoRunning);
|
||||
GeckoApp.setLaunchState(GeckoApp.LaunchState.GeckoRunning);
|
||||
sendPendingEventsToGecko();
|
||||
}
|
||||
|
||||
static void onXreExit() {
|
||||
// mLaunchState can only be Launched or GeckoRunning at this point
|
||||
GeckoApp.mAppContext.setLaunchState(GeckoApp.LaunchState.GeckoExiting);
|
||||
GeckoApp.setLaunchState(GeckoApp.LaunchState.GeckoExiting);
|
||||
Log.i("GeckoAppJava", "XRE exited");
|
||||
if (gRestartScheduled) {
|
||||
GeckoApp.mAppContext.doRestart();
|
||||
@ -682,7 +761,8 @@ public class GeckoAppShell
|
||||
}
|
||||
|
||||
static String[] getHandlersForIntent(Intent intent) {
|
||||
PackageManager pm = GeckoApp.mAppContext.getPackageManager();
|
||||
PackageManager pm =
|
||||
GeckoApp.surfaceView.getContext().getPackageManager();
|
||||
List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
|
||||
int numAttr = 4;
|
||||
String[] ret = new String[list.size() * numAttr];
|
||||
@ -781,7 +861,7 @@ public class GeckoAppShell
|
||||
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
try {
|
||||
GeckoApp.mAppContext.startActivity(intent);
|
||||
GeckoApp.surfaceView.getContext().startActivity(intent);
|
||||
return true;
|
||||
} catch(ActivityNotFoundException e) {
|
||||
return false;
|
||||
@ -800,7 +880,7 @@ public class GeckoAppShell
|
||||
getHandler().post(new Runnable() {
|
||||
@SuppressWarnings("deprecation")
|
||||
public void run() {
|
||||
Context context = GeckoApp.mAppContext;
|
||||
Context context = GeckoApp.surfaceView.getContext();
|
||||
String text = null;
|
||||
if (android.os.Build.VERSION.SDK_INT >= 11) {
|
||||
android.content.ClipboardManager cm = (android.content.ClipboardManager)
|
||||
@ -833,7 +913,7 @@ public class GeckoAppShell
|
||||
getHandler().post(new Runnable() {
|
||||
@SuppressWarnings("deprecation")
|
||||
public void run() {
|
||||
Context context = GeckoApp.mAppContext;
|
||||
Context context = GeckoApp.surfaceView.getContext();
|
||||
if (android.os.Build.VERSION.SDK_INT >= 11) {
|
||||
android.content.ClipboardManager cm = (android.content.ClipboardManager)
|
||||
context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
@ -982,19 +1062,21 @@ public class GeckoAppShell
|
||||
}
|
||||
|
||||
public static void performHapticFeedback(boolean aIsLongPress) {
|
||||
// TODO
|
||||
GeckoApp.surfaceView.
|
||||
performHapticFeedback(aIsLongPress ?
|
||||
HapticFeedbackConstants.LONG_PRESS :
|
||||
HapticFeedbackConstants.VIRTUAL_KEY);
|
||||
}
|
||||
|
||||
public static void showInputMethodPicker() {
|
||||
InputMethodManager imm = (InputMethodManager)
|
||||
GeckoApp.mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
InputMethodManager imm = (InputMethodManager) GeckoApp.surfaceView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.showInputMethodPicker();
|
||||
}
|
||||
|
||||
public static void setKeepScreenOn(final boolean on) {
|
||||
GeckoApp.mAppContext.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
// TODO
|
||||
GeckoApp.surfaceView.setKeepScreenOn(on);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1168,7 +1250,7 @@ public class GeckoAppShell
|
||||
}
|
||||
|
||||
public static void scanMedia(String aFile, String aMimeType) {
|
||||
Context context = GeckoApp.mAppContext;
|
||||
Context context = GeckoApp.surfaceView.getContext();
|
||||
GeckoMediaScannerClient client = new GeckoMediaScannerClient(context, aFile, aMimeType);
|
||||
}
|
||||
|
||||
@ -1180,7 +1262,7 @@ public class GeckoAppShell
|
||||
if (aExt != null && aExt.length() > 1 && aExt.charAt(0) == '.')
|
||||
aExt = aExt.substring(1);
|
||||
|
||||
PackageManager pm = GeckoApp.mAppContext.getPackageManager();
|
||||
PackageManager pm = GeckoApp.surfaceView.getContext().getPackageManager();
|
||||
Drawable icon = getDrawableForExtension(pm, aExt);
|
||||
if (icon == null) {
|
||||
// Use a generic icon
|
||||
@ -1585,11 +1667,8 @@ public class GeckoAppShell
|
||||
if (!accessibilityManager.isEnabled())
|
||||
return;
|
||||
|
||||
LayerController layerController = GeckoApp.mAppContext.getLayerController();
|
||||
LayerView v = layerController.getView();
|
||||
|
||||
AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
|
||||
event.setClassName(v.getClass().getName() + "$" + role);
|
||||
event.setClassName(GeckoApp.surfaceView.getClass().getName() + "$" + role);
|
||||
event.setPackageName(GeckoApp.mAppContext.getPackageName());
|
||||
event.setEnabled(enabled);
|
||||
event.setChecked(checked);
|
||||
|
@ -203,14 +203,14 @@ public class GeckoEvent {
|
||||
rangeForeColor, rangeBackColor);
|
||||
}
|
||||
|
||||
public GeckoEvent(int etype, Rect rect) {
|
||||
public GeckoEvent(int etype, Rect dirty) {
|
||||
if (etype != DRAW) {
|
||||
mType = INVALID;
|
||||
return;
|
||||
}
|
||||
|
||||
mType = etype;
|
||||
mRect = rect;
|
||||
mRect = dirty;
|
||||
}
|
||||
|
||||
public GeckoEvent(int etype, int w, int h, int screenw, int screenh) {
|
||||
|
@ -15,11 +15,11 @@
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
* Wes Johnston <wjohnston@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -35,31 +35,62 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.gfx;
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import org.json.JSONException;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import android.util.Log;
|
||||
|
||||
public class IntSize {
|
||||
public final int width, height;
|
||||
class GeckoGestureDetector implements GestureDetector.OnGestureListener {
|
||||
private GestureDetector mDetector;
|
||||
private static final String LOG_FILE_NAME = "GeckoGestureDetector";
|
||||
public GeckoGestureDetector(Context aContext) {
|
||||
mDetector = new GestureDetector(aContext, this);
|
||||
}
|
||||
|
||||
public IntSize(int inWidth, int inHeight) { width = inWidth; height = inHeight; }
|
||||
|
||||
public IntSize(JSONObject json) {
|
||||
try {
|
||||
width = json.getInt("width");
|
||||
height = json.getInt("height");
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
return mDetector.onTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() { return "(" + width + "," + height + ")"; }
|
||||
public boolean onDown(MotionEvent e) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public IntSize scale(float factor) {
|
||||
return new IntSize((int)Math.round(width * factor),
|
||||
(int)Math.round(height * factor));
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(MotionEvent motionEvent) {
|
||||
JSONObject ret = new JSONObject();
|
||||
try {
|
||||
ret.put("x", motionEvent.getX());
|
||||
ret.put("y", motionEvent.getY());
|
||||
} catch(Exception ex) {
|
||||
Log.w(LOG_FILE_NAME, "Error building return: " + ex);
|
||||
}
|
||||
|
||||
GeckoEvent e = new GeckoEvent("Gesture:LongPress", ret.toString());
|
||||
GeckoAppShell.sendEventToGecko(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowPress(MotionEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -42,10 +42,6 @@ import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
|
||||
import org.mozilla.fennec.gfx.InputConnectionHandler;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
import android.os.*;
|
||||
import android.app.*;
|
||||
import android.text.*;
|
||||
@ -55,15 +51,11 @@ import android.view.inputmethod.*;
|
||||
import android.content.*;
|
||||
import android.R;
|
||||
|
||||
import android.text.method.TextKeyListener;
|
||||
import android.text.method.KeyListener;
|
||||
|
||||
import android.util.*;
|
||||
|
||||
|
||||
public class GeckoInputConnection
|
||||
extends BaseInputConnection
|
||||
implements TextWatcher, InputConnectionHandler
|
||||
implements TextWatcher
|
||||
{
|
||||
private class ChangeNotification {
|
||||
public String mText;
|
||||
@ -89,31 +81,25 @@ public class GeckoInputConnection
|
||||
public GeckoInputConnection (View targetView) {
|
||||
super(targetView, true);
|
||||
mQueryResult = new SynchronousQueue<String>();
|
||||
|
||||
mEditableFactory = Editable.Factory.getInstance();
|
||||
initEditable("");
|
||||
mIMEState = IME_STATE_DISABLED;
|
||||
mIMETypeHint = "";
|
||||
mIMEActionHint = "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean beginBatchEdit() {
|
||||
Log.d("GeckoAppJava", "IME: beginBatchEdit");
|
||||
//Log.d("GeckoAppJava", "IME: beginBatchEdit");
|
||||
mBatchMode = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commitCompletion(CompletionInfo text) {
|
||||
Log.d("GeckoAppJava", "IME: commitCompletion");
|
||||
//Log.d("GeckoAppJava", "IME: commitCompletion");
|
||||
|
||||
return commitText(text.getText(), 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commitText(CharSequence text, int newCursorPosition) {
|
||||
Log.d("GeckoAppJava", "IME: commitText");
|
||||
//Log.d("GeckoAppJava", "IME: commitText");
|
||||
|
||||
setComposingText(text, newCursorPosition);
|
||||
finishComposingText();
|
||||
@ -123,7 +109,7 @@ public class GeckoInputConnection
|
||||
|
||||
@Override
|
||||
public boolean deleteSurroundingText(int leftLength, int rightLength) {
|
||||
Log.d("GeckoAppJava", "IME: deleteSurroundingText");
|
||||
//Log.d("GeckoAppJava", "IME: deleteSurroundingText");
|
||||
if (leftLength == 0 && rightLength == 0)
|
||||
return true;
|
||||
|
||||
@ -182,13 +168,13 @@ public class GeckoInputConnection
|
||||
|
||||
@Override
|
||||
public boolean endBatchEdit() {
|
||||
Log.d("GeckoAppJava", "IME: endBatchEdit");
|
||||
//Log.d("GeckoAppJava", "IME: endBatchEdit");
|
||||
|
||||
mBatchMode = false;
|
||||
|
||||
if (!mBatchChanges.isEmpty()) {
|
||||
InputMethodManager imm = (InputMethodManager)
|
||||
GeckoApp.mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
GeckoApp.surfaceView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
if (imm != null) {
|
||||
for (ChangeNotification n : mBatchChanges) {
|
||||
if (n.mText != null)
|
||||
@ -204,7 +190,7 @@ public class GeckoInputConnection
|
||||
|
||||
@Override
|
||||
public boolean finishComposingText() {
|
||||
Log.d("GeckoAppJava", "IME: finishComposingText");
|
||||
//Log.d("GeckoAppJava", "IME: finishComposingText");
|
||||
|
||||
if (mComposing) {
|
||||
// Set style to none
|
||||
@ -229,7 +215,7 @@ public class GeckoInputConnection
|
||||
|
||||
@Override
|
||||
public int getCursorCapsMode(int reqModes) {
|
||||
Log.d("GeckoAppJava", "IME: getCursorCapsMode");
|
||||
//Log.d("GeckoAppJava", "IME: getCursorCapsMode");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -244,7 +230,7 @@ public class GeckoInputConnection
|
||||
|
||||
@Override
|
||||
public boolean performContextMenuAction(int id) {
|
||||
Log.d("GeckoAppJava", "IME: performContextMenuAction");
|
||||
//Log.d("GeckoAppJava", "IME: performContextMenuAction");
|
||||
|
||||
// First we need to ask Gecko to tell us the full contents of the
|
||||
// text field we're about to operate on.
|
||||
@ -304,7 +290,7 @@ public class GeckoInputConnection
|
||||
if (!GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning))
|
||||
return null;
|
||||
|
||||
Log.d("GeckoAppJava", "IME: getExtractedText");
|
||||
//Log.d("GeckoAppJava", "IME: getExtractedText");
|
||||
|
||||
ExtractedText extract = new ExtractedText();
|
||||
extract.flags = 0;
|
||||
@ -356,7 +342,7 @@ public class GeckoInputConnection
|
||||
|
||||
@Override
|
||||
public CharSequence getTextAfterCursor(int length, int flags) {
|
||||
Log.d("GeckoAppJava", "IME: getTextAfterCursor");
|
||||
//Log.d("GeckoAppJava", "IME: getTextAfterCursor");
|
||||
|
||||
GeckoAppShell.sendEventToGecko(
|
||||
new GeckoEvent(GeckoEvent.IME_GET_SELECTION, 0, 0));
|
||||
@ -393,14 +379,14 @@ public class GeckoInputConnection
|
||||
|
||||
@Override
|
||||
public CharSequence getTextBeforeCursor(int length, int flags) {
|
||||
Log.d("GeckoAppJava", "IME: getTextBeforeCursor");
|
||||
//Log.d("GeckoAppJava", "IME: getTextBeforeCursor");
|
||||
|
||||
return getTextAfterCursor(-length, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setComposingText(CharSequence text, int newCursorPosition) {
|
||||
Log.d("GeckoAppJava", "IME: setComposingText");
|
||||
//Log.d("GeckoAppJava", "IME: setComposingText");
|
||||
|
||||
enableChangeNotifications();
|
||||
|
||||
@ -534,7 +520,7 @@ public class GeckoInputConnection
|
||||
|
||||
@Override
|
||||
public boolean setComposingRegion(int start, int end) {
|
||||
Log.d("GeckoAppJava", "IME: setComposingRegion(start=" + start + ", end=" + end + ")");
|
||||
//Log.d("GeckoAppJava", "IME: setComposingRegion(start=" + start + ", end=" + end + ")");
|
||||
if (start < 0 || end < start)
|
||||
return true;
|
||||
|
||||
@ -568,7 +554,7 @@ public class GeckoInputConnection
|
||||
|
||||
@Override
|
||||
public boolean setSelection(int start, int end) {
|
||||
Log.d("GeckoAppJava", "IME: setSelection");
|
||||
//Log.d("GeckoAppJava", "IME: setSelection");
|
||||
|
||||
if (mComposing) {
|
||||
/* Translate to fake selection positions */
|
||||
@ -616,8 +602,8 @@ public class GeckoInputConnection
|
||||
|
||||
public void notifyTextChange(InputMethodManager imm, String text,
|
||||
int start, int oldEnd, int newEnd) {
|
||||
Log.d("GeckoAppShell", String.format("IME: notifyTextChange: text=%s s=%d ne=%d oe=%d",
|
||||
text, start, newEnd, oldEnd));
|
||||
// Log.d("GeckoAppShell", String.format("IME: notifyTextChange: text=%s s=%d ne=%d oe=%d",
|
||||
// text, start, newEnd, oldEnd));
|
||||
if (!mChangeNotificationsEnabled)
|
||||
return;
|
||||
|
||||
@ -630,10 +616,8 @@ public class GeckoInputConnection
|
||||
|
||||
// If there are pending changes, that means this text is not the most up-to-date version
|
||||
// and we'll step on ourselves if we change the editable right now.
|
||||
View v = GeckoApp.mAppContext.getLayerController().getView();
|
||||
|
||||
if (mNumPendingChanges == 0 && !text.contentEquals(mEditable))
|
||||
setEditable(text);
|
||||
if (mNumPendingChanges == 0 && !text.contentEquals(GeckoApp.surfaceView.mEditable))
|
||||
GeckoApp.surfaceView.setEditable(text);
|
||||
|
||||
if (mUpdateRequest == null)
|
||||
return;
|
||||
@ -651,13 +635,14 @@ public class GeckoInputConnection
|
||||
|
||||
mUpdateExtract.text = text.substring(0, newEnd);
|
||||
mUpdateExtract.startOffset = 0;
|
||||
|
||||
imm.updateExtractedText(v, mUpdateRequest.token, mUpdateExtract);
|
||||
|
||||
imm.updateExtractedText(GeckoApp.surfaceView,
|
||||
mUpdateRequest.token, mUpdateExtract);
|
||||
}
|
||||
|
||||
public void notifySelectionChange(InputMethodManager imm,
|
||||
int start, int end) {
|
||||
Log.d("GeckoAppJava", String.format("IME: notifySelectionChange: s=%d e=%d", start, end));
|
||||
// Log.d("GeckoAppJava", String.format("IME: notifySelectionChange: s=%d e=%d", start, end));
|
||||
|
||||
if (!mChangeNotificationsEnabled)
|
||||
return;
|
||||
@ -667,23 +652,22 @@ public class GeckoInputConnection
|
||||
return;
|
||||
}
|
||||
|
||||
View v = GeckoApp.mAppContext.getLayerController().getView();
|
||||
if (mComposing)
|
||||
imm.updateSelection(v,
|
||||
mCompositionStart + mCompositionSelStart,
|
||||
mCompositionStart + mCompositionSelStart + mCompositionSelLen,
|
||||
mCompositionStart,
|
||||
mCompositionStart + mComposingText.length());
|
||||
imm.updateSelection(GeckoApp.surfaceView,
|
||||
mCompositionStart + mCompositionSelStart,
|
||||
mCompositionStart + mCompositionSelStart + mCompositionSelLen,
|
||||
mCompositionStart,
|
||||
mCompositionStart + mComposingText.length());
|
||||
else
|
||||
imm.updateSelection(v, start, end, -1, -1);
|
||||
imm.updateSelection(GeckoApp.surfaceView, start, end, -1, -1);
|
||||
|
||||
// We only change the selection if we are relatively sure that the text we have is
|
||||
// up-to-date. Bail out if we are stil expecting changes.
|
||||
if (mNumPendingChanges > 0)
|
||||
return;
|
||||
|
||||
int maxLen = mEditable.length();
|
||||
Selection.setSelection(mEditable,
|
||||
int maxLen = GeckoApp.surfaceView.mEditable.length();
|
||||
Selection.setSelection(GeckoApp.surfaceView.mEditable,
|
||||
Math.min(start, maxLen),
|
||||
Math.min(end, maxLen));
|
||||
}
|
||||
@ -700,8 +684,8 @@ public class GeckoInputConnection
|
||||
// TextWatcher
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count)
|
||||
{
|
||||
Log.d("GeckoAppShell", String.format("IME: onTextChanged: t=%s s=%d b=%d l=%d",
|
||||
s, start, before, count));
|
||||
// Log.d("GeckoAppShell", String.format("IME: onTextChanged: t=%s s=%d b=%d l=%d",
|
||||
// s, start, before, count));
|
||||
|
||||
mNumPendingChanges++;
|
||||
GeckoAppShell.sendEventToGecko(
|
||||
@ -748,331 +732,6 @@ public class GeckoInputConnection
|
||||
mChangeNotificationsEnabled = true;
|
||||
}
|
||||
|
||||
|
||||
public InputConnection onCreateInputConnection(EditorInfo outAttrs)
|
||||
{
|
||||
Log.d("GeckoAppJava", "IME: handleCreateInputConnection called");
|
||||
|
||||
outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
|
||||
outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE;
|
||||
outAttrs.actionLabel = null;
|
||||
mKeyListener = TextKeyListener.getInstance();
|
||||
|
||||
if (mIMEState == IME_STATE_PASSWORD)
|
||||
outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_PASSWORD;
|
||||
else if (mIMETypeHint.equalsIgnoreCase("url"))
|
||||
outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_URI;
|
||||
else if (mIMETypeHint.equalsIgnoreCase("email"))
|
||||
outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
|
||||
else if (mIMETypeHint.equalsIgnoreCase("search"))
|
||||
outAttrs.imeOptions = EditorInfo.IME_ACTION_SEARCH;
|
||||
else if (mIMETypeHint.equalsIgnoreCase("tel"))
|
||||
outAttrs.inputType = InputType.TYPE_CLASS_PHONE;
|
||||
else if (mIMETypeHint.equalsIgnoreCase("number") ||
|
||||
mIMETypeHint.equalsIgnoreCase("range"))
|
||||
outAttrs.inputType = InputType.TYPE_CLASS_NUMBER;
|
||||
else if (mIMETypeHint.equalsIgnoreCase("datetime") ||
|
||||
mIMETypeHint.equalsIgnoreCase("datetime-local"))
|
||||
outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
|
||||
InputType.TYPE_DATETIME_VARIATION_NORMAL;
|
||||
else if (mIMETypeHint.equalsIgnoreCase("date"))
|
||||
outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
|
||||
InputType.TYPE_DATETIME_VARIATION_DATE;
|
||||
else if (mIMETypeHint.equalsIgnoreCase("time"))
|
||||
outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
|
||||
InputType.TYPE_DATETIME_VARIATION_TIME;
|
||||
|
||||
if (mIMEActionHint.equalsIgnoreCase("go"))
|
||||
outAttrs.imeOptions = EditorInfo.IME_ACTION_GO;
|
||||
else if (mIMEActionHint.equalsIgnoreCase("done"))
|
||||
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
|
||||
else if (mIMEActionHint.equalsIgnoreCase("next"))
|
||||
outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT;
|
||||
else if (mIMEActionHint.equalsIgnoreCase("search"))
|
||||
outAttrs.imeOptions = EditorInfo.IME_ACTION_SEARCH;
|
||||
else if (mIMEActionHint.equalsIgnoreCase("send"))
|
||||
outAttrs.imeOptions = EditorInfo.IME_ACTION_SEND;
|
||||
else if (mIMEActionHint != null && mIMEActionHint.length() != 0)
|
||||
outAttrs.actionLabel = mIMEActionHint;
|
||||
|
||||
if (mIMELandscapeFS == false)
|
||||
outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI;
|
||||
|
||||
reset();
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case KeyEvent.ACTION_DOWN:
|
||||
return processKeyDown(keyCode, event, true);
|
||||
case KeyEvent.ACTION_UP:
|
||||
return processKeyUp(keyCode, event, true);
|
||||
case KeyEvent.ACTION_MULTIPLE:
|
||||
return onKeyMultiple(keyCode, event.getRepeatCount(), event);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
return processKeyDown(keyCode, event, false);
|
||||
}
|
||||
|
||||
private boolean processKeyDown(int keyCode, KeyEvent event, boolean isPreIme) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
case KeyEvent.KEYCODE_VOLUME_UP:
|
||||
case KeyEvent.KEYCODE_VOLUME_DOWN:
|
||||
case KeyEvent.KEYCODE_SEARCH:
|
||||
return false;
|
||||
case KeyEvent.KEYCODE_DEL:
|
||||
// See comments in GeckoInputConnection.onKeyDel
|
||||
if (onKeyDel()) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case KeyEvent.KEYCODE_ENTER:
|
||||
if ((event.getFlags() & KeyEvent.FLAG_EDITOR_ACTION) != 0 &&
|
||||
mIMEActionHint.equalsIgnoreCase("next"))
|
||||
event = new KeyEvent(event.getAction(), KeyEvent.KEYCODE_TAB);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (isPreIme && mIMEState != IME_STATE_DISABLED &&
|
||||
(event.getMetaState() & KeyEvent.META_ALT_ON) == 0)
|
||||
// Let active IME process pre-IME key events
|
||||
return false;
|
||||
|
||||
View v = GeckoApp.mAppContext.getLayerController().getView();
|
||||
|
||||
// KeyListener returns true if it handled the event for us.
|
||||
if (mIMEState == IME_STATE_DISABLED ||
|
||||
keyCode == KeyEvent.KEYCODE_ENTER ||
|
||||
keyCode == KeyEvent.KEYCODE_DEL ||
|
||||
(event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 ||
|
||||
!mKeyListener.onKeyDown(v, mEditable, keyCode, event))
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
return processKeyUp(keyCode, event, false);
|
||||
}
|
||||
|
||||
private boolean processKeyUp(int keyCode, KeyEvent event, boolean isPreIme) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
case KeyEvent.KEYCODE_SEARCH:
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (isPreIme && mIMEState != IME_STATE_DISABLED &&
|
||||
(event.getMetaState() & KeyEvent.META_ALT_ON) == 0)
|
||||
// Let active IME process pre-IME key events
|
||||
return false;
|
||||
View v = GeckoApp.mAppContext.getLayerController().getView();
|
||||
|
||||
if (mIMEState == IME_STATE_DISABLED ||
|
||||
keyCode == KeyEvent.KEYCODE_ENTER ||
|
||||
keyCode == KeyEvent.KEYCODE_DEL ||
|
||||
(event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 ||
|
||||
!mKeyListener.onKeyUp(v, mEditable, keyCode, event))
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
|
||||
View v = GeckoApp.mAppContext.getLayerController().getView();
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
InputMethodManager imm = (InputMethodManager)
|
||||
v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.toggleSoftInputFromWindow(v.getWindowToken(),
|
||||
imm.SHOW_FORCED, 0);
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public void notifyIME(int type, int state) {
|
||||
|
||||
View v = GeckoApp.mAppContext.getLayerController().getView();
|
||||
|
||||
Log.d("GeckoAppJava", "notifyIME");
|
||||
|
||||
if (v == null)
|
||||
return;
|
||||
|
||||
Log.d("GeckoAppJava", "notifyIME v!= null");
|
||||
|
||||
switch (type) {
|
||||
case NOTIFY_IME_RESETINPUTSTATE:
|
||||
|
||||
Log.d("GeckoAppJava", "notifyIME = reset");
|
||||
// Composition event is already fired from widget.
|
||||
// So reset IME flags.
|
||||
reset();
|
||||
|
||||
// Don't use IMEStateUpdater for reset.
|
||||
// Because IME may not work showSoftInput()
|
||||
// after calling restartInput() immediately.
|
||||
// So we have to call showSoftInput() delay.
|
||||
InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
if (imm != null) {
|
||||
// no way to reset IME status directly
|
||||
IMEStateUpdater.resetIME();
|
||||
} else {
|
||||
imm.restartInput(v);
|
||||
}
|
||||
|
||||
// keep current enabled state
|
||||
IMEStateUpdater.enableIME();
|
||||
break;
|
||||
|
||||
case NOTIFY_IME_CANCELCOMPOSITION:
|
||||
Log.d("GeckoAppJava", "notifyIME = cancel");
|
||||
IMEStateUpdater.resetIME();
|
||||
break;
|
||||
|
||||
case NOTIFY_IME_FOCUSCHANGE:
|
||||
Log.d("GeckoAppJava", "notifyIME = focus");
|
||||
IMEStateUpdater.resetIME();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyIMEEnabled(int state, String typeHint,
|
||||
String actionHint, boolean landscapeFS)
|
||||
{
|
||||
View v = GeckoApp.mAppContext.getLayerController().getView();
|
||||
|
||||
if (v == null)
|
||||
return;
|
||||
|
||||
/* When IME is 'disabled', IME processing is disabled.
|
||||
In addition, the IME UI is hidden */
|
||||
mIMEState = state;
|
||||
mIMETypeHint = typeHint;
|
||||
mIMEActionHint = actionHint;
|
||||
mIMELandscapeFS = landscapeFS;
|
||||
IMEStateUpdater.enableIME();
|
||||
}
|
||||
|
||||
|
||||
public void notifyIMEChange(String text, int start, int end, int newEnd) {
|
||||
View v = GeckoApp.mAppContext.getLayerController().getView();
|
||||
|
||||
if (v == null)
|
||||
return;
|
||||
|
||||
InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
if (imm == null)
|
||||
return;
|
||||
|
||||
Log.d("GeckoAppJava", String.format("IME: notifyIMEChange: t=%s s=%d ne=%d oe=%d",
|
||||
text, start, newEnd, end));
|
||||
|
||||
if (newEnd < 0)
|
||||
notifySelectionChange(imm, start, end);
|
||||
else
|
||||
notifyTextChange(imm, text, start, end, newEnd);
|
||||
}
|
||||
|
||||
|
||||
public void returnIMEQueryResult(String result, int selectionStart, int selectionLength) {
|
||||
mSelectionStart = selectionStart;
|
||||
mSelectionLength = selectionLength;
|
||||
try {
|
||||
mQueryResult.put(result);
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
|
||||
static private final Timer mIMETimer = new Timer();
|
||||
|
||||
static private final int NOTIFY_IME_RESETINPUTSTATE = 0;
|
||||
static private final int NOTIFY_IME_SETOPENSTATE = 1;
|
||||
static private final int NOTIFY_IME_CANCELCOMPOSITION = 2;
|
||||
static private final int NOTIFY_IME_FOCUSCHANGE = 3;
|
||||
|
||||
|
||||
/* Delay updating IME states (see bug 573800) */
|
||||
private static final class IMEStateUpdater extends TimerTask
|
||||
{
|
||||
static private IMEStateUpdater instance;
|
||||
private boolean mEnable, mReset;
|
||||
|
||||
static private IMEStateUpdater getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new IMEStateUpdater();
|
||||
mIMETimer.schedule(instance, 200);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
static public synchronized void enableIME() {
|
||||
getInstance().mEnable = true;
|
||||
}
|
||||
|
||||
static public synchronized void resetIME() {
|
||||
getInstance().mReset = true;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
Log.d("GeckoAppJava", "IME: run()");
|
||||
synchronized(IMEStateUpdater.class) {
|
||||
instance = null;
|
||||
}
|
||||
|
||||
View v = GeckoApp.mAppContext.getLayerController().getView();
|
||||
Log.d("GeckoAppJava", "IME: v="+v);
|
||||
|
||||
InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
if (imm == null)
|
||||
return;
|
||||
|
||||
if (mReset)
|
||||
imm.restartInput(v);
|
||||
|
||||
if (!mEnable)
|
||||
return;
|
||||
|
||||
if (mIMEState != IME_STATE_DISABLED &&
|
||||
mIMEState != IME_STATE_PLUGIN)
|
||||
imm.showSoftInput(v, 0);
|
||||
else
|
||||
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void setEditable(String contents)
|
||||
{
|
||||
mEditable.removeSpan(this);
|
||||
mEditable.replace(0, mEditable.length(), contents);
|
||||
mEditable.setSpan(this, 0, contents.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
Selection.setSelection(mEditable, contents.length());
|
||||
}
|
||||
|
||||
public void initEditable(String contents)
|
||||
{
|
||||
mEditable = mEditableFactory.newEditable(contents);
|
||||
mEditable.setSpan(this, 0, contents.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
Selection.setSelection(mEditable, contents.length());
|
||||
}
|
||||
|
||||
// Is a composition active?
|
||||
boolean mComposing;
|
||||
// Composition text when a composition is active
|
||||
@ -1088,20 +747,6 @@ public class GeckoInputConnection
|
||||
// Number of in flight changes
|
||||
int mNumPendingChanges;
|
||||
|
||||
// IME stuff
|
||||
public static final int IME_STATE_DISABLED = 0;
|
||||
public static final int IME_STATE_ENABLED = 1;
|
||||
public static final int IME_STATE_PASSWORD = 2;
|
||||
public static final int IME_STATE_PLUGIN = 3;
|
||||
|
||||
KeyListener mKeyListener;
|
||||
Editable mEditable;
|
||||
Editable.Factory mEditableFactory;
|
||||
static int mIMEState;
|
||||
static String mIMETypeHint;
|
||||
static String mIMEActionHint;
|
||||
static boolean mIMELandscapeFS;
|
||||
|
||||
private boolean mBatchMode;
|
||||
private boolean mChangeNotificationsEnabled = true;
|
||||
|
||||
|
827
embedding/android/GeckoSurfaceView.java
Normal file
827
embedding/android/GeckoSurfaceView.java
Normal file
@ -0,0 +1,827 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir@pobox.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.locks.*;
|
||||
import java.util.concurrent.atomic.*;
|
||||
import java.util.zip.*;
|
||||
import java.nio.*;
|
||||
|
||||
import android.os.*;
|
||||
import android.app.*;
|
||||
import android.text.*;
|
||||
import android.text.method.*;
|
||||
import android.view.*;
|
||||
import android.view.inputmethod.*;
|
||||
import android.content.*;
|
||||
import android.graphics.*;
|
||||
import android.widget.*;
|
||||
import android.hardware.*;
|
||||
import android.location.*;
|
||||
import android.graphics.drawable.*;
|
||||
import android.content.res.*;
|
||||
import android.util.*;
|
||||
|
||||
/*
|
||||
* GeckoSurfaceView implements a GL surface view,
|
||||
* similar to GLSurfaceView. However, since we
|
||||
* already have a thread for Gecko, we don't really want
|
||||
* a separate renderer thread that GLSurfaceView provides.
|
||||
*/
|
||||
class GeckoSurfaceView
|
||||
extends SurfaceView
|
||||
implements SurfaceHolder.Callback, SensorEventListener, LocationListener
|
||||
{
|
||||
private static final String LOG_FILE_NAME = "GeckoSurfaceView";
|
||||
|
||||
public GeckoSurfaceView(Context context) {
|
||||
super(context, null, android.R.style.Theme_Light_NoTitleBar);
|
||||
|
||||
getHolder().addCallback(this);
|
||||
inputConnection = new GeckoInputConnection(this);
|
||||
gestureScanner = new GeckoGestureDetector(context);
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
GeckoApp.mAppContext.getWindowManager().
|
||||
getDefaultDisplay().getMetrics(metrics);
|
||||
mWidth = metrics.widthPixels;
|
||||
mHeight = metrics.heightPixels;
|
||||
mBufferWidth = 0;
|
||||
mBufferHeight = 0;
|
||||
|
||||
mSurfaceLock = new ReentrantLock();
|
||||
|
||||
mEditableFactory = Editable.Factory.getInstance();
|
||||
initEditable("");
|
||||
mIMEState = IME_STATE_DISABLED;
|
||||
mIMETypeHint = "";
|
||||
mIMEActionHint = "";
|
||||
}
|
||||
|
||||
protected void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
/*
|
||||
* Called on main thread
|
||||
*/
|
||||
|
||||
public String getStartupBitmapFilePath() {
|
||||
File file = new File(Environment.getExternalStorageDirectory(),
|
||||
"lastScreen.png");
|
||||
return file.toString();
|
||||
}
|
||||
|
||||
public void hideStartupBitmap() {
|
||||
Log.e(LOG_FILE_NAME, "!!! hideStartupBitmap !!!");
|
||||
if (mShowingLoadScreen == false)
|
||||
return;
|
||||
|
||||
mStartupBitmap = null;
|
||||
mShowingLoadScreen = false;
|
||||
|
||||
surfaceCreated(getHolder());
|
||||
surfaceChanged(getHolder(), mFormat, mWidth, mHeight);
|
||||
}
|
||||
|
||||
public void showStartupBitmap() {
|
||||
Log.e(LOG_FILE_NAME, "!!! showStartupBitmap !!!");
|
||||
mShowingLoadScreen = true;
|
||||
}
|
||||
|
||||
public void loadStartupBitmap() {
|
||||
// This is blocking on the main thread and that is
|
||||
// okay. we want to get this image in as soon as
|
||||
// possible so that we can paint it to the screen.
|
||||
String filePath = getStartupBitmapFilePath();
|
||||
mStartupBitmap = BitmapFactory.decodeFile(filePath);
|
||||
}
|
||||
|
||||
public void drawStartupBitmap(SurfaceHolder holder, int width, int height) {
|
||||
Log.e(LOG_FILE_NAME, "!!! drawStartupBitmap !!!");
|
||||
|
||||
Canvas c = holder.lockCanvas();
|
||||
if (c == null) {
|
||||
Log.e(LOG_FILE_NAME, "!!! NO CANVAS !!!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mStartupBitmap == null) {
|
||||
Resources res = getResources();
|
||||
Drawable drawable = res.getDrawable(R.drawable.start);
|
||||
drawable.setBounds(0, 0, width, height);
|
||||
drawable.draw(c);
|
||||
|
||||
Paint paint = new Paint();
|
||||
c.drawText("Place holder. Missing screenshot.", 10.0f, 20.0f, paint);
|
||||
} else {
|
||||
Drawable drawable = new BitmapDrawable(mStartupBitmap);
|
||||
drawable.setBounds(0, 0, width, height);
|
||||
drawable.draw(c);
|
||||
}
|
||||
holder.unlockCanvasAndPost(c);
|
||||
}
|
||||
|
||||
public void draw(SurfaceHolder holder, ByteBuffer buffer) {
|
||||
Log.e(LOG_FILE_NAME, "!!! draw1 !!!");
|
||||
|
||||
if (buffer == null || buffer.capacity() != (mWidth * mHeight * 2))
|
||||
return;
|
||||
|
||||
synchronized (mSoftwareBuffer) {
|
||||
if (buffer != mSoftwareBuffer || mSoftwareBufferCopy == null)
|
||||
return;
|
||||
|
||||
Canvas c = holder.lockCanvas();
|
||||
if (c == null)
|
||||
return;
|
||||
mSoftwareBufferCopy.copyPixelsFromBuffer(buffer);
|
||||
c.drawBitmap(mSoftwareBufferCopy, 0, 0, null);
|
||||
holder.unlockCanvasAndPost(c);
|
||||
}
|
||||
}
|
||||
|
||||
public void draw(SurfaceHolder holder, Bitmap bitmap) {
|
||||
Log.e(LOG_FILE_NAME, "!!! draw2 !!!");
|
||||
|
||||
if (bitmap == null ||
|
||||
bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight)
|
||||
return;
|
||||
|
||||
synchronized (mSoftwareBitmap) {
|
||||
if (bitmap != mSoftwareBitmap)
|
||||
return;
|
||||
|
||||
Canvas c = holder.lockCanvas();
|
||||
if (c == null)
|
||||
return;
|
||||
c.drawBitmap(bitmap, 0, 0, null);
|
||||
holder.unlockCanvasAndPost(c);
|
||||
}
|
||||
}
|
||||
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
Log.i(LOG_FILE_NAME, "!!! surfaceChanged: fmt: " + format + " dim: " + width + " " + height);
|
||||
|
||||
mFormat = format;
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
|
||||
if (mShowingLoadScreen) {
|
||||
drawStartupBitmap(holder, width, height);
|
||||
return;
|
||||
}
|
||||
|
||||
// On pre-Honeycomb, force exactly one frame of the previous size
|
||||
// to render because the surface change is only seen by GLES after we
|
||||
// have swapped the back buffer (i.e. the buffer size only changes
|
||||
// after the next swap buffer). We need to make sure Gecko's view
|
||||
// resizes when Android's buffer resizes.
|
||||
// In Honeycomb, the buffer size changes immediately, so rendering a
|
||||
// frame of the previous size is unnecessary (and wrong).
|
||||
if (mDrawMode == DRAW_GLES_2 &&
|
||||
(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)) {
|
||||
// When we get a surfaceChange event, we have 0 to n paint events
|
||||
// waiting in the Gecko event queue. We will make the first
|
||||
// succeed and the abort the others.
|
||||
mDrawSingleFrame = true;
|
||||
if (!mInDrawing) {
|
||||
// Queue at least one paint event in case none are queued.
|
||||
GeckoAppShell.scheduleRedraw();
|
||||
}
|
||||
GeckoAppShell.geckoEventSync();
|
||||
mDrawSingleFrame = false;
|
||||
mAbortDraw = false;
|
||||
}
|
||||
|
||||
mSurfaceLock.lock();
|
||||
|
||||
if (mInDrawing) {
|
||||
Log.w(LOG_FILE_NAME, "!! surfaceChanged while mInDrawing is true!");
|
||||
}
|
||||
|
||||
boolean invalidSize;
|
||||
|
||||
if (width == 0 || height == 0) {
|
||||
mSoftwareBitmap = null;
|
||||
mSoftwareBuffer = null;
|
||||
mSoftwareBufferCopy = null;
|
||||
invalidSize = true;
|
||||
} else {
|
||||
invalidSize = false;
|
||||
}
|
||||
|
||||
boolean doSyncDraw =
|
||||
mDrawMode == DRAW_2D &&
|
||||
!invalidSize &&
|
||||
GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning);
|
||||
mSyncDraw = doSyncDraw;
|
||||
|
||||
mSurfaceValid = true;
|
||||
|
||||
Log.i(LOG_FILE_NAME, "!! surfaceChanged: fmt: " + format + " dim: " + width + " " + height);
|
||||
|
||||
try {
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
||||
|
||||
GeckoEvent e = new GeckoEvent(GeckoEvent.SIZE_CHANGED, width, height,
|
||||
metrics.widthPixels, metrics.heightPixels);
|
||||
GeckoAppShell.sendEventToGecko(e);
|
||||
} finally {
|
||||
mSurfaceLock.unlock();
|
||||
}
|
||||
|
||||
if (doSyncDraw) {
|
||||
GeckoAppShell.scheduleRedraw();
|
||||
|
||||
Object syncDrawObject = null;
|
||||
try {
|
||||
syncDrawObject = mSyncDraws.take();
|
||||
} catch (InterruptedException ie) {
|
||||
Log.e(LOG_FILE_NAME, "!! Threw exception while getting sync draw bitmap/buffer: ", ie);
|
||||
}
|
||||
if (syncDrawObject != null) {
|
||||
if (syncDrawObject instanceof Bitmap)
|
||||
draw(holder, (Bitmap)syncDrawObject);
|
||||
else
|
||||
draw(holder, (ByteBuffer)syncDrawObject);
|
||||
} else {
|
||||
Log.e(LOG_FILE_NAME, "!! Synchronised draw object is null");
|
||||
}
|
||||
} else if (!mShowingLoadScreen) {
|
||||
// Make sure a frame is drawn before we return
|
||||
// otherwise we see artifacts or a black screen
|
||||
GeckoAppShell.scheduleRedraw();
|
||||
GeckoAppShell.geckoEventSync();
|
||||
}
|
||||
|
||||
// if the surface changed size and we have the soft keyboard up, make sure
|
||||
// the focused input field is still in view or it might get hidden behind the
|
||||
// keyboard and be really hard to use
|
||||
if (mIMEState == IME_STATE_ENABLED) {
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent("ScrollTo:FocusedInput", null));
|
||||
}
|
||||
}
|
||||
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
// Delay sending this event if we are painting the
|
||||
// load screen. The native access paint path will
|
||||
// paint directly to the screen and we will see a
|
||||
// black screen while content is initally being
|
||||
// drawn.
|
||||
if (!mShowingLoadScreen) {
|
||||
Log.i(LOG_FILE_NAME, "!! surface created");
|
||||
GeckoEvent e = new GeckoEvent(GeckoEvent.SURFACE_CREATED);
|
||||
GeckoAppShell.sendEventToGecko(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveLast(boolean sync) {
|
||||
Log.i(LOG_FILE_NAME, "!! save last");
|
||||
GeckoEvent event = new GeckoEvent();
|
||||
event.mType = GeckoEvent.SAVE_STATE;
|
||||
event.mCharacters = getStartupBitmapFilePath();
|
||||
if (sync)
|
||||
GeckoAppShell.sendEventToGeckoSync(event);
|
||||
else
|
||||
GeckoAppShell.sendEventToGecko(event);
|
||||
}
|
||||
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
Log.i(LOG_FILE_NAME, "!! surface destroyed");
|
||||
mSurfaceValid = false;
|
||||
mSoftwareBuffer = null;
|
||||
mSoftwareBufferCopy = null;
|
||||
mSoftwareBitmap = null;
|
||||
GeckoEvent e = new GeckoEvent(GeckoEvent.SURFACE_DESTROYED);
|
||||
if (mDrawMode == DRAW_GLES_2) {
|
||||
// Ensure GL cleanup occurs before we return.
|
||||
GeckoAppShell.sendEventToGeckoSync(e);
|
||||
} else {
|
||||
GeckoAppShell.sendEventToGecko(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Bitmap getSoftwareDrawBitmap() {
|
||||
if (mSoftwareBitmap == null ||
|
||||
mSoftwareBitmap.getHeight() != mHeight ||
|
||||
mSoftwareBitmap.getWidth() != mWidth) {
|
||||
mSoftwareBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.RGB_565);
|
||||
}
|
||||
|
||||
mDrawMode = DRAW_2D;
|
||||
return mSoftwareBitmap;
|
||||
}
|
||||
|
||||
public ByteBuffer getSoftwareDrawBuffer() {
|
||||
// We store pixels in 565 format, so two bytes per pixel (explaining
|
||||
// the * 2 in the following check/allocation)
|
||||
if (mSoftwareBuffer == null ||
|
||||
mSoftwareBuffer.capacity() != (mWidth * mHeight * 2)) {
|
||||
mSoftwareBuffer = ByteBuffer.allocateDirect(mWidth * mHeight * 2);
|
||||
}
|
||||
|
||||
if (mSoftwareBufferCopy == null ||
|
||||
mSoftwareBufferCopy.getHeight() != mHeight ||
|
||||
mSoftwareBufferCopy.getWidth() != mWidth) {
|
||||
mSoftwareBufferCopy = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.RGB_565);
|
||||
}
|
||||
|
||||
mDrawMode = DRAW_2D;
|
||||
return mSoftwareBuffer;
|
||||
}
|
||||
|
||||
public Surface getSurface() {
|
||||
return getHolder().getSurface();
|
||||
}
|
||||
|
||||
/*
|
||||
* Called on Gecko thread
|
||||
*/
|
||||
|
||||
public static final int DRAW_ERROR = 0;
|
||||
public static final int DRAW_GLES_2 = 1;
|
||||
public static final int DRAW_2D = 2;
|
||||
// Drawing is disable when the surface buffer
|
||||
// has changed size but we haven't yet processed the
|
||||
// resize event.
|
||||
public static final int DRAW_DISABLED = 3;
|
||||
|
||||
public int beginDrawing() {
|
||||
|
||||
if (mInDrawing) {
|
||||
Log.e(LOG_FILE_NAME, "!! Recursive beginDrawing call!");
|
||||
return DRAW_ERROR;
|
||||
}
|
||||
|
||||
// Once we drawn our first frame after resize we can ignore
|
||||
// the other draw events until we handle the resize events.
|
||||
if (mAbortDraw) {
|
||||
return DRAW_DISABLED;
|
||||
}
|
||||
|
||||
/* Grab the lock, which we'll hold while we're drawing.
|
||||
* It gets released in endDrawing(), and is also used in surfaceChanged
|
||||
* to make sure that we don't change our surface details while
|
||||
* we're in the middle of drawing (and especially in the middle of
|
||||
* executing beginDrawing/endDrawing).
|
||||
*
|
||||
* We might not need to hold this lock in between
|
||||
* beginDrawing/endDrawing, and might just be able to make
|
||||
* surfaceChanged, beginDrawing, and endDrawing synchronized,
|
||||
* but this way is safer for now.
|
||||
*/
|
||||
mSurfaceLock.lock();
|
||||
|
||||
if (!mSurfaceValid) {
|
||||
Log.e(LOG_FILE_NAME, "!! Surface not valid");
|
||||
mSurfaceLock.unlock();
|
||||
return DRAW_ERROR;
|
||||
}
|
||||
|
||||
mInDrawing = true;
|
||||
mDrawMode = DRAW_GLES_2;
|
||||
return DRAW_GLES_2;
|
||||
}
|
||||
|
||||
public void endDrawing() {
|
||||
if (!mInDrawing) {
|
||||
Log.e(LOG_FILE_NAME, "!! endDrawing without beginDrawing!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mDrawSingleFrame)
|
||||
mAbortDraw = true;
|
||||
|
||||
try {
|
||||
if (!mSurfaceValid) {
|
||||
Log.e(LOG_FILE_NAME, "!! endDrawing with false mSurfaceValid");
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
mInDrawing = false;
|
||||
|
||||
if (!mSurfaceLock.isHeldByCurrentThread())
|
||||
Log.e(LOG_FILE_NAME, "!! endDrawing while mSurfaceLock not held by current thread!");
|
||||
|
||||
mSurfaceLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/* How this works:
|
||||
* Whenever we want to draw, we want to be sure that we do not lock
|
||||
* the canvas unless we're sure we can draw. Locking the canvas clears
|
||||
* the canvas to black in most cases, causing a black flash.
|
||||
* At the same time, the surface can resize/disappear at any moment
|
||||
* unless the canvas is locked.
|
||||
* Draws originate from a different thread so the surface could change
|
||||
* at any moment while we try to draw until we lock the canvas.
|
||||
*
|
||||
* Also, never try to lock the canvas while holding the surface lock
|
||||
* unless you're in SurfaceChanged, in which case the canvas was already
|
||||
* locked. Surface lock -> Canvas lock will lead to AB-BA deadlocks.
|
||||
*/
|
||||
public void draw2D(Bitmap bitmap, int width, int height) {
|
||||
// mSurfaceLock ensures that we get mSyncDraw/mSoftwareBitmap/etc.
|
||||
// set correctly before determining whether we should do a sync draw
|
||||
Log.e(LOG_FILE_NAME, "!!! draw2d1 !!!");
|
||||
mSurfaceLock.lock();
|
||||
try {
|
||||
if (mSyncDraw) {
|
||||
if (bitmap != mSoftwareBitmap || width != mWidth || height != mHeight)
|
||||
return;
|
||||
mSyncDraw = false;
|
||||
try {
|
||||
mSyncDraws.put(bitmap);
|
||||
} catch (InterruptedException ie) {
|
||||
Log.e(LOG_FILE_NAME, "!! Threw exception while getting sync draws queue: ", ie);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
mSurfaceLock.unlock();
|
||||
}
|
||||
|
||||
draw(getHolder(), bitmap);
|
||||
}
|
||||
|
||||
public void draw2D(ByteBuffer buffer, int stride) {
|
||||
Log.e(LOG_FILE_NAME, "!!! draw2d2 !!!");
|
||||
mSurfaceLock.lock();
|
||||
try {
|
||||
if (mSyncDraw) {
|
||||
if (buffer != mSoftwareBuffer || stride != (mWidth * 2))
|
||||
return;
|
||||
mSyncDraw = false;
|
||||
try {
|
||||
mSyncDraws.put(buffer);
|
||||
} catch (InterruptedException ie) {
|
||||
Log.e(LOG_FILE_NAME, "!! Threw exception while getting sync bitmaps queue: ", ie);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
mSurfaceLock.unlock();
|
||||
}
|
||||
|
||||
draw(getHolder(), buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCheckIsTextEditor () {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
||||
outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
|
||||
outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE;
|
||||
outAttrs.actionLabel = null;
|
||||
mKeyListener = TextKeyListener.getInstance();
|
||||
|
||||
if (mIMEState == IME_STATE_PASSWORD)
|
||||
outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_PASSWORD;
|
||||
else if (mIMETypeHint.equalsIgnoreCase("url"))
|
||||
outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_URI;
|
||||
else if (mIMETypeHint.equalsIgnoreCase("email"))
|
||||
outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
|
||||
else if (mIMETypeHint.equalsIgnoreCase("search"))
|
||||
outAttrs.imeOptions = EditorInfo.IME_ACTION_SEARCH;
|
||||
else if (mIMETypeHint.equalsIgnoreCase("tel"))
|
||||
outAttrs.inputType = InputType.TYPE_CLASS_PHONE;
|
||||
else if (mIMETypeHint.equalsIgnoreCase("number") ||
|
||||
mIMETypeHint.equalsIgnoreCase("range"))
|
||||
outAttrs.inputType = InputType.TYPE_CLASS_NUMBER;
|
||||
else if (mIMETypeHint.equalsIgnoreCase("datetime") ||
|
||||
mIMETypeHint.equalsIgnoreCase("datetime-local"))
|
||||
outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
|
||||
InputType.TYPE_DATETIME_VARIATION_NORMAL;
|
||||
else if (mIMETypeHint.equalsIgnoreCase("date"))
|
||||
outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
|
||||
InputType.TYPE_DATETIME_VARIATION_DATE;
|
||||
else if (mIMETypeHint.equalsIgnoreCase("time"))
|
||||
outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
|
||||
InputType.TYPE_DATETIME_VARIATION_TIME;
|
||||
|
||||
if (mIMEActionHint.equalsIgnoreCase("go"))
|
||||
outAttrs.imeOptions = EditorInfo.IME_ACTION_GO;
|
||||
else if (mIMEActionHint.equalsIgnoreCase("done"))
|
||||
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
|
||||
else if (mIMEActionHint.equalsIgnoreCase("next"))
|
||||
outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT;
|
||||
else if (mIMEActionHint.equalsIgnoreCase("search"))
|
||||
outAttrs.imeOptions = EditorInfo.IME_ACTION_SEARCH;
|
||||
else if (mIMEActionHint.equalsIgnoreCase("send"))
|
||||
outAttrs.imeOptions = EditorInfo.IME_ACTION_SEND;
|
||||
else if (mIMEActionHint != null && mIMEActionHint.length() != 0)
|
||||
outAttrs.actionLabel = mIMEActionHint;
|
||||
|
||||
if (mIMELandscapeFS == false)
|
||||
outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI;
|
||||
|
||||
inputConnection.reset();
|
||||
return inputConnection;
|
||||
}
|
||||
|
||||
public void setEditable(String contents)
|
||||
{
|
||||
mEditable.removeSpan(inputConnection);
|
||||
mEditable.replace(0, mEditable.length(), contents);
|
||||
mEditable.setSpan(inputConnection, 0, contents.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
Selection.setSelection(mEditable, contents.length());
|
||||
}
|
||||
|
||||
public void initEditable(String contents)
|
||||
{
|
||||
mEditable = mEditableFactory.newEditable(contents);
|
||||
mEditable.setSpan(inputConnection, 0, contents.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
Selection.setSelection(mEditable, contents.length());
|
||||
}
|
||||
|
||||
// accelerometer
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy)
|
||||
{
|
||||
}
|
||||
|
||||
public void onSensorChanged(SensorEvent event)
|
||||
{
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
|
||||
}
|
||||
|
||||
private class GeocoderTask extends AsyncTask<Location, Void, Void> {
|
||||
protected Void doInBackground(Location... location) {
|
||||
try {
|
||||
List<Address> addresses = mGeocoder.getFromLocation(location[0].getLatitude(),
|
||||
location[0].getLongitude(), 1);
|
||||
// grab the first address. in the future,
|
||||
// may want to expose multiple, or filter
|
||||
// for best.
|
||||
mLastGeoAddress = addresses.get(0);
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(location[0], mLastGeoAddress));
|
||||
} catch (Exception e) {
|
||||
Log.w(LOG_FILE_NAME, "GeocoderTask "+e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// geolocation
|
||||
public void onLocationChanged(Location location)
|
||||
{
|
||||
if (mGeocoder == null)
|
||||
mGeocoder = new Geocoder(getContext(), Locale.getDefault());
|
||||
|
||||
if (mLastGeoAddress == null) {
|
||||
new GeocoderTask().execute(location);
|
||||
}
|
||||
else {
|
||||
float[] results = new float[1];
|
||||
Location.distanceBetween(location.getLatitude(),
|
||||
location.getLongitude(),
|
||||
mLastGeoAddress.getLatitude(),
|
||||
mLastGeoAddress.getLongitude(),
|
||||
results);
|
||||
// pfm value. don't want to slam the
|
||||
// geocoder with very similar values, so
|
||||
// only call after about 100m
|
||||
if (results[0] > 100)
|
||||
new GeocoderTask().execute(location);
|
||||
}
|
||||
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(location, mLastGeoAddress));
|
||||
}
|
||||
|
||||
public void onProviderDisabled(String provider)
|
||||
{
|
||||
}
|
||||
|
||||
public void onProviderEnabled(String provider)
|
||||
{
|
||||
}
|
||||
|
||||
public void onStatusChanged(String provider, int status, Bundle extras)
|
||||
{
|
||||
}
|
||||
|
||||
// event stuff
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
requestFocus(FOCUS_UP, null);
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
|
||||
return gestureScanner.onTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
|
||||
if (event.isSystem())
|
||||
return super.onKeyPreIme(keyCode, event);
|
||||
|
||||
switch (event.getAction()) {
|
||||
case KeyEvent.ACTION_DOWN:
|
||||
return processKeyDown(keyCode, event, true);
|
||||
case KeyEvent.ACTION_UP:
|
||||
return processKeyUp(keyCode, event, true);
|
||||
case KeyEvent.ACTION_MULTIPLE:
|
||||
return onKeyMultiple(keyCode, event.getRepeatCount(), event);
|
||||
}
|
||||
return super.onKeyPreIme(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
return processKeyDown(keyCode, event, false);
|
||||
}
|
||||
|
||||
private boolean processKeyDown(int keyCode, KeyEvent event, boolean isPreIme) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
case KeyEvent.KEYCODE_VOLUME_UP:
|
||||
case KeyEvent.KEYCODE_VOLUME_DOWN:
|
||||
case KeyEvent.KEYCODE_SEARCH:
|
||||
return false;
|
||||
case KeyEvent.KEYCODE_DEL:
|
||||
// See comments in GeckoInputConnection.onKeyDel
|
||||
if (inputConnection != null &&
|
||||
inputConnection.onKeyDel()) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case KeyEvent.KEYCODE_ENTER:
|
||||
if ((event.getFlags() & KeyEvent.FLAG_EDITOR_ACTION) != 0 &&
|
||||
mIMEActionHint.equalsIgnoreCase("next"))
|
||||
event = new KeyEvent(event.getAction(), KeyEvent.KEYCODE_TAB);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (isPreIme && mIMEState != IME_STATE_DISABLED &&
|
||||
(event.getMetaState() & KeyEvent.META_ALT_ON) == 0)
|
||||
// Let active IME process pre-IME key events
|
||||
return false;
|
||||
|
||||
// KeyListener returns true if it handled the event for us.
|
||||
if (mIMEState == IME_STATE_DISABLED ||
|
||||
keyCode == KeyEvent.KEYCODE_ENTER ||
|
||||
keyCode == KeyEvent.KEYCODE_DEL ||
|
||||
(event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 ||
|
||||
!mKeyListener.onKeyDown(this, mEditable, keyCode, event))
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
return processKeyUp(keyCode, event, false);
|
||||
}
|
||||
|
||||
private boolean processKeyUp(int keyCode, KeyEvent event, boolean isPreIme) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
case KeyEvent.KEYCODE_SEARCH:
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (isPreIme && mIMEState != IME_STATE_DISABLED &&
|
||||
(event.getMetaState() & KeyEvent.META_ALT_ON) == 0)
|
||||
// Let active IME process pre-IME key events
|
||||
return false;
|
||||
|
||||
if (mIMEState == IME_STATE_DISABLED ||
|
||||
keyCode == KeyEvent.KEYCODE_ENTER ||
|
||||
keyCode == KeyEvent.KEYCODE_DEL ||
|
||||
(event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 ||
|
||||
!mKeyListener.onKeyUp(this, mEditable, keyCode, event))
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
InputMethodManager imm = (InputMethodManager)
|
||||
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.toggleSoftInputFromWindow(getWindowToken(),
|
||||
imm.SHOW_FORCED, 0);
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is this surface valid for drawing into?
|
||||
boolean mSurfaceValid;
|
||||
|
||||
// Are we actively between beginDrawing/endDrawing?
|
||||
boolean mInDrawing;
|
||||
|
||||
// Used to finish the current buffer before changing the surface size
|
||||
boolean mDrawSingleFrame = false;
|
||||
boolean mAbortDraw = false;
|
||||
|
||||
// Are we waiting for a buffer to draw in surfaceChanged?
|
||||
boolean mSyncDraw;
|
||||
|
||||
// True if gecko requests a buffer
|
||||
int mDrawMode;
|
||||
|
||||
static boolean mShowingLoadScreen = true;
|
||||
|
||||
// let's not change stuff around while we're in the middle of
|
||||
// starting drawing, ending drawing, or changing surface
|
||||
// characteristics
|
||||
ReentrantLock mSurfaceLock;
|
||||
|
||||
// Surface format, from surfaceChanged. Largely
|
||||
// useless.
|
||||
int mFormat;
|
||||
|
||||
// the dimensions of the surface
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
|
||||
// the dimensions of the buffer we're using for drawing,
|
||||
// that is the software buffer or the EGLSurface
|
||||
int mBufferWidth;
|
||||
int mBufferHeight;
|
||||
|
||||
// IME stuff
|
||||
public static final int IME_STATE_DISABLED = 0;
|
||||
public static final int IME_STATE_ENABLED = 1;
|
||||
public static final int IME_STATE_PASSWORD = 2;
|
||||
public static final int IME_STATE_PLUGIN = 3;
|
||||
|
||||
GeckoInputConnection inputConnection;
|
||||
GeckoGestureDetector gestureScanner;
|
||||
KeyListener mKeyListener;
|
||||
Editable mEditable;
|
||||
Editable.Factory mEditableFactory;
|
||||
int mIMEState;
|
||||
String mIMETypeHint;
|
||||
String mIMEActionHint;
|
||||
boolean mIMELandscapeFS;
|
||||
|
||||
// Software rendering
|
||||
Bitmap mSoftwareBitmap;
|
||||
ByteBuffer mSoftwareBuffer;
|
||||
Bitmap mSoftwareBufferCopy;
|
||||
Bitmap mStartupBitmap;
|
||||
|
||||
Geocoder mGeocoder;
|
||||
Address mLastGeoAddress;
|
||||
|
||||
final SynchronousQueue<Object> mSyncDraws = new SynchronousQueue<Object>();
|
||||
}
|
||||
|
@ -61,6 +61,8 @@ JAVAFILES = \
|
||||
GeckoEventListener.java \
|
||||
GeckoInputConnection.java \
|
||||
GeckoPreferences.java \
|
||||
GeckoSurfaceView.java \
|
||||
GeckoGestureDetector.java \
|
||||
GlobalHistory.java \
|
||||
PromptService.java \
|
||||
SurfaceInfo.java \
|
||||
@ -68,27 +70,6 @@ JAVAFILES = \
|
||||
Tabs.java \
|
||||
TabsTray.java \
|
||||
GeckoBatteryManager.java \
|
||||
gfx/BufferedCairoImage.java \
|
||||
gfx/CairoImage.java \
|
||||
gfx/CairoUtils.java \
|
||||
gfx/GeckoSoftwareLayerClient.java \
|
||||
gfx/InputConnectionHandler.java \
|
||||
gfx/IntPoint.java \
|
||||
gfx/IntRect.java \
|
||||
gfx/IntSize.java \
|
||||
gfx/Layer.java \
|
||||
gfx/LayerClient.java \
|
||||
gfx/LayerController.java \
|
||||
gfx/LayerRenderer.java \
|
||||
gfx/LayerView.java \
|
||||
gfx/NinePatchTileLayer.java \
|
||||
gfx/PlaceholderLayerClient.java \
|
||||
gfx/SingleTileLayer.java \
|
||||
gfx/TextureReaper.java \
|
||||
gfx/TextLayer.java \
|
||||
gfx/TileLayer.java \
|
||||
ui/PanZoomController.java \
|
||||
ui/ViewportController.java \
|
||||
$(NULL)
|
||||
|
||||
PROCESSEDJAVAFILES = \
|
||||
@ -251,9 +232,6 @@ MOZ_ANDROID_DRAWABLES += embedding/android/resources/drawable/addons.png
|
||||
embedding/android/resources/drawable/tabs_plus.png \
|
||||
embedding/android/resources/drawable/tabs_menu.png \
|
||||
embedding/android/resources/drawable/tabs_tray_bg.9.png \
|
||||
embedding/android/resources/drawable/checkerboard.png \
|
||||
embedding/android/resources/drawable/shadow.png \
|
||||
embedding/android/resources/drawable/pattern.png \
|
||||
$(NULL)
|
||||
|
||||
|
||||
|
@ -1,73 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.gfx;
|
||||
|
||||
import org.mozilla.fennec.gfx.CairoImage;
|
||||
import org.mozilla.fennec.gfx.CairoUtils;
|
||||
import android.graphics.Bitmap;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/** A Cairo image that simply saves a buffer of pixel data. */
|
||||
public class BufferedCairoImage extends CairoImage {
|
||||
private ByteBuffer mBuffer;
|
||||
private int mWidth, mHeight, mFormat;
|
||||
|
||||
/** Creates a buffered Cairo image from a byte buffer. */
|
||||
public BufferedCairoImage(ByteBuffer inBuffer, int inWidth, int inHeight, int inFormat) {
|
||||
mBuffer = inBuffer; mWidth = inWidth; mHeight = inHeight; mFormat = inFormat;
|
||||
}
|
||||
|
||||
/** Creates a buffered Cairo image from an Android bitmap. */
|
||||
public BufferedCairoImage(Bitmap bitmap) {
|
||||
mFormat = CairoUtils.bitmapConfigToCairoFormat(bitmap.getConfig());
|
||||
mWidth = bitmap.getWidth();
|
||||
mHeight = bitmap.getHeight();
|
||||
mBuffer = ByteBuffer.allocateDirect(mWidth * mHeight * 4);
|
||||
bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer lockBuffer() { return mBuffer; }
|
||||
@Override
|
||||
public int getWidth() { return mWidth; }
|
||||
@Override
|
||||
public int getHeight() { return mHeight; }
|
||||
@Override
|
||||
public int getFormat() { return mFormat; }
|
||||
}
|
||||
|
@ -1,60 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.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 lockBuffer();
|
||||
public void unlockBuffer() { /* By default, a no-op. */ }
|
||||
|
||||
public abstract int getWidth();
|
||||
public abstract int getHeight();
|
||||
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;
|
||||
}
|
||||
|
@ -1,120 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.gfx;
|
||||
|
||||
import org.mozilla.fennec.gfx.CairoImage;
|
||||
import android.graphics.Bitmap;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
/**
|
||||
* Utility methods useful when displaying Cairo bitmaps using OpenGL ES.
|
||||
*/
|
||||
public class CairoUtils {
|
||||
private CairoUtils() { /* Don't call me. */ }
|
||||
|
||||
public static int cairoFormatToGLInternalFormat(int cairoFormat) {
|
||||
switch (cairoFormat) {
|
||||
case CairoImage.FORMAT_ARGB32:
|
||||
return GL10.GL_RGBA;
|
||||
case CairoImage.FORMAT_RGB24:
|
||||
case CairoImage.FORMAT_RGB16_565:
|
||||
return GL10.GL_RGB;
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
public static int cairoFormatToGLFormat(int cairoFormat) {
|
||||
switch (cairoFormat) {
|
||||
case CairoImage.FORMAT_ARGB32:
|
||||
return GL10.GL_RGBA;
|
||||
case CairoImage.FORMAT_RGB24:
|
||||
case CairoImage.FORMAT_RGB16_565:
|
||||
return GL10.GL_RGB;
|
||||
case CairoImage.FORMAT_A8:
|
||||
case CairoImage.FORMAT_A1:
|
||||
return GL10.GL_ALPHA;
|
||||
default:
|
||||
throw new RuntimeException("Unknown Cairo format");
|
||||
}
|
||||
}
|
||||
|
||||
public static int cairoFormatToGLType(int cairoFormat) {
|
||||
switch (cairoFormat) {
|
||||
case CairoImage.FORMAT_ARGB32:
|
||||
case CairoImage.FORMAT_RGB24:
|
||||
case CairoImage.FORMAT_A8:
|
||||
return GL10.GL_UNSIGNED_BYTE;
|
||||
case CairoImage.FORMAT_A1:
|
||||
throw new RuntimeException("Cairo FORMAT_A1 unsupported in Android OpenGL");
|
||||
case CairoImage.FORMAT_RGB16_565:
|
||||
return GL10.GL_UNSIGNED_SHORT_5_6_5;
|
||||
default:
|
||||
throw new RuntimeException("Unknown Cairo format");
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,268 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.gfx;
|
||||
|
||||
import org.mozilla.fennec.gfx.CairoImage;
|
||||
import org.mozilla.fennec.gfx.IntRect;
|
||||
import org.mozilla.fennec.gfx.IntSize;
|
||||
import org.mozilla.fennec.gfx.LayerClient;
|
||||
import org.mozilla.fennec.gfx.LayerController;
|
||||
import org.mozilla.fennec.gfx.LayerRenderer;
|
||||
import org.mozilla.fennec.gfx.SingleTileLayer;
|
||||
import org.mozilla.fennec.ui.ViewportController;
|
||||
import org.mozilla.gecko.GeckoApp;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.util.Log;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* Transfers a software-rendered Gecko to an ImageLayer so that it can be rendered by our
|
||||
* compositor.
|
||||
*
|
||||
* TODO: Throttle down Gecko's priority when we pan and zoom.
|
||||
*/
|
||||
public class GeckoSoftwareLayerClient extends LayerClient {
|
||||
private Context mContext;
|
||||
private int mWidth, mHeight, mFormat;
|
||||
private ByteBuffer mBuffer;
|
||||
private Semaphore mBufferSemaphore;
|
||||
private SingleTileLayer mTileLayer;
|
||||
private ViewportController mViewportController;
|
||||
|
||||
private IntRect mGeckoVisibleRect; /* The viewport rect that Gecko is currently displaying. */
|
||||
|
||||
private IntRect mJSPanningToRect;
|
||||
/* The rect that we just told chrome JavaScript to pan to. */
|
||||
|
||||
private boolean mWaitingForJSPanZoom;
|
||||
/* This will be set to true if we are waiting on the chrome JavaScript to finish panning or
|
||||
* zooming before we can render. */
|
||||
|
||||
private CairoImage mCairoImage;
|
||||
|
||||
/* The initial page width and height that we use before a page is loaded. */
|
||||
private static final int PAGE_WIDTH = 980; /* Matches MobileSafari. */
|
||||
private static final int PAGE_HEIGHT = 1500;
|
||||
|
||||
public GeckoSoftwareLayerClient(Context context) {
|
||||
mContext = context;
|
||||
|
||||
mViewportController = new ViewportController(new IntSize(PAGE_WIDTH, PAGE_HEIGHT),
|
||||
new IntRect(0, 0, 1, 1));
|
||||
|
||||
mWidth = LayerController.TILE_WIDTH;
|
||||
mHeight = LayerController.TILE_HEIGHT;
|
||||
mFormat = CairoImage.FORMAT_RGB16_565;
|
||||
|
||||
mBuffer = ByteBuffer.allocateDirect(mWidth * mHeight * 2);
|
||||
mBufferSemaphore = new Semaphore(1);
|
||||
|
||||
mWaitingForJSPanZoom = false;
|
||||
|
||||
mCairoImage = new CairoImage() {
|
||||
@Override
|
||||
public ByteBuffer lockBuffer() {
|
||||
try {
|
||||
mBufferSemaphore.acquire();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return mBuffer;
|
||||
}
|
||||
@Override
|
||||
public void unlockBuffer() {
|
||||
mBufferSemaphore.release();
|
||||
}
|
||||
@Override
|
||||
public int getWidth() { return mWidth; }
|
||||
@Override
|
||||
public int getHeight() { return mHeight; }
|
||||
@Override
|
||||
public int getFormat() { return mFormat; }
|
||||
};
|
||||
|
||||
mTileLayer = new SingleTileLayer();
|
||||
}
|
||||
|
||||
/** Attaches the root layer to the layer controller so that Gecko appears. */
|
||||
@Override
|
||||
public void init() {
|
||||
getLayerController().setRoot(mTileLayer);
|
||||
}
|
||||
|
||||
public void beginDrawing() {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Would be cleaner if this took an android.graphics.Rect instead, but that would require
|
||||
* a little more JNI magic.
|
||||
*/
|
||||
public void endDrawing(int x, int y, int width, int height) {
|
||||
LayerController controller = getLayerController();
|
||||
//controller.unzoom(); /* FIXME */
|
||||
controller.notifyViewOfGeometryChange();
|
||||
|
||||
mViewportController.setVisibleRect(mGeckoVisibleRect);
|
||||
|
||||
if (mGeckoVisibleRect != null) {
|
||||
IntRect layerRect = mViewportController.untransformVisibleRect(mGeckoVisibleRect,
|
||||
getPageSize());
|
||||
mTileLayer.origin = layerRect.getOrigin();
|
||||
}
|
||||
|
||||
repaint(new IntRect(x, y, width, height));
|
||||
}
|
||||
|
||||
private void repaint(IntRect rect) {
|
||||
mTileLayer.paintSubimage(mCairoImage, rect);
|
||||
}
|
||||
|
||||
/** Called whenever the chrome JS finishes panning or zooming to some location. */
|
||||
public void jsPanZoomCompleted(IntRect rect) {
|
||||
mGeckoVisibleRect = rect;
|
||||
if (mWaitingForJSPanZoom)
|
||||
render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires a lock on the back buffer and returns it, blocking until it's unlocked. This
|
||||
* function is for Gecko to use.
|
||||
*/
|
||||
public ByteBuffer lockBuffer() {
|
||||
try {
|
||||
mBufferSemaphore.acquire();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the lock on the back buffer. After this call, it is forbidden for Gecko to touch
|
||||
* the buffer. This function is, again, for Gecko to use.
|
||||
*/
|
||||
public void unlockBuffer() {
|
||||
mBufferSemaphore.release();
|
||||
}
|
||||
|
||||
/** Called whenever the page changes size. */
|
||||
public void setPageSize(IntSize pageSize) {
|
||||
Log.e("Fennec", "### Setting page size to " + pageSize);
|
||||
mViewportController.setPageSize(pageSize);
|
||||
getLayerController().setPageSize(pageSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void geometryChanged() {
|
||||
mViewportController.setVisibleRect(getTransformedVisibleRect());
|
||||
render();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntSize getPageSize() { return mViewportController.getPageSize(); }
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
LayerController layerController = getLayerController();
|
||||
IntRect visibleRect = layerController.getVisibleRect();
|
||||
IntRect tileRect = mViewportController.widenRect(visibleRect);
|
||||
tileRect = mViewportController.clampRect(tileRect);
|
||||
|
||||
IntSize pageSize = layerController.getPageSize();
|
||||
IntRect viewportRect = mViewportController.transformVisibleRect(tileRect, pageSize);
|
||||
|
||||
/* Prevent null pointer exceptions at the start. */
|
||||
if (mGeckoVisibleRect == null)
|
||||
mGeckoVisibleRect = viewportRect;
|
||||
|
||||
if (!getLayerController().getRedrawHint())
|
||||
return;
|
||||
|
||||
/* If Gecko's visible rect is the same as our visible rect, then we can actually kick off a
|
||||
* draw event. */
|
||||
if (mGeckoVisibleRect.equals(viewportRect)) {
|
||||
mWaitingForJSPanZoom = false;
|
||||
mJSPanningToRect = null;
|
||||
GeckoAppShell.scheduleRedraw();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Otherwise, we need to get Gecko's visible rect equal to our visible rect before we can
|
||||
* safely draw. If we're just waiting for chrome JavaScript to catch up, we do nothing.
|
||||
* This check avoids bombarding the chrome JavaScript with messages. */
|
||||
if (mWaitingForJSPanZoom && mJSPanningToRect != null &&
|
||||
mJSPanningToRect.equals(viewportRect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* We send Gecko a message telling it to move its visible rect to the appropriate spot and
|
||||
* set a flag to remind us to try the redraw again. */
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent("PanZoom:PanZoom",
|
||||
"{\"x\": " + viewportRect.x +
|
||||
", \"y\": " + viewportRect.y +
|
||||
", \"width\": " + LayerController.TILE_WIDTH +
|
||||
", \"height\": " + LayerController.TILE_HEIGHT +
|
||||
", \"zoomFactor\": " + getZoomFactor() + "}"));
|
||||
|
||||
mWaitingForJSPanZoom = true;
|
||||
mJSPanningToRect = viewportRect;
|
||||
}
|
||||
|
||||
/* Returns the dimensions of the box in page coordinates that the user is viewing. */
|
||||
private IntRect getTransformedVisibleRect() {
|
||||
LayerController layerController = getLayerController();
|
||||
return mViewportController.transformVisibleRect(layerController.getVisibleRect(),
|
||||
layerController.getPageSize());
|
||||
}
|
||||
|
||||
private float getZoomFactor() {
|
||||
return 1.0f; // FIXME
|
||||
/*LayerController layerController = getLayerController();
|
||||
return mViewportController.getZoomFactor(layerController.getVisibleRect(),
|
||||
layerController.getPageSize(),
|
||||
layerController.getScreenSize());*/
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +0,0 @@
|
||||
package org.mozilla.fennec.gfx;
|
||||
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
public interface InputConnectionHandler
|
||||
{
|
||||
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);
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.gfx;
|
||||
|
||||
public class IntPoint {
|
||||
public final int x, y;
|
||||
|
||||
public IntPoint(int inX, int inY) { x = inX; y = inY; }
|
||||
|
||||
@Override
|
||||
public String toString() { return "(" + x + ", " + y + ")"; }
|
||||
|
||||
/** Returns the result of adding the given point to this point. */
|
||||
public IntPoint add(IntPoint other) { return new IntPoint(x + other.x, y + other.y); }
|
||||
|
||||
/** Returns the result of subtracting the given point from this point. */
|
||||
public IntPoint subtract(IntPoint other) { return new IntPoint(x - other.x, y - other.y); }
|
||||
|
||||
/** Returns the result of multiplying both components by the given scalar. */
|
||||
public IntPoint scale(float scale) {
|
||||
return new IntPoint((int)Math.round((float)x * scale), (int)Math.round((float)y * scale));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,108 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.gfx;
|
||||
|
||||
import org.mozilla.fennec.gfx.IntPoint;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class IntRect implements Cloneable {
|
||||
public final int x, y, width, height;
|
||||
|
||||
public IntRect(int inX, int inY, int inWidth, int inHeight) {
|
||||
x = inX; y = inY; width = inWidth; height = inHeight;
|
||||
}
|
||||
|
||||
public IntRect(JSONObject json) {
|
||||
try {
|
||||
x = json.getInt("x");
|
||||
y = json.getInt("y");
|
||||
width = json.getInt("width");
|
||||
height = json.getInt("height");
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() { return new IntRect(x, y, width, height); }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof IntRect))
|
||||
return false;
|
||||
IntRect otherRect = (IntRect)other;
|
||||
return x == otherRect.x && y == otherRect.y && width == otherRect.width &&
|
||||
height == otherRect.height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() { return "(" + x + "," + y + "," + width + "," + height + ")"; }
|
||||
|
||||
public IntPoint getOrigin() { return new IntPoint(x, y); }
|
||||
public IntPoint getCenter() { return new IntPoint(x + width / 2, y + height / 2); }
|
||||
|
||||
public int getRight() { return x + width; }
|
||||
public int getBottom() { return y + height; }
|
||||
|
||||
/** Scales all four dimensions of this rectangle by the given factor. */
|
||||
public IntRect scaleAll(float factor) {
|
||||
return new IntRect((int)Math.round(x * factor),
|
||||
(int)Math.round(y * factor),
|
||||
(int)Math.round(width * factor),
|
||||
(int)Math.round(height * factor));
|
||||
}
|
||||
|
||||
/** Returns true if and only if the given rectangle is fully enclosed within this one. */
|
||||
public boolean contains(IntRect other) {
|
||||
return x <= other.x && y <= other.y &&
|
||||
getRight() >= other.getRight() &&
|
||||
getBottom() >= other.getBottom();
|
||||
}
|
||||
|
||||
/** Returns the intersection of this rectangle with another rectangle. */
|
||||
public IntRect intersect(IntRect other) {
|
||||
int left = Math.max(x, other.x);
|
||||
int top = Math.max(y, other.y);
|
||||
int right = Math.min(getRight(), other.getRight());
|
||||
int bottom = Math.min(getBottom(), other.getBottom());
|
||||
return new IntRect(left, top, Math.max(right - left, 0), Math.max(bottom - top, 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,65 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.gfx;
|
||||
|
||||
import org.mozilla.fennec.gfx.IntPoint;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
public abstract class Layer {
|
||||
public IntPoint origin;
|
||||
|
||||
public Layer() {
|
||||
origin = new IntPoint(0, 0);
|
||||
}
|
||||
|
||||
/** Draws the layer. Automatically applies the translation. */
|
||||
public final void draw(GL10 gl) {
|
||||
gl.glPushMatrix();
|
||||
gl.glTranslatef(origin.x, origin.y, 0.0f);
|
||||
onDraw(gl);
|
||||
gl.glPopMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses implement this method to perform drawing.
|
||||
*
|
||||
* Invariant: The current matrix mode must be GL_MODELVIEW both before and after this call.
|
||||
*/
|
||||
protected abstract void onDraw(GL10 gl);
|
||||
}
|
||||
|
@ -1,69 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.gfx;
|
||||
|
||||
import org.mozilla.fennec.gfx.IntRect;
|
||||
import org.mozilla.fennec.gfx.IntSize;
|
||||
import org.mozilla.fennec.gfx.LayerController;
|
||||
|
||||
/**
|
||||
* A layer client provides tiles and manages other information used by the layer controller.
|
||||
*/
|
||||
public abstract class LayerClient {
|
||||
private LayerController mLayerController;
|
||||
protected float mZoomFactor;
|
||||
|
||||
public abstract void geometryChanged();
|
||||
public abstract IntSize getPageSize();
|
||||
|
||||
/** Called whenever the page changes size. */
|
||||
public abstract void setPageSize(IntSize pageSize);
|
||||
|
||||
public abstract void init();
|
||||
protected abstract void render();
|
||||
|
||||
public LayerClient() {
|
||||
mZoomFactor = 1.0f;
|
||||
}
|
||||
|
||||
public LayerController getLayerController() { return mLayerController; }
|
||||
public void setLayerController(LayerController layerController) {
|
||||
mLayerController = layerController;
|
||||
}
|
||||
}
|
||||
|
@ -1,279 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.gfx;
|
||||
|
||||
import org.mozilla.fennec.gfx.IntRect;
|
||||
import org.mozilla.fennec.gfx.IntSize;
|
||||
import org.mozilla.fennec.gfx.Layer;
|
||||
import org.mozilla.fennec.gfx.LayerClient;
|
||||
import org.mozilla.fennec.gfx.LayerView;
|
||||
import org.mozilla.fennec.ui.PanZoomController;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.ScaleGestureDetector;
|
||||
import android.view.View.OnTouchListener;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* The layer controller manages a tile that represents the visible page. It does panning and
|
||||
* zooming natively by delegating to a panning/zooming controller. Touch events can be dispatched
|
||||
* to a higher-level view.
|
||||
*/
|
||||
public class LayerController {
|
||||
private Layer mRootLayer; /* The root layer. */
|
||||
private LayerView mView; /* The main rendering view. */
|
||||
private Context mContext; /* The current context. */
|
||||
private IntRect mVisibleRect; /* The current visible region. */
|
||||
private IntSize mScreenSize; /* The screen size of the viewport. */
|
||||
private IntSize mPageSize; /* The current page size. */
|
||||
|
||||
private PanZoomController mPanZoomController;
|
||||
/*
|
||||
* The panning and zooming controller, which interprets pan and zoom gestures for us and
|
||||
* updates our visible rect appropriately.
|
||||
*/
|
||||
|
||||
private OnTouchListener mOnTouchListener; /* The touch listener. */
|
||||
private LayerClient mLayerClient; /* The layer client. */
|
||||
|
||||
private ArrayList<OnGeometryChangeListener> mOnGeometryChangeListeners;
|
||||
/* A list of listeners that will be notified whenever the geometry changes. */
|
||||
private ArrayList<OnPageSizeChangeListener> mOnPageSizeChangeListeners;
|
||||
/* A list of listeners that will be notified whenever the page size changes. */
|
||||
|
||||
/* NB: These must be powers of two due to the OpenGL ES 1.x restriction on NPOT textures. */
|
||||
public static final int TILE_WIDTH = 1024;
|
||||
public static final int TILE_HEIGHT = 2048;
|
||||
|
||||
public LayerController(Context context, LayerClient layerClient) {
|
||||
mContext = context;
|
||||
|
||||
mOnGeometryChangeListeners = new ArrayList<OnGeometryChangeListener>();
|
||||
mOnPageSizeChangeListeners = new ArrayList<OnPageSizeChangeListener>();
|
||||
|
||||
mVisibleRect = new IntRect(0, 0, 1, 1); /* Gets filled in when the surface changes. */
|
||||
mScreenSize = new IntSize(1, 1);
|
||||
|
||||
if (layerClient != null)
|
||||
setLayerClient(layerClient);
|
||||
else
|
||||
mPageSize = new IntSize(LayerController.TILE_WIDTH, LayerController.TILE_HEIGHT);
|
||||
|
||||
mPanZoomController = new PanZoomController(this);
|
||||
mView = new LayerView(context, this);
|
||||
}
|
||||
|
||||
public void setRoot(Layer layer) { mRootLayer = layer; }
|
||||
|
||||
public void setLayerClient(LayerClient layerClient) {
|
||||
mLayerClient = layerClient;
|
||||
mPageSize = layerClient.getPageSize();
|
||||
layerClient.setLayerController(this);
|
||||
}
|
||||
|
||||
public Layer getRoot() { return mRootLayer; }
|
||||
public LayerView getView() { return mView; }
|
||||
public Context getContext() { return mContext; }
|
||||
public IntRect getVisibleRect() { return mVisibleRect; }
|
||||
public IntSize getScreenSize() { return mScreenSize; }
|
||||
public IntSize getPageSize() { return mPageSize; }
|
||||
|
||||
public Bitmap getBackgroundPattern() { return getDrawable("pattern"); }
|
||||
public Bitmap getCheckerboardPattern() { return getDrawable("checkerboard"); }
|
||||
public Bitmap getShadowPattern() { return getDrawable("shadow"); }
|
||||
|
||||
public GestureDetector.OnGestureListener getGestureListener() { return mPanZoomController; }
|
||||
public ScaleGestureDetector.OnScaleGestureListener getScaleGestureListener() { return mPanZoomController; }
|
||||
|
||||
private Bitmap getDrawable(String name) {
|
||||
Resources resources = mContext.getResources();
|
||||
int resourceID = resources.getIdentifier(name, "drawable", mContext.getPackageName());
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inScaled = false;
|
||||
return BitmapFactory.decodeResource(mContext.getResources(), resourceID, options);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that the zoom factor of the layer controller differs from the zoom factor of the layer
|
||||
* client (i.e. the page).
|
||||
*/
|
||||
public float getZoomFactor() { return (float)mScreenSize.width / (float)mVisibleRect.width; }
|
||||
|
||||
/**
|
||||
* The view calls this to indicate that the screen changed size.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
public void setScreenSize(int width, int height) {
|
||||
float zoomFactor = getZoomFactor(); /* Must come first. */
|
||||
|
||||
mScreenSize = new IntSize(width, height);
|
||||
setVisibleRect(mVisibleRect.x, mVisibleRect.y,
|
||||
(int)Math.round((float)width / zoomFactor),
|
||||
(int)Math.round((float)height / zoomFactor));
|
||||
|
||||
notifyLayerClientOfGeometryChange();
|
||||
}
|
||||
|
||||
public void setNeedsDisplay() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
public void scrollTo(int x, int y) {
|
||||
setVisibleRect(x, y, mVisibleRect.width, mVisibleRect.height);
|
||||
}
|
||||
|
||||
public void setVisibleRect(int x, int y, int width, int height) {
|
||||
mVisibleRect = new IntRect(x, y, width, height);
|
||||
setNeedsDisplay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the zoom factor to 1, adjusting the visible rect accordingly. The Gecko layer client
|
||||
* calls this function after a zoom has completed and Gecko is done rendering the new visible
|
||||
* region.
|
||||
*/
|
||||
public void unzoom() {
|
||||
float zoomFactor = getZoomFactor();
|
||||
mVisibleRect = new IntRect((int)Math.round(mVisibleRect.x * zoomFactor),
|
||||
(int)Math.round(mVisibleRect.y * zoomFactor),
|
||||
mScreenSize.width, mScreenSize.height);
|
||||
mPageSize = mPageSize.scale(zoomFactor);
|
||||
setNeedsDisplay();
|
||||
}
|
||||
|
||||
public void setPageSize(IntSize size) {
|
||||
mPageSize = size.scale(getZoomFactor());
|
||||
mView.notifyRendererOfPageSizeChange();
|
||||
}
|
||||
|
||||
public boolean post(Runnable action) { return mView.post(action); }
|
||||
|
||||
public void setOnTouchListener(OnTouchListener onTouchListener) {
|
||||
mOnTouchListener = onTouchListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* The view as well as the controller itself use this method to notify the layer client that
|
||||
* the geometry changed.
|
||||
*/
|
||||
public void notifyLayerClientOfGeometryChange() {
|
||||
if (mLayerClient != null)
|
||||
mLayerClient.geometryChanged();
|
||||
}
|
||||
|
||||
// Informs the view and the panning and zooming controller that the geometry changed.
|
||||
public void notifyViewOfGeometryChange() {
|
||||
mView.geometryChanged();
|
||||
mPanZoomController.geometryChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this controller is fine with performing a redraw operation or false if it
|
||||
* would prefer that the action didn't take place.
|
||||
*/
|
||||
public boolean getRedrawHint() {
|
||||
if (checkerboarding() || mPanZoomController.getRedrawHint()) {
|
||||
Log.e("Fennec", "### checkerboarding? " + checkerboarding() + " pan/zoom? " +
|
||||
mPanZoomController.getRedrawHint());
|
||||
}
|
||||
return checkerboarding() || mPanZoomController.getRedrawHint();
|
||||
}
|
||||
|
||||
private IntRect getTileRect() {
|
||||
return new IntRect(mRootLayer.origin.x, mRootLayer.origin.y, TILE_WIDTH, TILE_HEIGHT);
|
||||
}
|
||||
|
||||
// Returns true if a checkerboard is visible.
|
||||
private boolean checkerboarding() {
|
||||
IntRect pageRect = new IntRect(0, 0, mPageSize.width, mPageSize.height);
|
||||
return !getTileRect().contains(mVisibleRect.intersect(pageRect));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 top left corner of the root layer, in the coordinate system of the
|
||||
* layer itself. This method is used by the viewport controller as part of the process of
|
||||
* translating touch events to Gecko's coordinate system.
|
||||
*/
|
||||
public IntPoint convertViewPointToLayerPoint(IntPoint viewPoint) {
|
||||
if (mRootLayer == null)
|
||||
return null;
|
||||
|
||||
// Undo the transforms.
|
||||
IntPoint scaledPoint = viewPoint.scale(1.0f / getZoomFactor());
|
||||
return mVisibleRect.getOrigin().add(scaledPoint).subtract(mRootLayer.origin);
|
||||
}
|
||||
|
||||
/*
|
||||
* Gesture detection. This is handled only at a high level in this class; we dispatch to the
|
||||
* pan/zoom controller to do the dirty work.
|
||||
*/
|
||||
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
boolean result = mPanZoomController.onTouchEvent(event);
|
||||
if (mOnTouchListener != null)
|
||||
result = mOnTouchListener.onTouch(mView, event) || result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Objects that wish to listen for changes in the layer geometry (visible rect or screen size)
|
||||
* should implement this interface and register themselves with addOnGeometryChangeListener().
|
||||
*/
|
||||
public static interface OnGeometryChangeListener {
|
||||
public void onGeometryChange(LayerController sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Objects that wish to listen for changes in the page size should implement this interface and
|
||||
* register themselves with addOnPageSizeChangeListener().
|
||||
*/
|
||||
public static interface OnPageSizeChangeListener {
|
||||
public void onPageSizeChange(LayerController sender);
|
||||
}
|
||||
}
|
||||
|
@ -1,205 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.gfx;
|
||||
|
||||
import org.mozilla.fennec.gfx.BufferedCairoImage;
|
||||
import org.mozilla.fennec.gfx.IntRect;
|
||||
import org.mozilla.fennec.gfx.IntSize;
|
||||
import org.mozilla.fennec.gfx.LayerController;
|
||||
import org.mozilla.fennec.gfx.LayerView;
|
||||
import org.mozilla.fennec.gfx.NinePatchTileLayer;
|
||||
import org.mozilla.fennec.gfx.SingleTileLayer;
|
||||
import org.mozilla.fennec.gfx.TextureReaper;
|
||||
import org.mozilla.fennec.gfx.TextLayer;
|
||||
import org.mozilla.fennec.gfx.TileLayer;
|
||||
import android.content.Context;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.WindowManager;
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* The layer renderer implements the rendering logic for a layer view.
|
||||
*/
|
||||
public class LayerRenderer implements GLSurfaceView.Renderer {
|
||||
private LayerView mView;
|
||||
private SingleTileLayer mBackgroundLayer;
|
||||
private SingleTileLayer mCheckerboardLayer;
|
||||
private NinePatchTileLayer mShadowLayer;
|
||||
private TextLayer mFPSLayer;
|
||||
|
||||
// FPS display
|
||||
private long mFrameCountTimestamp;
|
||||
private int mFrameCount; // number of frames since last timestamp
|
||||
|
||||
public LayerRenderer(LayerView view) {
|
||||
mView = view;
|
||||
|
||||
/* FIXME: Layers should not be directly connected to the layer controller. */
|
||||
LayerController controller = view.getController();
|
||||
mBackgroundLayer = new SingleTileLayer(true);
|
||||
mBackgroundLayer.paintImage(new BufferedCairoImage(controller.getBackgroundPattern()));
|
||||
mCheckerboardLayer = new SingleTileLayer(true);
|
||||
mCheckerboardLayer.paintImage(new BufferedCairoImage(controller.getCheckerboardPattern()));
|
||||
mShadowLayer = new NinePatchTileLayer(controller);
|
||||
mShadowLayer.paintImage(new BufferedCairoImage(controller.getShadowPattern()));
|
||||
mFPSLayer = new TextLayer(new IntSize(64, 32));
|
||||
mFPSLayer.setText("-- FPS");
|
||||
|
||||
mFrameCountTimestamp = System.currentTimeMillis();
|
||||
mFrameCount = 0;
|
||||
}
|
||||
|
||||
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||||
gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
gl.glClearDepthf(1.0f); /* FIXME: Is this needed? */
|
||||
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
|
||||
gl.glShadeModel(GL10.GL_SMOOTH); /* FIXME: Is this needed? */
|
||||
gl.glDisable(GL10.GL_DITHER);
|
||||
gl.glEnable(GL10.GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
public void onDrawFrame(GL10 gl) {
|
||||
checkFPS();
|
||||
TextureReaper.get().reap(gl);
|
||||
|
||||
LayerController controller = mView.getController();
|
||||
|
||||
//Log.e("Fennec", "visible rect: " + controller.getVisibleRect());
|
||||
|
||||
/* FIXME: Is this clear needed? */
|
||||
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
/* Draw the background. */
|
||||
gl.glLoadIdentity();
|
||||
mBackgroundLayer.draw(gl);
|
||||
|
||||
/* Draw the drop shadow. */
|
||||
setupPageTransform(gl);
|
||||
mShadowLayer.draw(gl);
|
||||
|
||||
/* Draw the checkerboard. */
|
||||
IntRect pageRect = clampToScreen(getPageRect());
|
||||
IntSize screenSize = controller.getScreenSize();
|
||||
gl.glEnable(GL10.GL_SCISSOR_TEST);
|
||||
gl.glScissor(pageRect.x, screenSize.height - (pageRect.y + pageRect.height),
|
||||
pageRect.width, pageRect.height);
|
||||
|
||||
gl.glLoadIdentity();
|
||||
mCheckerboardLayer.draw(gl);
|
||||
|
||||
/* Draw the layer the client added to us. */
|
||||
setupPageTransform(gl);
|
||||
|
||||
Layer rootLayer = controller.getRoot();
|
||||
if (rootLayer != null)
|
||||
rootLayer.draw(gl);
|
||||
|
||||
gl.glDisable(GL10.GL_SCISSOR_TEST);
|
||||
|
||||
/* Draw the FPS. */
|
||||
gl.glLoadIdentity();
|
||||
gl.glEnable(GL10.GL_BLEND);
|
||||
mFPSLayer.draw(gl);
|
||||
gl.glDisable(GL10.GL_BLEND);
|
||||
}
|
||||
|
||||
public void pageSizeChanged() {
|
||||
mShadowLayer.recreateVertexBuffers();
|
||||
}
|
||||
|
||||
private void setupPageTransform(GL10 gl) {
|
||||
LayerController controller = mView.getController();
|
||||
IntRect visibleRect = controller.getVisibleRect();
|
||||
float zoomFactor = controller.getZoomFactor();
|
||||
|
||||
gl.glLoadIdentity();
|
||||
gl.glScalef(zoomFactor, zoomFactor, 1.0f);
|
||||
gl.glTranslatef(-visibleRect.x, -visibleRect.y, 0.0f);
|
||||
}
|
||||
|
||||
private IntRect getPageRect() {
|
||||
LayerController controller = mView.getController();
|
||||
float zoomFactor = controller.getZoomFactor();
|
||||
IntRect visibleRect = controller.getVisibleRect();
|
||||
IntSize pageSize = controller.getPageSize();
|
||||
|
||||
return new IntRect((int)Math.round(-zoomFactor * visibleRect.x),
|
||||
(int)Math.round(-zoomFactor * visibleRect.y),
|
||||
(int)Math.round(zoomFactor * pageSize.width),
|
||||
(int)Math.round(zoomFactor * pageSize.height));
|
||||
}
|
||||
|
||||
private IntRect clampToScreen(IntRect rect) {
|
||||
LayerController controller = mView.getController();
|
||||
IntSize screenSize = controller.getScreenSize();
|
||||
|
||||
int left = Math.max(0, rect.x);
|
||||
int top = Math.max(0, rect.y);
|
||||
int right = Math.min(screenSize.width, rect.getRight());
|
||||
int bottom = Math.min(screenSize.height, rect.getBottom());
|
||||
return new IntRect(left, top, right - left, bottom - top);
|
||||
}
|
||||
|
||||
public void onSurfaceChanged(GL10 gl, int width, int height) {
|
||||
gl.glViewport(0, 0, width, height);
|
||||
gl.glMatrixMode(GL10.GL_PROJECTION);
|
||||
gl.glLoadIdentity();
|
||||
gl.glOrthof(0.0f, (float)width, (float)height, 0.0f, -10.0f, 10.0f);
|
||||
gl.glMatrixMode(GL10.GL_MODELVIEW);
|
||||
gl.glLoadIdentity();
|
||||
|
||||
mView.setScreenSize(width, height);
|
||||
|
||||
/* TODO: Throw away tile images? */
|
||||
}
|
||||
|
||||
private void checkFPS() {
|
||||
if (System.currentTimeMillis() >= mFrameCountTimestamp + 1000) {
|
||||
mFrameCountTimestamp = System.currentTimeMillis();
|
||||
mFPSLayer.setText(mFrameCount + " FPS");
|
||||
mFrameCount = 0;
|
||||
} else {
|
||||
mFrameCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,148 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.gfx;
|
||||
|
||||
import org.mozilla.fennec.gfx.InputConnectionHandler;
|
||||
import org.mozilla.fennec.gfx.LayerController;
|
||||
import android.content.Context;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.ScaleGestureDetector;
|
||||
|
||||
/**
|
||||
* A view rendered by the layer compositor.
|
||||
*
|
||||
* This view delegates to LayerRenderer to actually do the drawing. Its role is largely that of a
|
||||
* mediator between the LayerRenderer and the LayerController.
|
||||
*/
|
||||
public class LayerView extends GLSurfaceView {
|
||||
private Context mContext;
|
||||
private LayerController mController;
|
||||
private InputConnectionHandler mInputConnectionHandler;
|
||||
private LayerRenderer mRenderer;
|
||||
private GestureDetector mGestureDetector;
|
||||
private ScaleGestureDetector mScaleGestureDetector;
|
||||
|
||||
public LayerView(Context context, LayerController controller) {
|
||||
super(context);
|
||||
|
||||
mContext = context;
|
||||
mController = controller;
|
||||
mRenderer = new LayerRenderer(this);
|
||||
setRenderer(mRenderer);
|
||||
mGestureDetector = new GestureDetector(context, controller.getGestureListener());
|
||||
mScaleGestureDetector = new ScaleGestureDetector(context, controller.getScaleGestureListener());
|
||||
mInputConnectionHandler = null;
|
||||
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (mGestureDetector.onTouchEvent(event))
|
||||
return true;
|
||||
mScaleGestureDetector.onTouchEvent(event);
|
||||
if (mScaleGestureDetector.isInProgress())
|
||||
return true;
|
||||
return mController.onTouchEvent(event);
|
||||
}
|
||||
|
||||
public LayerController getController() { return mController; }
|
||||
public void geometryChanged() { /* TODO: Schedule a redraw. */ }
|
||||
|
||||
public void notifyRendererOfPageSizeChange() {
|
||||
mRenderer.pageSizeChanged();
|
||||
}
|
||||
|
||||
/** The LayerRenderer calls this to indicate that the window has changed size. */
|
||||
public void setScreenSize(int width, int height) {
|
||||
mController.setScreenSize(width, height);
|
||||
}
|
||||
|
||||
public void setInputConnectionHandler(InputConnectionHandler handler) {
|
||||
mInputConnectionHandler = handler;
|
||||
}
|
||||
|
||||
@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)
|
||||
return mInputConnectionHandler.onKeyPreIme(keyCode, event);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (mInputConnectionHandler != null)
|
||||
return mInputConnectionHandler.onKeyDown(keyCode, event);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
|
||||
if (mInputConnectionHandler != null)
|
||||
return mInputConnectionHandler.onKeyLongPress(keyCode, event);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
|
||||
if (mInputConnectionHandler != null)
|
||||
return mInputConnectionHandler.onKeyMultiple(keyCode, repeatCount, event);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (mInputConnectionHandler != null)
|
||||
return mInputConnectionHandler.onKeyUp(keyCode, event);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1,176 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.gfx;
|
||||
|
||||
import org.mozilla.fennec.gfx.IntSize;
|
||||
import org.mozilla.fennec.gfx.LayerController;
|
||||
import org.mozilla.fennec.gfx.TileLayer;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
/**
|
||||
* Encapsulates the logic needed to draw a nine-patch bitmap using OpenGL ES.
|
||||
*
|
||||
* For more information on nine-patch bitmaps, see the following document:
|
||||
* http://developer.android.com/guide/topics/graphics/2d-graphics.html#nine-patch
|
||||
*/
|
||||
public class NinePatchTileLayer extends TileLayer {
|
||||
private FloatBuffer mSideTexCoordBuffer, mSideVertexBuffer;
|
||||
private FloatBuffer mTopTexCoordBuffer, mTopVertexBuffer;
|
||||
private LayerController mLayerController;
|
||||
|
||||
private static final int PATCH_SIZE = 16;
|
||||
private static final int TEXTURE_SIZE = 48;
|
||||
|
||||
/*
|
||||
* We divide the nine-patch bitmap up into the "sides" and the "tops":
|
||||
*
|
||||
* Top
|
||||
* |
|
||||
* v
|
||||
* +---+---+---+
|
||||
* | | | |
|
||||
* | +---+ |
|
||||
* | |XXX| | <-- Side
|
||||
* | +---+ |
|
||||
* | | | |
|
||||
* +---+---+---+
|
||||
*/
|
||||
|
||||
private static final float[] SIDE_TEX_COORDS = {
|
||||
0.0f, 0.0f,
|
||||
0.25f, 0.0f,
|
||||
0.0f, 0.25f,
|
||||
0.25f, 0.25f,
|
||||
0.0f, 0.50f,
|
||||
0.25f, 0.50f,
|
||||
0.0f, 0.75f,
|
||||
0.25f, 0.75f,
|
||||
};
|
||||
|
||||
private static final float[] TOP_TEX_COORDS = {
|
||||
0.25f, 0.0f,
|
||||
0.50f, 0.0f,
|
||||
0.25f, 0.25f,
|
||||
0.50f, 0.25f,
|
||||
};
|
||||
|
||||
public NinePatchTileLayer(LayerController layerController) {
|
||||
super(false);
|
||||
|
||||
mLayerController = layerController;
|
||||
|
||||
mSideTexCoordBuffer = createBuffer(SIDE_TEX_COORDS);
|
||||
mTopTexCoordBuffer = createBuffer(TOP_TEX_COORDS);
|
||||
|
||||
recreateVertexBuffers();
|
||||
}
|
||||
|
||||
public void recreateVertexBuffers() {
|
||||
IntSize pageSize = mLayerController.getPageSize();
|
||||
|
||||
float[] sideVertices = {
|
||||
-PATCH_SIZE, -PATCH_SIZE, 0.0f,
|
||||
0.0f, -PATCH_SIZE, 0.0f,
|
||||
-PATCH_SIZE, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f,
|
||||
-PATCH_SIZE, pageSize.height, 0.0f,
|
||||
0.0f, pageSize.height, 0.0f,
|
||||
-PATCH_SIZE, PATCH_SIZE + pageSize.height, 0.0f,
|
||||
0.0f, PATCH_SIZE + pageSize.height, 0.0f
|
||||
};
|
||||
|
||||
float[] topVertices = {
|
||||
0.0f, -PATCH_SIZE, 0.0f,
|
||||
pageSize.width, -PATCH_SIZE, 0.0f,
|
||||
0.0f, 0.0f, 0.0f,
|
||||
pageSize.width, 0.0f, 0.0f
|
||||
};
|
||||
|
||||
mSideVertexBuffer = createBuffer(sideVertices);
|
||||
mTopVertexBuffer = createBuffer(topVertices);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTileDraw(GL10 gl) {
|
||||
IntSize pageSize = mLayerController.getPageSize();
|
||||
|
||||
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
|
||||
gl.glEnable(GL10.GL_BLEND);
|
||||
|
||||
gl.glBindTexture(GL10.GL_TEXTURE_2D, getTextureID());
|
||||
|
||||
/* Left side */
|
||||
drawTriangles(gl, mSideVertexBuffer, mSideTexCoordBuffer, 8);
|
||||
|
||||
/* Top */
|
||||
drawTriangles(gl, mTopVertexBuffer, mTopTexCoordBuffer, 4);
|
||||
|
||||
/* Right side */
|
||||
gl.glMatrixMode(GL10.GL_MODELVIEW);
|
||||
gl.glPushMatrix();
|
||||
gl.glTranslatef(pageSize.width + PATCH_SIZE, 0.0f, 0.0f);
|
||||
gl.glMatrixMode(GL10.GL_TEXTURE);
|
||||
gl.glPushMatrix();
|
||||
gl.glTranslatef(0.50f, 0.0f, 0.0f);
|
||||
|
||||
drawTriangles(gl, mSideVertexBuffer, mSideTexCoordBuffer, 8);
|
||||
|
||||
gl.glMatrixMode(GL10.GL_TEXTURE);
|
||||
gl.glPopMatrix();
|
||||
gl.glMatrixMode(GL10.GL_MODELVIEW); /* Not strictly necessary, but here for clarity. */
|
||||
gl.glPopMatrix();
|
||||
|
||||
/* Bottom */
|
||||
gl.glMatrixMode(GL10.GL_MODELVIEW);
|
||||
gl.glPushMatrix();
|
||||
gl.glTranslatef(0.0f, pageSize.height + PATCH_SIZE, 0.0f);
|
||||
gl.glMatrixMode(GL10.GL_TEXTURE);
|
||||
gl.glPushMatrix();
|
||||
gl.glTranslatef(0.0f, 0.50f, 0.0f);
|
||||
|
||||
drawTriangles(gl, mTopVertexBuffer, mTopTexCoordBuffer, 4);
|
||||
|
||||
gl.glMatrixMode(GL10.GL_TEXTURE);
|
||||
gl.glPopMatrix();
|
||||
gl.glMatrixMode(GL10.GL_MODELVIEW);
|
||||
gl.glPopMatrix();
|
||||
|
||||
gl.glDisable(GL10.GL_BLEND);
|
||||
}
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.gfx;
|
||||
|
||||
import org.mozilla.fennec.gfx.BufferedCairoImage;
|
||||
import org.mozilla.fennec.gfx.CairoUtils;
|
||||
import org.mozilla.fennec.gfx.IntSize;
|
||||
import org.mozilla.fennec.gfx.LayerClient;
|
||||
import org.mozilla.fennec.gfx.SingleTileLayer;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import java.io.File;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* A stand-in for Gecko that renders cached content of the previous page. We use this until Gecko
|
||||
* is up, then we hand off control to it.
|
||||
*/
|
||||
public class PlaceholderLayerClient extends LayerClient {
|
||||
private Context mContext;
|
||||
private IntSize mPageSize;
|
||||
private int mWidth, mHeight, mFormat;
|
||||
private ByteBuffer mBuffer;
|
||||
|
||||
private PlaceholderLayerClient(Context context, Bitmap bitmap) {
|
||||
mContext = context;
|
||||
mPageSize = new IntSize(995, 1250); /* TODO */
|
||||
|
||||
mWidth = bitmap.getWidth();
|
||||
mHeight = bitmap.getHeight();
|
||||
mFormat = CairoUtils.bitmapConfigToCairoFormat(bitmap.getConfig());
|
||||
mBuffer = ByteBuffer.allocateDirect(mWidth * mHeight * 4);
|
||||
bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
|
||||
}
|
||||
|
||||
public static PlaceholderLayerClient createInstance(Context context) {
|
||||
File path = new File(Environment.getExternalStorageDirectory(), "lastScreen.png");
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inScaled = false;
|
||||
Bitmap bitmap = BitmapFactory.decodeFile("" + path, options);
|
||||
if (bitmap == null)
|
||||
return null;
|
||||
|
||||
return new PlaceholderLayerClient(context, bitmap);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
SingleTileLayer tileLayer = new SingleTileLayer();
|
||||
getLayerController().setRoot(tileLayer);
|
||||
tileLayer.paintImage(new BufferedCairoImage(mBuffer, mWidth, mHeight, mFormat));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void geometryChanged() { /* no-op */ }
|
||||
@Override
|
||||
public IntSize getPageSize() { return mPageSize; }
|
||||
@Override
|
||||
public void render() { /* no-op */ }
|
||||
|
||||
/** Called whenever the page changes size. */
|
||||
@Override
|
||||
public void setPageSize(IntSize pageSize) { mPageSize = pageSize; }
|
||||
}
|
||||
|
@ -1,107 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.gfx;
|
||||
|
||||
import org.mozilla.fennec.gfx.CairoImage;
|
||||
import org.mozilla.fennec.gfx.CairoUtils;
|
||||
import org.mozilla.fennec.gfx.IntSize;
|
||||
import org.mozilla.fennec.gfx.LayerController;
|
||||
import org.mozilla.fennec.gfx.TileLayer;
|
||||
import android.util.Log;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
/**
|
||||
* Encapsulates the logic needed to draw a single textured tile.
|
||||
*/
|
||||
public class SingleTileLayer extends TileLayer {
|
||||
private FloatBuffer mTexCoordBuffer, mVertexBuffer;
|
||||
|
||||
private static final float[] VERTICES = {
|
||||
0.0f, 0.0f, 0.0f,
|
||||
1.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f,
|
||||
1.0f, 1.0f, 0.0f
|
||||
};
|
||||
|
||||
private static final float[] TEX_COORDS = {
|
||||
0.0f, 0.0f,
|
||||
1.0f, 0.0f,
|
||||
0.0f, 1.0f,
|
||||
1.0f, 1.0f
|
||||
};
|
||||
|
||||
public SingleTileLayer() { this(false); }
|
||||
|
||||
public SingleTileLayer(boolean repeat) {
|
||||
super(repeat);
|
||||
|
||||
mVertexBuffer = createBuffer(VERTICES);
|
||||
mTexCoordBuffer = createBuffer(TEX_COORDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTileDraw(GL10 gl) {
|
||||
IntSize size = getSize();
|
||||
|
||||
if (repeats()) {
|
||||
gl.glMatrixMode(GL10.GL_TEXTURE);
|
||||
gl.glPushMatrix();
|
||||
gl.glScalef(LayerController.TILE_WIDTH / size.width,
|
||||
LayerController.TILE_HEIGHT / size.height,
|
||||
1.0f);
|
||||
|
||||
gl.glMatrixMode(GL10.GL_MODELVIEW);
|
||||
gl.glScalef(LayerController.TILE_WIDTH, LayerController.TILE_HEIGHT, 1.0f);
|
||||
} else {
|
||||
gl.glScalef(size.width, size.height, 1.0f);
|
||||
}
|
||||
|
||||
gl.glBindTexture(GL10.GL_TEXTURE_2D, getTextureID());
|
||||
drawTriangles(gl, mVertexBuffer, mTexCoordBuffer, 4);
|
||||
|
||||
if (repeats()) {
|
||||
gl.glMatrixMode(GL10.GL_TEXTURE);
|
||||
gl.glPopMatrix();
|
||||
gl.glMatrixMode(GL10.GL_MODELVIEW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,99 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.gfx;
|
||||
|
||||
import org.mozilla.fennec.gfx.BufferedCairoImage;
|
||||
import org.mozilla.fennec.gfx.CairoImage;
|
||||
import org.mozilla.fennec.gfx.IntSize;
|
||||
import org.mozilla.fennec.gfx.SingleTileLayer;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Typeface;
|
||||
import android.util.Log;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
/**
|
||||
* Draws text on a layer. This is used for the frame rate meter.
|
||||
*/
|
||||
public class TextLayer extends SingleTileLayer {
|
||||
private ByteBuffer mBuffer;
|
||||
private BufferedCairoImage mImage;
|
||||
private IntSize mSize;
|
||||
private String mText;
|
||||
|
||||
public TextLayer(IntSize size) {
|
||||
super(false);
|
||||
|
||||
mBuffer = ByteBuffer.allocateDirect(size.width * size.height * 4);
|
||||
mSize = size;
|
||||
mImage = new BufferedCairoImage(mBuffer, size.width, size.height,
|
||||
CairoImage.FORMAT_ARGB32);
|
||||
mText = "";
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
mText = text;
|
||||
renderText();
|
||||
paintImage(mImage);
|
||||
}
|
||||
|
||||
private void renderText() {
|
||||
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(mText) + 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(mText, 6.0f, 18.0f, textPaint);
|
||||
|
||||
bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
|
||||
}
|
||||
}
|
||||
|
@ -1,71 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.gfx;
|
||||
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
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)
|
||||
mDeadTextureIDs.add(textureID);
|
||||
}
|
||||
|
||||
public void reap(GL10 gl) {
|
||||
int[] deadTextureIDs = new int[mDeadTextureIDs.size()];
|
||||
for (int i = 0; i < deadTextureIDs.length; i++)
|
||||
deadTextureIDs[i] = mDeadTextureIDs.get(i);
|
||||
mDeadTextureIDs.clear();
|
||||
|
||||
gl.glDeleteTextures(deadTextureIDs.length, deadTextureIDs, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,191 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.gfx;
|
||||
|
||||
import org.mozilla.fennec.gfx.CairoImage;
|
||||
import org.mozilla.fennec.gfx.IntSize;
|
||||
import org.mozilla.fennec.gfx.Layer;
|
||||
import org.mozilla.fennec.gfx.TextureReaper;
|
||||
import android.util.Log;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
/**
|
||||
* Base class for tile layers, which encapsulate the logic needed to draw textured tiles in OpenGL
|
||||
* ES.
|
||||
*/
|
||||
public abstract class TileLayer extends Layer {
|
||||
private CairoImage mImage;
|
||||
private boolean mRepeat;
|
||||
private IntSize mSize;
|
||||
private int[] mTextureIDs;
|
||||
|
||||
private IntRect mTextureUploadRect;
|
||||
/* The rect that needs to be uploaded to the texture. */
|
||||
|
||||
public TileLayer(boolean repeat) {
|
||||
super();
|
||||
mRepeat = repeat;
|
||||
mTextureUploadRect = null;
|
||||
}
|
||||
|
||||
public IntSize getSize() { return mSize; }
|
||||
|
||||
protected boolean repeats() { return mRepeat; }
|
||||
protected int getTextureID() { return mTextureIDs[0]; }
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
if (mTextureIDs != null)
|
||||
TextureReaper.get().add(mTextureIDs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses implement this method to perform tile drawing.
|
||||
*
|
||||
* Invariant: The current matrix mode must be GL_MODELVIEW both before and after this call.
|
||||
*/
|
||||
protected abstract void onTileDraw(GL10 gl);
|
||||
|
||||
@Override
|
||||
protected void onDraw(GL10 gl) {
|
||||
if (mImage == null)
|
||||
return;
|
||||
if (mTextureUploadRect != null)
|
||||
uploadTexture(gl);
|
||||
|
||||
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
|
||||
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
|
||||
gl.glPushMatrix();
|
||||
|
||||
onTileDraw(gl);
|
||||
|
||||
gl.glPopMatrix();
|
||||
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
|
||||
}
|
||||
|
||||
public void paintSubimage(CairoImage image, IntRect rect) {
|
||||
mImage = image;
|
||||
mTextureUploadRect = rect;
|
||||
|
||||
/*
|
||||
* Assert that the image has a power-of-two size. OpenGL ES < 2.0 doesn't support NPOT
|
||||
* textures and OpenGL ES doesn't seem to let us efficiently slice up a NPOT bitmap.
|
||||
*/
|
||||
int width = mImage.getWidth(), height = mImage.getHeight();
|
||||
assert (width & (width - 1)) == 0;
|
||||
assert (height & (height - 1)) == 0;
|
||||
}
|
||||
|
||||
public void paintImage(CairoImage image) {
|
||||
paintSubimage(image, new IntRect(0, 0, image.getWidth(), image.getHeight()));
|
||||
}
|
||||
|
||||
private void uploadTexture(GL10 gl) {
|
||||
boolean newTexture = mTextureIDs == null;
|
||||
if (newTexture) {
|
||||
mTextureIDs = new int[1];
|
||||
gl.glGenTextures(mTextureIDs.length, mTextureIDs, 0);
|
||||
}
|
||||
|
||||
int width = mImage.getWidth(), height = mImage.getHeight();
|
||||
mSize = new IntSize(width, height);
|
||||
|
||||
int cairoFormat = mImage.getFormat();
|
||||
int internalFormat = CairoUtils.cairoFormatToGLInternalFormat(cairoFormat);
|
||||
int format = CairoUtils.cairoFormatToGLFormat(cairoFormat);
|
||||
int type = CairoUtils.cairoFormatToGLType(cairoFormat);
|
||||
|
||||
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIDs[0]);
|
||||
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
|
||||
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
|
||||
|
||||
int repeatMode = mRepeat ? GL10.GL_REPEAT : GL10.GL_CLAMP_TO_EDGE;
|
||||
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, repeatMode);
|
||||
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, repeatMode);
|
||||
|
||||
ByteBuffer buffer = mImage.lockBuffer();
|
||||
try {
|
||||
Log.e("Fennec", "### Texture upload rect height is " + mTextureUploadRect.height);
|
||||
|
||||
if (newTexture) {
|
||||
/* The texture is new; we have to upload the whole image. */
|
||||
gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, internalFormat, mSize.width, mSize.height, 0,
|
||||
format, type, buffer);
|
||||
} else {
|
||||
/*
|
||||
* The texture is already existing, so upload only the changed rect. We have to
|
||||
* widen to the full width of the texture because we can't count on the device
|
||||
* having support for GL_EXT_unpack_subimage, and going line-by-line is too slow.
|
||||
*/
|
||||
Buffer viewBuffer = buffer.slice();
|
||||
int bpp = CairoUtils.bitsPerPixelForCairoFormat(cairoFormat) / 8;
|
||||
viewBuffer.position(mTextureUploadRect.y * width * bpp);
|
||||
|
||||
gl.glTexSubImage2D(gl.GL_TEXTURE_2D,
|
||||
0, 0, mTextureUploadRect.y, width, mTextureUploadRect.height,
|
||||
format, type, viewBuffer);
|
||||
}
|
||||
} finally {
|
||||
mImage.unlockBuffer();
|
||||
}
|
||||
|
||||
mTextureUploadRect = null;
|
||||
}
|
||||
|
||||
protected static FloatBuffer createBuffer(float[] values) {
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(values.length * 4);
|
||||
byteBuffer.order(ByteOrder.nativeOrder());
|
||||
|
||||
FloatBuffer floatBuffer = byteBuffer.asFloatBuffer();
|
||||
floatBuffer.put(values);
|
||||
floatBuffer.position(0);
|
||||
return floatBuffer;
|
||||
}
|
||||
|
||||
protected static void drawTriangles(GL10 gl, FloatBuffer vertexBuffer,
|
||||
FloatBuffer texCoordBuffer, int count) {
|
||||
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
|
||||
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texCoordBuffer);
|
||||
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, count);
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 199 B |
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
Before Width: | Height: | Size: 881 B |
@ -1,607 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.ui;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.fennec.gfx.IntPoint;
|
||||
import org.mozilla.fennec.gfx.IntRect;
|
||||
import org.mozilla.fennec.gfx.IntSize;
|
||||
import org.mozilla.fennec.gfx.LayerController;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ScaleGestureDetector;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/*
|
||||
* Handles the kinetic scrolling and zooming physics for a layer controller.
|
||||
*
|
||||
* Many ideas are from Joe Hewitt's Scrollability:
|
||||
* https://github.com/joehewitt/scrollability/
|
||||
*/
|
||||
public class PanZoomController
|
||||
extends GestureDetector.SimpleOnGestureListener
|
||||
implements ScaleGestureDetector.OnScaleGestureListener
|
||||
{
|
||||
private static final String LOG_NAME = "PanZoomController";
|
||||
|
||||
private LayerController mController;
|
||||
|
||||
private static final float FRICTION = 0.97f;
|
||||
// Animation stops if the velocity is below this value.
|
||||
private static final int STOPPED_THRESHOLD = 4;
|
||||
// The percentage of the surface which can be overscrolled before it must snap back.
|
||||
private static final float SNAP_LIMIT = 0.75f;
|
||||
// The rate of deceleration when the surface has overscrolled.
|
||||
private static final float OVERSCROLL_DECEL_RATE = 0.04f;
|
||||
// The duration of animation when bouncing back.
|
||||
private static final int SNAP_TIME = 150;
|
||||
// The number of subdivisions we should consider when plotting the ease-out transition. Higher
|
||||
// values make the animation more accurate, but slower to plot.
|
||||
private static final int SUBDIVISION_COUNT = 1000;
|
||||
|
||||
// The surface is still scrolling.
|
||||
private static final int FLING_STATE_SCROLLING = 0;
|
||||
// The surface is snapping back into place after overscrolling.
|
||||
private static final int FLING_STATE_SNAPPING = 1;
|
||||
|
||||
private long mLastTimestamp;
|
||||
private Timer mFlingTimer;
|
||||
private Axis mX, mY;
|
||||
/* The span at the first zoom event (in unzoomed page coordinates). */
|
||||
private float mInitialZoomSpan;
|
||||
/* The zoom focus at the first zoom event (in unzoomed page coordinates). */
|
||||
private IntPoint mInitialZoomFocus;
|
||||
|
||||
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, /* touch-start followed by move */
|
||||
PANNING_HOLD, /* in panning, but haven't moved. similar to TOUCHING but after starting a pan */
|
||||
PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
|
||||
}
|
||||
|
||||
private PanZoomState mState;
|
||||
|
||||
public PanZoomController(LayerController controller) {
|
||||
mController = controller;
|
||||
mX = new Axis(); mY = new Axis();
|
||||
mState = PanZoomState.NOTHING;
|
||||
|
||||
populatePositionAndLength();
|
||||
}
|
||||
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN: return onTouchStart(event);
|
||||
case MotionEvent.ACTION_MOVE: return onTouchMove(event);
|
||||
case MotionEvent.ACTION_UP: return onTouchEnd(event);
|
||||
case MotionEvent.ACTION_CANCEL: return onTouchCancel(event);
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void geometryChanged() {
|
||||
populatePositionAndLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this controller is fine with performing a redraw operation or false if the
|
||||
* controller would prefer that this action didn't take place. This is used to optimize the
|
||||
* sending of pan/zoom events; when this returns false, a performance-critical operation like
|
||||
* a pan or a zoom is taking place.
|
||||
*/
|
||||
public boolean getRedrawHint() {
|
||||
switch (mState) {
|
||||
case NOTHING:
|
||||
case TOUCHING:
|
||||
case PANNING_HOLD:
|
||||
return true;
|
||||
case FLING:
|
||||
case PANNING:
|
||||
case PINCHING:
|
||||
return false;
|
||||
}
|
||||
Log.e(LOG_NAME, "Unhandled case " + mState + " in getRedrawHint");
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Panning/scrolling
|
||||
*/
|
||||
|
||||
private boolean onTouchStart(MotionEvent event) {
|
||||
switch (mState) {
|
||||
case FLING:
|
||||
if (mFlingTimer != null) {
|
||||
mFlingTimer.cancel();
|
||||
mFlingTimer = null;
|
||||
}
|
||||
// fall through
|
||||
case NOTHING:
|
||||
mState = PanZoomState.TOUCHING;
|
||||
mX.touchPos = event.getX(0);
|
||||
mY.touchPos = event.getY(0);
|
||||
return false;
|
||||
case TOUCHING:
|
||||
case PANNING:
|
||||
case PANNING_HOLD:
|
||||
case PINCHING:
|
||||
mState = PanZoomState.PINCHING;
|
||||
return false;
|
||||
}
|
||||
Log.e(LOG_NAME, "Unhandled case " + mState + " in onTouchStart");
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean onTouchMove(MotionEvent event) {
|
||||
switch (mState) {
|
||||
case NOTHING:
|
||||
case FLING:
|
||||
// should never happen
|
||||
Log.e(LOG_NAME, "Received impossible touch move while in " + mState);
|
||||
return false;
|
||||
case TOUCHING:
|
||||
mLastTimestamp = System.currentTimeMillis();
|
||||
// fall through
|
||||
case PANNING_HOLD:
|
||||
mState = PanZoomState.PANNING;
|
||||
// fall through
|
||||
case PANNING:
|
||||
track(event, System.currentTimeMillis());
|
||||
return true;
|
||||
case PINCHING:
|
||||
// scale gesture listener will handle this
|
||||
return false;
|
||||
}
|
||||
Log.e(LOG_NAME, "Unhandled case " + mState + " in onTouchMove");
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean onTouchEnd(MotionEvent event) {
|
||||
switch (mState) {
|
||||
case NOTHING:
|
||||
case FLING:
|
||||
// should never happen
|
||||
Log.e(LOG_NAME, "Received impossible touch end while in " + mState);
|
||||
return false;
|
||||
case TOUCHING:
|
||||
mState = PanZoomState.NOTHING;
|
||||
// TODO: send click to gecko
|
||||
return false;
|
||||
case PANNING:
|
||||
case PANNING_HOLD:
|
||||
mState = PanZoomState.FLING;
|
||||
fling(System.currentTimeMillis());
|
||||
return true;
|
||||
case PINCHING:
|
||||
int points = event.getPointerCount();
|
||||
if (points == 1) {
|
||||
// last touch up
|
||||
mState = PanZoomState.NOTHING;
|
||||
} else if (points == 2) {
|
||||
int pointRemovedIndex = event.getActionIndex();
|
||||
int pointRemainingIndex = 1 - pointRemovedIndex; // kind of a hack
|
||||
mState = PanZoomState.TOUCHING;
|
||||
mX.touchPos = event.getX(pointRemainingIndex);
|
||||
mY.touchPos = event.getY(pointRemainingIndex);
|
||||
} else {
|
||||
// still pinching, do nothing
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Log.e(LOG_NAME, "Unhandled case " + mState + " in onTouchEnd");
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean onTouchCancel(MotionEvent event) {
|
||||
mState = PanZoomState.NOTHING;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void track(MotionEvent event, long timestamp) {
|
||||
long timeStep = timestamp - mLastTimestamp;
|
||||
mLastTimestamp = timestamp;
|
||||
|
||||
float zoomFactor = mController.getZoomFactor();
|
||||
mX.velocity = (mX.touchPos - event.getX(0)) / zoomFactor;
|
||||
mY.velocity = (mY.touchPos - event.getY(0)) / zoomFactor;
|
||||
mX.touchPos = event.getX(0); mY.touchPos = event.getY(0);
|
||||
|
||||
float absVelocity = (float)Math.sqrt(mX.velocity * mX.velocity +
|
||||
mY.velocity * mY.velocity);
|
||||
if (absVelocity < STOPPED_THRESHOLD)
|
||||
mState = PanZoomState.PANNING_HOLD;
|
||||
|
||||
mX.applyEdgeResistance(); mX.displace();
|
||||
mY.applyEdgeResistance(); mY.displace();
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
private void fling(long timestamp) {
|
||||
long timeStep = timestamp - mLastTimestamp;
|
||||
mLastTimestamp = timestamp;
|
||||
|
||||
if (mState != PanZoomState.FLING)
|
||||
mX.velocity = mY.velocity = 0.0f;
|
||||
|
||||
mX.displace(); mY.displace();
|
||||
|
||||
if (mFlingTimer != null)
|
||||
mFlingTimer.cancel();
|
||||
|
||||
mX.startFling(); mY.startFling();
|
||||
|
||||
mFlingTimer = new Timer();
|
||||
mFlingTimer.scheduleAtFixedRate(new TimerTask() {
|
||||
public void run() { mController.post(new FlingRunnable()); }
|
||||
}, 0, 1000L/60L);
|
||||
}
|
||||
|
||||
private void updatePosition() {
|
||||
Log.e("Fennec", "moving to " + mX.viewportPos + ", " + mY.viewportPos);
|
||||
|
||||
mController.scrollTo(mX.viewportPos, mY.viewportPos);
|
||||
mController.notifyLayerClientOfGeometryChange();
|
||||
}
|
||||
|
||||
// Populates the viewport info and length in the axes.
|
||||
private void populatePositionAndLength() {
|
||||
IntSize pageSize = mController.getPageSize();
|
||||
IntRect visibleRect = mController.getVisibleRect();
|
||||
IntSize screenSize = mController.getScreenSize();
|
||||
|
||||
Log.e("Fennec", "page size: " + pageSize + " visible rect: " + visibleRect +
|
||||
"screen size: " + screenSize);
|
||||
|
||||
mX.setPageLength(pageSize.width);
|
||||
mX.viewportPos = visibleRect.x;
|
||||
mX.setViewportLength(visibleRect.width);
|
||||
|
||||
mY.setPageLength(pageSize.height);
|
||||
mY.viewportPos = visibleRect.y;
|
||||
mY.setViewportLength(visibleRect.height);
|
||||
}
|
||||
|
||||
// The callback that performs the fling animation.
|
||||
private class FlingRunnable implements Runnable {
|
||||
public void run() {
|
||||
populatePositionAndLength();
|
||||
mX.advanceFling(); mY.advanceFling();
|
||||
|
||||
// If both X and Y axes are overscrolled, we have to wait until both axes have stopped to
|
||||
// snap back to avoid a jarring effect.
|
||||
boolean waitingToSnapX = mX.getFlingState() == Axis.FlingStates.WAITING_TO_SNAP;
|
||||
boolean waitingToSnapY = mY.getFlingState() == Axis.FlingStates.WAITING_TO_SNAP;
|
||||
if ((mX.getOverscroll() == Axis.Overscroll.PLUS || mX.getOverscroll() == Axis.Overscroll.MINUS) &&
|
||||
(mY.getOverscroll() == Axis.Overscroll.PLUS || mY.getOverscroll() == Axis.Overscroll.MINUS))
|
||||
{
|
||||
if (waitingToSnapX && waitingToSnapY) {
|
||||
mX.startSnap(); mY.startSnap();
|
||||
}
|
||||
} else {
|
||||
if (waitingToSnapX)
|
||||
mX.startSnap();
|
||||
if (waitingToSnapY)
|
||||
mY.startSnap();
|
||||
}
|
||||
|
||||
mX.displace(); mY.displace();
|
||||
updatePosition();
|
||||
|
||||
if (mX.getFlingState() == Axis.FlingStates.STOPPED &&
|
||||
mY.getFlingState() == Axis.FlingStates.STOPPED) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void stop() {
|
||||
mState = PanZoomState.NOTHING;
|
||||
if (mFlingTimer != null) {
|
||||
mFlingTimer.cancel();
|
||||
mFlingTimer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private float computeElasticity(float excess, float viewportLength) {
|
||||
return 1.0f - excess / (viewportLength * SNAP_LIMIT);
|
||||
}
|
||||
|
||||
// Physics information for one axis (X or Y).
|
||||
private static class Axis {
|
||||
public enum FlingStates {
|
||||
STOPPED,
|
||||
SCROLLING,
|
||||
WAITING_TO_SNAP,
|
||||
SNAPPING,
|
||||
}
|
||||
|
||||
public 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)
|
||||
}
|
||||
|
||||
public float touchPos; /* Position of the last touch. */
|
||||
public float velocity; /* Velocity in this direction. */
|
||||
|
||||
private FlingStates mFlingState; /* The fling state we're in on this axis. */
|
||||
private EaseOutAnimation mSnapAnim; /* The animation when the page is snapping back. */
|
||||
|
||||
/* These three need to be kept in sync with the layer controller. */
|
||||
public int viewportPos;
|
||||
private int mViewportLength;
|
||||
private int mScreenLength;
|
||||
private int mPageLength;
|
||||
|
||||
public FlingStates getFlingState() { return mFlingState; }
|
||||
|
||||
public void setViewportLength(int viewportLength) { mViewportLength = viewportLength; }
|
||||
public void setScreenLength(int screenLength) { mScreenLength = screenLength; }
|
||||
public void setPageLength(int pageLength) { mPageLength = pageLength; }
|
||||
|
||||
private int getViewportEnd() { return viewportPos + mViewportLength; }
|
||||
|
||||
public Overscroll getOverscroll() {
|
||||
boolean minus = (viewportPos < 0);
|
||||
boolean plus = (getViewportEnd() > mPageLength);
|
||||
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 int getExcess() {
|
||||
switch (getOverscroll()) {
|
||||
case MINUS: return Math.min(-viewportPos, mPageLength - getViewportEnd());
|
||||
case PLUS: return Math.min(viewportPos, getViewportEnd() - mPageLength);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Applies resistance along the edges when tracking.
|
||||
public void applyEdgeResistance() {
|
||||
int excess = getExcess();
|
||||
if (excess > 0)
|
||||
velocity *= SNAP_LIMIT - (float)excess / mViewportLength;
|
||||
}
|
||||
|
||||
public void startFling() { mFlingState = FlingStates.SCROLLING; }
|
||||
|
||||
// Advances a fling animation by one step.
|
||||
public void advanceFling() {
|
||||
switch (mFlingState) {
|
||||
case SCROLLING:
|
||||
scroll();
|
||||
return;
|
||||
case WAITING_TO_SNAP:
|
||||
// We don't do anything until the controller switches us into the snapping state.
|
||||
return;
|
||||
case SNAPPING:
|
||||
snap();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Performs one frame of a scroll operation if applicable.
|
||||
private void scroll() {
|
||||
// If we aren't overscrolled, just apply friction.
|
||||
int excess = getExcess();
|
||||
if (excess == 0) {
|
||||
velocity *= FRICTION;
|
||||
if (Math.abs(velocity) < 0.1f) {
|
||||
velocity = 0.0f;
|
||||
mFlingState = FlingStates.STOPPED;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, decrease the velocity linearly.
|
||||
float elasticity = 1.0f - excess / (mViewportLength * SNAP_LIMIT);
|
||||
if (getOverscroll() == Overscroll.MINUS)
|
||||
velocity = Math.min((velocity + OVERSCROLL_DECEL_RATE) * elasticity, 0.0f);
|
||||
else // must be Overscroll.PLUS
|
||||
velocity = Math.max((velocity - OVERSCROLL_DECEL_RATE) * elasticity, 0.0f);
|
||||
|
||||
if (Math.abs(velocity) < 0.3f) {
|
||||
velocity = 0.0f;
|
||||
mFlingState = FlingStates.WAITING_TO_SNAP;
|
||||
}
|
||||
}
|
||||
|
||||
// Starts a snap-into-place operation.
|
||||
public void startSnap() {
|
||||
switch (getOverscroll()) {
|
||||
case MINUS:
|
||||
mSnapAnim = new EaseOutAnimation(viewportPos, viewportPos + getExcess());
|
||||
break;
|
||||
case PLUS:
|
||||
mSnapAnim = new EaseOutAnimation(viewportPos, viewportPos - getExcess());
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Not overscrolled at startSnap()");
|
||||
}
|
||||
|
||||
mFlingState = FlingStates.SNAPPING;
|
||||
}
|
||||
|
||||
// Performs one frame of a snap-into-place operation.
|
||||
private void snap() {
|
||||
mSnapAnim.advance();
|
||||
viewportPos = (int)Math.round(mSnapAnim.getPosition());
|
||||
|
||||
if (mSnapAnim.getFinished()) {
|
||||
mSnapAnim = null;
|
||||
mFlingState = FlingStates.STOPPED;
|
||||
}
|
||||
}
|
||||
|
||||
// Performs displacement of the viewport position according to the current velocity.
|
||||
public void displace() { viewportPos += velocity; }
|
||||
}
|
||||
|
||||
private static class EaseOutAnimation {
|
||||
private float[] mFrames;
|
||||
private float mPosition;
|
||||
private float mOrigin;
|
||||
private float mDest;
|
||||
private long mTimestamp;
|
||||
private boolean mFinished;
|
||||
|
||||
public EaseOutAnimation(float position, float dest) {
|
||||
mPosition = mOrigin = position;
|
||||
mDest = dest;
|
||||
mFrames = new float[SNAP_TIME];
|
||||
mTimestamp = System.currentTimeMillis();
|
||||
mFinished = false;
|
||||
plot(position, dest, mFrames);
|
||||
}
|
||||
|
||||
public float getPosition() { return mPosition; }
|
||||
public boolean getFinished() { return mFinished; }
|
||||
|
||||
private void advance() {
|
||||
int frame = (int)(System.currentTimeMillis() - mTimestamp);
|
||||
if (frame >= SNAP_TIME) {
|
||||
mPosition = mDest;
|
||||
mFinished = true;
|
||||
return;
|
||||
}
|
||||
|
||||
mPosition = mFrames[frame];
|
||||
}
|
||||
|
||||
private static void plot(float from, float to, float[] frames) {
|
||||
int nextX = 0;
|
||||
for (int i = 0; i < SUBDIVISION_COUNT; i++) {
|
||||
float t = (float)i / (float)SUBDIVISION_COUNT;
|
||||
float xPos = (3.0f*t*t - 2.0f*t*t*t) * (float)frames.length;
|
||||
if ((int)xPos < nextX)
|
||||
continue;
|
||||
|
||||
int oldX = nextX;
|
||||
nextX = (int)xPos;
|
||||
|
||||
float yPos = 1.74f*t*t - 0.74f*t*t*t;
|
||||
float framePos = from + (to - from) * yPos;
|
||||
|
||||
while (oldX < nextX)
|
||||
frames[oldX++] = framePos;
|
||||
|
||||
if (nextX >= frames.length)
|
||||
break;
|
||||
}
|
||||
|
||||
// Pad out any remaining frames.
|
||||
while (nextX < frames.length) {
|
||||
frames[nextX] = frames[nextX - 1];
|
||||
nextX++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Zooming
|
||||
*/
|
||||
@Override
|
||||
public boolean onScale(ScaleGestureDetector detector) {
|
||||
mState = PanZoomState.PINCHING;
|
||||
float newZoom = detector.getCurrentSpan() / mInitialZoomSpan;
|
||||
|
||||
IntSize screenSize = mController.getScreenSize();
|
||||
float x = mInitialZoomFocus.x - (detector.getFocusX() / newZoom);
|
||||
float y = mInitialZoomFocus.y - (detector.getFocusY() / newZoom);
|
||||
float width = screenSize.width / newZoom;
|
||||
float height = screenSize.height / newZoom;
|
||||
mController.setVisibleRect((int)Math.round(x), (int)Math.round(y),
|
||||
(int)Math.round(width), (int)Math.round(height));
|
||||
mController.notifyLayerClientOfGeometryChange();
|
||||
populatePositionAndLength();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScaleBegin(ScaleGestureDetector detector) {
|
||||
mState = PanZoomState.PINCHING;
|
||||
IntRect initialZoomRect = (IntRect)mController.getVisibleRect().clone();
|
||||
float initialZoom = mController.getZoomFactor();
|
||||
|
||||
mInitialZoomFocus = new IntPoint((int)Math.round(initialZoomRect.x + (detector.getFocusX() / initialZoom)),
|
||||
(int)Math.round(initialZoomRect.y + (detector.getFocusY() / initialZoom)));
|
||||
mInitialZoomSpan = detector.getCurrentSpan() / initialZoom;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScaleEnd(ScaleGestureDetector detector) {
|
||||
mState = PanZoomState.PANNING_HOLD;
|
||||
mLastTimestamp = System.currentTimeMillis();
|
||||
mX.touchPos = detector.getFocusX();
|
||||
mY.touchPos = detector.getFocusY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(MotionEvent motionEvent) {
|
||||
JSONObject ret = new JSONObject();
|
||||
try {
|
||||
IntPoint point = new IntPoint((int)Math.round(motionEvent.getX()),
|
||||
(int)Math.round(motionEvent.getY()));
|
||||
point = mController.convertViewPointToLayerPoint(point);
|
||||
ret.put("x", point.x);
|
||||
ret.put("y", point.y);
|
||||
Log.e(LOG_NAME, "Long press at " + motionEvent.getX() + ", " + motionEvent.getY());
|
||||
} catch(Exception ex) {
|
||||
Log.w(LOG_NAME, "Error building return: " + ex);
|
||||
}
|
||||
|
||||
GeckoEvent e = new GeckoEvent("Gesture:LongPress", ret.toString());
|
||||
GeckoAppShell.sendEventToGecko(e);
|
||||
}
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Android code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009-2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Patrick Walton <pcwalton@mozilla.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.fennec.ui;
|
||||
|
||||
import org.mozilla.fennec.gfx.IntPoint;
|
||||
import org.mozilla.fennec.gfx.IntRect;
|
||||
import org.mozilla.fennec.gfx.IntSize;
|
||||
import org.mozilla.fennec.gfx.LayerController;
|
||||
|
||||
/** Manages the dimensions of the page viewport. */
|
||||
public class ViewportController {
|
||||
private IntSize mPageSize;
|
||||
private IntRect mVisibleRect;
|
||||
|
||||
public ViewportController(IntSize pageSize, IntRect visibleRect) {
|
||||
mPageSize = pageSize;
|
||||
mVisibleRect = visibleRect;
|
||||
}
|
||||
|
||||
private int clamp(int min, int value, int max) {
|
||||
if (max < min)
|
||||
return min;
|
||||
return (value < min) ? min : (value > max) ? max : value;
|
||||
}
|
||||
|
||||
/** Returns the given rect, clamped to the boundaries of a tile. */
|
||||
public IntRect clampRect(IntRect rect) {
|
||||
int x = clamp(0, rect.x, mPageSize.width - LayerController.TILE_WIDTH);
|
||||
int y = clamp(0, rect.y, mPageSize.height - LayerController.TILE_HEIGHT);
|
||||
return new IntRect(x, y, rect.width, rect.height);
|
||||
}
|
||||
|
||||
/** Returns the coordinates of a tile centered on the given rect. */
|
||||
public static IntRect widenRect(IntRect rect) {
|
||||
IntPoint center = rect.getCenter();
|
||||
return new IntRect(center.x - LayerController.TILE_WIDTH / 2,
|
||||
center.y - LayerController.TILE_HEIGHT / 2,
|
||||
LayerController.TILE_WIDTH,
|
||||
LayerController.TILE_HEIGHT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the layer controller's visible rect, page size, and screen size, returns the zoom
|
||||
* factor.
|
||||
*/
|
||||
public float getZoomFactor(IntRect layerVisibleRect, IntSize layerPageSize,
|
||||
IntSize screenSize) {
|
||||
IntRect transformed = transformVisibleRect(layerVisibleRect, layerPageSize);
|
||||
return (float)screenSize.width / (float)transformed.width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the visible rectangle that the user is viewing and the layer controller's page size,
|
||||
* returns the dimensions of the box that this corresponds to on the page.
|
||||
*/
|
||||
public IntRect transformVisibleRect(IntRect layerVisibleRect, IntSize layerPageSize) {
|
||||
float zoomFactor = (float)layerPageSize.width / (float)mPageSize.width;
|
||||
return layerVisibleRect.scaleAll(1.0f / zoomFactor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the visible rectangle that the user is viewing and the layer controller's page size,
|
||||
* returns the dimensions in layer coordinates that this corresponds to.
|
||||
*/
|
||||
public IntRect untransformVisibleRect(IntRect viewportVisibleRect, IntSize layerPageSize) {
|
||||
float zoomFactor = (float)layerPageSize.width / (float)mPageSize.width;
|
||||
return viewportVisibleRect.scaleAll(zoomFactor);
|
||||
}
|
||||
|
||||
public IntSize getPageSize() { return mPageSize; }
|
||||
public void setPageSize(IntSize pageSize) { mPageSize = pageSize; }
|
||||
public IntRect getVisibleRect() { return mVisibleRect; }
|
||||
public void setVisibleRect(IntRect visibleRect) { mVisibleRect = visibleRect; }
|
||||
}
|
||||
|
@ -1869,7 +1869,6 @@ CreateSurfaceForWindow(nsIWidget *aWidget, EGLConfig config)
|
||||
{
|
||||
EGLSurface surface;
|
||||
|
||||
#ifdef PCWALTON_BROKEN
|
||||
|
||||
#ifdef DEBUG
|
||||
sEGLLibrary.DumpEGLConfig(config);
|
||||
@ -1888,8 +1887,6 @@ CreateSurfaceForWindow(nsIWidget *aWidget, EGLConfig config)
|
||||
printf_stderr("got surface %p\n", surface);
|
||||
#else
|
||||
surface = sEGLLibrary.fCreateWindowSurface(EGL_DISPLAY(), config, GET_NATIVE_WINDOW(aWidget), 0);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
return surface;
|
||||
|
@ -1986,7 +1986,7 @@ nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Since making new layers is expensive, only use nsDisplayScrollLayer
|
||||
// if the area is scrollable or a display port has been set.
|
||||
// if the area is scrollable.
|
||||
nsRect scrollRange = GetScrollRange();
|
||||
ScrollbarStyles styles = GetScrollbarStylesFromFrame();
|
||||
mShouldBuildLayer =
|
||||
@ -1997,7 +1997,7 @@ nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
scrollRange.height > 0) &&
|
||||
(!mIsRoot || !mOuter->PresContext()->IsRootContentDocument()));
|
||||
|
||||
if (usingDisplayport || ShouldBuildLayer()) {
|
||||
if (ShouldBuildLayer()) {
|
||||
// ScrollLayerWrapper must always be created because it initializes the
|
||||
// scroll layer count. The display lists depend on this.
|
||||
ScrollLayerWrapper wrapper(mOuter, mScrolledFrame);
|
||||
@ -2020,14 +2020,6 @@ nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
nsRect clip;
|
||||
clip = mScrollPort + aBuilder->ToReferenceFrame(mOuter);
|
||||
|
||||
// Make sure to expand the clipping rectangle if we're using a displayport
|
||||
if (usingDisplayport) {
|
||||
if (dirtyRect.width > clip.width)
|
||||
clip.width = dirtyRect.width;
|
||||
if (dirtyRect.height > clip.height)
|
||||
clip.height = dirtyRect.height;
|
||||
}
|
||||
|
||||
nscoord radii[8];
|
||||
// Our override of GetBorderRadii ensures we never have a radius at
|
||||
// the corners where we have a scrollbar.
|
||||
|
@ -417,24 +417,6 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
|
||||
nsDisplayList list;
|
||||
// Clip children to the child root frame's rectangle
|
||||
|
||||
// Expand the clip rectangle if this element has a display port set
|
||||
nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
|
||||
if (rootScrollFrame) {
|
||||
nsIContent* content = rootScrollFrame->GetContent();
|
||||
if (content) {
|
||||
nsRect displayPort;
|
||||
bool usingDisplayport =
|
||||
nsLayoutUtils::GetDisplayPort(content, &displayPort);
|
||||
if (usingDisplayport) {
|
||||
if (displayPort.width > subdocBoundsInParentUnits.width)
|
||||
subdocBoundsInParentUnits.width = displayPort.width;
|
||||
if (displayPort.height > subdocBoundsInParentUnits.height)
|
||||
subdocBoundsInParentUnits.height = displayPort.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rv = list.AppendNewToTop(
|
||||
new (aBuilder) nsDisplayClip(aBuilder, this, &childItems,
|
||||
subdocBoundsInParentUnits));
|
||||
|
@ -145,7 +145,6 @@ var BrowserApp = {
|
||||
Services.obs.addObserver(this, "Preferences:Set", false);
|
||||
Services.obs.addObserver(this, "ScrollTo:FocusedInput", false);
|
||||
Services.obs.addObserver(this, "Sanitize:ClearAll", false);
|
||||
Services.obs.addObserver(this, "PanZoom:PanZoom", false);
|
||||
|
||||
Services.obs.addObserver(XPInstallObserver, "addon-install-blocked", false);
|
||||
Services.obs.addObserver(XPInstallObserver, "addon-install-started", false);
|
||||
@ -243,12 +242,6 @@ var BrowserApp = {
|
||||
return null;
|
||||
},
|
||||
|
||||
getPageSizeForBrowser: function getPageSizeForBrowser(aBrowser) {
|
||||
let html = aBrowser.contentDocument.documentElement;
|
||||
let body = aBrowser.contentDocument.body;
|
||||
return { width: body.scrollWidth, height: body.scrollHeight };
|
||||
},
|
||||
|
||||
loadURI: function loadURI(aURI, aParams) {
|
||||
let browser = this.selectedBrowser;
|
||||
if (!browser)
|
||||
@ -472,22 +465,6 @@ var BrowserApp = {
|
||||
focused.scrollIntoView(false);
|
||||
},
|
||||
|
||||
panZoom: function(aData) {
|
||||
let data = JSON.parse(aData);
|
||||
|
||||
let browser = this.selectedBrowser;
|
||||
|
||||
dump("### JS side PanZoom to " + aData);
|
||||
|
||||
/*let documentElement = browser.contentDocument.documentElement;
|
||||
documentElement.style.MozTransform = 'translate(-' + data.x + 'px, -' + data.y + 'px) ' +
|
||||
'translate(-50%, -50%) scale(' + data.zoomFactor + ') ' +
|
||||
'translate(50%, 50%)';*/
|
||||
browser.contentWindow.scrollTo(data.x, data.y);
|
||||
|
||||
sendMessageToJava({ gecko: { type: "PanZoom:Ack", rect: data } });
|
||||
},
|
||||
|
||||
updateScrollbarsFor: function(aElement) {
|
||||
// only draw the scrollbars if we're scrolling the root content element
|
||||
let doc = this.selectedBrowser.contentDocument;
|
||||
@ -528,13 +505,6 @@ var BrowserApp = {
|
||||
this.horizScroller.setAttribute("panning", "");
|
||||
},
|
||||
|
||||
/* FIXME: Awful hack to tide us over until the display port is usable. */
|
||||
fakeDisplayPort: function(aBrowser) {
|
||||
let html = aBrowser.contentDocument.documentElement;
|
||||
html.style.width = '980px';
|
||||
html.style.height = '1500px';
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
let browser = this.selectedBrowser;
|
||||
if (!browser)
|
||||
@ -569,8 +539,6 @@ var BrowserApp = {
|
||||
this.scrollToFocusedInput(browser);
|
||||
} else if (aTopic == "Sanitize:ClearAll") {
|
||||
Sanitizer.sanitize();
|
||||
} else if (aTopic == "PanZoom:PanZoom") {
|
||||
this.panZoom(aData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -905,8 +873,6 @@ Tab.prototype = {
|
||||
|
||||
this.browser = document.createElement("browser");
|
||||
this.browser.setAttribute("type", "content");
|
||||
this.browser.setAttribute("width", "980");
|
||||
this.browser.setAttribute("height", "480");
|
||||
BrowserApp.deck.appendChild(this.browser);
|
||||
this.browser.stop();
|
||||
|
||||
@ -980,7 +946,7 @@ Tab.prototype = {
|
||||
state: aStateFlags
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
sendMessageToJava(message);
|
||||
}
|
||||
},
|
||||
@ -1089,11 +1055,12 @@ var BrowserEventHandler = {
|
||||
window.addEventListener("mouseup", this, true);
|
||||
window.addEventListener("mousemove", this, true);
|
||||
|
||||
BrowserApp.deck.addEventListener("MozMagnifyGestureStart", this, true);
|
||||
BrowserApp.deck.addEventListener("MozMagnifyGestureUpdate", this, true);
|
||||
BrowserApp.deck.addEventListener("DOMContentLoaded", this, true);
|
||||
BrowserApp.deck.addEventListener("DOMLinkAdded", this, true);
|
||||
BrowserApp.deck.addEventListener("DOMTitleChanged", this, true);
|
||||
BrowserApp.deck.addEventListener("DOMUpdatePageReport", PopupBlockerObserver.onUpdatePageReport, false);
|
||||
BrowserApp.deck.addEventListener("MozScrolledAreaChanged", this, true);
|
||||
},
|
||||
|
||||
handleEvent: function(aEvent) {
|
||||
@ -1121,14 +1088,11 @@ var BrowserEventHandler = {
|
||||
if (/^about:/.test(aEvent.originalTarget.documentURI)) {
|
||||
let browser = BrowserApp.getBrowserForDocument(aEvent.originalTarget);
|
||||
browser.addEventListener("click", ErrorPageEventHandler, false);
|
||||
browser.addEventListener("pagehide", function listener() {
|
||||
browser.addEventListener("pagehide", function () {
|
||||
browser.removeEventListener("click", ErrorPageEventHandler, false);
|
||||
browser.removeEventListener("pagehide", listener, true);
|
||||
browser.removeEventListener("pagehide", arguments.callee, true);
|
||||
}, true);
|
||||
}
|
||||
|
||||
BrowserApp.fakeDisplayPort(browser);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1292,6 +1256,9 @@ var BrowserEventHandler = {
|
||||
break;
|
||||
|
||||
case "mouseup":
|
||||
if (!this.panning)
|
||||
break;
|
||||
|
||||
this.panning = false;
|
||||
|
||||
// hide the scrollbars in case we're done scrolling. if the
|
||||
@ -1479,22 +1446,41 @@ var BrowserEventHandler = {
|
||||
}
|
||||
break;
|
||||
|
||||
case "MozScrolledAreaChanged":
|
||||
dump("### Resize!");
|
||||
case "MozMagnifyGestureStart":
|
||||
this._pinchDelta = 0;
|
||||
this.zoomCallbackFired = true;
|
||||
break;
|
||||
|
||||
/* TODO: Only for tab in foreground */
|
||||
let browser = BrowserApp.getBrowserForDocument(aEvent.target);
|
||||
if (!browser) {
|
||||
dump("### Resize: No browser!");
|
||||
return;
|
||||
case "MozMagnifyGestureUpdate":
|
||||
if (!aEvent.delta)
|
||||
break;
|
||||
|
||||
this._pinchDelta += aEvent.delta;
|
||||
|
||||
if ((Math.abs(this._pinchDelta) >= 1) && this.zoomCallbackFired) {
|
||||
// pinchDelta is the difference in pixels since the last call, so can
|
||||
// be viewed as the number of extra/fewer pixels visible.
|
||||
//
|
||||
// We can work out the new zoom level by looking at the window width
|
||||
// and height, and the existing zoom level.
|
||||
let currentZoom = BrowserApp.selectedBrowser.markupDocumentViewer.fullZoom;
|
||||
let currentSize = Math.sqrt(Math.pow(window.innerWidth, 2) + Math.pow(window.innerHeight, 2));
|
||||
let newZoom = ((currentSize * currentZoom) + this._pinchDelta) / currentSize;
|
||||
|
||||
let self = this;
|
||||
let callback = {
|
||||
onBeforePaint: function zoomCallback(timeStamp) {
|
||||
BrowserApp.selectedBrowser.markupDocumentViewer.fullZoom = newZoom;
|
||||
self.zoomCallbackFired = true;
|
||||
}
|
||||
};
|
||||
|
||||
this._pinchDelta = 0;
|
||||
|
||||
// Use mozRequestAnimationFrame to stop from flooding fullZoom
|
||||
this.zoomCallbackFired = false;
|
||||
window.mozRequestAnimationFrame(callback);
|
||||
}
|
||||
|
||||
sendMessageToJava({
|
||||
gecko: {
|
||||
type: "PanZoom:Resize",
|
||||
size: BrowserApp.getPageSizeForBrowser(browser)
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
},
|
||||
@ -1549,14 +1535,16 @@ var BrowserEventHandler = {
|
||||
/* Element is scrollable if its scroll-size exceeds its client size, and:
|
||||
* - It has overflow 'auto' or 'scroll'
|
||||
* - It's a textarea
|
||||
* We don't consider HTML/BODY nodes here, since Java pans those.
|
||||
* - It's an HTML/BODY node
|
||||
*/
|
||||
if (checkElem) {
|
||||
if (((elem.scrollHeight > elem.clientHeight) ||
|
||||
(elem.scrollWidth > elem.clientWidth)) &&
|
||||
(elem.style.overflow == 'auto' ||
|
||||
elem.style.overflow == 'scroll' ||
|
||||
elem.localName == 'textarea')) {
|
||||
elem.localName == 'textarea' ||
|
||||
elem.localName == 'html' ||
|
||||
elem.localName == 'body')) {
|
||||
scrollable = true;
|
||||
break;
|
||||
}
|
||||
|
@ -233,7 +233,6 @@ SHELL_WRAPPER1(nativeRun, jstring)
|
||||
SHELL_WRAPPER1(notifyGeckoOfEvent, jobject)
|
||||
SHELL_WRAPPER0(processNextNativeEvent)
|
||||
SHELL_WRAPPER1(setSurfaceView, jobject)
|
||||
SHELL_WRAPPER1(setSoftwareLayerClient, jobject)
|
||||
SHELL_WRAPPER0(onResume)
|
||||
SHELL_WRAPPER0(onLowMemory)
|
||||
SHELL_WRAPPER3(callObserver, jstring, jstring, jstring)
|
||||
@ -641,7 +640,6 @@ loadLibs(const char *apkName)
|
||||
GETFUNC(notifyGeckoOfEvent);
|
||||
GETFUNC(processNextNativeEvent);
|
||||
GETFUNC(setSurfaceView);
|
||||
GETFUNC(setSoftwareLayerClient);
|
||||
GETFUNC(onResume);
|
||||
GETFUNC(onLowMemory);
|
||||
GETFUNC(callObserver);
|
||||
|
@ -772,9 +772,9 @@ AndroidBridge::GetAccessibilityEnabled()
|
||||
}
|
||||
|
||||
void
|
||||
AndroidBridge::SetSoftwareLayerClient(jobject obj)
|
||||
AndroidBridge::SetSurfaceView(jobject obj)
|
||||
{
|
||||
mSoftwareLayerClient.Init(obj);
|
||||
mSurfaceView.Init(obj);
|
||||
}
|
||||
|
||||
void
|
||||
@ -784,6 +784,51 @@ AndroidBridge::ShowInputMethodPicker()
|
||||
mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jShowInputMethodPicker);
|
||||
}
|
||||
|
||||
void *
|
||||
AndroidBridge::CallEglCreateWindowSurface(void *dpy, void *config, AndroidGeckoSurfaceView &sview)
|
||||
{
|
||||
ALOG_BRIDGE("AndroidBridge::CallEglCreateWindowSurface");
|
||||
AutoLocalJNIFrame jniFrame;
|
||||
|
||||
/*
|
||||
* This is basically:
|
||||
*
|
||||
* s = EGLContext.getEGL().eglCreateWindowSurface(new EGLDisplayImpl(dpy),
|
||||
* new EGLConfigImpl(config),
|
||||
* view.getHolder(), null);
|
||||
* return s.mEGLSurface;
|
||||
*
|
||||
* We can't do it from java, because the EGLConfigImpl constructor is private.
|
||||
*/
|
||||
|
||||
jobject surfaceHolder = sview.GetSurfaceHolder();
|
||||
if (!surfaceHolder)
|
||||
return nsnull;
|
||||
|
||||
// grab some fields and methods we'll need
|
||||
jmethodID constructConfig = mJNIEnv->GetMethodID(jEGLConfigImplClass, "<init>", "(I)V");
|
||||
jmethodID constructDisplay = mJNIEnv->GetMethodID(jEGLDisplayImplClass, "<init>", "(I)V");
|
||||
|
||||
jmethodID getEgl = mJNIEnv->GetStaticMethodID(jEGLContextClass, "getEGL", "()Ljavax/microedition/khronos/egl/EGL;");
|
||||
jmethodID createWindowSurface = mJNIEnv->GetMethodID(jEGL10Class, "eglCreateWindowSurface", "(Ljavax/microedition/khronos/egl/EGLDisplay;Ljavax/microedition/khronos/egl/EGLConfig;Ljava/lang/Object;[I)Ljavax/microedition/khronos/egl/EGLSurface;");
|
||||
|
||||
jobject egl = mJNIEnv->CallStaticObjectMethod(jEGLContextClass, getEgl);
|
||||
|
||||
jobject jdpy = mJNIEnv->NewObject(jEGLDisplayImplClass, constructDisplay, (int) dpy);
|
||||
jobject jconf = mJNIEnv->NewObject(jEGLConfigImplClass, constructConfig, (int) config);
|
||||
|
||||
// make the call
|
||||
jobject surf = mJNIEnv->CallObjectMethod(egl, createWindowSurface, jdpy, jconf, surfaceHolder, NULL);
|
||||
if (!surf)
|
||||
return nsnull;
|
||||
|
||||
jfieldID sfield = mJNIEnv->GetFieldID(jEGLSurfaceImplClass, "mEGLSurface", "I");
|
||||
|
||||
jint realSurface = mJNIEnv->GetIntField(surf, sfield);
|
||||
|
||||
return (void*) realSurface;
|
||||
}
|
||||
|
||||
bool
|
||||
AndroidBridge::GetStaticIntField(const char *className, const char *fieldName, PRInt32* aInt)
|
||||
{
|
||||
|
@ -40,7 +40,6 @@
|
||||
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCOMArray.h"
|
||||
@ -150,8 +149,8 @@ public:
|
||||
|
||||
void ScheduleRestart();
|
||||
|
||||
void SetSoftwareLayerClient(jobject jobj);
|
||||
AndroidGeckoSoftwareLayerClient &GetSoftwareLayerClient() { return mSoftwareLayerClient; }
|
||||
void SetSurfaceView(jobject jobj);
|
||||
AndroidGeckoSurfaceView& SurfaceView() { return mSurfaceView; }
|
||||
|
||||
bool GetHandlersForURL(const char *aURL,
|
||||
nsIMutableArray* handlersArray = nsnull,
|
||||
@ -248,6 +247,9 @@ public:
|
||||
int mEntries;
|
||||
};
|
||||
|
||||
/* See GLHelpers.java as to why this is needed */
|
||||
void *CallEglCreateWindowSurface(void *dpy, void *config, AndroidGeckoSurfaceView& surfaceView);
|
||||
|
||||
bool GetStaticStringField(const char *classID, const char *field, nsAString &result);
|
||||
|
||||
bool GetStaticIntField(const char *className, const char *fieldName, PRInt32* aInt);
|
||||
@ -312,8 +314,8 @@ protected:
|
||||
JNIEnv *mJNIEnv;
|
||||
void *mThread;
|
||||
|
||||
// the software rendering layer client
|
||||
AndroidGeckoSoftwareLayerClient mSoftwareLayerClient;
|
||||
// the GeckoSurfaceView
|
||||
AndroidGeckoSurfaceView mSurfaceView;
|
||||
|
||||
// the GeckoAppShell java class
|
||||
jclass mGeckoAppShellClass;
|
||||
|
@ -44,7 +44,6 @@
|
||||
#include <jni.h>
|
||||
#include <pthread.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nsAppShell.h"
|
||||
#include "nsWindow.h"
|
||||
@ -68,7 +67,7 @@ extern "C" {
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_nativeInit(JNIEnv *, jclass);
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyGeckoOfEvent(JNIEnv *, jclass, jobject event);
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_processNextNativeEvent(JNIEnv *, jclass);
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_setSoftwareLayerClient(JNIEnv *jenv, jclass, jobject sv);
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_setSurfaceView(JNIEnv *jenv, jclass, jobject sv);
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onResume(JNIEnv *, jclass);
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onLowMemory(JNIEnv *, jclass);
|
||||
NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_callObserver(JNIEnv *, jclass, jstring observerKey, jstring topic, jstring data);
|
||||
@ -95,9 +94,8 @@ NS_EXPORT void JNICALL
|
||||
Java_org_mozilla_gecko_GeckoAppShell_notifyGeckoOfEvent(JNIEnv *jenv, jclass jc, jobject event)
|
||||
{
|
||||
// poke the appshell
|
||||
if (nsAppShell::gAppShell) {
|
||||
if (nsAppShell::gAppShell)
|
||||
nsAppShell::gAppShell->PostEvent(new AndroidGeckoEvent(jenv, event));
|
||||
}
|
||||
}
|
||||
|
||||
NS_EXPORT void JNICALL
|
||||
@ -109,9 +107,9 @@ Java_org_mozilla_gecko_GeckoAppShell_processNextNativeEvent(JNIEnv *jenv, jclass
|
||||
}
|
||||
|
||||
NS_EXPORT void JNICALL
|
||||
Java_org_mozilla_gecko_GeckoAppShell_setSoftwareLayerClient(JNIEnv *jenv, jclass, jobject obj)
|
||||
Java_org_mozilla_gecko_GeckoAppShell_setSurfaceView(JNIEnv *jenv, jclass, jobject obj)
|
||||
{
|
||||
AndroidBridge::Bridge()->SetSoftwareLayerClient(jenv->NewGlobalRef(obj));
|
||||
AndroidBridge::Bridge()->SetSurfaceView(jenv->NewGlobalRef(obj));
|
||||
}
|
||||
|
||||
NS_EXPORT void JNICALL
|
||||
|
@ -103,11 +103,15 @@ jmethodID AndroidAddress::jGetSubLocalityMethod;
|
||||
jmethodID AndroidAddress::jGetSubThoroughfareMethod;
|
||||
jmethodID AndroidAddress::jGetThoroughfareMethod;
|
||||
|
||||
jclass AndroidGeckoSoftwareLayerClient::jGeckoSoftwareLayerClientClass = 0;
|
||||
jmethodID AndroidGeckoSoftwareLayerClient::jLockBufferMethod = 0;
|
||||
jmethodID AndroidGeckoSoftwareLayerClient::jUnlockBufferMethod = 0;
|
||||
jmethodID AndroidGeckoSoftwareLayerClient::jBeginDrawingMethod = 0;
|
||||
jmethodID AndroidGeckoSoftwareLayerClient::jEndDrawingMethod = 0;
|
||||
jclass AndroidGeckoSurfaceView::jGeckoSurfaceViewClass = 0;
|
||||
jmethodID AndroidGeckoSurfaceView::jBeginDrawingMethod = 0;
|
||||
jmethodID AndroidGeckoSurfaceView::jEndDrawingMethod = 0;
|
||||
jmethodID AndroidGeckoSurfaceView::jDraw2DBitmapMethod = 0;
|
||||
jmethodID AndroidGeckoSurfaceView::jDraw2DBufferMethod = 0;
|
||||
jmethodID AndroidGeckoSurfaceView::jGetSoftwareDrawBitmapMethod = 0;
|
||||
jmethodID AndroidGeckoSurfaceView::jGetSoftwareDrawBufferMethod = 0;
|
||||
jmethodID AndroidGeckoSurfaceView::jGetSurfaceMethod = 0;
|
||||
jmethodID AndroidGeckoSurfaceView::jGetHolderMethod = 0;
|
||||
|
||||
#define JNI() (AndroidBridge::JNI())
|
||||
|
||||
@ -127,11 +131,10 @@ void
|
||||
mozilla::InitAndroidJavaWrappers(JNIEnv *jEnv)
|
||||
{
|
||||
AndroidGeckoEvent::InitGeckoEventClass(jEnv);
|
||||
AndroidGeckoSurfaceView::InitGeckoSurfaceViewClass(jEnv);
|
||||
AndroidPoint::InitPointClass(jEnv);
|
||||
AndroidRect::InitRectClass(jEnv);
|
||||
AndroidLocation::InitLocationClass(jEnv);
|
||||
AndroidAddress::InitAddressClass(jEnv);
|
||||
AndroidGeckoSoftwareLayerClient::InitGeckoSoftwareLayerClientClass(jEnv);
|
||||
}
|
||||
|
||||
void
|
||||
@ -170,6 +173,23 @@ AndroidGeckoEvent::InitGeckoEventClass(JNIEnv *jEnv)
|
||||
jAddressField = getField("mAddress", "Landroid/location/Address;");
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGeckoSurfaceView::InitGeckoSurfaceViewClass(JNIEnv *jEnv)
|
||||
{
|
||||
initInit();
|
||||
|
||||
jGeckoSurfaceViewClass = getClassGlobalRef("org/mozilla/gecko/GeckoSurfaceView");
|
||||
|
||||
jBeginDrawingMethod = getMethod("beginDrawing", "()I");
|
||||
jGetSoftwareDrawBitmapMethod = getMethod("getSoftwareDrawBitmap", "()Landroid/graphics/Bitmap;");
|
||||
jGetSoftwareDrawBufferMethod = getMethod("getSoftwareDrawBuffer", "()Ljava/nio/ByteBuffer;");
|
||||
jEndDrawingMethod = getMethod("endDrawing", "()V");
|
||||
jDraw2DBitmapMethod = getMethod("draw2D", "(Landroid/graphics/Bitmap;II)V");
|
||||
jDraw2DBufferMethod = getMethod("draw2D", "(Ljava/nio/ByteBuffer;I)V");
|
||||
jGetSurfaceMethod = getMethod("getSurface", "()Landroid/view/Surface;");
|
||||
jGetHolderMethod = getMethod("getHolder", "()Landroid/view/SurfaceHolder;");
|
||||
}
|
||||
|
||||
void
|
||||
AndroidLocation::InitLocationClass(JNIEnv *jEnv)
|
||||
{
|
||||
@ -284,20 +304,6 @@ AndroidRect::InitRectClass(JNIEnv *jEnv)
|
||||
jRightField = getField("right", "I");
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGeckoSoftwareLayerClient::InitGeckoSoftwareLayerClientClass(JNIEnv *jEnv)
|
||||
{
|
||||
initInit();
|
||||
|
||||
jGeckoSoftwareLayerClientClass =
|
||||
getClassGlobalRef("org/mozilla/fennec/gfx/GeckoSoftwareLayerClient");
|
||||
|
||||
jLockBufferMethod = getMethod("lockBuffer", "()Ljava/nio/ByteBuffer;");
|
||||
jUnlockBufferMethod = getMethod("unlockBuffer", "()V");
|
||||
jBeginDrawingMethod = getMethod("beginDrawing", "()V");
|
||||
jEndDrawingMethod = getMethod("endDrawing", "(IIII)V");
|
||||
}
|
||||
|
||||
#undef initInit
|
||||
#undef initClassGlobalRef
|
||||
#undef getField
|
||||
@ -417,9 +423,7 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
|
||||
break;
|
||||
|
||||
case DRAW:
|
||||
ALOG("### Draw, before ReadRectField");
|
||||
ReadRectField(jenv);
|
||||
ALOG("### Draw, after ReadRectField");
|
||||
break;
|
||||
|
||||
case ORIENTATION_EVENT:
|
||||
@ -476,10 +480,10 @@ AndroidGeckoEvent::Init(int aType)
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGeckoEvent::Init(int aType, const nsIntRect &aRect)
|
||||
AndroidGeckoEvent::Init(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
mType = aType;
|
||||
mRect = aRect;
|
||||
mType = DRAW;
|
||||
mRect.SetEmpty();
|
||||
}
|
||||
|
||||
void
|
||||
@ -495,6 +499,64 @@ AndroidGeckoEvent::Init(AndroidGeckoEvent *aResizeEvent)
|
||||
mP1.y = aResizeEvent->mP1.y;
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGeckoSurfaceView::Init(jobject jobj)
|
||||
{
|
||||
NS_ASSERTION(wrapped_obj == nsnull, "Init called on non-null wrapped_obj!");
|
||||
|
||||
wrapped_obj = jobj;
|
||||
}
|
||||
|
||||
int
|
||||
AndroidGeckoSurfaceView::BeginDrawing()
|
||||
{
|
||||
NS_ASSERTION(!isNull(), "BeginDrawing called on null surfaceview!");
|
||||
|
||||
return JNI()->CallIntMethod(wrapped_obj, jBeginDrawingMethod);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGeckoSurfaceView::EndDrawing()
|
||||
{
|
||||
JNI()->CallVoidMethod(wrapped_obj, jEndDrawingMethod);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGeckoSurfaceView::Draw2D(jobject bitmap, int width, int height)
|
||||
{
|
||||
JNI()->CallVoidMethod(wrapped_obj, jDraw2DBitmapMethod, bitmap, width, height);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGeckoSurfaceView::Draw2D(jobject buffer, int stride)
|
||||
{
|
||||
JNI()->CallVoidMethod(wrapped_obj, jDraw2DBufferMethod, buffer, stride);
|
||||
}
|
||||
|
||||
jobject
|
||||
AndroidGeckoSurfaceView::GetSoftwareDrawBitmap()
|
||||
{
|
||||
return JNI()->CallObjectMethod(wrapped_obj, jGetSoftwareDrawBitmapMethod);
|
||||
}
|
||||
|
||||
jobject
|
||||
AndroidGeckoSurfaceView::GetSoftwareDrawBuffer()
|
||||
{
|
||||
return JNI()->CallObjectMethod(wrapped_obj, jGetSoftwareDrawBufferMethod);
|
||||
}
|
||||
|
||||
jobject
|
||||
AndroidGeckoSurfaceView::GetSurface()
|
||||
{
|
||||
return JNI()->CallObjectMethod(wrapped_obj, jGetSurfaceMethod);
|
||||
}
|
||||
|
||||
jobject
|
||||
AndroidGeckoSurfaceView::GetSurfaceHolder()
|
||||
{
|
||||
return JNI()->CallObjectMethod(wrapped_obj, jGetHolderMethod);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidPoint::Init(JNIEnv *jenv, jobject jobj)
|
||||
{
|
||||
@ -511,73 +573,18 @@ AndroidPoint::Init(JNIEnv *jenv, jobject jobj)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGeckoSoftwareLayerClient::Init(jobject jobj)
|
||||
{
|
||||
NS_ASSERTION(wrapped_obj == nsnull, "Init called on non-null wrapped_obj!");
|
||||
|
||||
wrapped_obj = jobj;
|
||||
}
|
||||
|
||||
jobject
|
||||
AndroidGeckoSoftwareLayerClient::LockBuffer()
|
||||
{
|
||||
NS_ASSERTION(!isNull(), "LockBuffer() called on null software layer client!");
|
||||
|
||||
return JNI()->CallObjectMethod(wrapped_obj, jLockBufferMethod);
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
AndroidGeckoSoftwareLayerClient::LockBufferBits()
|
||||
{
|
||||
return reinterpret_cast<unsigned char *>(JNI()->GetDirectBufferAddress(LockBuffer()));
|
||||
}
|
||||
|
||||
jobject
|
||||
AndroidGeckoSoftwareLayerClient::UnlockBuffer()
|
||||
{
|
||||
NS_ASSERTION(!isNull(), "UnlockBuffer() called on null software layer client!");
|
||||
|
||||
return JNI()->CallObjectMethod(wrapped_obj, jUnlockBufferMethod);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGeckoSoftwareLayerClient::BeginDrawing()
|
||||
{
|
||||
NS_ASSERTION(!isNull(), "BeginDrawing() called on null software layer client!");
|
||||
|
||||
return JNI()->CallVoidMethod(wrapped_obj, jBeginDrawingMethod);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidGeckoSoftwareLayerClient::EndDrawing(const nsIntRect &aRect)
|
||||
{
|
||||
NS_ASSERTION(!isNull(), "EndDrawing() called on null software layer client!");
|
||||
|
||||
return JNI()->CallVoidMethod(wrapped_obj, jEndDrawingMethod, aRect.x, aRect.y, aRect.width,
|
||||
aRect.height);
|
||||
}
|
||||
|
||||
void
|
||||
AndroidRect::Init(JNIEnv *jenv, jobject jobj)
|
||||
{
|
||||
NS_ASSERTION(wrapped_obj == nsnull, "Init called on non-null wrapped_obj!");
|
||||
|
||||
ALOG("AndroidRect::Init point a");
|
||||
|
||||
wrapped_obj = jobj;
|
||||
|
||||
ALOG("AndroidRect::Init point b");
|
||||
|
||||
if (jobj) {
|
||||
ALOG("AndroidRect::Init point c");
|
||||
|
||||
mTop = jenv->GetIntField(jobj, jTopField);
|
||||
mLeft = jenv->GetIntField(jobj, jLeftField);
|
||||
mRight = jenv->GetIntField(jobj, jRightField);
|
||||
mBottom = jenv->GetIntField(jobj, jBottomField);
|
||||
|
||||
ALOG("AndroidRect::Init point d");
|
||||
} else {
|
||||
mTop = 0;
|
||||
mLeft = 0;
|
||||
|
@ -149,27 +149,48 @@ protected:
|
||||
static jfieldID jTopField;
|
||||
};
|
||||
|
||||
class AndroidGeckoSoftwareLayerClient : public WrappedJavaObject {
|
||||
class AndroidGeckoSurfaceView : public WrappedJavaObject
|
||||
{
|
||||
public:
|
||||
static void InitGeckoSoftwareLayerClientClass(JNIEnv *jEnv);
|
||||
static void InitGeckoSurfaceViewClass(JNIEnv *jEnv);
|
||||
|
||||
AndroidGeckoSurfaceView() { }
|
||||
AndroidGeckoSurfaceView(jobject jobj) {
|
||||
Init(jobj);
|
||||
}
|
||||
|
||||
void Init(jobject jobj);
|
||||
|
||||
AndroidGeckoSoftwareLayerClient() {}
|
||||
AndroidGeckoSoftwareLayerClient(jobject jobj) { Init(jobj); }
|
||||
enum {
|
||||
DRAW_ERROR = 0,
|
||||
DRAW_GLES_2 = 1,
|
||||
DRAW_2D = 2,
|
||||
DRAW_DISABLED = 3
|
||||
};
|
||||
|
||||
jobject LockBuffer();
|
||||
unsigned char *LockBufferBits();
|
||||
jobject UnlockBuffer();
|
||||
void BeginDrawing();
|
||||
void EndDrawing(const nsIntRect &aRect);
|
||||
int BeginDrawing();
|
||||
jobject GetSoftwareDrawBitmap();
|
||||
jobject GetSoftwareDrawBuffer();
|
||||
void EndDrawing();
|
||||
void Draw2D(jobject bitmap, int width, int height);
|
||||
void Draw2D(jobject buffer, int stride);
|
||||
|
||||
private:
|
||||
static jclass jGeckoSoftwareLayerClientClass;
|
||||
static jmethodID jLockBufferMethod;
|
||||
static jmethodID jUnlockBufferMethod;
|
||||
jobject GetSurface();
|
||||
|
||||
// must have a JNI local frame when calling this,
|
||||
// and you'd better know what you're doing
|
||||
jobject GetSurfaceHolder();
|
||||
|
||||
protected:
|
||||
static jclass jGeckoSurfaceViewClass;
|
||||
static jmethodID jBeginDrawingMethod;
|
||||
static jmethodID jEndDrawingMethod;
|
||||
static jmethodID jDraw2DBitmapMethod;
|
||||
static jmethodID jDraw2DBufferMethod;
|
||||
static jmethodID jGetSoftwareDrawBitmapMethod;
|
||||
static jmethodID jGetSoftwareDrawBufferMethod;
|
||||
static jmethodID jGetSurfaceMethod;
|
||||
static jmethodID jGetHolderMethod;
|
||||
};
|
||||
|
||||
class AndroidKeyEvent
|
||||
@ -364,8 +385,8 @@ public:
|
||||
AndroidGeckoEvent(int aType) {
|
||||
Init(aType);
|
||||
}
|
||||
AndroidGeckoEvent(int aType, const nsIntRect &aRect) {
|
||||
Init(aType, aRect);
|
||||
AndroidGeckoEvent(int x1, int y1, int x2, int y2) {
|
||||
Init(x1, y1, x2, y2);
|
||||
}
|
||||
AndroidGeckoEvent(JNIEnv *jenv, jobject jobj) {
|
||||
Init(jenv, jobj);
|
||||
@ -376,7 +397,7 @@ public:
|
||||
|
||||
void Init(JNIEnv *jenv, jobject jobj);
|
||||
void Init(int aType);
|
||||
void Init(int aType, const nsIntRect &aRect);
|
||||
void Init(int x1, int y1, int x2, int y2);
|
||||
void Init(AndroidGeckoEvent *aResizeEvent);
|
||||
|
||||
int Action() { return mAction; }
|
||||
|
@ -60,8 +60,6 @@
|
||||
#include "prlog.h"
|
||||
#endif
|
||||
|
||||
#define DEBUG_ANDROID_EVENTS 1
|
||||
|
||||
#ifdef DEBUG_ANDROID_EVENTS
|
||||
#define EVLOG(args...) ALOG(args)
|
||||
#else
|
||||
|
@ -76,12 +76,6 @@ using mozilla::unused;
|
||||
|
||||
#include "imgIEncoder.h"
|
||||
|
||||
#include "nsStringGlue.h"
|
||||
|
||||
// NB: Keep these in sync with LayerController.java in embedding/android/.
|
||||
#define TILE_WIDTH 1024
|
||||
#define TILE_HEIGHT 2048
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, nsBaseWidget)
|
||||
@ -141,6 +135,7 @@ static nsRefPtr<gl::GLContext> sGLContext;
|
||||
static bool sFailedToCreateGLContext = false;
|
||||
static bool sValidSurface;
|
||||
static bool sSurfaceExists = false;
|
||||
static void *sNativeWindow = nsnull;
|
||||
|
||||
// Multitouch swipe thresholds in inches
|
||||
static const double SWIPE_MAX_PINCH_DELTA_INCHES = 0.4;
|
||||
@ -301,15 +296,6 @@ nsWindow::ConfigureChildren(const nsTArray<nsIWidget::Configuration>& config)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsWindow::RedrawAll()
|
||||
{
|
||||
nsIntRect entireRect(0, 0, TILE_WIDTH, TILE_HEIGHT);
|
||||
AndroidGeckoEvent *event = new AndroidGeckoEvent(AndroidGeckoEvent::DRAW,
|
||||
entireRect);
|
||||
nsAppShell::gAppShell->PostEvent(event);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindow::SetParent(nsIWidget *aNewParent)
|
||||
{
|
||||
@ -330,7 +316,7 @@ nsWindow::SetParent(nsIWidget *aNewParent)
|
||||
|
||||
// if we are now in the toplevel window's hierarchy, schedule a redraw
|
||||
if (FindTopLevel() == TopWindow())
|
||||
RedrawAll();
|
||||
nsAppShell::gAppShell->PostEvent(new AndroidGeckoEvent(-1, -1, -1, -1));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -398,7 +384,7 @@ nsWindow::Show(bool aState)
|
||||
}
|
||||
}
|
||||
} else if (FindTopLevel() == TopWindow()) {
|
||||
RedrawAll();
|
||||
nsAppShell::gAppShell->PostEvent(new AndroidGeckoEvent(-1, -1, -1, -1));
|
||||
}
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
@ -525,7 +511,7 @@ nsWindow::Resize(PRInt32 aX,
|
||||
|
||||
// Should we skip honoring aRepaint here?
|
||||
if (aRepaint && FindTopLevel() == TopWindow())
|
||||
RedrawAll();
|
||||
nsAppShell::gAppShell->PostEvent(new AndroidGeckoEvent(-1, -1, -1, -1));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -578,8 +564,7 @@ NS_IMETHODIMP
|
||||
nsWindow::Invalidate(const nsIntRect &aRect,
|
||||
bool aIsSynchronous)
|
||||
{
|
||||
AndroidGeckoEvent *event = new AndroidGeckoEvent(AndroidGeckoEvent::DRAW, aRect);
|
||||
nsAppShell::gAppShell->PostEvent(event);
|
||||
nsAppShell::gAppShell->PostEvent(new AndroidGeckoEvent(-1, -1, -1, -1));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -648,7 +633,7 @@ nsWindow::BringToFront()
|
||||
|
||||
// force a window resize
|
||||
nsAppShell::gAppShell->ResendLastResizeEvent(this);
|
||||
RedrawAll();
|
||||
nsAppShell::gAppShell->PostEvent(new AndroidGeckoEvent(-1, -1, -1, -1));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -812,8 +797,7 @@ nsWindow::DrawToFile(const nsAString &path)
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
nsIntRect boundsRect(0, 0, mBounds.width, mBounds.height);
|
||||
bool result = DrawTo(imgSurface, boundsRect);
|
||||
bool result = DrawTo(imgSurface);
|
||||
NS_ENSURE_TRUE(result, PR_FALSE);
|
||||
|
||||
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance("@mozilla.org/image/encoder;2?type=image/png");
|
||||
@ -973,12 +957,27 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae)
|
||||
|
||||
case AndroidGeckoEvent::SURFACE_CREATED:
|
||||
sSurfaceExists = true;
|
||||
|
||||
if (AndroidBridge::Bridge()->HasNativeWindowAccess()) {
|
||||
AndroidGeckoSurfaceView& sview(AndroidBridge::Bridge()->SurfaceView());
|
||||
jobject surface = sview.GetSurface();
|
||||
if (surface) {
|
||||
sNativeWindow = AndroidBridge::Bridge()->AcquireNativeWindow(surface);
|
||||
if (sNativeWindow) {
|
||||
AndroidBridge::Bridge()->SetNativeWindowFormat(sNativeWindow, AndroidBridge::WINDOW_FORMAT_RGB_565);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AndroidGeckoEvent::SURFACE_DESTROYED:
|
||||
if (sGLContext && sValidSurface) {
|
||||
sGLContext->ReleaseSurface();
|
||||
}
|
||||
if (sNativeWindow) {
|
||||
AndroidBridge::Bridge()->ReleaseNativeWindow(sNativeWindow);
|
||||
sNativeWindow = nsnull;
|
||||
}
|
||||
sSurfaceExists = false;
|
||||
sValidSurface = false;
|
||||
break;
|
||||
@ -1014,15 +1013,11 @@ nsWindow::OnAndroidEvent(AndroidGeckoEvent *ae)
|
||||
}
|
||||
|
||||
bool
|
||||
nsWindow::DrawTo(gfxASurface *targetSurface, const nsIntRect &invalidRect)
|
||||
nsWindow::DrawTo(gfxASurface *targetSurface)
|
||||
{
|
||||
if (!mIsVisible)
|
||||
return false;
|
||||
|
||||
nsWindowType windowType;
|
||||
GetWindowType(windowType);
|
||||
ALOG("Window type is %d", (int)windowType);
|
||||
|
||||
nsEventStatus status;
|
||||
nsIntRect boundsRect(0, 0, mBounds.width, mBounds.height);
|
||||
|
||||
@ -1040,7 +1035,7 @@ nsWindow::DrawTo(gfxASurface *targetSurface, const nsIntRect &invalidRect)
|
||||
// If we have no covering child, then we need to render this.
|
||||
if (coveringChildIndex == -1) {
|
||||
nsPaintEvent event(true, NS_PAINT, this);
|
||||
event.region = boundsRect.Intersect(invalidRect);
|
||||
event.region = boundsRect;
|
||||
switch (GetLayerManager(nsnull)->GetBackendType()) {
|
||||
case LayerManager::LAYERS_BASIC: {
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(targetSurface);
|
||||
@ -1095,7 +1090,7 @@ nsWindow::DrawTo(gfxASurface *targetSurface, const nsIntRect &invalidRect)
|
||||
targetSurface->SetDeviceOffset(offset + gfxPoint(mChildren[i]->mBounds.x,
|
||||
mChildren[i]->mBounds.y));
|
||||
|
||||
bool ok = mChildren[i]->DrawTo(targetSurface, invalidRect);
|
||||
bool ok = mChildren[i]->DrawTo(targetSurface);
|
||||
|
||||
if (!ok) {
|
||||
ALOG("nsWindow[%p]::DrawTo child %d[%p] returned FALSE!", (void*) this, i, (void*)mChildren[i]);
|
||||
@ -1111,6 +1106,11 @@ nsWindow::DrawTo(gfxASurface *targetSurface, const nsIntRect &invalidRect)
|
||||
void
|
||||
nsWindow::OnDraw(AndroidGeckoEvent *ae)
|
||||
{
|
||||
|
||||
if (!sSurfaceExists) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsTopLevel()) {
|
||||
ALOG("##### redraw for window %p, which is not a toplevel window -- sending to toplevel!", (void*) this);
|
||||
DumpWindows();
|
||||
@ -1125,27 +1125,96 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
|
||||
|
||||
AndroidBridge::AutoLocalJNIFrame jniFrame;
|
||||
|
||||
AndroidGeckoSurfaceView& sview(AndroidBridge::Bridge()->SurfaceView());
|
||||
|
||||
NS_ASSERTION(!sview.isNull(), "SurfaceView is null!");
|
||||
|
||||
if (GetLayerManager(nsnull)->GetBackendType() == LayerManager::LAYERS_BASIC) {
|
||||
AndroidGeckoSoftwareLayerClient &client =
|
||||
AndroidBridge::Bridge()->GetSoftwareLayerClient();
|
||||
if (sNativeWindow) {
|
||||
unsigned char *bits;
|
||||
int width, height, format, stride;
|
||||
if (!AndroidBridge::Bridge()->LockWindow(sNativeWindow, &bits, &width, &height, &format, &stride)) {
|
||||
ALOG("failed to lock buffer - skipping draw");
|
||||
return;
|
||||
}
|
||||
|
||||
client.BeginDrawing();
|
||||
unsigned char *bits = client.LockBufferBits();
|
||||
if (!bits || format != AndroidBridge::WINDOW_FORMAT_RGB_565 ||
|
||||
width != mBounds.width || height != mBounds.height) {
|
||||
|
||||
nsRefPtr<gfxImageSurface> targetSurface =
|
||||
new gfxImageSurface(bits, gfxIntSize(TILE_WIDTH, TILE_HEIGHT), TILE_WIDTH * 2,
|
||||
gfxASurface::ImageFormatRGB16_565);
|
||||
if (targetSurface->CairoStatus()) {
|
||||
ALOG("### Failed to create a valid surface from the bitmap");
|
||||
ALOG("surface is not expected dimensions or format - skipping draw");
|
||||
AndroidBridge::Bridge()->UnlockWindow(sNativeWindow);
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxImageSurface> targetSurface =
|
||||
new gfxImageSurface(bits,
|
||||
gfxIntSize(mBounds.width, mBounds.height),
|
||||
stride * 2,
|
||||
gfxASurface::ImageFormatRGB16_565);
|
||||
if (targetSurface->CairoStatus()) {
|
||||
ALOG("### Failed to create a valid surface from the bitmap");
|
||||
} else {
|
||||
DrawTo(targetSurface);
|
||||
}
|
||||
|
||||
AndroidBridge::Bridge()->UnlockWindow(sNativeWindow);
|
||||
} else if (AndroidBridge::Bridge()->HasNativeBitmapAccess()) {
|
||||
jobject bitmap = sview.GetSoftwareDrawBitmap();
|
||||
if (!bitmap) {
|
||||
ALOG("no bitmap to draw into - skipping draw");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AndroidBridge::Bridge()->ValidateBitmap(bitmap, mBounds.width, mBounds.height))
|
||||
return;
|
||||
|
||||
void *buf = AndroidBridge::Bridge()->LockBitmap(bitmap);
|
||||
if (buf == nsnull) {
|
||||
ALOG("### Software drawing, but failed to lock bitmap.");
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxImageSurface> targetSurface =
|
||||
new gfxImageSurface((unsigned char *)buf,
|
||||
gfxIntSize(mBounds.width, mBounds.height),
|
||||
mBounds.width * 2,
|
||||
gfxASurface::ImageFormatRGB16_565);
|
||||
if (targetSurface->CairoStatus()) {
|
||||
ALOG("### Failed to create a valid surface from the bitmap");
|
||||
} else {
|
||||
DrawTo(targetSurface);
|
||||
}
|
||||
|
||||
AndroidBridge::Bridge()->UnlockBitmap(bitmap);
|
||||
sview.Draw2D(bitmap, mBounds.width, mBounds.height);
|
||||
} else {
|
||||
DrawTo(targetSurface, ae->Rect());
|
||||
}
|
||||
jobject bytebuf = sview.GetSoftwareDrawBuffer();
|
||||
if (!bytebuf) {
|
||||
ALOG("no buffer to draw into - skipping draw");
|
||||
return;
|
||||
}
|
||||
|
||||
client.UnlockBuffer();
|
||||
client.EndDrawing(ae->Rect());
|
||||
void *buf = AndroidBridge::JNI()->GetDirectBufferAddress(bytebuf);
|
||||
int cap = AndroidBridge::JNI()->GetDirectBufferCapacity(bytebuf);
|
||||
if (!buf || cap != (mBounds.width * mBounds.height * 2)) {
|
||||
ALOG("### Software drawing, but unexpected buffer size %d expected %d (or no buffer %p)!", cap, mBounds.width * mBounds.height * 2, buf);
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxImageSurface> targetSurface =
|
||||
new gfxImageSurface((unsigned char *)buf,
|
||||
gfxIntSize(mBounds.width, mBounds.height),
|
||||
mBounds.width * 2,
|
||||
gfxASurface::ImageFormatRGB16_565);
|
||||
if (targetSurface->CairoStatus()) {
|
||||
ALOG("### Failed to create a valid surface");
|
||||
} else {
|
||||
DrawTo(targetSurface);
|
||||
}
|
||||
|
||||
sview.Draw2D(bytebuf, mBounds.width * 2);
|
||||
}
|
||||
} else {
|
||||
ALOG("### GL layers are disabled for now in the native UI Fennec");
|
||||
#if 0
|
||||
int drawType = sview.BeginDrawing();
|
||||
|
||||
if (drawType == AndroidGeckoSurfaceView::DRAW_DISABLED) {
|
||||
@ -1165,10 +1234,9 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
|
||||
|
||||
NS_ASSERTION(sGLContext, "Drawing with GLES without a GL context?");
|
||||
|
||||
DrawTo(nsnull, ae->P0(), ae->Alpha());
|
||||
DrawTo(nsnull);
|
||||
|
||||
sview.EndDrawing();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,7 +178,7 @@ public:
|
||||
protected:
|
||||
void BringToFront();
|
||||
nsWindow *FindTopLevel();
|
||||
bool DrawTo(gfxASurface *targetSurface, const nsIntRect &aRect);
|
||||
bool DrawTo(gfxASurface *targetSurface);
|
||||
bool DrawToFile(const nsAString &path);
|
||||
bool IsTopLevel();
|
||||
void OnIMEAddRange(mozilla::AndroidGeckoEvent *ae);
|
||||
@ -220,7 +220,6 @@ private:
|
||||
void DispatchGestureEvent(PRUint32 msg, PRUint32 direction, double delta,
|
||||
const nsIntPoint &refPoint, PRUint64 time);
|
||||
void HandleSpecialKey(mozilla::AndroidGeckoEvent *ae);
|
||||
void RedrawAll();
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
nsRefPtr<nsAccessible> mRootAccessible;
|
||||
|
Loading…
Reference in New Issue
Block a user