зеркало из https://github.com/mozilla/pjs.git
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:
Родитель
60bbbf48d6
Коммит
359b61ec2b
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Двоичные данные
embedding/android/resources/drawable/checkerboard.png
Двоичные данные
embedding/android/resources/drawable/checkerboard.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 199 B |
Двоичные данные
embedding/android/resources/drawable/pattern.png
Двоичные данные
embedding/android/resources/drawable/pattern.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 12 KiB |
Двоичные данные
embedding/android/resources/drawable/shadow.png
Двоичные данные
embedding/android/resources/drawable/shadow.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче