PageRenderTime 90ms CodeModel.GetById 14ms app.highlight 68ms RepoModel.GetById 2ms app.codeStats 0ms

/embedding/android/GeckoSurfaceView.java

http://github.com/zpao/v8monkey
Java | 833 lines | 592 code | 112 blank | 129 comment | 159 complexity | a9b9599141e39107414f648de0d2708d MD5 | raw file
  1/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  2 * ***** BEGIN LICENSE BLOCK *****
  3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4 *
  5 * The contents of this file are subject to the Mozilla Public License Version
  6 * 1.1 (the "License"); you may not use this file except in compliance with
  7 * the License. You may obtain a copy of the License at
  8 * http://www.mozilla.org/MPL/
  9 *
 10 * Software distributed under the License is distributed on an "AS IS" basis,
 11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 12 * for the specific language governing rights and limitations under the
 13 * License.
 14 *
 15 * The Original Code is Mozilla Android code.
 16 *
 17 * The Initial Developer of the Original Code is Mozilla Foundation.
 18 * Portions created by the Initial Developer are Copyright (C) 2010
 19 * the Initial Developer. All Rights Reserved.
 20 *
 21 * Contributor(s):
 22 *   Vladimir Vukicevic <vladimir@pobox.com>
 23 *
 24 * Alternatively, the contents of this file may be used under the terms of
 25 * either the GNU General Public License Version 2 or later (the "GPL"), or
 26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 27 * in which case the provisions of the GPL or the LGPL are applicable instead
 28 * of those above. If you wish to allow use of your version of this file only
 29 * under the terms of either the GPL or the LGPL, and not to allow others to
 30 * use your version of this file under the terms of the MPL, indicate your
 31 * decision by deleting the provisions above and replace them with the notice
 32 * and other provisions required by the GPL or the LGPL. If you do not delete
 33 * the provisions above, a recipient may use your version of this file under
 34 * the terms of any one of the MPL, the GPL or the LGPL.
 35 *
 36 * ***** END LICENSE BLOCK ***** */
 37
 38package org.mozilla.gecko;
 39
 40import java.io.*;
 41import java.util.*;
 42import java.util.concurrent.*;
 43import java.util.concurrent.locks.*;
 44import java.util.concurrent.atomic.*;
 45import java.util.zip.*;
 46import java.nio.*;
 47
 48import android.os.*;
 49import android.app.*;
 50import android.text.*;
 51import android.text.method.*;
 52import android.view.*;
 53import android.view.inputmethod.*;
 54import android.content.*;
 55import android.graphics.*;
 56import android.widget.*;
 57import android.hardware.*;
 58import android.location.*;
 59import android.graphics.drawable.*;
 60import android.content.res.*;
 61
 62import android.util.*;
 63
 64/*
 65 * GeckoSurfaceView implements a GL surface view,
 66 * similar to GLSurfaceView.  However, since we
 67 * already have a thread for Gecko, we don't really want
 68 * a separate renderer thread that GLSurfaceView provides.
 69 */
 70class GeckoSurfaceView
 71    extends SurfaceView
 72    implements SurfaceHolder.Callback, SensorEventListener, LocationListener
 73{
 74    private static final String LOG_FILE_NAME = "GeckoSurfaceView";
 75
 76    public GeckoSurfaceView(Context context) {
 77        super(context);
 78
 79        getHolder().addCallback(this);
 80        inputConnection = new GeckoInputConnection(this);
 81        setFocusable(true);
 82        setFocusableInTouchMode(true);
 83        
 84        DisplayMetrics metrics = new DisplayMetrics();
 85        GeckoApp.mAppContext.getWindowManager().
 86            getDefaultDisplay().getMetrics(metrics);
 87        mWidth = metrics.widthPixels;
 88        mHeight = metrics.heightPixels;
 89        mBufferWidth = 0;
 90        mBufferHeight = 0;
 91
 92        mSurfaceLock = new ReentrantLock();
 93
 94        mEditableFactory = Editable.Factory.getInstance();
 95        initEditable("");
 96        mIMEState = IME_STATE_DISABLED;
 97        mIMETypeHint = "";
 98        mIMEActionHint = "";
 99    }
100
101    protected void finalize() throws Throwable {
102        super.finalize();
103    }
104
105    void drawSplashScreen() {
106        this.drawSplashScreen(getHolder(), mWidth, mHeight);
107    }
108
109    void drawSplashScreen(SurfaceHolder holder, int width, int height) {
110        // No splash screen for Honeycomb or greater
111        if (Build.VERSION.SDK_INT >= 11) {
112            Log.i(LOG_FILE_NAME, "skipping splash screen");
113            return;
114        }
115
116        Canvas c = holder.lockCanvas();
117        if (c == null) {
118            Log.i(LOG_FILE_NAME, "canvas is null");
119            return;
120        }
121
122        Resources res = getResources();
123
124        File watchDir = new File(GeckoApp.sGREDir, "components");
125        if (watchDir.exists() == false) {
126            // Just show the simple splash screen for "new profile" startup
127            c.drawColor(res.getColor(R.color.splash_background));
128            Drawable drawable = res.getDrawable(R.drawable.splash);
129            int w = drawable.getIntrinsicWidth();
130            int h = drawable.getIntrinsicHeight();
131            int x = (width - w) / 2;
132            int y = (height - h) / 2 - 16;
133            drawable.setBounds(x, y, x + w, y + h);
134            drawable.draw(c);
135
136            Paint p = new Paint();
137            p.setTextAlign(Paint.Align.CENTER);
138            p.setTextSize(32f);
139            p.setAntiAlias(true);
140            p.setColor(res.getColor(R.color.splash_msgfont));
141            c.drawText(res.getString(R.string.splash_firstrun), width / 2, y + h + 16, p);
142        } else {
143            // Show the static UI for normal startup
144            DisplayMetrics metrics = new DisplayMetrics();
145            GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
146    
147            // Default to DENSITY_HIGH sizes
148            int toolbarHeight = 80;
149            int faviconOffset = 25;
150            float urlHeight = 24f;
151            int urlOffsetX = 80;
152            int urlOffsetY = 48;
153            if (metrics.densityDpi == DisplayMetrics.DENSITY_MEDIUM) {
154                toolbarHeight = 53;
155                faviconOffset = 10;
156                urlHeight = 16f;
157                urlOffsetX = 53;
158                urlOffsetY = 32;
159            }
160    
161            c.drawColor(res.getColor(R.color.splash_content));
162            Drawable toolbar = res.getDrawable(Build.VERSION.SDK_INT > 8 ?
163                                               R.drawable.splash_v9 :
164                                               R.drawable.splash_v8);
165            toolbar.setBounds(0, 0, width, toolbarHeight);
166            toolbar.draw(c);
167    
168            // XUL/CSS always uses 32px width and height for favicon
169            Drawable favicon = res.getDrawable(R.drawable.favicon32);
170            favicon.setBounds(faviconOffset, faviconOffset, 32 + faviconOffset, 32 + faviconOffset);
171            favicon.draw(c);
172    
173            if (GeckoSurfaceView.mSplashURL != "") {
174                TextPaint p = new TextPaint();
175                p.setTextAlign(Paint.Align.LEFT);
176                p.setTextSize(urlHeight);
177                p.setAntiAlias(true);
178                p.setColor(res.getColor(R.color.splash_urlfont));
179                String url = TextUtils.ellipsize(GeckoSurfaceView.mSplashURL, p, width - urlOffsetX * 2, TextUtils.TruncateAt.END).toString();
180                c.drawText(url, urlOffsetX, urlOffsetY, p);
181            }
182        }
183        holder.unlockCanvasAndPost(c);
184    }
185
186    /*
187     * Called on main thread
188     */
189
190    public void draw(SurfaceHolder holder, ByteBuffer buffer) {
191        if (buffer == null || buffer.capacity() != (mWidth * mHeight * 2))
192            return;
193
194        synchronized (mSoftwareBuffer) {
195            if (buffer != mSoftwareBuffer || mSoftwareBufferCopy == null)
196                return;
197
198            Canvas c = holder.lockCanvas();
199            if (c == null)
200                return;
201            mSoftwareBufferCopy.copyPixelsFromBuffer(buffer);
202            c.drawBitmap(mSoftwareBufferCopy, 0, 0, null);
203            holder.unlockCanvasAndPost(c);
204        }
205    }
206
207    public void draw(SurfaceHolder holder, Bitmap bitmap) {
208        if (bitmap == null ||
209            bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight)
210            return;
211
212        synchronized (mSoftwareBitmap) {
213            if (bitmap != mSoftwareBitmap)
214                return;
215
216            Canvas c = holder.lockCanvas();
217            if (c == null)
218                return;
219            c.drawBitmap(bitmap, 0, 0, null);
220            holder.unlockCanvasAndPost(c);
221        }
222    }
223
224    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
225
226        // On pre-Honeycomb, force exactly one frame of the previous size
227        // to render because the surface change is only seen by GLES after we
228        // have swapped the back buffer (i.e. the buffer size only changes 
229        // after the next swap buffer). We need to make sure Gecko's view 
230        // resizes when Android's buffer resizes.
231        // In Honeycomb, the buffer size changes immediately, so rendering a
232        // frame of the previous size is unnecessary (and wrong).
233        if (mDrawMode == DRAW_GLES_2 && 
234            (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)) {
235            // When we get a surfaceChange event, we have 0 to n paint events 
236            // waiting in the Gecko event queue. We will make the first
237            // succeed and the abort the others.
238            mDrawSingleFrame = true;
239            if (!mInDrawing) { 
240                // Queue at least one paint event in case none are queued.
241                GeckoAppShell.scheduleRedraw();
242            }
243            GeckoAppShell.geckoEventSync();
244            mDrawSingleFrame = false;
245            mAbortDraw = false;
246        }
247
248        if (mShowingSplashScreen)
249            drawSplashScreen(holder, width, height);
250
251        mSurfaceLock.lock();
252
253        if (mInDrawing) {
254            Log.w(LOG_FILE_NAME, "surfaceChanged while mInDrawing is true!");
255        }
256
257        boolean invalidSize;
258
259        if (width == 0 || height == 0) {
260            mSoftwareBitmap = null;
261            mSoftwareBuffer = null;
262            mSoftwareBufferCopy = null;
263            invalidSize = true;
264        } else {
265            invalidSize = false;
266        }
267
268        boolean doSyncDraw =
269            mDrawMode == DRAW_2D &&
270            !invalidSize &&
271            GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning);
272        mSyncDraw = doSyncDraw;
273
274        mFormat = format;
275        mWidth = width;
276        mHeight = height;
277        mSurfaceValid = true;
278
279        Log.i(LOG_FILE_NAME, "surfaceChanged: fmt: " + format + " dim: " + width + " " + height);
280
281        try {
282            DisplayMetrics metrics = new DisplayMetrics();
283            GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
284
285            GeckoEvent e = new GeckoEvent(GeckoEvent.SIZE_CHANGED, width, height,
286                                          metrics.widthPixels, metrics.heightPixels);
287            GeckoAppShell.sendEventToGecko(e);
288        } finally {
289            mSurfaceLock.unlock();
290        }
291
292        if (doSyncDraw) {
293            GeckoAppShell.scheduleRedraw();
294
295            Object syncDrawObject = null;
296            try {
297                syncDrawObject = mSyncDraws.take();
298            } catch (InterruptedException ie) {
299                Log.e(LOG_FILE_NAME, "Threw exception while getting sync draw bitmap/buffer: ", ie);
300            }
301            if (syncDrawObject != null) {
302                if (syncDrawObject instanceof Bitmap)
303                    draw(holder, (Bitmap)syncDrawObject);
304                else
305                    draw(holder, (ByteBuffer)syncDrawObject);
306            } else {
307                Log.e("GeckoSurfaceViewJava", "Synchronised draw object is null");
308            }
309        } else if (!mShowingSplashScreen) {
310            // Make sure a frame is drawn before we return
311            // otherwise we see artifacts or a black screen
312            GeckoAppShell.scheduleRedraw();
313            GeckoAppShell.geckoEventSync();
314        }
315    }
316
317    public void surfaceCreated(SurfaceHolder holder) {
318        Log.i(LOG_FILE_NAME, "surface created");
319        GeckoEvent e = new GeckoEvent(GeckoEvent.SURFACE_CREATED);
320        GeckoAppShell.sendEventToGecko(e);
321        if (mShowingSplashScreen)
322            drawSplashScreen();
323    }
324
325    public void surfaceDestroyed(SurfaceHolder holder) {
326        Log.i(LOG_FILE_NAME, "surface destroyed");
327        mSurfaceValid = false;
328        mSoftwareBuffer = null;
329        mSoftwareBufferCopy = null;
330        mSoftwareBitmap = null;
331        GeckoEvent e = new GeckoEvent(GeckoEvent.SURFACE_DESTROYED);
332        if (mDrawMode == DRAW_GLES_2) {
333            // Ensure GL cleanup occurs before we return.
334            GeckoAppShell.sendEventToGeckoSync(e);
335        } else {
336            GeckoAppShell.sendEventToGecko(e);
337        }
338    }
339
340    public Bitmap getSoftwareDrawBitmap() {
341        if (mSoftwareBitmap == null ||
342            mSoftwareBitmap.getHeight() != mHeight ||
343            mSoftwareBitmap.getWidth() != mWidth) {
344            mSoftwareBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.RGB_565);
345        }
346
347        mDrawMode = DRAW_2D;
348        return mSoftwareBitmap;
349    }
350
351    public ByteBuffer getSoftwareDrawBuffer() {
352        // We store pixels in 565 format, so two bytes per pixel (explaining
353        // the * 2 in the following check/allocation)
354        if (mSoftwareBuffer == null ||
355            mSoftwareBuffer.capacity() != (mWidth * mHeight * 2)) {
356            mSoftwareBuffer = ByteBuffer.allocateDirect(mWidth * mHeight * 2);
357        }
358
359        if (mSoftwareBufferCopy == null ||
360            mSoftwareBufferCopy.getHeight() != mHeight ||
361            mSoftwareBufferCopy.getWidth() != mWidth) {
362            mSoftwareBufferCopy = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.RGB_565);
363        }
364
365        mDrawMode = DRAW_2D;
366        return mSoftwareBuffer;
367    }
368
369    public Surface getSurface() {
370        return getHolder().getSurface();
371    }
372
373    /*
374     * Called on Gecko thread
375     */
376
377    public static final int DRAW_ERROR = 0;
378    public static final int DRAW_GLES_2 = 1;
379    public static final int DRAW_2D = 2;
380    // Drawing is disable when the surface buffer
381    // has changed size but we haven't yet processed the
382    // resize event.
383    public static final int DRAW_DISABLED = 3;
384
385    public int beginDrawing() {
386        if (mInDrawing) {
387            Log.e(LOG_FILE_NAME, "Recursive beginDrawing call!");
388            return DRAW_ERROR;
389        }
390
391        // Once we drawn our first frame after resize we can ignore
392        // the other draw events until we handle the resize events.
393        if (mAbortDraw) {
394            return DRAW_DISABLED;
395        }
396
397        /* Grab the lock, which we'll hold while we're drawing.
398         * It gets released in endDrawing(), and is also used in surfaceChanged
399         * to make sure that we don't change our surface details while
400         * we're in the middle of drawing (and especially in the middle of
401         * executing beginDrawing/endDrawing).
402         *
403         * We might not need to hold this lock in between
404         * beginDrawing/endDrawing, and might just be able to make
405         * surfaceChanged, beginDrawing, and endDrawing synchronized,
406         * but this way is safer for now.
407         */
408        mSurfaceLock.lock();
409
410        if (!mSurfaceValid) {
411            Log.e(LOG_FILE_NAME, "Surface not valid");
412            mSurfaceLock.unlock();
413            return DRAW_ERROR;
414        }
415
416        mInDrawing = true;
417        mDrawMode = DRAW_GLES_2;
418        return DRAW_GLES_2;
419    }
420
421    public void endDrawing() {
422        if (!mInDrawing) {
423            Log.e(LOG_FILE_NAME, "endDrawing without beginDrawing!");
424            return;
425        }
426
427       if (mDrawSingleFrame)
428            mAbortDraw = true;
429
430        try {
431            if (!mSurfaceValid) {
432                Log.e(LOG_FILE_NAME, "endDrawing with false mSurfaceValid");
433                return;
434            }
435        } finally {
436            mInDrawing = false;
437
438            if (!mSurfaceLock.isHeldByCurrentThread())
439                Log.e(LOG_FILE_NAME, "endDrawing while mSurfaceLock not held by current thread!");
440
441            mSurfaceLock.unlock();
442        }
443    }
444
445    /* How this works:
446     * Whenever we want to draw, we want to be sure that we do not lock
447     * the canvas unless we're sure we can draw. Locking the canvas clears
448     * the canvas to black in most cases, causing a black flash.
449     * At the same time, the surface can resize/disappear at any moment
450     * unless the canvas is locked.
451     * Draws originate from a different thread so the surface could change
452     * at any moment while we try to draw until we lock the canvas.
453     *
454     * Also, never try to lock the canvas while holding the surface lock
455     * unless you're in SurfaceChanged, in which case the canvas was already
456     * locked. Surface lock -> Canvas lock will lead to AB-BA deadlocks.
457     */
458    public void draw2D(Bitmap bitmap, int width, int height) {
459        // mSurfaceLock ensures that we get mSyncDraw/mSoftwareBitmap/etc.
460        // set correctly before determining whether we should do a sync draw
461        mSurfaceLock.lock();
462        try {
463            if (mSyncDraw) {
464                if (bitmap != mSoftwareBitmap || width != mWidth || height != mHeight)
465                    return;
466                mSyncDraw = false;
467                try {
468                    mSyncDraws.put(bitmap);
469                } catch (InterruptedException ie) {
470                    Log.e(LOG_FILE_NAME, "Threw exception while getting sync draws queue: ", ie);
471                }
472                return;
473            }
474        } finally {
475            mSurfaceLock.unlock();
476        }
477
478        draw(getHolder(), bitmap);
479    }
480
481    public void draw2D(ByteBuffer buffer, int stride) {
482        mSurfaceLock.lock();
483        try {
484            if (mSyncDraw) {
485                if (buffer != mSoftwareBuffer || stride != (mWidth * 2))
486                    return;
487                mSyncDraw = false;
488                try {
489                    mSyncDraws.put(buffer);
490                } catch (InterruptedException ie) {
491                    Log.e(LOG_FILE_NAME, "Threw exception while getting sync bitmaps queue: ", ie);
492                }
493                return;
494            }
495        } finally {
496            mSurfaceLock.unlock();
497        }
498
499        draw(getHolder(), buffer);
500    }
501
502    @Override
503    public boolean onCheckIsTextEditor () {
504        return false;
505    }
506
507    @Override
508    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
509        outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
510        outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE;
511        outAttrs.actionLabel = null;
512        mKeyListener = TextKeyListener.getInstance();
513
514        if (mIMEState == IME_STATE_PASSWORD)
515            outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_PASSWORD;
516        else if (mIMETypeHint.equalsIgnoreCase("url"))
517            outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_URI;
518        else if (mIMETypeHint.equalsIgnoreCase("email"))
519            outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
520        else if (mIMETypeHint.equalsIgnoreCase("search"))
521            outAttrs.imeOptions = EditorInfo.IME_ACTION_SEARCH;
522        else if (mIMETypeHint.equalsIgnoreCase("tel"))
523            outAttrs.inputType = InputType.TYPE_CLASS_PHONE;
524        else if (mIMETypeHint.equalsIgnoreCase("number") ||
525                 mIMETypeHint.equalsIgnoreCase("range"))
526            outAttrs.inputType = InputType.TYPE_CLASS_NUMBER;
527        else if (mIMETypeHint.equalsIgnoreCase("datetime") ||
528                 mIMETypeHint.equalsIgnoreCase("datetime-local"))
529            outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
530                                 InputType.TYPE_DATETIME_VARIATION_NORMAL;
531        else if (mIMETypeHint.equalsIgnoreCase("date"))
532            outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
533                                 InputType.TYPE_DATETIME_VARIATION_DATE;
534        else if (mIMETypeHint.equalsIgnoreCase("time"))
535            outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
536                                 InputType.TYPE_DATETIME_VARIATION_TIME;
537
538        if (mIMEActionHint.equalsIgnoreCase("go"))
539            outAttrs.imeOptions = EditorInfo.IME_ACTION_GO;
540        else if (mIMEActionHint.equalsIgnoreCase("done"))
541            outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
542        else if (mIMEActionHint.equalsIgnoreCase("next"))
543            outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT;
544        else if (mIMEActionHint.equalsIgnoreCase("search"))
545            outAttrs.imeOptions = EditorInfo.IME_ACTION_SEARCH;
546        else if (mIMEActionHint.equalsIgnoreCase("send"))
547            outAttrs.imeOptions = EditorInfo.IME_ACTION_SEND;
548        else if (mIMEActionHint != null && mIMEActionHint.length() != 0)
549            outAttrs.actionLabel = mIMEActionHint;
550
551        if (mIMELandscapeFS == false)
552            outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI;
553
554        inputConnection.reset();
555        return inputConnection;
556    }
557
558    public void setEditable(String contents)
559    {
560        mEditable.removeSpan(inputConnection);
561        mEditable.replace(0, mEditable.length(), contents);
562        mEditable.setSpan(inputConnection, 0, contents.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
563        Selection.setSelection(mEditable, contents.length());
564    }
565
566    public void initEditable(String contents)
567    {
568        mEditable = mEditableFactory.newEditable(contents);
569        mEditable.setSpan(inputConnection, 0, contents.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
570        Selection.setSelection(mEditable, contents.length());
571    }
572
573    // accelerometer
574    public void onAccuracyChanged(Sensor sensor, int accuracy)
575    {
576    }
577
578    public void onSensorChanged(SensorEvent event)
579    {
580        GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
581    }
582
583    private class GeocoderTask extends AsyncTask<Location, Void, Void> {
584        protected Void doInBackground(Location... location) {
585            try {
586                List<Address> addresses = mGeocoder.getFromLocation(location[0].getLatitude(),
587                                                                    location[0].getLongitude(), 1);
588                // grab the first address.  in the future,
589                // may want to expose multiple, or filter
590                // for best.
591                mLastGeoAddress = addresses.get(0);
592                GeckoAppShell.sendEventToGecko(new GeckoEvent(location[0], mLastGeoAddress));
593            } catch (Exception e) {
594                Log.w(LOG_FILE_NAME, "GeocoderTask "+e);
595            }
596            return null;
597        }
598    }
599
600    // geolocation
601    public void onLocationChanged(Location location)
602    {
603        if (mGeocoder == null)
604            mGeocoder = new Geocoder(getContext(), Locale.getDefault());
605
606        if (mLastGeoAddress == null) {
607            new GeocoderTask().execute(location);
608        }
609        else {
610            float[] results = new float[1];
611            Location.distanceBetween(location.getLatitude(),
612                                     location.getLongitude(),
613                                     mLastGeoAddress.getLatitude(),
614                                     mLastGeoAddress.getLongitude(),
615                                     results);
616            // pfm value.  don't want to slam the
617            // geocoder with very similar values, so
618            // only call after about 100m
619            if (results[0] > 100)
620                new GeocoderTask().execute(location);
621        }
622
623        GeckoAppShell.sendEventToGecko(new GeckoEvent(location, mLastGeoAddress));
624    }
625
626    public void onProviderDisabled(String provider)
627    {
628    }
629
630    public void onProviderEnabled(String provider)
631    {
632    }
633
634    public void onStatusChanged(String provider, int status, Bundle extras)
635    {
636    }
637
638    // event stuff
639    public boolean onTouchEvent(MotionEvent event) {
640        GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
641        return true;
642    }
643
644    @Override
645    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
646        if (event.isSystem())
647            return super.onKeyPreIme(keyCode, event);
648
649        switch (event.getAction()) {
650            case KeyEvent.ACTION_DOWN:
651                return processKeyDown(keyCode, event, true);
652            case KeyEvent.ACTION_UP:
653                return processKeyUp(keyCode, event, true);
654            case KeyEvent.ACTION_MULTIPLE:
655                return onKeyMultiple(keyCode, event.getRepeatCount(), event);
656        }
657        return super.onKeyPreIme(keyCode, event);
658    }
659
660    @Override
661    public boolean onKeyDown(int keyCode, KeyEvent event) {
662        return processKeyDown(keyCode, event, false);
663    }
664
665    private boolean processKeyDown(int keyCode, KeyEvent event, boolean isPreIme) {
666        switch (keyCode) {
667            case KeyEvent.KEYCODE_BACK:
668                if (event.getRepeatCount() == 0) {
669                    event.startTracking();
670                    return true;
671                } else {
672                    return false;
673                }
674            case KeyEvent.KEYCODE_MENU:
675                if (event.getRepeatCount() == 0) {
676                    event.startTracking();
677                    break;
678                } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
679                    break;
680                }
681                // Ignore repeats for KEYCODE_MENU; they confuse the widget code.
682                return false;
683            case KeyEvent.KEYCODE_VOLUME_UP:
684            case KeyEvent.KEYCODE_VOLUME_DOWN:
685            case KeyEvent.KEYCODE_SEARCH:
686                return false;
687            case KeyEvent.KEYCODE_DEL:
688                // See comments in GeckoInputConnection.onKeyDel
689                if (inputConnection != null &&
690                    inputConnection.onKeyDel()) {
691                    return true;
692                }
693                break;
694            case KeyEvent.KEYCODE_ENTER:
695                if ((event.getFlags() & KeyEvent.FLAG_EDITOR_ACTION) != 0 &&
696                    mIMEActionHint.equalsIgnoreCase("next"))
697                    event = new KeyEvent(event.getAction(), KeyEvent.KEYCODE_TAB);
698                break;
699            default:
700                break;
701        }
702
703        if (isPreIme && mIMEState != IME_STATE_DISABLED &&
704            (event.getMetaState() & KeyEvent.META_ALT_ON) == 0)
705            // Let active IME process pre-IME key events
706            return false;
707
708        // KeyListener returns true if it handled the event for us.
709        if (mIMEState == IME_STATE_DISABLED ||
710            keyCode == KeyEvent.KEYCODE_ENTER ||
711            keyCode == KeyEvent.KEYCODE_DEL ||
712            (event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 ||
713            !mKeyListener.onKeyDown(this, mEditable, keyCode, event))
714            GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
715        return true;
716    }
717
718    @Override
719    public boolean onKeyUp(int keyCode, KeyEvent event) {
720        return processKeyUp(keyCode, event, false);
721    }
722
723    private boolean processKeyUp(int keyCode, KeyEvent event, boolean isPreIme) {
724        switch (keyCode) {
725            case KeyEvent.KEYCODE_BACK:
726                if (!event.isTracking() || event.isCanceled())
727                    return false;
728                break;
729            default:
730                break;
731        }
732
733        if (isPreIme && mIMEState != IME_STATE_DISABLED &&
734            (event.getMetaState() & KeyEvent.META_ALT_ON) == 0)
735            // Let active IME process pre-IME key events
736            return false;
737
738        if (mIMEState == IME_STATE_DISABLED ||
739            keyCode == KeyEvent.KEYCODE_ENTER ||
740            keyCode == KeyEvent.KEYCODE_DEL ||
741            (event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 ||
742            !mKeyListener.onKeyUp(this, mEditable, keyCode, event))
743            GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
744        return true;
745    }
746
747    @Override
748    public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
749        GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
750        return true;
751    }
752
753    @Override
754    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
755        switch (keyCode) {
756            case KeyEvent.KEYCODE_BACK:
757                GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
758                return true;
759            case KeyEvent.KEYCODE_MENU:
760                InputMethodManager imm = (InputMethodManager)
761                    getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
762                imm.toggleSoftInputFromWindow(getWindowToken(),
763                                              imm.SHOW_FORCED, 0);
764                return true;
765            default:
766                break;
767        }
768        return false;
769    }
770
771    // Is this surface valid for drawing into?
772    boolean mSurfaceValid;
773
774    // Are we actively between beginDrawing/endDrawing?
775    boolean mInDrawing;
776
777    // Used to finish the current buffer before changing the surface size
778    boolean mDrawSingleFrame = false;
779    boolean mAbortDraw = false;
780
781    // Are we waiting for a buffer to draw in surfaceChanged?
782    boolean mSyncDraw;
783
784    // True if gecko requests a buffer
785    int mDrawMode;
786
787    static boolean mShowingSplashScreen = true;
788    static String  mSplashURL = "";
789
790    // let's not change stuff around while we're in the middle of
791    // starting drawing, ending drawing, or changing surface
792    // characteristics
793    ReentrantLock mSurfaceLock;
794
795    // Surface format, from surfaceChanged.  Largely
796    // useless.
797    int mFormat;
798
799    // the dimensions of the surface
800    int mWidth;
801    int mHeight;
802
803    // the dimensions of the buffer we're using for drawing,
804    // that is the software buffer or the EGLSurface
805    int mBufferWidth;
806    int mBufferHeight;
807
808    // IME stuff
809    public static final int IME_STATE_DISABLED = 0;
810    public static final int IME_STATE_ENABLED = 1;
811    public static final int IME_STATE_PASSWORD = 2;
812    public static final int IME_STATE_PLUGIN = 3;
813
814    GeckoInputConnection inputConnection;
815    KeyListener mKeyListener;
816    Editable mEditable;
817    Editable.Factory mEditableFactory;
818    int mIMEState;
819    String mIMETypeHint;
820    String mIMEActionHint;
821    boolean mIMELandscapeFS;
822
823    // Software rendering
824    Bitmap mSoftwareBitmap;
825    ByteBuffer mSoftwareBuffer;
826    Bitmap mSoftwareBufferCopy;
827
828    Geocoder mGeocoder;
829    Address  mLastGeoAddress;
830
831    final SynchronousQueue<Object> mSyncDraws = new SynchronousQueue<Object>();
832}
833