When my app is running in background and I click its icon, it comes from background to foreground. Which function is triggered at first? I searched, after coming from background it triggers the public void activate() in the class which is extended by Application.
Now I have a GUI class, lets say Login.java, which extends UiApplication. What should I write in this class to see that Application has come from background to foreground?
And when going to Background I use this code
public boolean onClose() {
UiApplication.getUiApplication().invokeAndWait(new Runnable() {
public void run() {
MyScreen.setBackgroundFlag("true");
UiApplication.getUiApplication().requestBackground();
};
});
//Application.getApplication().requestForeground();
//System.exit(0);
return true;
}
Related
Initial Issue
I need to register ProcessLifecycleOwner as described here Xamarin.Android Architecture Components in my Application.OnCreate method.
But it had resulted in the error with 6.2.2 version of MvvmCross:
MvvmCross.Exceptions.MvxIoCResolveException: Failed to resolve type MvvmCross.ViewModels.IMvxAppStart occurred
or just stuck on the Splash Screen with 6.2.3.
Fix
Those problems were fixed by advice from Xamarin.Android mvvmcross app crashes when launching with intent filter.
[Application]
public class App : MvxAndroidApplication<Setup, Core.App>
{
public App(IntPtr reference, JniHandleOwnership transfer) :
base(reference, transfer) { }
public override void OnCreate()
{
MvxAndroidSetupSingleton
.EnsureSingletonAvailable(ApplicationContext)
.EnsureInitialized();
base.OnCreate();
}
}
Current Issue
However Splash Screen dissapeared too, only blue background from default theme was left.
A workaround I've found:
public override void OnCreate()
{
Task.Run(() => MvxAndroidSetupSingleton
.EnsureSingletonAvailable(ApplicationContext)
.EnsureInitialized());
base.OnCreate();
}
But due to parallelism it is not reliable, sometimes works, sometimes crashes.
Question
How Splash Screen can be restored?
Your approach is most likely blocking on the UI thread which us causing the UI to block during the time that the expected splash screen is suppose to show.
Try using an async event handler to allow for a non blocking UI call
[Application]
public class App : MvxAndroidApplication<Setup, Core.App> {
public App(IntPtr reference, JniHandleOwnership transfer) :
base(reference, transfer) {
EnsureInitialized = onEnsureInitialized; //Subscribe to event
}
private event EventHandler EnsureInitialized = delegate { };
private async void onEnsureInitialized(object sender, EventArgs args) {
await Task.Run(() => MvxAndroidSetupSingleton.EnsureSingletonAvailable(ApplicationContext)
.EnsureInitialized());
}
public override void OnCreate() {
EnsureInitialized(this, EventArgs.Empty); //Raise event
base.OnCreate();
}
}
How to get notified when Xamarin Native Android app goes to sleep or is terminated?
When searching, I only found an answer for Xamarin.Forms where the Application object allows to override OnSleep.
The background of this question is that I want to save settings when the app either goes to background or is terminated.
Just like the OnSleep method of Xamarin Forms the OnPause method is called in Android Native when the app goes into the background.
You can override OnPause in both an Activity and a Fragment something like this:
protected override void OnPause()
{
base.OnPause();
// Add your code here
}
Update
You can do the same on application level by adding the Android Application class :
Add a new C# class file in your project called MainApplication.cs.
Then add the Application.IActivityLifecycleCallbacks interface where you can find the activity paused method with the activity context in which it was paused so you can add it and do the needful.
#if DEBUG
[Application(Debuggable = true)]
#else
[Application(Debuggable = false)]
#endif
public class MainApplication : Application , Application.IActivityLifecycleCallbacks
{
public MainApplication(IntPtr handle, JniHandleOwnership transer)
: base(handle, transer)
{
}
public void OnActivityPaused(Android.App.Activity activity)
{
base.OnCreate();
// Add some code
}
public override void OnTerminate()
{
base.OnTerminate();
UnregisterActivityLifecycleCallbacks(this);
}
public override void OnCreate()
{
base.OnCreate();
RegisterActivityLifecycleCallbacks(this);
}
}
I am using a ListField Control to display data returned from xml webservice. I want to refresh the ListField or the screen every minute to update the ListField with new records or data.
I tried using the code below but it is not working properly (It is hanging).
public MyApp() {
// Push a screen onto the UI stack for rendering.
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
UiApplication.getUiApplication().pushScreen(new MyScreen());
}
},5000,true);
}
ResponseHandler handler = new ResponseHandler();
ListField listUsers = new ListField(handler.getItem().size());
public MyScreen() {
setTitle("yQAforum");
//Fetch the xml from the web service
String wsReturnString = GlobalV.Fetch_Webservice("myDs");
//Parse returned xml
SAXParserImpl saxparser = new SAXParserImpl();
ByteArrayInputStream stream = new ByteArrayInputStream(wsReturnString.getBytes());
try {
saxparser.parse( stream, handler );
}
catch ( Exception e ) {
response.setText( "Unable to parse response.");
}
//Return vector sze from the handler class
listUsers.setSize(handler.getItem().size());
listUsers.setCallback(this);
listUsers.setEmptyString("No Users found", 0);
add(listUsers);
}
You are attempting to fetch data from your webservice on the UI thread. That's almost always the wrong thing to do.
The UI thread (also known as the main thread) is responsible for drawing the UI, and tracking user actions, like touches, or navigation via a trackpad/trackball. If the UI thread is blocked waiting for a remote web server to respond, it cannot service the UI.
There's a couple changes you should make:
public MyApp() {
// Push a screen onto the UI stack for rendering.
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
UiApplication.getUiApplication().pushScreen(new MyScreen());
}
},5000,true);
}
should be changed to
public MyApp() {
// Push a screen onto the UI stack for rendering.
pushScreen(new MyScreen());
}
The MyApp() constructor will already be called on the UI thread, so there is no need to use invokeLater() to perform the pushScreen() call on the UI thread. It already will be called on the UI thread, if run from within the MyApp constructor. Also, the 5000 msec delay isn't really helpful. This will just delay the startup of your app by 5 seconds, which users will hate.
If you are trying to implement a splash screen, or something similar, when the app starts up, please search stack overflow for "BlackBerry splash screen", and I'm sure you'll find results.
Now, once your MyScreen class is created, you should take care not to fetch web service results from the UI thread. The MyScreen constructor will be run on the UI thread. If you want, you can initiate a web service request on a background thread, once the screen is shown. One way to do that is to use onUiEngineAttached():
protected void onUiEngineAttached(boolean attached) {
if (attached) {
// TODO: you might want to show some sort of animated
// progress UI here, so the user knows you are fetching data
Timer timer = new Timer();
// schedule the web service task to run every minute
timer.schedule(new WebServiceTask(), 0, 60*1000);
}
}
public MyScreen() {
setTitle("yQAforum");
listUsers.setEmptyString("No Users found", 0);
listUsers.setCallback(this);
add(listUsers);
}
private class WebServiceTask extends TimerTask {
public void run() {
//Fetch the xml from the web service
String wsReturnString = GlobalV.Fetch_Webservice("myDs");
//Parse returned xml
SAXParserImpl saxparser = new SAXParserImpl();
ByteArrayInputStream stream = new ByteArrayInputStream(wsReturnString.getBytes());
try {
saxparser.parse( stream, handler );
}
catch ( Exception e ) {
response.setText( "Unable to parse response.");
}
// now, update the UI back on the UI thread:
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
// TODO: record the currently selected, or focused, row
//Return vector sze from the handler class
listUsers.setSize(handler.getItem().size());
// Note: if you don't see the list content update, you might need to call
// listUsers.invalidate();
// here to force a refresh. I can't remember if calling setSize() is enough.
// TODO: set the previously selected, or focused, row
}
});
}
}
You'll need to add some error handling, in case the web service doesn't respond, or takes longer than a minute (you wouldn't want to be making a new request, if the last one hadn't finished).
But, this should get you started.
Note: once you fix the problem with running network code on the UI thread, you may still find that your code doesn't work. There could be problems fetching the web service data. You'll have to debug that. I am only showing you one problem with the code posted. If you still have problems with the web service fetch, post another question (with the UI thread problem fixed). Thanks.
I need to use a dialog. That appears 2-3 seconds and after closes automatically.
Which object should I use On BlackBerry?
you can also use
Status.show(String message)
Shows a status screen for two seconds.
or
Status.show(String message, Bitmap bitmap, int time)
Shows a status screen with specified
icon, for specified time.
Create a class that extends PopupScreen and use a TimerTask to automatically close it. So you would have code in your constructor that looks sort of like this:
Timer timer = new Timer();
timer.schedule(new TimerTask(){
public void run()
{
if(TestScreen.this.isDisplayed())
{
synchronized (Application.getEventLock())
{
TestScreen.this.close();
}
}
}
}, WAIT_TIME_IN_MILLISECONDS);
I have an app that is listening in background and when the user clicks "send" it displays a dialogue. However I need to bring my app to foreground so the user answers some questions before letting the message go. but I haven't been able to do this, this is the code in my SendListener:
SendListener sl = new SendListener(){
public boolean sendMessage(Message msg){
Dialog myDialog = new Dialog(Dialog.D_OK,
"message from within SendListener",
Dialog.OK,Bitmap.getPredefinedBitmap(Bitmap.EXCLAMATION),
Dialog.GLOBAL_STATUS)
{
//Override inHolster to prevent the Dialog from being dismissed
//when a user holsters their BlackBerry. This can
//cause a deadlock situation as the Messages
//application tries to save a draft of the message
//while the SendListener is waiting for the user to
//dismiss the Dialog.
public void inHolster()
{
}
};
//Obtain the application triggering the SendListener.
Application currentApp = Application.getApplication();
//Detect if the application is a UiApplication (has a GUI).
if( currentApp instanceof UiApplication )
{
//The sendMessage method is being triggered from
//within a UiApplication.
//Display the dialog using is show method.
myDialog.show();
App.requestForeground();
}
else
{
//The sendMessage method is being triggered from
// within an application (background application).
Ui.getUiEngine().pushGlobalScreen( myDialog, 1,
UiEngine.GLOBAL_MODAL );
}
return true;
}
};
store.addSendListener(sl);
App is an object I created above:
Application App = Application.getApplication();
I have also tried to invoke the App to foreground using its processID but so far no luck.
i have managed to achieve something similar to what you're describing but the difference is, my dialogs are displayed asynchronously, which might actually be easier... so in your case..
the first i could suggest you try is get the event lock before pushing the screen, ala:
synchronized(Application.getEventLock()){
final UiEngine ui = Ui.getUiEngine();
ui.pushGlobalScreen(theScreen, 1, UiEngine.GLOBAL_MODAL);
}
I would also just create a custom class of type MainScreen and push that instead of plain Dialog.
There, that's better (now with code formatting).
public class MYSendListener implements SendListener {
private UiApplication _myApp;
public MySendListener(UiApplication myApp) {
_myApp = myApp;
}
public boolean sendMessage(Message m) {
...
_myApp.requestForeground();
}
}
Cache your app instance inside your send listener when you construct it, and use that when sendMessage is fired.
Application.getApplication() only gets you the app of the calling thread.