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();
});
}
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 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.
I'm implementing a Filepicker in my app to allow users to choose photos from their phones. The code I'm using is as follows:
Calling the Filepicker:
try
{
UiApplication.getUiApplication().invokeLater(new Runnable()
{
public void run()
{
FilePicker fp = FilePicker.getInstance();
fileListener = new FilePickListener();
fp.setListener(fileListener);
fp.show();
}
});
}
catch (Exception e)
{
UiApplication.getUiApplication().invokeLater(new Runnable()
{
public void run()
{
Dialog.alert("Please check your data card..");
}
});
}
And the method to get the filename in my FilePickListener:
public void selectionDone(String str)
{
this.currFileName = str;
int index = str.lastIndexOf('/');
Dialog.alert("Filename: "+str.substring(index+1).trim());
}
This works perfectly in most handsets that I've tried it on (which have been a mix of handsets with some running OS5 and some running OS6). But on some, like the 8900 (running OS v5.0.0.411) it doesn't work properly. The Filepicker gets called and appears, but when any file gets selected, the selectionDone method doesn't get called. I've tested it on two separate 8900s and both have the same problem.
Does anyone have an idea why it works on certain handsets and not other?
You are a victim of a known RIM issue: FilePicker throws ControlledAccessException.
The issue is marked as "Fixed". However there is no info in which OS version they fixed it. (Is it so difficult to tell such a useful info?)
But from the comments to the issue:
We experience the very same issue with OS 5.0.0.321 on a Bold 9700. However, the issue does NOT appear on OS 5.0.0.464
so my guess would be they fixed it in OS 5.0.0.464. But that's not the end - in OS 6 FilePicker appears broken in early versions of OS 6 again. The conclusion - just don't use it. Use a custom file browser screen to pick a file. There is a sample in SDK 4.7.0 named FileExplorerDemo, check it for implementation details.
This is a known issue. FilePicker does not open on some devices and return an error, like the 8900 device. You can catch this error on some devices by adding the catch (Error e) { }
UiApplication.getUiApplication().invokeLater(new Runnable()
{
public void run()
{
FilePicker fp = FilePicker.getInstance();
fileListener = new FilePickListener();
fp.setListener(fileListener);
fp.show();
}
});
}
catch (Exception e)
{
UiApplication.getUiApplication().invokeLater(new Runnable()
{
public void run()
{
Dialog.alert("Please check your data card..");
}
});
}
catch (Error e)
{
UiApplication.getUiApplication().invokeLater(new Runnable()
{
public void run()
{
Dialog.alert("This device does not support File Picker");
}
});
}
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 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