03-09-2012 12:05 PM
Hello,
I need my app to display a pop-up window to the user even if the app is in the background.
How can I do this if my app is built using html5 and javascripts?
Thank you for any help
Solved! Go to Solution.
03-09-2012 01:31 PM
Hello Wadi,
Unfortunately the standard alert approach does not work if your app is in the background; no alert is displayed upon execution of that code, not even after returning to your application following an event that should have triggered an alert.
For Smartphones, I approach that comes to mind would be a JavaScript extension that trigger some Java code, displaying a Dialog to the user.
JavaScript Extensions:
Displaying a Dialog in Java:
Erik Oros
BlackBerry Development Advisor
03-10-2012 09:36 AM
Can you please take a look at these classes that make my javascript Extension for the Popup:
PopupExtension.java
package samplecode;
import org.w3c.dom.Document;
import net.rim.device.api.browser.field2.BrowserField;
import net.rim.device.api.script.ScriptEngine;
import net.rim.device.api.web.WidgetConfig;
import net.rim.device.api.web.WidgetExtension;
public final class AlertExtension implements WidgetExtension {
public String[] getFeatureList() {
String[] result = new String[1];
result[0] = "sample.alert";
return result;
}
public void loadFeature(String feature, String version,
Document doc, ScriptEngine scriptEngine) throws Exception {
if (feature == "sample.alert") {
scriptEngine.addExtension("sample.alert", new AlertNamespace());
}
}
public void register(WidgetConfig arg0, BrowserField arg1) {
// TODO Auto-generated method stub
}
public void unloadFeatures(Document arg0) {
// TODO Auto-generated method stub
}
}PopupNamespace.java:
package samplecode;
import net.rim.device.api.script.Scriptable;
import net.rim.device.api.system.UiApplication;
public final class PopupNamespace extends Scriptable {
private PopupFunction callPopup;
public PopupNamespace() {
this.callPopup = new PopupFunction();
}
}PopupFunction.java
package samplecode;
import net.rim.device.api.script.ScriptableFunction;
import net.rim.device.api.system.UiApplication;
public final class PopupFunction extends ScriptableFunction {
public Object invoke(Object obj, Object[] args) throws Exception
{
synchronized(
Application.getEventLock()){
UiEngine ui = Ui.getUiEngine();
Screen screen = new Dialog(Dialog.D_OK, "Look out!!!",
Dialog.OK,
Bitmap.getPredefinedBitmap(Bitmap.EXCLAMATION),
Manager.VERTICAL_SCROLL);
ui.pushGlobalScreen(screen, 1, UiEngine.GLOBAL_QUEUE);
}
}
}
Now In my javascript: sample.popup.FUNCTION();
What am I supposted to call instead of FUNCTION?
Thank you so much for your help. I really appreciate it ![]()
03-14-2012 06:15 PM
Hello Wadi,
This turned out to be a bit of a monster post. If you have any question at any stage of the post, please do not hesitate to ask. Let's dive in.
There are a few issues that I'm seeing with the code you provided.
PopupExtension.java
PopupNamespace.java
PopupFunction.java
I'll show you a comparison of my files where I managed to get the Dialog displayed from within my WebWorks application; regardless of whether the application is in the foreground or background.
For reference, the creation of my java files is based on the code samples available here:
To start, I have SandboxExtension.java. This is the equivalent of your PopupExtension.java.
package sandbox.extension;
import net.rim.device.api.browser.field2.BrowserField;
import net.rim.device.api.script.ScriptEngine;
import net.rim.device.api.web.WidgetConfig;
import net.rim.device.api.web.WidgetExtension;
import org.w3c.dom.Document;
public final class SandboxExtension implements WidgetExtension {
String _widgetNameForFutureUse;
BrowserField _browserFieldForFutureUse;
public String[] getFeatureList() {
String[] result = new String[1];
result[0] = "sandbox.popup";
return result;
}
public void loadFeature(String feature, String version, Document doc, ScriptEngine scriptEngine) throws Exception {
if (feature.equals("sandbox.popup")) {
scriptEngine.addExtension("sandbox.popup", new SandboxScriptable());
}
}
public void register(WidgetConfig widgetConfig, BrowserField browserField) {
_widgetNameForFutureUse = widgetConfig.getName();
_browserFieldForFutureUse = browserField;
}
public void unloadFeatures(Document doc) {
}
}
Key Differences
The next file is SandboxScriptable.java. This is the equivalent of your PopupNamespace.java file. Note that This Class must refer to the Object that is created in the above file. In my case I created a SandboxScriptable Object, so I am now implementing SandboxScriptable.java to define that Object.
package sandbox.extension;
import net.rim.device.api.script.Scriptable;
public final class SandboxScriptable extends Scriptable {
private SandboxPopup _sandbox;
public SandboxScriptable() {
_sandbox = new SandboxPopup();
}
public Object getField(String name) throws Exception {
if(name.equals("doPopup")) {
return this._sandbox;
}
return super.getField(name);
}
}
Key Differences
The final Class is SandboxPopup.java. This is the equivalent of your PopupFunction.java. Above, we create and return a SandboxPopup Object, so in this file we are actually implementing what that Object actually is.
package sandbox.extension;
import net.rim.device.api.script.ScriptableFunction;
import net.rim.device.api.system.Application;
import net.rim.device.api.ui.UiEngine;
import net.rim.device.api.ui.Ui;
import net.rim.device.api.ui.Screen;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.Manager;
public final class SandboxPopup extends ScriptableFunction {
public Object invoke(Object obj, Object[] args) throws Exception {
synchronized(Application.getEventLock()) {
UiEngine ui = Ui.getUiEngine();
Screen screen = new Dialog(Dialog.D_OK, "Hello World!", Dialog.OK, Bitmap.getPredefinedBitmap(Bitmap.EXCLAMATION), Manager.VERTICAL_SCROLL);
ui.pushGlobalScreen(screen, 1, UiEngine.GLOBAL_QUEUE);
}
return "UNDEFINED";
}
}
Key Differences
And that's the gist of the java files. Basically, it is a chain of three classes; the first references the second, and the second references the third. In your sample, that chain is slightly broken, however can be fixed fairly quickly based on the notes above.
The next part that must be done is to associate your Javascript Extension with WebWorks. This can be done in one of two ways.
I opted for the latter as I find that approach a little more straightforward. The first thing we need to do is copy our java files into the extension folder. The root extension folder I was using is as follows:
C:\Program Files\Research In Motion\BlackBerry WebWorks SDK 2.3.0.9\ext\
Within this folder, I created a sandbox.extension sub-folder to house my extension:
C:\Program Files\Research In Motion\BlackBerry WebWorks SDK 2.3.0.9\ext\sandbox.extension
I then copied the src folder housing my java files to get the following file structure:
Note that the src/sandbox/extension path is not a coincidence because sandbox/extension is the folder structure as dictated by the package I used in my java files. Specifically:
package sandbox.extension;
The final thing that is missing is a library.xml file. I used the library.xml files available on the WebWorks Community API Github page as a starting point and made some modifications to get the following.
<library isWhitelist="true"> <extension id="sandbox.extension"> <entryClass>sandbox.extension.SandboxExtension</entryClass> </extension> <platforms> <platform value="JAVA"> <target version="default" config="JAVA" /> </platform> </platforms> <configurations> <configuration name="JAVA" version="1.0" > <src type="text/java" path="src" comment="Sample" /> </configuration> </configurations> <features> <feature id="sandbox.popup" version="1.0.0" /> </features> </library>
Key Notes
This file was created under the sandbox.extension root folder, specifically:
C:\Program Files\Research In Motion\BlackBerry WebWorks SDK 2.3.0.9\ext\sandbox.extension
And that is it for configuring the SDK. Now, when I package an application that leverages this extension, I do not need to include anything extra in my actual application because this functionality is now integrated directly with my SDK. To actually use this functionality, I need to:
My config.xml file looks like as follows.
<?xml version="1.0" encoding="utf-8"?> <widget xmlns="http://www.w3.org/ns/widgets" xmlns:rim="http://www.blackberry.com/ns/widgets" version="1.0.0"> <name>Sandbox</name> <author>Oros</author> <content src="index.html"/> <feature id="sandbox.popup" required="true" version="1.0.0"/> </widget>
Very basic. The <content> element indicates that index.html will be our main entry point. The important part here is that I included the <feature> element with id="sandbox.popup". You will note that this is the same id that we defined in library.xml.
Finally, here is a small sample application that gives the user a button. Once that button is clicked, a timer is invoked for 5 seconds. At the end of those 5 seconds, the extension (and in turn a popup Dialog) is invoked.
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<button type="button" onclick="fire()">Go Button Go!</button>
<script type="text/javascript">
function invokeExtension() {
sandbox.popup.doPopup();
}
function fire() {
setTimeout(invokeExtension, 5000);
}
</script>
</body>
</html>
As you can see, we add a <button> to our page that, when clicked, will trigger the fire function. The fire function calls setTimeout with a founter of 5000 milliseconds.
The reason for this is to give you the option of staying within the application, or minimizing the application with the red, end-call button. Regardless of whether you are in the application, or the application is minimized, after 5000 milliseconds, the invokeExtension function is called.
Within invokeExtension, you can see us calling sandbox.popup.doPopup. sandbox.popup is the Object we've defined in library.xml and subsequenty implemented within our java files. doPopup is the function that we defined to be handled within the java files.
Again, if you have any questions about the above, please do let me know.
Erik Oros
BlackBerry Development Advisor
03-15-2012 02:06 PM
03-16-2012 05:05 PM
Hello Wadi,
That's right. Whatever you return will be converted to the corresponding type in JavaScript. For instance, in my method I am calling the following, regardless of any action taken:
return "UNDEFINED";
However, if I were to create a blacking/modal popup instead, and then retrieve the user's choice, then I could return any value I like.
Currently, I am calling:
sandbox.popup.doPopup();
However if I were to modify that to:
alert(sandbox.popup.doPopup();
Then I would be alerted with the String "UNDEFINED". Again, this is because that is the only return that I have defined my my Java function.
With a few modifications you can return an actually meaningful value and use that in your JavaScript as required.
Erik Oros
BlackBerry Development Advisor
03-19-2012 06:23 PM
Sorry to bother you again with this, but I figured since it's on the same topic and anyone else checking this thread might have this problem as well. Anyways, this dialog method is working great on OS6 and OS7. However on OS5, i'm getting this error when the popup method is called:
java.lang.RunTimeException: pushGlobalScreen(modal) called bya non-event thread
This is my SandboxPopup.java right now:
public final class SandboxPopup extends ScriptableFunction {
public Object invoke(Object obj, Object[] args) throws Exception {
String msg = ((String)args[0]);
synchronized(Application.getEventLock()) {
UiEngine ui = Ui.getUiEngine();
Screen screen = new Dialog(Dialog.D_YES_NO, msg, Dialog.NO, Bitmap.getPredefinedBitmap(Bitmap.EXCLAMATION), Manager.VERTICAL_SCROLL);
ui.pushGlobalScreen(screen, 1, UiEngine.GLOBAL_MODAL);
int response = ((Dialog)screen).getSelectedValue();
if (response == Dialog.YES) {
return "yes";
}
else
return "no";
}
}
}
I remember facing something like this when I was developing in Java. The solution then was to determine if the screen being pushed was from the background or not, like this code below:
synchronized(Application.getEventLock()){
final Dialog d = new Dialog(Dialog.D_YES_NO, msg,
Dialog.NO, Bitmap.getPredefinedBitmap(Bitmap.EXCLAMATION), Manager.VERTICAL_SCROLL);
if ( Application.isEventDispatchThread() ) {
// We have the Event Thread, can just show the Dialog
UiApplication.getUiApplication().pushModalScreen(d );
} else {
// running in background
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
UiApplication.getUiApplication().pushGlobalScreen( d, 1, UiEngine.GLOBAL_MODAL);
if (d.getSelectedValue() == Dialog.YES )
{
isOn = false;
lstOnOff.setSelectedIndex(1);
}
}
});
}
if (d.getSelectedValue() == Dialog.YES )
{
isOn = false;
lstOnOff.setSelectedIndex(1);
}
The problem now is how to return to the Javascript when the code goes into the invokeLater? I can't manipulate the javascipt needed in the run() part. I'm hoping there is an easy fix that I'm missing somewhere, please note again that this problem only occurs with OS 5.
Thank you up front ![]()
03-21-2012 05:07 PM
Hello Wadi,
My personal recommendation would be the continued use the GLOBAL_QUEUE option instead of GLOBAL_MODAL. From there, you can leverage DialogClosedListener to wait for the user to make their choice.
Of course, this brings about a similar problem where we are now receiving the input in a function that is not directly returning a value.
It is possible to sequence our Java function properly through the use of a lock Object in conjunction with a wait/notify implementation. However, we can also leverage a Javascript callback method, which would be my recommendation here; both for simplicity, and that generally good feeling you get when you don't have to hack stuff together.
To accomplish this, I've modified the way I am invoking the extension in my JavaScript:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<button type="button" onclick="invokeExtension();">Callback!</button>
<script type="text/javascript">
function invokeCallback(choice) {
alert(choice);
}
function invokeExtension() {
try {
sandbox.popup.doPopup(invokeCallback);
} catch (err) {
alert(err);
}
}
</script>
</body>
</html>
As you can see, we have one button that calls invokeExtension. We then call doPopup in the same fashion, however now we are passing in an argument; specifically the callback function that we want invoked.
When that callback function is invoked, it is expected to be passed one argument (namely the choice that the user made) and that value will be displayed.
So let's look at the Java link between our Javascript function and its callback. The only file that we actually need to modify here is SandboxPopup.java; the Java implementation of our extension.
package sandbox.extension; import net.rim.device.api.script.ScriptableFunction; import net.rim.device.api.ui.UiEngine; import net.rim.device.api.ui.Ui; import net.rim.device.api.ui.component.Dialog; import net.rim.device.api.ui.component.DialogClosedListener; import net.rim.device.api.system.Application; import net.rim.device.api.system.Bitmap; import net.rim.device.api.ui.Manager; public final class SandboxPopup extends ScriptableFunction { private static ScriptableFunction _callback = null; public Object invoke(Object obj, Object[] args) throws Exception { try { _callback = (ScriptableFunction)args[0]; Dialog dialog = new Dialog(Dialog.D_YES_NO, "Hello World?", Dialog.YES, Bitmap.getPredefinedBitmap(Bitmap.EXCLAMATION), Manager.VERTICAL_SCROLL); dialog.setDialogClosedListener(new DialogClosedListener() { public void dialogClosed(Dialog dialog, int choice) { try { _callback.invoke(null, new Object[] {new Integer(choice)}); } catch(Exception ex) {} } }); synchronized(Application.getEventLock()) { Ui.getUiEngine().pushGlobalScreen(dialog, 1, UiEngine.GLOBAL_QUEUE); } } catch(Exception ex) { return ex.toString(); } return "Success."; } }
After the imports, the first thing we're doing is converting args[0] into a ScriptableFunction and storing it in our private static variable _callback. Note that args[0] corresponds to the first argument passed in our Javascript call:
sandbox.popup.doPopup(invokeCallback); //invokeCallback becomes args[0]
From there, we set a DialogClosedListener (which I will talk about in a second), and then the rest is the same. We synchronize on Application.getEventLock() and I then return an arbitrary String. Note that I've added a try/catch block around everything and return the error now; this is simply for debugging purposes.
So, looking at our DialogClosedListener, we really only have one line (wrapped in its own try/catch block.)
We take the _callback Object that we initialized at the very start, and call its invoke method. You can find detailed information about the arguments we pass here:
http://www.blackberry.com/developers/docs/7.1.0api
However the key part is that we are creating an Object array which will be converted to a parameter list. Specifically, in our callback function:
function invokeCallback(choice) {
alert(choice);
}
choice will correspond to new Integer(choice) of the Object array. This conversion between Java and Javascript Objects happens automatically for standard types.
Erik Oros
BlackBerry Development Advisor
03-21-2012 05:39 PM