author | Alex Pakhotin <alexp@mozilla.com> |
Mon, 14 Jun 2010 19:17:37 -0700 | |
changeset 51723 | 8e6af865ffdb7200db3ef9f21df180179c754f8d |
parent 51722 | 3ec56ec77529977a390dee9effbb23f9cedab232 |
child 51724 | b68354fbfb713ad6ff7ced7c63a43469d96e8955 |
push id | 15400 |
push user | blassey@mozilla.com |
push date | Mon, 30 Aug 2010 01:33:35 +0000 |
treeherder | mozilla-central@8e6af865ffdb [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | blassey, blocking-fennec |
bugs | 569402 |
milestone | 2.0b5pre |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/embedding/android/AndroidManifest.xml.in +++ b/embedding/android/AndroidManifest.xml.in @@ -56,15 +56,25 @@ </intent-filter> <!-- For debugging --> <intent-filter> <action android:name="org.mozilla.gecko.DEBUG" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> - <receiver android:enabled="true" android:name="Restarter"> - <intent-filter> + + <activity android:name="NotificationHandler" + android:label="@MOZ_APP_DISPLAYNAME@ Notification" + android:theme="@android:style/Theme.NoTitleBar"> + <intent-filter> + <action android:name="org.mozilla.gecko.ACTION_ALERT_CLICK" /> + <action android:name="org.mozilla.gecko.ACTION_ALERT_CLEAR" /> + </intent-filter> + </activity> + + <receiver android:enabled="true" android:name="Restarter"> + <intent-filter> <action android:name="org.mozilla.gecko.restart@MOZ_APP_NAME@" /> - </intent-filter> - </receiver> + </intent-filter> + </receiver> </application> </manifest>
--- a/embedding/android/GeckoApp.java +++ b/embedding/android/GeckoApp.java @@ -52,16 +52,19 @@ import android.graphics.*; import android.widget.*; import android.hardware.*; import android.util.*; abstract public class GeckoApp extends Activity { + public static final String ACTION_ALERT_CLICK = "org.mozilla.gecko.ACTION_ALERT_CLICK"; + public static final String ACTION_ALERT_CLEAR = "org.mozilla.gecko.ACTION_ALERT_CLEAR"; + public static FrameLayout mainLayout; public static GeckoSurfaceView surfaceView; public static GeckoApp mAppContext; ProgressDialog mProgressDialog; void launch() { // unpack files in the components directory @@ -426,9 +429,13 @@ abstract public class GeckoApp String amCmd = "/system/bin/am broadcast -a " + action + getEnvString() + " -n org.mozilla." + getAppName() + "/org.mozilla." + getAppName() + ".Restarter"; Log.i("GeckoAppJava", amCmd); Runtime.getRuntime().exec(amCmd); } catch (Exception e) { Log.i("GeckoAppJava", e.toString()); } System.exit(0); } + + public void handleNotification(String action, String alertName, String alertCookie) { + GeckoAppShell.handleNotification(action, alertName, alertCookie); + } }
--- a/embedding/android/GeckoAppShell.java +++ b/embedding/android/GeckoAppShell.java @@ -86,16 +86,18 @@ class GeckoAppShell public static native void nativeInit(); public static native void nativeRun(String args); // helper methods public static native void setInitialSize(int width, int height); public static native void setSurfaceView(GeckoSurfaceView sv); public static native void putenv(String map); public static native void onResume(); + public static native void callObserver(String observerKey, String topic, String data); + public static native void removeObserver(String observerKey); // java-side stuff public static void loadGeckoLibs() { // The package data lib directory isn't placed in ld.so's // search path, so we have to manually load libraries that // libxul will depend on. Not ideal. // MozAlloc @@ -434,9 +436,74 @@ class GeckoAppShell } static void setClipboardText(String text) { Context context = GeckoApp.surfaceView.getContext(); ClipboardManager cm = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); cm.setText(text); } + + static void showAlertNotification(String imageUrl, String alertTitle, String alertText, + String alertCookie, String alertName) { + Log.i("GeckoAppJava", "GeckoAppShell.showAlertNotification\n" + + "- image = '" + imageUrl + "'\n" + + "- title = '" + alertTitle + "'\n" + + "- text = '" + alertText +"'\n" + + "- cookie = '" + alertCookie +"'\n" + + "- name = '" + alertName + "'"); + + int icon = R.drawable.icon; // Just use the app icon by default + + Uri imageUri = Uri.parse(imageUrl); + String scheme = imageUri.getScheme(); + + if ("drawable".equals(scheme)) { + String resource = imageUri.getSchemeSpecificPart(); + if ("//alertdownloads".equals(resource)) + icon = R.drawable.alertdownloads; + else if ("//alertaddons".equals(resource)) + icon = R.drawable.alertaddons; + } + + int notificationID = alertName.hashCode(); + + Notification notification = new Notification(icon, alertTitle, System.currentTimeMillis()); + + // The intent to launch when the user clicks the expanded notification + Intent notificationIntent = new Intent(GeckoApp.ACTION_ALERT_CLICK); + notificationIntent.setClassName(GeckoApp.mAppContext, + "org.mozilla." + GeckoApp.mAppContext.getAppName() + ".NotificationHandler"); + + // Put the strings into the intent as an URI "alert:<name>#<cookie>" + Uri dataUri = Uri.fromParts("alert", alertName, alertCookie); + notificationIntent.setData(dataUri); + + PendingIntent contentIntent = PendingIntent.getActivity(GeckoApp.mAppContext, 0, notificationIntent, 0); + notification.setLatestEventInfo(GeckoApp.mAppContext, alertTitle, alertText, contentIntent); + + // The intent to execute when the status entry is deleted by the user with the "Clear All Notifications" button + Intent clearNotificationIntent = new Intent(GeckoApp.ACTION_ALERT_CLEAR); + clearNotificationIntent.setClassName(GeckoApp.mAppContext, + "org.mozilla." + GeckoApp.mAppContext.getAppName() + ".NotificationHandler"); + clearNotificationIntent.setData(dataUri); + + PendingIntent pendingClearIntent = PendingIntent.getActivity(GeckoApp.mAppContext, 0, clearNotificationIntent, 0); + notification.deleteIntent = pendingClearIntent; + + // Show the notification + NotificationManager notificationManager = (NotificationManager) + GeckoApp.mAppContext.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.notify(notificationID, notification); + Log.i("GeckoAppJava", "Created notification ID " + notificationID); + } + + public static void handleNotification(String action, String alertName, String alertCookie) { + if (GeckoApp.ACTION_ALERT_CLICK.equals(action)) { + Log.i("GeckoAppJava", "GeckoAppShell.handleNotification: callObserver(alertclickcallback)"); + callObserver(alertName, "alertclickcallback", alertCookie); + } + + Log.i("GeckoAppJava", "GeckoAppShell.handleNotification: callObserver(alertfinished)"); + callObserver(alertName, "alertfinished", alertCookie); + removeObserver(alertName); + } }
--- a/embedding/android/Makefile.in +++ b/embedding/android/Makefile.in @@ -46,45 +46,60 @@ include $(topsrcdir)/ipc/app/defs.mk JAVAFILES = \ GeckoApp.java \ GeckoAppShell.java \ GeckoEvent.java \ GeckoSurfaceView.java \ GeckoInputConnection.java \ $(NULL) +PROCESSEDJAVAFILES = \ + App.java \ + Restarter.java \ + NotificationHandler.java \ + $(NULL) + DEFINES += \ -DMOZ_APP_DISPLAYNAME=$(MOZ_APP_DISPLAYNAME) \ -DMOZ_APP_NAME=$(MOZ_APP_NAME) \ -DMOZ_CHILD_PROCESS_NAME=$(MOZ_CHILD_PROCESS_NAME) GARBAGE += \ AndroidManifest.xml \ classes.dex \ $(MOZ_APP_NAME).apk \ - App.java \ - Restarter.java \ + $(PROCESSEDJAVAFILES) \ gecko.ap_ \ gecko-unaligned.apk \ gecko-unsigned-unaligned.apk \ $(NULL) GARBAGE_DIRS += res libs dist classes DIRS = utils # Bug 567884 - Need a way to find appropriate icons during packaging ifeq ($(MOZ_APP_NAME),fennec) ICON_PATH = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_48x48.png -ICON_PATH_HI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_72x72.png +ICON_PATH_HDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_72x72.png else ICON_PATH = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/icon48.png -ICON_PATH_HI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/icon64.png +ICON_PATH_HDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/icon64.png endif +RES_DRAWABLE = \ + res/drawable/alertaddons.png \ + res/drawable/alertdownloads.png \ + $(NULL) + +RES_DRAWABLE_HDPI = \ + res/drawable-hdpi/alertaddons.png \ + res/drawable-hdpi/alertdownloads.png \ + $(NULL) + NATIVE_LIBS = $(shell cat $(DIST)/bin/dependentlibs.list) libxpcom.so libnssckbi.so libfreebl3.so libmozutils.so FULL_LIBS = $(addprefix libs/armeabi/,$(NATIVE_LIBS)) # We'll strip all the libs by default, due to size, but we might # want to have a non-stripped version for debugging. We should # really pull out debuginfo and stuff. ifdef NO_STRIP DO_STRIP=echo not stripping @@ -115,34 +130,45 @@ include $(topsrcdir)/config/rules.mk # Override the Java settings with some specific android settings include $(topsrcdir)/config/android-common.mk tools:: $(MOZ_APP_NAME).apk # Note that we're going to set up a dependency directly between embed_android.dex and the java files # Instead of on the .class files, since more than one .class file might be produced per .java file -classes.dex: $(JAVAFILES) App.java Restarter.java +classes.dex: $(JAVAFILES) $(PROCESSEDJAVAFILES) R.java $(NSINSTALL) -D classes - $(JAVAC) $(JAVAC_FLAGS) -d classes $(addprefix $(srcdir)/,$(JAVAFILES)) App.java Restarter.java + $(JAVAC) $(JAVAC_FLAGS) -d classes $(addprefix $(srcdir)/,$(JAVAFILES)) $(PROCESSEDJAVAFILES) R.java $(DX) --dex --output=$@ classes -AndroidManifest.xml App.java Restarter.java : % : %.in +AndroidManifest.xml $(PROCESSEDJAVAFILES): % : %.in $(PYTHON) $(topsrcdir)/config/Preprocessor.py \ $(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $< > $@ res/drawable/icon.png: $(MOZ_APP_ICON) $(NSINSTALL) -D res/drawable cp $(ICON_PATH) $@ res/drawable-hdpi/icon.png: $(MOZ_APP_ICON) $(NSINSTALL) -D res/drawable-hdpi - cp $(ICON_PATH_HI) $@ + cp $(ICON_PATH_HDPI) $@ + +$(RES_DRAWABLE): + $(NSINSTALL) -D res/drawable + cp $(topsrcdir)/mobile/app/android/drawable/* res/drawable/ -gecko.ap_: AndroidManifest.xml res/drawable/icon.png res/drawable-hdpi/icon.png +$(RES_DRAWABLE_HDPI): + $(NSINSTALL) -D res/drawable-hdpi + cp $(topsrcdir)/mobile/app/android/drawable-hdpi/* res/drawable-hdpi/ + +R.java: $(MOZ_APP_ICON) $(RES_DRAWABLE) $(RES_DRAWABLE_HDPI) + $(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -J . --custom-package org.mozilla.gecko + +gecko.ap_: AndroidManifest.xml res/drawable/icon.png res/drawable-hdpi/icon.png $(RES_DRAWABLE) $(RES_DRAWABLE_HDPI) $(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -F $@ libs/armeabi/%: $(DIST)/lib/% @$(NSINSTALL) -D libs/armeabi @cp -L -v $< $@ @$(DO_STRIP) $@ # Bug 567873 - Android packaging should use standard packaging code
new file mode 100644 --- /dev/null +++ b/embedding/android/NotificationHandler.java.in @@ -0,0 +1,104 @@ +/* -*- Mode: Java; 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 Corporation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Alex Pakhotin <alexp@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#filter substitution +package org.mozilla.@MOZ_APP_NAME@; + +import android.app.Activity; +import android.app.NotificationManager; +import android.content.Intent; +import android.content.ActivityNotFoundException; +import android.os.Bundle; +import android.util.Log; +import android.net.Uri; + +public class NotificationHandler + extends Activity +{ + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Log.i("GeckoAppJava", "NotificationHandler.onCreate"); + + Intent intent = getIntent(); + if (intent != null) + handleIntent(intent); + + finish(); + } + + protected void handleIntent(Intent notificationIntent) { + String action = notificationIntent.getAction(); + String alertName = ""; + String alertCookie = ""; + Uri data = notificationIntent.getData(); + if (data != null) { + alertName = data.getSchemeSpecificPart(); + alertCookie = data.getFragment(); + if (alertCookie == null) + alertCookie = ""; + } + + Log.i("GeckoAppJava", "NotificationHandler.handleIntent\n" + + "- action = '" + action + "'\n" + + "- alertName = '" + alertName + "'\n" + + "- alertCookie = '" + alertCookie + "'"); + + int notificationID = alertName.hashCode(); + + Log.i("GeckoAppJava", "Handle notification ID " + notificationID); + + NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + notificationManager.cancel(notificationID); + + if (App.mAppContext != null) { + // This should call the observer, if any + App.mAppContext.handleNotification(action, alertName, alertCookie); + } + + if (App.ACTION_ALERT_CLICK.equals(action)) { + // Start or bring to front the main activity + Intent appIntent = new Intent(Intent.ACTION_MAIN); + appIntent.setClassName(this, "org.mozilla.@MOZ_APP_NAME@.App"); + try { + startActivity(appIntent); + } catch (ActivityNotFoundException e) { + Log.e("GeckoAppJava", "NotificationHandler Exception: " + e.getMessage()); + } + } + } +}
--- a/toolkit/components/alerts/public/nsIAlertsService.idl +++ b/toolkit/components/alerts/public/nsIAlertsService.idl @@ -51,19 +51,21 @@ interface nsIAlertsService : nsISupports * @param text The contents of the alert. * @param textClickable If true, causes the alert text to look like a link * and notifies the listener when user attempts to * click the alert text. * @param cookie A blind cookie the alert will pass back to the * consumer during the alert listener callbacks. * @param alertListener Used for callbacks. May be null if the caller * doesn't care about callbacks. - * @param name The name of the notification. This is currently - * only used on OS X with Growl. On OS X with Growl, - * users can disable notifications with a given name. + * @param name The name of the notification. This is currently + * only used on OS X with Growl and Android. + * On OS X with Growl, users can disable notifications + * with a given name. On Android the name is hashed + * and used as a notification ID. * * @throws NS_ERROR_NOT_AVAILABLE If the notification cannot be displayed. * * The following arguments will be passed to the alertListener's observe() * method: * subject - null * topic - "alertfinished" when the alert goes away * "alertclickcallback" when the text is clicked
--- a/toolkit/components/alerts/src/nsAlertsService.cpp +++ b/toolkit/components/alerts/src/nsAlertsService.cpp @@ -17,47 +17,55 @@ * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 2002 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Scott MacGregor <mscott@netscape.com> * Jens Bannmann <jens.b@web.de> + * Alex Pakhotin <alexp@mozilla.com> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsAlertsService.h" + +#ifdef ANDROID +#include "AndroidBridge.h" +#else + #include "nsISupportsArray.h" #include "nsXPCOM.h" #include "nsISupportsPrimitives.h" #include "nsIServiceManager.h" #include "nsIDOMWindowInternal.h" #include "nsIWindowWatcher.h" #include "nsDependentString.h" #include "nsWidgetsCID.h" #include "nsILookAndFeel.h" #include "nsToolkitCompsCID.h" static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID); #define ALERT_CHROME_URL "chrome://global/content/alerts/alert.xul" +#endif // !ANDROID + NS_IMPL_THREADSAFE_ADDREF(nsAlertsService) NS_IMPL_THREADSAFE_RELEASE(nsAlertsService) NS_INTERFACE_MAP_BEGIN(nsAlertsService) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAlertsService) NS_INTERFACE_MAP_ENTRY(nsIAlertsService) NS_INTERFACE_MAP_END_THREADSAFE @@ -69,16 +77,21 @@ nsAlertsService::~nsAlertsService() {} NS_IMETHODIMP nsAlertsService::ShowAlertNotification(const nsAString & aImageUrl, const nsAString & aAlertTitle, const nsAString & aAlertText, PRBool aAlertTextClickable, const nsAString & aAlertCookie, nsIObserver * aAlertListener, const nsAString & aAlertName) { +#ifdef ANDROID + mozilla::AndroidBridge::Bridge()->ShowAlertNotification(aImageUrl, aAlertTitle, aAlertText, aAlertCookie, + aAlertListener, aAlertName); + return NS_OK; +#else // Check if there is an optional service that handles system-level notifications nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_SYSTEMALERTSERVICE_CONTRACTID)); nsresult rv; if (sysAlerts) { rv = sysAlerts->ShowAlertNotification(aImageUrl, aAlertTitle, aAlertText, aAlertTextClickable, aAlertCookie, aAlertListener, aAlertName); if (NS_SUCCEEDED(rv)) return rv; @@ -151,9 +164,10 @@ NS_IMETHODIMP nsAlertsService::ShowAlert rv = argsArray->AppendElement(ifptr); NS_ENSURE_SUCCESS(rv, rv); } rv = wwatch->OpenWindow(0, ALERT_CHROME_URL, "_blank", "chrome,dialog=yes,titlebar=no,popup=yes", argsArray, getter_AddRefs(newWindow)); return rv; +#endif // !ANDROID }
--- a/widget/src/android/AndroidBridge.cpp +++ b/widget/src/android/AndroidBridge.cpp @@ -41,16 +41,17 @@ #include "mozilla/dom/ContentChild.h" #include "nsXULAppAPI.h" #endif #include <pthread.h> #include <prthread.h> #include "nsXPCOMStrings.h" #include "AndroidBridge.h" +#include "nsAppShell.h" using namespace mozilla; static PRUintn sJavaEnvThreadIndex = 0; AndroidBridge *AndroidBridge::sBridge = 0; static void @@ -105,16 +106,17 @@ AndroidBridge::Init(JNIEnv *jEnv, jNotifyXreExit = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "onXreExit", "()V"); jGetHandlersForMimeType = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getHandlersForMimeType", "(Ljava/lang/String;)[Ljava/lang/String;"); jGetHandlersForProtocol = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getHandlersForProtocol", "(Ljava/lang/String;)[Ljava/lang/String;"); jOpenUriExternal = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "openUriExternal", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z"); jGetMimeTypeFromExtension = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getMimeTypeFromExtension", "(Ljava/lang/String;)Ljava/lang/String;"); jMoveTaskToBack = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "moveTaskToBack", "()V"); jGetClipboardText = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getClipboardText", "()Ljava/lang/String;"); jSetClipboardText = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setClipboardText", "(Ljava/lang/String;)V"); + jShowAlertNotification = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "showAlertNotification", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); jEGLContextClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("javax/microedition/khronos/egl/EGLContext")); jEGL10Class = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("javax/microedition/khronos/egl/EGL10")); jEGLSurfaceImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLSurfaceImpl")); jEGLContextImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLContextImpl")); jEGLConfigImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLConfigImpl")); jEGLDisplayImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLDisplayImpl")); @@ -399,16 +401,40 @@ AndroidBridge::ClipboardHasText() void AndroidBridge::EmptyClipboard() { mJNIEnv->CallStaticObjectMethod(mGeckoAppShellClass, jSetClipboardText, nsnull); } void +AndroidBridge::ShowAlertNotification(const nsAString& aImageUrl, + const nsAString& aAlertTitle, + const nsAString& aAlertText, + const nsAString& aAlertCookie, + nsIObserver *aAlertListener, + const nsAString& aAlertName) +{ + ALOG("ShowAlertNotification"); + + AutoLocalJNIFrame jniFrame; + + if (nsAppShell::gAppShell && aAlertListener) + nsAppShell::gAppShell->AddObserver(aAlertName, aAlertListener); + + jvalue args[5]; + args[0].l = mJNIEnv->NewString(nsPromiseFlatString(aImageUrl).get(), aImageUrl.Length()); + args[1].l = mJNIEnv->NewString(nsPromiseFlatString(aAlertTitle).get(), aAlertTitle.Length()); + args[2].l = mJNIEnv->NewString(nsPromiseFlatString(aAlertText).get(), aAlertText.Length()); + args[3].l = mJNIEnv->NewString(nsPromiseFlatString(aAlertCookie).get(), aAlertCookie.Length()); + args[4].l = mJNIEnv->NewString(nsPromiseFlatString(aAlertName).get(), aAlertName.Length()); + mJNIEnv->CallStaticVoidMethodA(mGeckoAppShellClass, jShowAlertNotification, args); +} + +void AndroidBridge::SetSurfaceView(jobject obj) { mSurfaceView.Init(obj); } void * AndroidBridge::CallEglCreateWindowSurface(void *dpy, void *config, AndroidGeckoSurfaceView &sview) {
--- a/widget/src/android/AndroidBridge.h +++ b/widget/src/android/AndroidBridge.h @@ -38,16 +38,17 @@ #ifndef AndroidBridge_h__ #define AndroidBridge_h__ #include <jni.h> #include <android/log.h> #include "nsCOMPtr.h" #include "nsIRunnable.h" +#include "nsIObserver.h" #include "AndroidJavaWrappers.h" #include "nsVoidArray.h" // Some debug #defines // #define ANDROID_DEBUG_EVENTS // #define ANDROID_DEBUG_WIDGET @@ -129,16 +130,23 @@ public: bool GetClipboardText(nsAString& aText); void SetClipboardText(const nsAString& aText); void EmptyClipboard(); bool ClipboardHasText(); + void ShowAlertNotification(const nsAString& aImageUrl, + const nsAString& aAlertTitle, + const nsAString& aAlertText, + const nsAString& aAlertData, + nsIObserver *aAlertListener, + const nsAString& aAlertName); + struct AutoLocalJNIFrame { AutoLocalJNIFrame(int nEntries = 128) : mEntries(nEntries) { AndroidBridge::Bridge()->JNI()->PushLocalFrame(mEntries); } // Note! Calling Purge makes all previous local refs created in // the AutoLocalJNIFrame's scope INVALID; be sure that you locked down // any local refs that you need to keep around in global refs! void Purge() { @@ -186,16 +194,17 @@ protected: jmethodID jGetOutstandingDrawEvents; jmethodID jGetHandlersForMimeType; jmethodID jGetHandlersForProtocol; jmethodID jOpenUriExternal; jmethodID jGetMimeTypeFromExtension; jmethodID jMoveTaskToBack; jmethodID jGetClipboardText; jmethodID jSetClipboardText; + jmethodID jShowAlertNotification; // stuff we need for CallEglCreateWindowSurface jclass jEGLSurfaceImplClass; jclass jEGLContextImplClass; jclass jEGLConfigImplClass; jclass jEGLDisplayImplClass; jclass jEGLContextClass; jclass jEGL10Class;
--- a/widget/src/android/AndroidJNI.cpp +++ b/widget/src/android/AndroidJNI.cpp @@ -31,16 +31,17 @@ * 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 ***** */ #include "nsILocalFile.h" +#include "nsString.h" #include "AndroidBridge.h" #include <jni.h> #include <pthread.h> #include <dlfcn.h> #include "nsAppShell.h" @@ -52,16 +53,18 @@ using namespace mozilla; /* Forward declare all the JNI methods as extern "C" */ 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_setSurfaceView(JNIEnv *jenv, jclass, jobject sv); NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_setInitialSize(JNIEnv *jenv, jclass, int width, int height); NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onResume(JNIEnv *, jclass); + NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_callObserver(JNIEnv *, jclass, jstring observerKey, jstring topic, jstring data); + NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_removeObserver(JNIEnv *jenv, jclass, jstring jObserverKey); } /* * Incoming JNI methods */ NS_EXPORT void JNICALL @@ -93,8 +96,35 @@ Java_org_mozilla_gecko_GeckoAppShell_set } NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onResume(JNIEnv *jenv, jclass jc) { if (nsAppShell::gAppShell) nsAppShell::gAppShell->OnResume(); } + +NS_EXPORT void JNICALL +Java_org_mozilla_gecko_GeckoAppShell_callObserver(JNIEnv *jenv, jclass, jstring jObserverKey, jstring jTopic, jstring jData) +{ + if (!nsAppShell::gAppShell) + return; + + nsJNIString sObserverKey(jObserverKey, jenv); + nsJNIString sTopic(jTopic, jenv); + nsJNIString sData(jData, jenv); + + nsAppShell::gAppShell->CallObserver(sObserverKey, sTopic, sData); +} + +NS_EXPORT void JNICALL +Java_org_mozilla_gecko_GeckoAppShell_removeObserver(JNIEnv *jenv, jclass, jstring jObserverKey) +{ + if (!nsAppShell::gAppShell) + return; + + const jchar *observerKey = jenv->GetStringChars(jObserverKey, NULL); + nsString sObserverKey(observerKey); + sObserverKey.SetLength(jenv->GetStringLength(jObserverKey)); + jenv->ReleaseStringChars(jObserverKey, observerKey); + + nsAppShell::gAppShell->RemoveObserver(sObserverKey); +}
--- a/widget/src/android/AndroidJavaWrappers.cpp +++ b/widget/src/android/AndroidJavaWrappers.cpp @@ -436,21 +436,22 @@ AndroidRect::Init(JNIEnv *jenv, jobject } else { mTop = 0; mLeft = 0; mRight = 0; mBottom = 0; } } -nsJNIString::nsJNIString(jstring jstr) +nsJNIString::nsJNIString(jstring jstr, JNIEnv *jenv) { if (!jstr) { SetIsVoid(PR_TRUE); return; } - const jchar* jCharPtr = JNI()->GetStringChars(jstr, false); - int len = JNI()->GetStringLength(jstr); - nsresult rv; + JNIEnv *jni = jenv; + if (!jni) + jni = JNI(); + const jchar* jCharPtr = jni->GetStringChars(jstr, false); + int len = jni->GetStringLength(jstr); Assign(jCharPtr, len); - JNI()->ReleaseStringChars(jstr, jCharPtr); - + jni->ReleaseStringChars(jstr, jCharPtr); }
--- a/widget/src/android/AndroidJavaWrappers.h +++ b/widget/src/android/AndroidJavaWrappers.h @@ -463,14 +463,14 @@ public: IME_GET_SELECTION = 6, IME_ADD_RANGE = 7 }; }; class nsJNIString : public nsString { public: - nsJNIString(jstring jstr); + nsJNIString(jstring jstr, JNIEnv *jenv = NULL); }; } #endif
--- a/widget/src/android/nsAppShell.cpp +++ b/widget/src/android/nsAppShell.cpp @@ -113,16 +113,18 @@ nsAppShell::Init() #endif mQueueLock = PR_NewLock(); mCondLock = PR_NewLock(); mPausedLock = PR_NewLock(); mQueueCond = PR_NewCondVar(mCondLock); mPaused = PR_NewCondVar(mPausedLock); + mObserversHash.Init(); + return nsBaseAppShell::Init(); } void nsAppShell::ScheduleNativeEventCallback() { EVLOG("nsAppShell::ScheduleNativeEventCallback pth: %p thread: %p main: %d", (void*) pthread_self(), (void*) NS_GetCurrentThread(), NS_IsMainThread()); @@ -362,16 +364,77 @@ void nsAppShell::OnResume() { PR_Lock(mPausedLock); PR_NotifyCondVar(mPaused); PR_Unlock(mPausedLock); } +nsresult +nsAppShell::AddObserver(const nsAString &aObserverKey, nsIObserver *aObserver) +{ + NS_ASSERTION(aObserver != nsnull, "nsAppShell::AddObserver: aObserver is null!"); + return mObserversHash.Put(aObserverKey, aObserver) ? NS_OK : NS_ERROR_OUT_OF_MEMORY; +} + +/** + * The XPCOM event that will call the observer on the main thread. + */ +class ObserverCaller : public nsRunnable { +public: + ObserverCaller(nsIObserver *aObserver, const char *aTopic, const PRUnichar *aData) : + mObserver(aObserver), mTopic(aTopic), mData(aData) { + NS_ASSERTION(aObserver != nsnull, "ObserverCaller: aObserver is null!"); + } + + NS_IMETHOD Run() { + ALOG("ObserverCaller::Run: observer = %p, topic = '%s')", + (nsIObserver*)mObserver, mTopic.get()); + mObserver->Observe(nsnull, mTopic.get(), mData.get()); + return NS_OK; + } + +private: + nsCOMPtr<nsIObserver> mObserver; + nsCString mTopic; + nsString mData; +}; + +void +nsAppShell::CallObserver(const nsAString &aObserverKey, const nsAString &aTopic, const nsAString &aData) +{ + nsCOMPtr<nsIObserver> observer; + mObserversHash.Get(aObserverKey, getter_AddRefs(observer)); + + if (!observer) { + ALOG("nsAppShell::CallObserver: Observer was not found!"); + return; + } + + const NS_ConvertUTF16toUTF8 sTopic(aTopic); + const nsPromiseFlatString& sData = PromiseFlatString(aData); + + if (NS_IsMainThread()) { + // This branch will unlikely be hit, have it just in case + observer->Observe(nsnull, sTopic.get(), sData.get()); + } else { + // Java is not running on main thread, so we have to use NS_DispatchToMainThread + nsCOMPtr<nsIRunnable> observerCaller = new ObserverCaller(observer, sTopic.get(), sData.get()); + nsresult rv = NS_DispatchToMainThread(observerCaller); + ALOG("NS_DispatchToMainThread result: %d", rv); + } +} + +void +nsAppShell::RemoveObserver(const nsAString &aObserverKey) +{ + mObserversHash.Remove(aObserverKey); +} + // Used by IPC code namespace mozilla { bool ProcessNextEvent() { return nsAppShell::gAppShell->ProcessNextNativeEvent(PR_TRUE) ? true : false; }
--- a/widget/src/android/nsAppShell.h +++ b/widget/src/android/nsAppShell.h @@ -37,16 +37,17 @@ * ***** END LICENSE BLOCK ***** */ #ifndef nsAppShell_h__ #define nsAppShell_h__ #include "nsBaseAppShell.h" #include "nsCOMPtr.h" #include "nsTArray.h" +#include "nsInterfaceHashtable.h" #include "prcvar.h" namespace mozilla { class AndroidGeckoEvent; bool ProcessNextEvent(); void NotifyEvent(); } @@ -65,26 +66,31 @@ public: void NotifyNativeEvent(); virtual PRBool ProcessNextNativeEvent(PRBool mayWait); void PostEvent(mozilla::AndroidGeckoEvent *event); void RemoveNextEvent(); void OnResume(); + nsresult AddObserver(const nsAString &aObserverKey, nsIObserver *aObserver); + void CallObserver(const nsAString &aObserverKey, const nsAString &aTopic, const nsAString &aData); + void RemoveObserver(const nsAString &aObserverKey); + protected: virtual void ScheduleNativeEventCallback(); virtual ~nsAppShell(); int mNumDraws; PRLock *mQueueLock; PRLock *mCondLock; PRLock *mPausedLock; PRCondVar *mQueueCond; PRCondVar *mPaused; nsTArray<mozilla::AndroidGeckoEvent *> mEventQueue; + nsInterfaceHashtable<nsStringHashKey, nsIObserver> mObserversHash; mozilla::AndroidGeckoEvent *GetNextEvent(); mozilla::AndroidGeckoEvent *PeekNextEvent(); }; #endif // nsAppShell_h__