bug 634530 - Fennec fails to startup on Droid Pro when the device is connected to...
[mozilla-central.git] / embedding / android / GeckoAppShell.java
blob5968ddf5d8c8db06c4be52bc09c8a69b64c67a86
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
2 * ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
15 * The Original Code is Mozilla Android code.
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.
21 * Contributor(s):
22 * Vladimir Vukicevic <vladimir@pobox.com>
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.
36 * ***** END LICENSE BLOCK ***** */
38 package org.mozilla.gecko;
40 import java.io.*;
41 import java.lang.reflect.*;
42 import java.nio.*;
43 import java.nio.channels.*;
44 import java.text.*;
45 import java.util.*;
46 import java.util.zip.*;
48 import android.os.*;
49 import android.app.*;
50 import android.text.*;
51 import android.view.*;
52 import android.view.inputmethod.*;
53 import android.content.*;
54 import android.content.res.*;
55 import android.content.pm.*;
56 import android.graphics.*;
57 import android.widget.*;
58 import android.hardware.*;
59 import android.location.*;
61 import android.util.*;
62 import android.net.Uri;
63 import android.net.ConnectivityManager;
64 import android.net.NetworkInfo;
66 class GeckoAppShell
68 // static members only
69 private GeckoAppShell() { }
71 static private LinkedList<GeckoEvent> gPendingEvents =
72 new LinkedList<GeckoEvent>();
74 static private boolean gRestartScheduled = false;
76 static private final Timer mIMETimer = new Timer();
77 static private final HashMap<Integer, AlertNotification>
78 mAlertNotifications = new HashMap<Integer, AlertNotification>();
80 static private final int NOTIFY_IME_RESETINPUTSTATE = 0;
81 static private final int NOTIFY_IME_SETOPENSTATE = 1;
82 static private final int NOTIFY_IME_CANCELCOMPOSITION = 2;
83 static private final int NOTIFY_IME_FOCUSCHANGE = 3;
85 static public final long kFreeSpaceThreshold = 157286400L; // 150MB
86 static private final long kLibFreeSpaceBuffer = 20971520L; // 29MB
87 static private File sCacheFile = null;
88 static private int sFreeSpace = -1;
90 /* The Android-side API: API methods that Android calls */
92 // Initialization methods
93 public static native void nativeInit();
94 public static native void nativeRun(String args);
96 // helper methods
97 public static native void setSurfaceView(GeckoSurfaceView sv);
98 public static native void putenv(String map);
99 public static native void onResume();
100 public static native void onLowMemory();
101 public static native void callObserver(String observerKey, String topic, String data);
102 public static native void removeObserver(String observerKey);
103 public static native void loadLibs(String apkName, boolean shouldExtract);
104 public static native void onChangeNetworkLinkStatus(String status);
106 public static File getCacheDir() {
107 if (sCacheFile == null)
108 sCacheFile = GeckoApp.mAppContext.getCacheDir();
109 return sCacheFile;
112 public static long getFreeSpace() {
113 try {
114 if (sFreeSpace == -1) {
115 File cacheDir = getCacheDir();
116 if (cacheDir != null) {
117 StatFs cacheStats = new StatFs(cacheDir.getPath());
118 sFreeSpace = cacheStats.getFreeBlocks() *
119 cacheStats.getBlockSize();
120 } else {
121 Log.i("GeckoAppShell", "Unable to get cache dir");
124 } catch (Exception e) {
125 Log.e("GeckoAppShell", "exception while stating cache dir: ", e);
127 return sFreeSpace;
130 static boolean moveFile(File inFile, File outFile)
132 Log.i("GeckoAppShell", "moving " + inFile + " to " + outFile);
133 if (outFile.isDirectory())
134 outFile = new File(outFile, inFile.getName());
135 try {
136 if (inFile.renameTo(outFile))
137 return true;
138 } catch (SecurityException se) {
139 Log.w("GeckoAppShell", "error trying to rename file", se);
141 try {
142 long lastModified = inFile.lastModified();
143 outFile.createNewFile();
144 // so copy it instead
145 FileChannel inChannel = new FileInputStream(inFile).getChannel();
146 FileChannel outChannel = new FileOutputStream(outFile).getChannel();
147 long size = inChannel.size();
148 long transferred = inChannel.transferTo(0, size, outChannel);
149 inChannel.close();
150 outChannel.close();
151 outFile.setLastModified(lastModified);
153 if (transferred == size)
154 inFile.delete();
155 else
156 return false;
157 } catch (Exception e) {
158 Log.e("GeckoAppShell", "exception while moving file: ", e);
159 try {
160 outFile.delete();
161 } catch (SecurityException se) {
162 Log.w("GeckoAppShell", "error trying to delete file", se);
164 return false;
166 return true;
169 static boolean moveDir(File from, File to) {
170 try {
171 to.mkdirs();
172 if (from.renameTo(to))
173 return true;
174 } catch (SecurityException se) {
175 Log.w("GeckoAppShell", "error trying to rename file", se);
177 File[] files = from.listFiles();
178 boolean retVal = true;
179 if (files == null)
180 return false;
181 try {
182 Iterator fileIterator = Arrays.asList(files).iterator();
183 while (fileIterator.hasNext()) {
184 File file = (File)fileIterator.next();
185 File dest = new File(to, file.getName());
186 if (file.isDirectory())
187 retVal = moveDir(file, dest) ? retVal : false;
188 else
189 retVal = moveFile(file, dest) ? retVal : false;
191 from.delete();
192 } catch(Exception e) {
193 Log.e("GeckoAppShell", "error trying to move file", e);
195 return retVal;
198 // java-side stuff
199 public static void loadGeckoLibs(String apkName) {
200 // The package data lib directory isn't placed in ld.so's
201 // search path, so we have to manually load libraries that
202 // libxul will depend on. Not ideal.
203 System.loadLibrary("mozutils");
204 GeckoApp geckoApp = GeckoApp.mAppContext;
205 String homeDir;
206 if (Build.VERSION.SDK_INT < 8 ||
207 geckoApp.getApplication().getPackageResourcePath().startsWith("/data")) {
208 File home = geckoApp.getFilesDir();
209 homeDir = home.getPath();
210 // handle the application being moved to phone from sdcard
211 File profileDir = new File(homeDir, "mozilla");
212 File oldHome = new File("/data/data/" +
213 GeckoApp.mAppContext.getPackageName() + "/mozilla");
214 if (oldHome.exists())
215 moveDir(oldHome, profileDir);
216 if (Build.VERSION.SDK_INT >= 8) {
217 File extHome = geckoApp.getExternalFilesDir(null);
218 File extProf = new File (extHome, "mozilla");
219 if (extHome != null && extProf != null && extProf.exists())
220 moveDir(extProf, profileDir);
222 } else {
223 File home = geckoApp.getExternalFilesDir(null);
224 homeDir = home.getPath();
225 // handle the application being moved to phone from sdcard
226 File profileDir = new File(homeDir, "mozilla");
227 File oldHome = new File("/data/data/" +
228 GeckoApp.mAppContext.getPackageName() + "/mozilla");
229 if (oldHome.exists())
230 moveDir(oldHome, profileDir);
232 File intHome = geckoApp.getFilesDir();
233 File intProf = new File(intHome, "mozilla");
234 if (intHome != null && intProf != null && intProf.exists())
235 moveDir(intProf, profileDir);
237 GeckoAppShell.putenv("HOME=" + homeDir);
238 GeckoAppShell.putenv("GRE_HOME=" + GeckoApp.sGREDir.getPath());
239 Intent i = geckoApp.getIntent();
240 String env = i.getStringExtra("env0");
241 Log.i("GeckoApp", "env0: "+ env);
242 for (int c = 1; env != null; c++) {
243 GeckoAppShell.putenv(env);
244 env = i.getStringExtra("env" + c);
245 Log.i("GeckoApp", "env"+ c +": "+ env);
248 File f = geckoApp.getDir("tmp", Context.MODE_WORLD_READABLE |
249 Context.MODE_WORLD_WRITEABLE );
251 if (!f.exists())
252 f.mkdirs();
254 GeckoAppShell.putenv("TMPDIR=" + f.getPath());
256 f = Environment.getDownloadCacheDirectory();
257 GeckoAppShell.putenv("EXTERNAL_STORAGE=" + f.getPath());
259 File cacheFile = getCacheDir();
260 GeckoAppShell.putenv("CACHE_PATH=" + cacheFile.getPath());
262 // gingerbread introduces File.getUsableSpace(). We should use that.
263 long freeSpace = getFreeSpace();
264 try {
265 File downloadDir = null;
266 File updatesDir = null;
267 if (Build.VERSION.SDK_INT >= 8) {
268 downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
269 updatesDir = GeckoApp.mAppContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
270 } else {
271 updatesDir = downloadDir = new File(Environment.getExternalStorageDirectory().getPath(), "download");
273 GeckoAppShell.putenv("DOWNLOADS_DIRECTORY=" + downloadDir.getPath());
274 GeckoAppShell.putenv("UPDATES_DIRECTORY=" + updatesDir.getPath());
276 catch (Exception e) {
277 Log.i("GeckoApp", "No download directory has been found: " + e);
280 putLocaleEnv();
282 if (freeSpace + kLibFreeSpaceBuffer < kFreeSpaceThreshold) {
283 // remove any previously extracted libs since we're apparently low
284 File[] files = cacheFile.listFiles();
285 if (files != null) {
286 Iterator cacheFiles = Arrays.asList(files).iterator();
287 while (cacheFiles.hasNext()) {
288 File libFile = (File)cacheFiles.next();
289 if (libFile.getName().endsWith(".so"))
290 libFile.delete();
294 loadLibs(apkName, freeSpace > kFreeSpaceThreshold);
297 private static void putLocaleEnv() {
298 GeckoAppShell.putenv("LANG=" + Locale.getDefault().toString());
299 NumberFormat nf = NumberFormat.getInstance();
300 if (nf instanceof DecimalFormat) {
301 DecimalFormat df = (DecimalFormat)nf;
302 DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
304 GeckoAppShell.putenv("LOCALE_DECIMAL_POINT=" + dfs.getDecimalSeparator());
305 GeckoAppShell.putenv("LOCALE_THOUSANDS_SEP=" + dfs.getGroupingSeparator());
306 GeckoAppShell.putenv("LOCALE_GROUPING=" + (char)df.getGroupingSize());
310 public static void runGecko(String apkPath, String args, String url) {
311 // run gecko -- it will spawn its own thread
312 GeckoAppShell.nativeInit();
314 // Tell Gecko where the target surface view is for rendering
315 GeckoAppShell.setSurfaceView(GeckoApp.surfaceView);
317 // First argument is the .apk path
318 String combinedArgs = apkPath + " -omnijar " + apkPath;
319 if (args != null)
320 combinedArgs += " " + args;
321 if (url != null)
322 combinedArgs += " " + url;
323 // and go
324 GeckoAppShell.nativeRun(combinedArgs);
327 private static GeckoEvent mLastDrawEvent;
329 private static void sendPendingEventsToGecko() {
330 try {
331 while (!gPendingEvents.isEmpty()) {
332 GeckoEvent e = gPendingEvents.removeFirst();
333 notifyGeckoOfEvent(e);
335 } catch (NoSuchElementException e) {}
338 public static void sendEventToGecko(GeckoEvent e) {
339 if (GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning)) {
340 notifyGeckoOfEvent(e);
341 } else {
342 gPendingEvents.addLast(e);
346 // Tell the Gecko event loop that an event is available.
347 public static native void notifyGeckoOfEvent(GeckoEvent event);
350 * The Gecko-side API: API methods that Gecko calls
352 public static void scheduleRedraw() {
353 // Redraw everything
354 scheduleRedraw(0, -1, -1, -1, -1);
357 public static void scheduleRedraw(int nativeWindow, int x, int y, int w, int h) {
358 GeckoEvent e;
360 if (x == -1) {
361 e = new GeckoEvent(GeckoEvent.DRAW, null);
362 } else {
363 e = new GeckoEvent(GeckoEvent.DRAW, new Rect(x, y, w, h));
366 e.mNativeWindow = nativeWindow;
368 sendEventToGecko(e);
371 /* Delay updating IME states (see bug 573800) */
372 private static final class IMEStateUpdater extends TimerTask
374 static private IMEStateUpdater instance;
375 private boolean mEnable, mReset;
377 static private IMEStateUpdater getInstance() {
378 if (instance == null) {
379 instance = new IMEStateUpdater();
380 mIMETimer.schedule(instance, 200);
382 return instance;
385 static public synchronized void enableIME() {
386 getInstance().mEnable = true;
389 static public synchronized void resetIME() {
390 getInstance().mReset = true;
393 public void run() {
394 synchronized(IMEStateUpdater.class) {
395 instance = null;
398 InputMethodManager imm = (InputMethodManager)
399 GeckoApp.surfaceView.getContext().getSystemService(
400 Context.INPUT_METHOD_SERVICE);
401 if (imm == null)
402 return;
404 if (mReset)
405 imm.restartInput(GeckoApp.surfaceView);
407 if (!mEnable)
408 return;
410 if (GeckoApp.surfaceView.mIMEState !=
411 GeckoSurfaceView.IME_STATE_DISABLED)
412 imm.showSoftInput(GeckoApp.surfaceView, 0);
413 else
414 imm.hideSoftInputFromWindow(
415 GeckoApp.surfaceView.getWindowToken(), 0);
419 public static void notifyIME(int type, int state) {
420 if (GeckoApp.surfaceView == null)
421 return;
423 switch (type) {
424 case NOTIFY_IME_RESETINPUTSTATE:
425 GeckoApp.surfaceView.inputConnection.finishComposingText();
426 IMEStateUpdater.resetIME();
427 // keep current enabled state
428 IMEStateUpdater.enableIME();
429 break;
431 case NOTIFY_IME_CANCELCOMPOSITION:
432 IMEStateUpdater.resetIME();
433 break;
435 case NOTIFY_IME_FOCUSCHANGE:
436 GeckoApp.surfaceView.mIMEFocus = state != 0;
437 IMEStateUpdater.resetIME();
438 break;
442 public static void notifyIMEEnabled(int state, String typeHint,
443 String actionHint) {
444 if (GeckoApp.surfaceView == null)
445 return;
447 /* When IME is 'disabled', IME processing is disabled.
448 In addition, the IME UI is hidden */
449 GeckoApp.surfaceView.mIMEState = state;
450 GeckoApp.surfaceView.mIMETypeHint = typeHint;
451 GeckoApp.surfaceView.mIMEActionHint = actionHint;
452 IMEStateUpdater.enableIME();
455 public static void notifyIMEChange(String text, int start, int end, int newEnd) {
456 if (GeckoApp.surfaceView == null ||
457 GeckoApp.surfaceView.inputConnection == null)
458 return;
460 InputMethodManager imm = (InputMethodManager)
461 GeckoApp.surfaceView.getContext().getSystemService(
462 Context.INPUT_METHOD_SERVICE);
463 if (imm == null)
464 return;
466 if (newEnd < 0)
467 GeckoApp.surfaceView.inputConnection.notifySelectionChange(
468 imm, start, end);
469 else
470 GeckoApp.surfaceView.inputConnection.notifyTextChange(
471 imm, text, start, end, newEnd);
474 public static void enableAccelerometer(boolean enable) {
475 SensorManager sm = (SensorManager)
476 GeckoApp.surfaceView.getContext().getSystemService(Context.SENSOR_SERVICE);
478 if (enable) {
479 Sensor accelSensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
480 if (accelSensor == null)
481 return;
483 sm.registerListener(GeckoApp.surfaceView, accelSensor, SensorManager.SENSOR_DELAY_GAME);
484 } else {
485 sm.unregisterListener(GeckoApp.surfaceView);
489 public static void enableLocation(boolean enable) {
490 LocationManager lm = (LocationManager)
491 GeckoApp.surfaceView.getContext().getSystemService(Context.LOCATION_SERVICE);
493 if (enable) {
494 Criteria crit = new Criteria();
495 crit.setAccuracy(Criteria.ACCURACY_FINE);
496 String provider = lm.getBestProvider(crit, true);
497 if (provider == null)
498 return;
500 Location loc = lm.getLastKnownLocation(provider);
501 if (loc != null)
502 sendEventToGecko(new GeckoEvent(loc));
503 lm.requestLocationUpdates(provider, 100, (float).5, GeckoApp.surfaceView, Looper.getMainLooper());
504 } else {
505 lm.removeUpdates(GeckoApp.surfaceView);
509 public static void moveTaskToBack() {
510 GeckoApp.mAppContext.moveTaskToBack(true);
513 public static void returnIMEQueryResult(String result, int selectionStart, int selectionLength) {
514 GeckoApp.surfaceView.inputConnection.mSelectionStart = selectionStart;
515 GeckoApp.surfaceView.inputConnection.mSelectionLength = selectionLength;
516 try {
517 GeckoApp.surfaceView.inputConnection.mQueryResult.put(result);
518 } catch (InterruptedException e) {
522 static void onAppShellReady()
524 // mLaunchState can only be Launched at this point
525 GeckoApp.setLaunchState(GeckoApp.LaunchState.GeckoRunning);
526 sendPendingEventsToGecko();
529 static void onXreExit() {
530 // mLaunchState can only be Launched or GeckoRunning at this point
531 GeckoApp.setLaunchState(GeckoApp.LaunchState.GeckoExiting);
532 Log.i("GeckoAppJava", "XRE exited");
533 if (gRestartScheduled) {
534 GeckoApp.mAppContext.doRestart();
535 } else {
536 Log.i("GeckoAppJava", "we're done, good bye");
537 GeckoApp.mAppContext.finish();
538 System.exit(0);
542 static void scheduleRestart() {
543 Log.i("GeckoAppJava", "scheduling restart");
544 gRestartScheduled = true;
547 // "Installs" an application by creating a shortcut
548 static void installWebApplication(String aURI, String aTitle, String aIconData) {
549 Log.w("GeckoAppJava", "installWebApplication for " + aURI + " [" + aTitle + "]");
551 // the intent to be launched by the shortcut
552 Intent shortcutIntent = new Intent("org.mozilla.fennec.WEBAPP");
553 shortcutIntent.setClassName(GeckoApp.mAppContext,
554 GeckoApp.mAppContext.getPackageName() + ".App");
555 shortcutIntent.putExtra("args", "--webapp=" + aURI);
557 Intent intent = new Intent();
558 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
559 intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, aTitle);
560 byte[] raw = Base64.decode(aIconData.substring(22), Base64.DEFAULT);
561 Bitmap bitmap = BitmapFactory.decodeByteArray(raw, 0, raw.length);
562 intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap);
563 intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
564 GeckoApp.mAppContext.sendBroadcast(intent);
567 static String[] getHandlersForMimeType(String aMimeType, String aAction) {
568 Intent intent = getIntentForActionString(aAction);
569 if (aMimeType != null && aMimeType.length() > 0)
570 intent.setType(aMimeType);
571 return getHandlersForIntent(intent);
574 static String[] getHandlersForURL(String aURL, String aAction) {
575 // aURL may contain the whole URL or just the protocol
576 Uri uri = aURL.indexOf(':') >= 0 ? Uri.parse(aURL) : new Uri.Builder().scheme(aURL).build();
577 Intent intent = getIntentForActionString(aAction);
578 intent.setData(uri);
579 return getHandlersForIntent(intent);
582 static String[] getHandlersForIntent(Intent intent) {
583 PackageManager pm =
584 GeckoApp.surfaceView.getContext().getPackageManager();
585 List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
586 int numAttr = 4;
587 String[] ret = new String[list.size() * numAttr];
588 for (int i = 0; i < list.size(); i++) {
589 ResolveInfo resolveInfo = list.get(i);
590 ret[i * numAttr] = resolveInfo.loadLabel(pm).toString();
591 if (resolveInfo.isDefault)
592 ret[i * numAttr + 1] = "default";
593 else
594 ret[i * numAttr + 1] = "";
595 ret[i * numAttr + 2] = resolveInfo.activityInfo.applicationInfo.packageName;
596 ret[i * numAttr + 3] = resolveInfo.activityInfo.name;
598 return ret;
601 static Intent getIntentForActionString(String aAction) {
602 // Default to the view action if no other action as been specified.
603 if (aAction != null && aAction.length() > 0)
604 return new Intent(aAction);
605 else
606 return new Intent(Intent.ACTION_VIEW);
609 static String getMimeTypeFromExtensions(String aFileExt) {
610 android.webkit.MimeTypeMap mtm =
611 android.webkit.MimeTypeMap.getSingleton();
612 StringTokenizer st = new StringTokenizer(aFileExt, "., ");
613 String type = null;
614 String subType = null;
615 while (st.hasMoreElements()) {
616 String ext = st.nextToken();
617 String mt = mtm.getMimeTypeFromExtension(ext);
618 if (mt == null)
619 continue;
620 int slash = mt.indexOf('/');
621 String tmpType = mt.substring(0, slash);
622 if (!tmpType.equalsIgnoreCase(type))
623 type = type == null ? tmpType : "*";
624 String tmpSubType = mt.substring(slash + 1);
625 if (!tmpSubType.equalsIgnoreCase(subType))
626 subType = subType == null ? tmpSubType : "*";
628 if (type == null)
629 type = "*";
630 if (subType == null)
631 subType = "*";
632 return type + "/" + subType;
635 static boolean openUriExternal(String aUriSpec, String aMimeType, String aPackageName,
636 String aClassName, String aAction, String aTitle) {
637 Intent intent = getIntentForActionString(aAction);
638 if (aAction.equalsIgnoreCase(Intent.ACTION_SEND)) {
639 intent.putExtra(Intent.EXTRA_TEXT, aUriSpec);
640 intent.putExtra(Intent.EXTRA_SUBJECT, aTitle);
641 if (aMimeType != null && aMimeType.length() > 0)
642 intent.setType(aMimeType);
643 } else if (aMimeType.length() > 0) {
644 intent.setDataAndType(Uri.parse(aUriSpec), aMimeType);
645 } else {
646 intent.setData(Uri.parse(aUriSpec));
648 if (aPackageName.length() > 0 && aClassName.length() > 0)
649 intent.setClassName(aPackageName, aClassName);
651 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
652 try {
653 GeckoApp.surfaceView.getContext().startActivity(intent);
654 return true;
655 } catch(ActivityNotFoundException e) {
656 return false;
660 static String getClipboardText() {
661 Context context = GeckoApp.surfaceView.getContext();
662 ClipboardManager cm = (ClipboardManager)
663 context.getSystemService(Context.CLIPBOARD_SERVICE);
664 if (!cm.hasText())
665 return null;
666 return cm.getText().toString();
669 static void setClipboardText(String text) {
670 Context context = GeckoApp.surfaceView.getContext();
671 ClipboardManager cm = (ClipboardManager)
672 context.getSystemService(Context.CLIPBOARD_SERVICE);
673 cm.setText(text);
676 public static void showAlertNotification(String aImageUrl, String aAlertTitle, String aAlertText,
677 String aAlertCookie, String aAlertName) {
678 Log.i("GeckoAppJava", "GeckoAppShell.showAlertNotification\n" +
679 "- image = '" + aImageUrl + "'\n" +
680 "- title = '" + aAlertTitle + "'\n" +
681 "- text = '" + aAlertText +"'\n" +
682 "- cookie = '" + aAlertCookie +"'\n" +
683 "- name = '" + aAlertName + "'");
685 int icon = R.drawable.icon; // Just use the app icon by default
687 Uri imageUri = Uri.parse(aImageUrl);
688 String scheme = imageUri.getScheme();
689 if ("drawable".equals(scheme)) {
690 String resource = imageUri.getSchemeSpecificPart();
691 resource = resource.substring(resource.lastIndexOf('/') + 1);
692 try {
693 Class drawableClass = R.drawable.class;
694 Field f = drawableClass.getField(resource);
695 icon = f.getInt(null);
696 } catch (Exception e) {} // just means the resource doesn't exist
699 int notificationID = aAlertName.hashCode();
701 // Remove the old notification with the same ID, if any
702 removeNotification(notificationID);
704 AlertNotification notification = new AlertNotification(GeckoApp.mAppContext,
705 notificationID, icon, aAlertTitle, aAlertText, System.currentTimeMillis());
707 // The intent to launch when the user clicks the expanded notification
708 Intent notificationIntent = new Intent(GeckoApp.ACTION_ALERT_CLICK);
709 notificationIntent.setClassName(GeckoApp.mAppContext,
710 GeckoApp.mAppContext.getPackageName() + ".NotificationHandler");
712 // Put the strings into the intent as an URI "alert:<name>#<cookie>"
713 Uri dataUri = Uri.fromParts("alert", aAlertName, aAlertCookie);
714 notificationIntent.setData(dataUri);
716 PendingIntent contentIntent = PendingIntent.getBroadcast(GeckoApp.mAppContext, 0, notificationIntent, 0);
717 notification.setLatestEventInfo(GeckoApp.mAppContext, aAlertTitle, aAlertText, contentIntent);
719 // The intent to execute when the status entry is deleted by the user with the "Clear All Notifications" button
720 Intent clearNotificationIntent = new Intent(GeckoApp.ACTION_ALERT_CLEAR);
721 clearNotificationIntent.setClassName(GeckoApp.mAppContext,
722 GeckoApp.mAppContext.getPackageName() + ".NotificationHandler");
723 clearNotificationIntent.setData(dataUri);
724 notification.deleteIntent = PendingIntent.getBroadcast(GeckoApp.mAppContext, 0, clearNotificationIntent, 0);
726 mAlertNotifications.put(notificationID, notification);
728 notification.show();
730 Log.i("GeckoAppJava", "Created notification ID " + notificationID);
733 public static void alertsProgressListener_OnProgress(String aAlertName, long aProgress, long aProgressMax, String aAlertText) {
734 Log.i("GeckoAppJava", "GeckoAppShell.alertsProgressListener_OnProgress\n" +
735 "- name = '" + aAlertName +"', " +
736 "progress = " + aProgress +" / " + aProgressMax + ", text = '" + aAlertText + "'");
738 int notificationID = aAlertName.hashCode();
739 AlertNotification notification = mAlertNotifications.get(notificationID);
740 if (notification != null)
741 notification.updateProgress(aAlertText, aProgress, aProgressMax);
743 if (aProgress == aProgressMax) {
744 // Hide the notification at 100%
745 removeObserver(aAlertName);
746 removeNotification(notificationID);
750 public static void alertsProgressListener_OnCancel(String aAlertName) {
751 Log.i("GeckoAppJava", "GeckoAppShell.alertsProgressListener_OnCancel('" + aAlertName + "'");
753 removeObserver(aAlertName);
755 int notificationID = aAlertName.hashCode();
756 removeNotification(notificationID);
759 public static void handleNotification(String aAction, String aAlertName, String aAlertCookie) {
760 int notificationID = aAlertName.hashCode();
762 if (GeckoApp.ACTION_ALERT_CLICK.equals(aAction)) {
763 Log.i("GeckoAppJava", "GeckoAppShell.handleNotification: callObserver(alertclickcallback)");
764 callObserver(aAlertName, "alertclickcallback", aAlertCookie);
766 AlertNotification notification = mAlertNotifications.get(notificationID);
767 if (notification != null && notification.isProgressStyle()) {
768 // When clicked, keep the notification, if it displays a progress
769 return;
773 callObserver(aAlertName, "alertfinished", aAlertCookie);
775 removeObserver(aAlertName);
777 removeNotification(notificationID);
780 private static void removeNotification(int notificationID) {
781 mAlertNotifications.remove(notificationID);
783 NotificationManager notificationManager = (NotificationManager)
784 GeckoApp.mAppContext.getSystemService(Context.NOTIFICATION_SERVICE);
785 notificationManager.cancel(notificationID);
788 public static int getDpi() {
789 DisplayMetrics metrics = new DisplayMetrics();
790 GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
791 return metrics.densityDpi;
794 public static void setFullScreen(boolean fullscreen) {
795 GeckoApp.mFullscreen = fullscreen;
797 // force a reconfiguration to hide/show the system bar
798 GeckoApp.mAppContext.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
799 GeckoApp.mAppContext.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
800 GeckoApp.mAppContext.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
803 public static String showFilePicker(String aFilters) {
804 return GeckoApp.mAppContext.
805 showFilePicker(getMimeTypeFromExtensions(aFilters));
808 public static void performHapticFeedback(boolean aIsLongPress) {
809 GeckoApp.surfaceView.
810 performHapticFeedback(aIsLongPress ?
811 HapticFeedbackConstants.LONG_PRESS :
812 HapticFeedbackConstants.VIRTUAL_KEY);
815 public static void showInputMethodPicker() {
816 InputMethodManager imm = (InputMethodManager) GeckoApp.surfaceView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
817 imm.showInputMethodPicker();
820 public static void hideProgressDialog() {
821 GeckoApp.surfaceView.mShowingSplashScreen = false;
824 public static void setKeepScreenOn(final boolean on) {
825 GeckoApp.mAppContext.runOnUiThread(new Runnable() {
826 public void run() {
827 GeckoApp.surfaceView.setKeepScreenOn(on);
832 public static boolean isNetworkLinkUp() {
833 ConnectivityManager cm = (ConnectivityManager)
834 GeckoApp.mAppContext.getSystemService(Context.CONNECTIVITY_SERVICE);
835 NetworkInfo info = cm.getActiveNetworkInfo();
836 if (info == null || !info.isConnected())
837 return false;
838 return true;
841 public static boolean isNetworkLinkKnown() {
842 ConnectivityManager cm = (ConnectivityManager)
843 GeckoApp.mAppContext.getSystemService(Context.CONNECTIVITY_SERVICE);
844 if (cm.getActiveNetworkInfo() == null)
845 return false;
846 return true;