diff --git a/embedding/android/GeckoApp.java b/embedding/android/GeckoApp.java index bb7920f48f6..aff54e7bd3e 100644 --- a/embedding/android/GeckoApp.java +++ b/embedding/android/GeckoApp.java @@ -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 sExtraMenuItems = new Vector(); - 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 { - protected Void doInBackground(Location... location) { - try { - List
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); - } } diff --git a/embedding/android/GeckoAppShell.java b/embedding/android/GeckoAppShell.java index 563dd1abf67..b5be98b3c17 100644 --- a/embedding/android/GeckoAppShell.java +++ b/embedding/android/GeckoAppShell.java @@ -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 mAlertNotifications = new HashMap(); + 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 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); diff --git a/embedding/android/GeckoEvent.java b/embedding/android/GeckoEvent.java index a00deb0897f..286130ce681 100644 --- a/embedding/android/GeckoEvent.java +++ b/embedding/android/GeckoEvent.java @@ -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) { diff --git a/embedding/android/gfx/IntSize.java b/embedding/android/GeckoGestureDetector.java similarity index 54% rename from embedding/android/gfx/IntSize.java rename to embedding/android/GeckoGestureDetector.java index 7a20545bc91..1eaae7cb443 100644 --- a/embedding/android/gfx/IntSize.java +++ b/embedding/android/GeckoGestureDetector.java @@ -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 + * Wes Johnston * * 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; } } - diff --git a/embedding/android/GeckoInputConnection.java b/embedding/android/GeckoInputConnection.java index 50907c27d09..2137465ea03 100644 --- a/embedding/android/GeckoInputConnection.java +++ b/embedding/android/GeckoInputConnection.java @@ -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(); - - 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; diff --git a/embedding/android/GeckoSurfaceView.java b/embedding/android/GeckoSurfaceView.java new file mode 100644 index 00000000000..79504716cfb --- /dev/null +++ b/embedding/android/GeckoSurfaceView.java @@ -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 + * + * 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 { + protected Void doInBackground(Location... location) { + try { + List
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 mSyncDraws = new SynchronousQueue(); +} + diff --git a/embedding/android/Makefile.in b/embedding/android/Makefile.in index 6cc92347b42..6705b658798 100644 --- a/embedding/android/Makefile.in +++ b/embedding/android/Makefile.in @@ -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) diff --git a/embedding/android/gfx/BufferedCairoImage.java b/embedding/android/gfx/BufferedCairoImage.java deleted file mode 100644 index 24bc65aaeb3..00000000000 --- a/embedding/android/gfx/BufferedCairoImage.java +++ /dev/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 - * - * 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; } -} - diff --git a/embedding/android/gfx/CairoImage.java b/embedding/android/gfx/CairoImage.java deleted file mode 100644 index d12ee35e534..00000000000 --- a/embedding/android/gfx/CairoImage.java +++ /dev/null @@ -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 - * - * 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; -} - diff --git a/embedding/android/gfx/CairoUtils.java b/embedding/android/gfx/CairoUtils.java deleted file mode 100644 index e67db535647..00000000000 --- a/embedding/android/gfx/CairoUtils.java +++ /dev/null @@ -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 - * - * 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"); - } - } -} - diff --git a/embedding/android/gfx/GeckoSoftwareLayerClient.java b/embedding/android/gfx/GeckoSoftwareLayerClient.java deleted file mode 100644 index d7a9f7195f7..00000000000 --- a/embedding/android/gfx/GeckoSoftwareLayerClient.java +++ /dev/null @@ -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 - * - * 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());*/ - } -} - diff --git a/embedding/android/gfx/InputConnectionHandler.java b/embedding/android/gfx/InputConnectionHandler.java deleted file mode 100644 index d422379e31c..00000000000 --- a/embedding/android/gfx/InputConnectionHandler.java +++ /dev/null @@ -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); -} diff --git a/embedding/android/gfx/IntPoint.java b/embedding/android/gfx/IntPoint.java deleted file mode 100644 index d156af43d6d..00000000000 --- a/embedding/android/gfx/IntPoint.java +++ /dev/null @@ -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 - * - * 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)); - } -} - - diff --git a/embedding/android/gfx/IntRect.java b/embedding/android/gfx/IntRect.java deleted file mode 100644 index 78417084263..00000000000 --- a/embedding/android/gfx/IntRect.java +++ /dev/null @@ -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 - * - * 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)); - } -} - - diff --git a/embedding/android/gfx/Layer.java b/embedding/android/gfx/Layer.java deleted file mode 100644 index 102cc12124c..00000000000 --- a/embedding/android/gfx/Layer.java +++ /dev/null @@ -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 - * - * 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); -} - diff --git a/embedding/android/gfx/LayerClient.java b/embedding/android/gfx/LayerClient.java deleted file mode 100644 index 6a126792e5e..00000000000 --- a/embedding/android/gfx/LayerClient.java +++ /dev/null @@ -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 - * - * 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; - } -} - diff --git a/embedding/android/gfx/LayerController.java b/embedding/android/gfx/LayerController.java deleted file mode 100644 index 2c8ce9c59e1..00000000000 --- a/embedding/android/gfx/LayerController.java +++ /dev/null @@ -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 - * - * 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 mOnGeometryChangeListeners; - /* A list of listeners that will be notified whenever the geometry changes. */ - private ArrayList 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(); - mOnPageSizeChangeListeners = new ArrayList(); - - 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); - } -} - diff --git a/embedding/android/gfx/LayerRenderer.java b/embedding/android/gfx/LayerRenderer.java deleted file mode 100644 index 7e070f38b0b..00000000000 --- a/embedding/android/gfx/LayerRenderer.java +++ /dev/null @@ -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 - * - * 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++; - } - } -} - diff --git a/embedding/android/gfx/LayerView.java b/embedding/android/gfx/LayerView.java deleted file mode 100644 index 5e1b92875c5..00000000000 --- a/embedding/android/gfx/LayerView.java +++ /dev/null @@ -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 - * - * 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; - } -} - diff --git a/embedding/android/gfx/NinePatchTileLayer.java b/embedding/android/gfx/NinePatchTileLayer.java deleted file mode 100644 index 1b04e6c110d..00000000000 --- a/embedding/android/gfx/NinePatchTileLayer.java +++ /dev/null @@ -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 - * - * 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); - } -} diff --git a/embedding/android/gfx/PlaceholderLayerClient.java b/embedding/android/gfx/PlaceholderLayerClient.java deleted file mode 100644 index c6509836221..00000000000 --- a/embedding/android/gfx/PlaceholderLayerClient.java +++ /dev/null @@ -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 - * - * 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; } -} - diff --git a/embedding/android/gfx/SingleTileLayer.java b/embedding/android/gfx/SingleTileLayer.java deleted file mode 100644 index 339facafc4c..00000000000 --- a/embedding/android/gfx/SingleTileLayer.java +++ /dev/null @@ -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 - * - * 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); - } - } -} - diff --git a/embedding/android/gfx/TextLayer.java b/embedding/android/gfx/TextLayer.java deleted file mode 100644 index 2d553d4f351..00000000000 --- a/embedding/android/gfx/TextLayer.java +++ /dev/null @@ -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 - * - * 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()); - } -} - diff --git a/embedding/android/gfx/TextureReaper.java b/embedding/android/gfx/TextureReaper.java deleted file mode 100644 index 4052c75d780..00000000000 --- a/embedding/android/gfx/TextureReaper.java +++ /dev/null @@ -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 - * - * 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 mDeadTextureIDs; - - private TextureReaper() { mDeadTextureIDs = new ArrayList(); } - - 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); - } -} - - diff --git a/embedding/android/gfx/TileLayer.java b/embedding/android/gfx/TileLayer.java deleted file mode 100644 index 5d09fc92b4a..00000000000 --- a/embedding/android/gfx/TileLayer.java +++ /dev/null @@ -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 - * - * 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); - } -} - diff --git a/embedding/android/resources/drawable/checkerboard.png b/embedding/android/resources/drawable/checkerboard.png deleted file mode 100644 index 57cfbe80fdc..00000000000 Binary files a/embedding/android/resources/drawable/checkerboard.png and /dev/null differ diff --git a/embedding/android/resources/drawable/pattern.png b/embedding/android/resources/drawable/pattern.png deleted file mode 100644 index 1c85f162615..00000000000 Binary files a/embedding/android/resources/drawable/pattern.png and /dev/null differ diff --git a/embedding/android/resources/drawable/shadow.png b/embedding/android/resources/drawable/shadow.png deleted file mode 100644 index 3ce69155c6b..00000000000 Binary files a/embedding/android/resources/drawable/shadow.png and /dev/null differ diff --git a/embedding/android/ui/PanZoomController.java b/embedding/android/ui/PanZoomController.java deleted file mode 100644 index ac6e1773018..00000000000 --- a/embedding/android/ui/PanZoomController.java +++ /dev/null @@ -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 - * - * 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); - } -} diff --git a/embedding/android/ui/ViewportController.java b/embedding/android/ui/ViewportController.java deleted file mode 100644 index 9b19f192159..00000000000 --- a/embedding/android/ui/ViewportController.java +++ /dev/null @@ -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 - * - * 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; } -} - diff --git a/gfx/thebes/GLContextProviderEGL.cpp b/gfx/thebes/GLContextProviderEGL.cpp index 0f6f466b8e4..b9c27b9365f 100644 --- a/gfx/thebes/GLContextProviderEGL.cpp +++ b/gfx/thebes/GLContextProviderEGL.cpp @@ -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; diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index b734318850a..49fd06e5a6f 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -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. diff --git a/layout/generic/nsSubDocumentFrame.cpp b/layout/generic/nsSubDocumentFrame.cpp index 95230f02f02..de2482616ef 100644 --- a/layout/generic/nsSubDocumentFrame.cpp +++ b/layout/generic/nsSubDocumentFrame.cpp @@ -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)); diff --git a/mobile/chrome/content/browser.js b/mobile/chrome/content/browser.js index 63086815e8c..c53d947d29c 100644 --- a/mobile/chrome/content/browser.js +++ b/mobile/chrome/content/browser.js @@ -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; } diff --git a/other-licenses/android/APKOpen.cpp b/other-licenses/android/APKOpen.cpp index 9af3d94a851..a21ea572a46 100644 --- a/other-licenses/android/APKOpen.cpp +++ b/other-licenses/android/APKOpen.cpp @@ -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); diff --git a/widget/src/android/AndroidBridge.cpp b/widget/src/android/AndroidBridge.cpp index 8771d737dae..3c20e1f9d33 100644 --- a/widget/src/android/AndroidBridge.cpp +++ b/widget/src/android/AndroidBridge.cpp @@ -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, "", "(I)V"); + jmethodID constructDisplay = mJNIEnv->GetMethodID(jEGLDisplayImplClass, "", "(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) { diff --git a/widget/src/android/AndroidBridge.h b/widget/src/android/AndroidBridge.h index ee9e3307cd2..66aba2a3b97 100644 --- a/widget/src/android/AndroidBridge.h +++ b/widget/src/android/AndroidBridge.h @@ -40,7 +40,6 @@ #include #include -#include #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; diff --git a/widget/src/android/AndroidJNI.cpp b/widget/src/android/AndroidJNI.cpp index cb06d107909..92df3db0b25 100644 --- a/widget/src/android/AndroidJNI.cpp +++ b/widget/src/android/AndroidJNI.cpp @@ -44,7 +44,6 @@ #include #include #include -#include #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 diff --git a/widget/src/android/AndroidJavaWrappers.cpp b/widget/src/android/AndroidJavaWrappers.cpp index 9de528b9931..5a1ca90e7ab 100644 --- a/widget/src/android/AndroidJavaWrappers.cpp +++ b/widget/src/android/AndroidJavaWrappers.cpp @@ -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(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; diff --git a/widget/src/android/AndroidJavaWrappers.h b/widget/src/android/AndroidJavaWrappers.h index 32e7f86a963..9467f796731 100644 --- a/widget/src/android/AndroidJavaWrappers.h +++ b/widget/src/android/AndroidJavaWrappers.h @@ -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; } diff --git a/widget/src/android/nsAppShell.cpp b/widget/src/android/nsAppShell.cpp index 4bce7137cb7..9d50114cadc 100644 --- a/widget/src/android/nsAppShell.cpp +++ b/widget/src/android/nsAppShell.cpp @@ -60,8 +60,6 @@ #include "prlog.h" #endif -#define DEBUG_ANDROID_EVENTS 1 - #ifdef DEBUG_ANDROID_EVENTS #define EVLOG(args...) ALOG(args) #else diff --git a/widget/src/android/nsWindow.cpp b/widget/src/android/nsWindow.cpp index 3e1ddabb35a..262bc648e13 100644 --- a/widget/src/android/nsWindow.cpp +++ b/widget/src/android/nsWindow.cpp @@ -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 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& 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 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 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 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 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 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 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 } } diff --git a/widget/src/android/nsWindow.h b/widget/src/android/nsWindow.h index bee1bfd694c..e9140ceacb3 100644 --- a/widget/src/android/nsWindow.h +++ b/widget/src/android/nsWindow.h @@ -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 mRootAccessible;