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