I am trying to create a background app which will run at system startup. When I run it manually (from the ribbon), the screen appears but when I run the app after making it a startup app (Auto-run on startup option in descriptor), nothing appears on screen. I am trying the following code;
public class AppClass extends UiApplication {
public static void main(String[] args) {
AppClass theApp = new AppClass();
theApp.enterEventDispatcher();
}
public AppClass() {
pushScreen(new AppScreen());
}
}
And this is the screen class;
public final class AppScreen extends MainScreen {
private LabelField label;
public AppScreen() {
setTitle("AppTitle");
label = new LabelField();
label.setText("Ready.");
add(label);
}
}
I am expecting that its a UI app so its screen should be visible no matter if is auto-run at startup or run manually. If I need to do something to make it work as expected, please guide me about it, I am new to BlackBerry development.
I am developing in the following environment;
BlackBerry JDE Eclipse Plugin 1.5.0
BlackBerry OS 4.5
Auto start applications are run before the OS has completed booting so there isn't any support for the user interface. I suspect your application is being launched but failing on some UI call. The documented way to write an application that is to auto run and run from the home screen is to provide an alternated entry point for the auto run with arguments that tell the program it has been auto run. Then use the API to wait until the OS is ready for UI applications.
public class AppClass extends UiApplication {
public static void main(String[] args) {
if (args.length > 0 && args[0].equals("auto-run")) {
// auto start, wait for OS
while (ApplicationManager.getApplicationManager().inStartup()) {
Thread.sleep(10000);
}
/*
** Do auto-run UI stuff here
*/
} else {
AppClass theApp = new AppClass();
theApp.enterEventDispatcher();
}
}
public AppClass() {
pushScreen(new AppScreen());
}
}
Call getApplication().requestForeground(); from the constructor of your AppScreen class so that your screen will be visible.
public final class AppScreen extends MainScreen {
private LabelField label;
public AppScreen() {
setTitle("AppTitle");
label = new LabelField();
label.setText("Ready.");
add(label);
getApplication().requestForeground();
}
}
Once the app is running in background, we have to bring it to foreground explicitly to show UI element and that is what we are doing here.
Related
I have an MvvmCross application, and I am using the MvvmCross ViewModel Lifecycle functions to realize
certain actions when the view appears, moves to background, and moves to foreground:
public override async void ViewAppeared()
public override async void ViewAppearing()
public override void ViewDisappearing()
public override void ViewDisappeared()
public override void ViewDestroy(bool viewFinishing)
Those functions work great in my Android device.
But for iOS they do not get fired when the application moves to background or to foreground
(although, except for ViewDestroy, they fire when navigating between the screens in the app in iOS)
1)Is that the intended behavior, or I am missing something?
2)If so, what is the approach we have to follow, when there are actions that we need to do when the app moves to foreground/background (like stopping timers)?
Should we maybe have two implementations one for android, and one for ios? I also tried the ViewDidDisappear method in the MvxBaseViewController,
still it is not activated when the app moves to background. There is a way in Xamarin/MvvmCross to hook into the native ios applicationDidEnterBackground?
Edit:
I have tried Ranjit´s answer, but it seems to be a problem subscribing to the message. Here is my test code:
AppDelegate.cs:
public override void DidEnterBackground(UIApplication application)
{
base.DidEnterBackground(application);
var message = new LocationMessage(
this,
34
);
_messenger = Mvx.IoCProvider.Resolve<IMvxMessenger>();
_messenger.Publish(message);
}
Base class:
public abstract class GenericMvxViewModel : MvxViewModel
{
private IMvxMessenger _messenger;
protected GenericMvxViewModel()
{
// other stuff
_messenger = Mvx.IoCProvider.Resolve<IMvxMessenger>();
_messenger.Subscribe<LocationMessage>(OnLocationMessage);
}
protected virtual void OnLocationMessage(LocationMessage locationMessage){}
}
ViewModel:
public class MyClassViewModel : GenericMvxViewModel
{
protected override void OnLocationMessage(LocationMessage locationMessage)
{
Debug.WriteLine(locationMessage.Lat);
}
}
The message is published in the AppDelegate.cs, but the OnLocationMessage method in the viewmodel is never executed.
Also I was wondering how to unsubscribe properly the message. ViewDestroy seems the most natural place, but as mentioned before it is never called on iOS
Your code should work. I am using same kind of function in my app it was working fine
GenericMvxViewModel Code
private MvxSubscriptionToken _locationEventToken;
public override void ViewAppeared()
{
SubscribeBaseLocationEvent();
base.ViewAppeared();
}
public override void ViewDisappeared()
{
if (StaticStorage.IsApplicationInForeground)
{
UnSubscribeBaseLocationEvent();
}
base.ViewDisappeared();
}
public void SubscribeBaseLocationEvent()
{
if (_locationEventToken == null)
{
_locationEventToken = Messenger.Subscribe<LocationMessage>(OnLocationMessage);
}
}
public void UnSubscribeBaseLocationEvent()
{
if (_locationEventToken != null)
{
Messenger.Unsubscribe<LocationMessage>(_locationEventToken);
_locationEventToken = null;
}
}
AppDelegate Code
public override void DidEnterBackground(UIApplication application)
{
base.DidEnterBackground(application);
StaticStorage.IsApplicationInForeground = false;
_messenger.Publish(new LocationMessage( this, 34 ));
}
public override void WillEnterForeground(UIApplication application)
{
StaticStorage.IsApplicationInForeground = true;
}
Android
protected override void OnResume()
{
StaticStorage.IsApplicationInForeground = true;
base.OnResume();
}
protected override void OnStop()
{
StaticStorage.IsApplicationInForeground = false;
base.OnStart();
}
When application is moving from one view to other view, we need to unsubscribe the event. But not when application moves to background. So, IsApplicationInForeground flag will help to solve this issue for android. Because for android when application goes to background ViewDisappeared will be called.
In my case. I have one common activity which holds remaining all views of fragment. So, I have added this code in common activity. Not sure in your case how you are using. But implementation will be similar.
I have a UI application that is set to autorun on startup which spawns a few threads doing some work in the background. It has one screen that displays information about the work the background threads are doing.
class AppName extends UiApplication implements SystemListener2 {
private static AppName app;
public static void main(String[] args) {
app = new AppName();
if (ApplicationManager.getApplicationManager().inStartup()) {
app.addSystemListener(app);
} else {
app.initializeLater();
}
app.enterEventDispatcher();
}
public AppName() {
pushScreen(new InfoScreen());
requestBackground();
}
private void initialize() {
// Spawns some threads doing work in the background
}
private void initializeLater() {
invokeLater(new Runnable() {
public void run() {
initialize();
}
});
}
public void powerUp() {
removeSystemListener(this);
initialize();
}
}
When I run the app in the simulator it works fine. Upon startup everything runs and then when I click the icon the screen is foregrounded and displayed. This is the output from the simulator:
3:20:26.612: AM: Starting AppName
3:20:26.612: AM: AppName already running
3:20:26.612: AM: Foreground is requested: AppName(304)
3:20:26.628: AM: Foreground is set: AppName(304)
However, on the device the screen never displays. This is the device debugger output:
[0.0] Starting AppName
[0.0] AppName already running
As you can see the foreground request is never made. I confirmed this by overriding the UiApplication.activate() method and putting in a System.out message to see if it was being called but it's not getting called.
Does anyone have any idea why this is happening?
try using this
UiApplication.getUiApplication().invokeAndWait(new Runnable() {
public void run() {
UiApplication.getUiApplication().requestForeground();
});
}
I have an alternate entry point set to auto-run to push a dialog with a particular time;
It runs successfully. The alternate entry point will auto-run when the device is on, as expected. However, when I run my app interactively, the dialog shows twice. How can I prevent this?
public static void main(String[] args) {
if ( args != null && args.length > 0 && args[0].equals("autorun")) {
MyApp theApp = new MyApp();
theApp.enterEventDispatcher();
} else {
MyApp theApp = new MyApp();
theApp.pushScreen(new MyScreen());
theApp.enterEventDispatcher();
}
}
public MyApp() {
this.addRealtimeClockListener(this);
}
public void clockUpdated() {
checkTimetoPushDialog();
}
So, if I understand this correctly, clockUpdated is invoked as part of the Realtime Clock listener. The listener is added when MyApp is constructed, and MyApp is constructed for both the foreground and background app.
So it's not so surprising that the dialog is pushed for both the foreground and background app.
If you don't want to see the dialog when the user starts the app, avoid adding the listener when you are not an auto-start process.
I got the book Advance Black Berry 6 Development.
I was able to get the midlet example to work, but not the first example for a CLDC program. It seems like it never gets to the code and when I run the app I get a blank white screen.
I tried to put a break point but it never went off.
Here is the code
package test.org;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
public class cHelloUniverses extends UiApplication{
public static void main(String[] args)
{
(new cHelloUniverses()).start();
}
public void start()
{
MainScreen main = new MainScreen();
LabelField label= new LabelField("Hello Ted");
main.add(label);
UiApplication app = UiApplication.getUiApplication();
app.pushScreen(main);
app.enterEventDispatcher();
}
}
Replace your start() method with this:
public void start()
{
MainScreen main = new MainScreen();
LabelField label= new LabelField("Hello Ted");
main.add(label);
this.pushScreen(main);
this.enterEventDispatcher();
}
I created app which user can start from menu and from icon. I do not use GlobalEventListener in my app, just register ApplicationMenuitem. And now I am getting error: previous instance still active when launch my app.
Steps to reproduce not so trivial:
launch app from icon
do not close it, just switch to another app
launch app from icon again
I founded article in blackberry's forum about it , but I can't find solution where I should remove my ApplicationMenuItem: it added on phone boot and should show all the time.
My code:
public class Jingu extends UiApplication {
public static void main(String[] args) {
ApplicationManager app = ApplicationManager.getApplicationManager();
boolean keepGoing = true;
while (keepGoing) {
if (app.inStartup()) {
try {
Thread.sleep(1000);
} catch (Exception e) {}
} else {
keepGoing = false;
}
}
Jingu theApp = new Jingu();
theApp.initMenuItem();
theApp.showMainScreen();
theApp.enterEventDispatcher();
}
public Jingu() {
}
public void showMainScreen() {
showScreen(new JinguMainScreen(this));
}
public void initMenuItem() {
// Create menu item
Object o = RuntimeStore.getRuntimeStore().get(JinguMenuItem.MY_MENU_ID);
// register only if not done already.
if (o == null) {
new JinguMenuItem(this).registerInstance();
}
}
public void showScreen(Screen aScreen) {
synchronized (Application.getEventLock()) {
try {
UiApplication.getUiApplication().popScreen(aScreen);
} catch (Exception e) {
}
UiApplication.getUiApplication().pushScreen(aScreen);
}
}
}
public class JinguMenuItem extends ApplicationMenuItem {
public static final long MY_MENU_ID = 0xb9739d5240d5943dL;
private final Jingu jingu;
public JinguMenuItem(Jingu jingu) {
super(0x350100);
this.jingu = jingu;
}
public void registerInstance() {
Object menuItem = RuntimeStore.getRuntimeStore().remove(MY_MENU_ID);
if (menuItem == null) {
ApplicationMenuItemRepository amir = ApplicationMenuItemRepository.getInstance();
amir.addMenuItem(ApplicationMenuItemRepository.MENUITEM_SYSTEM, this);
RuntimeStore.getRuntimeStore().put(MY_MENU_ID, this);
}
}
public Object run(Object context) {
jingu.setDefaultFont(Font.getDefault());
jingu.setMainApp(false);
jingu.setBbmEditField(null);
jingu.showMainScreen();
return context;
}
public String toString() {
return "My Menu";
}
}
plz advice where I should delete ApplicationMenuItem in my app?
my regards,
Vadim
If you are registering an ApplicationMenuItem from your application, as a user I would consider it bad style for your application to remove and exit, even if RIM provided a way to do this. You may want to separate your application into two parts. One provides the minimal support for responding to the ApplicationMenuItem selection, that starts automatically and runs in the background. The other has all the rest and can run and exit as needed.
My solution for this situation is:
create alternative entry point and run it on app load
register menu in it
do not use runtimeStore