I am able to press the back button to from LAYOUT2 back to LAYOUT1.
But how would I go from LAYOUT3 to LAYOUT2 to LAYOUT1?
I have tried doing different things such as this
In the MainActivity I have this
public override void OnBackPressed()
{
var intent = new Intent(this, typeof(MainActivity));
StartActivity(intent);
}
In the SecondActivity I have this
public override void OnBackPressed()
{
var intent = new Intent(this, typeof(Menu));
StartActivity(intent);
}
With that code above it just stays on the SecondActivity and doesn't go back to the MainActivity - why?
By default app behaviour is simply go to last activity.
you can try without any extra coding.
I think nothing will be needed.
Remove your overrides for the OnBackPressed and the behaviour you're expecting will be achieved.
Related
I am working with Xamarin.Forms on a project which is required to have a background service that will lookup for a date to remind the user of some important activity, but has to behave like an alarm and to be like a popup page or some sort of where I can make some XAML and bind some pieces of information from SQLite.
I have made a service that works in the background, I have made a subscription via MessagingCenter, and all is working so well the only thing I can't do is just from the background to simply open designed page bindings and VM will do the rest of the job.
I spent 5 days searching around but nothing seems to work...
Help will be well appreciated.
I have found a different solution to my problem. using AlarmManager here is a very simple code that will just start the new activity I have just created a new constructor in app.xaml.cs that will open the reminder page as the main page. and LoadApp will be called from ReminderActivity but with
LoadApplication(new App(true));
void LoadAlarm()
{
var alarmIntent = new Intent(this, typeof(AlarmReceiver));
var pending = PendingIntent.GetBroadcast(this, 0, alarmIntent, PendingIntentFlags.UpdateCurrent);
var alarmManager = GetSystemService(AlarmService).JavaCast<AlarmManager>();
alarmManager.Set(AlarmType.ElapsedRealtime, SystemClock.ElapsedRealtime() + 600 * 1000, pending);
}
and
[BroadcastReceiver]
public class AlarmReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
intent = new Intent(Android.App.Application.Context, typeof(ReminderActivity));
intent.AddFlags(ActivityFlags.NewTask | ActivityFlags.ClearTask);
Android.App.Application.Context.StartActivity(intent);
}
}
I have a method I used in MvvmCross 4.x that was used with the NotificationCompat.Builder to set a PendingIntent of a notification to display a ViewModel when the notification is clicked by the user. I'm trying to convert this method to use the MvvmCross 5.x IMvxNavigationService but can't see how to setup the presentation parameters, and get a PendingIntent using the new navigation API.
private PendingIntent RouteNotificationViewModelPendingIntent(int controlNumber, RouteNotificationContext notificationContext, string stopType)
{
var request = MvxViewModelRequest<RouteNotificationViewModel>.GetDefaultRequest();
request.ParameterValues = new Dictionary<string, string>
{
{ "controlNumber", controlNumber.ToString() },
{ "notificationContext", notificationContext.ToString() },
{ "stopType", stopType }
};
var translator = Mvx.Resolve<IMvxAndroidViewModelRequestTranslator>();
var intent = translator.GetIntentFor(request);
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.ClearTask);
return PendingIntent.GetActivity(Application.Context,
_notificationId,
intent,
PendingIntentFlags.UpdateCurrent);
}
The RouteNotificationViewModel does appear when I click the notification but Prepare and Initialize are not being called. What is necessary to convert this method from MvvmCross 4.x style of navigation to MvvmCross 5.x style of navigation?
It's possible to do this in MvvmCross 5+ but it's not as clean as it previously was.
For starters you want to specify the singleTop launch mode for your activity:
[Activity(LaunchMode = LaunchMode.SingleTop, ...)]
public class MainActivity : MvxAppCompatActivity
Generate the notification PendingIntent like this:
var intent = new Intent(Context, typeof(MainActivity));
intent.AddFlags(ActivityFlags.SingleTop);
// Putting an extra in the Intent to pass data to the MainActivity
intent.PutExtra("from_notification", true);
var pendingIntent = PendingIntent.GetActivity(Context, notificationId, intent, 0);
Now there are two places to handle this Intent from MainActivity while still allowing the use of MvvmCross navigation service:
If the app was not running while the notification was clicked then OnCreate will be called.
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
if (bundle == null && Intent.HasExtra("from_notification"))
{
// The notification was clicked while the app was not running.
// Calling MvxNavigationService multiple times in a row here won't always work as expected. Use a Task.Delay(), Handler.Post(), or even an MvvmCross custom presentation hint to make it work as needed.
}
}
If the app was running while the notification was clicked then OnNewIntent will be called.
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
if (intent.HasExtra("from_notification"))
{
// The notification was clicked while the app was already running.
// Back stack is already setup.
// Show a new fragment using MvxNavigationService.
}
}
I have Xamarin.Android todo list mobile app using Prism.
The problem is:
In android system, I can create shortcut to open specific list in
app.
When I open app, and press home button, it remains on background
(thats ok)
When I then run app from desktop shortcut, it opens
android activity and when I create new PrismApplication (
LoadApplication(new App()); ) everything is running OK, but after
creating viewmodel for view, app is still using old viewmodel from
before.
I made this workaroud and I use same instance of PrismApplication:
static App xamApp;
protected override void OnCreate(Bundle bundle)
{
if (xamApp == null)
{
Forms.Init(this, bundle);
xamApp = new App();
}
LoadApplication(xamApp);
xamApp.Redirect(Intent.GetStringExtra("ListID"));
}
Now, problem is redirecting. This code:
public void Redirect(string listId)
{
NavigationService.NavigateAsync($"MainPage/MainNavigationPage/TodoList?id={listId}", animated: false);
}
leads to the error:
System.InvalidOperationException: Master and Detail must be set before adding MasterDetailPage to a container.
Prism should take care of Binding of Detail in MasterDetailPage by the "TodoList" from NavigateAsync uri.
Does enyone know what can be the problem here?
So I finally got it working.
First I used LaunchMode = LaunchMode.SingleTask in my ActivityAttribute of MainActivity
[Activity(Label = "..", LaunchMode = LaunchMode.SingleTask, Icon = "#drawable/icon", Theme = "#style/MainTheme", MainLauncher = true]
public class MainActivity : FormsAppCompatActivity
Then I used OnNewIntent method of FormsAppCompatActivity so after app is on backgroud, only this event is launched :
protected override void OnNewIntent(Intent intent)
{
var listId = intent.GetStringExtra("ListID");
((App)App.Current).Redirect(listId);
}
Now even $"MainNavigationPage/TodoList?id={listId}" works
Based on the info you provided, I am assuming that when the app is launched again, it is already running, your previous MasterDetail page is already on the stack. IN your reset method, you want to reset your navigation stack to the new uri passing in the parameter. IN this case, you should use an absolute uri. This means try adding a "/" prefix to your uri. So something like this:
public void Redirect(string listId)
{
NavigationService.NavigateAsync($"/MainPage/MainNavigationPage/TodoList?id={listId}", animated: false);
}
I'm pretty new to xamarin / android development but this is pretty much what I'm trying conceptually:
Have a main activity that checks whether or not a user is already logged in
If the user is logged in, start the MainAppActivity
If the user is not logged in, start the OnboardingActivity
The reason I think these should be in different activities is for general design reasons (separation of concerns etc) - but also because both activities have different themes. (The Onboarding process has no title bar)
In code, this should be pretty much what I need:
[Activity(Label = "MyApp", MainLauncher = true, Icon = "#drawable/icon", Theme = "#android:style/Theme.Black.NoTitleBar")]
public class MainActivity : Activity
{
private readonly TransactionService _transactionService;
public MainActivity()
{
var isLoggedIn = true; // This will be loaded from somewhere
if(isLoggedIn)
{
var intent = new Intent(this, typeof(MainAppActivity));
StartActivity(intent);
}
else
{
var intent = new Intent(this, typeof(OnboardingActivity));
StartActivity(intent);
}
}
}
However, this code just throws an unspecified exception.
If I try the same thing during the protected override void OnCreate(Bundle bundle) process, I get an exception as well.
However, when I bind something like that to a button event and click it manually, it will work. (That is not my intent, but to test there is nothing wrong with my activities)
So, question is, how do I do something like this?
Edit: Here is the stacktrace if I try it in the constructor:
0x29 in System.Diagnostics.Debugger.Mono_UnhandledException_internal C#
0x1 in System.Diagnostics.Debugger.Mono_UnhandledException at /Users/builder/data/lanes/1978/f98871a9/source/mono/mcs/class/corlib/System.Diagnostics/Debugger.cs:122,4 C#
0x6 in Android.Runtime.UncaughtExceptionHandler.UncaughtException at /Users/builder/data/lanes/1978/f98871a9/source/monodroid/src/Mono.Android/src/Runtime/UncaughtExceptionHandler.cs:35,4 C#
0x1C in Java.Lang.Thread.IUncaughtExceptionHandlerInvoker.n_UncaughtException_Ljava_lang_Thread_Ljava_lang_Throwable_ at /Users/builder/data/lanes/1978/f98871a9/source/monodroid/src/Mono.Android/platforms/android-21/src/generated/Java.Lang.Thread.cs:221,5 C#
0x1D in object.b8bd1d31-3e2e-454d-bd94-9d5dea40eddb C#
I have not used xamarin, so I could be wrong.
FYR, on native android, you should do that in the onCreate of your MainActivity instead of Constructor
I am using monodroid to make an application. In the application the user clicks a button which goes to a listview, that listview has multiple options, the user selects on option and then that option is sent back to the original class where a textview is changed to the same text as the option on the listview that was selected.
Below is the class that sends the data
protected override void OnListItemClick(ListView l, View v, int position, long id)
{
int t = labels[position];
String text = t + "";
Intent send = new Intent(this, typeof(CreateVehicle));
send.SetFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop);
send.PutExtra("t", "Data from FirstActivity");
StartActivity(send);
}
Below is the receiving class:
protected override void OnResume()
{
base.OnResume(); // Always call the superclass first.
TextView tvYearChange = FindViewById<TextView>(Resource.Id.tvYearchange);
string text = Intent.GetStringExtra("t") ?? "work";
tvYearChange.SetText(text, null);
}
If someone could figure out why I'm not receiving the data that would be great.
I am not sure if I got your problem correctly but I guess your Main activity is calling an activity that has a ListView and then you want the result back in the Main activity, right?
If so, the code you displayed is not the right way to do what you want. What you are looking for is to use StartActivityForResult and Overriding the onActivityResult method in your main activity.
I don't know exactly how to use Monodroid and C# but I can give you an example in Java that I am sure will help you understand how to get what you want:
Let's say my ListView activity is called myList and extends a ListActivity and my main activity is called MainActivity. Below is the OnListItemClick method of myList:
#Override
protected void onListItemClick(ListView l, View v, int position, long id){
super.onListItemClick(l, v, position, id);
String text = String.valueOf(labels[position]);
// Create the intent with the extras
Intent send = new Intent();
send.putExtra("text_from_list", text);
// Set the result to OK (meaning the extras are put as expected by the caller)
setResult(RESULT_OK, send);
// you need this so the caller (MainActivity) knows that the user completed
// this activity (myList) as expected (clicking on the item to return a result)
// and didn't just leave the activity (back button)
// Close this List Activity
finish();
}
Below is the method in my Main Activity that calls myList:
private void callListActivity(){
Intent call = new Intent(MainActivity.this, myList.class);
startActivityForResult(call, 1);
// The second field is just a number to identify which activity
// returned a result when a result is received (you can have
// several calls to different activities expecting results from
// each one of them). This number is called requestCode.
}
You will have to Override the onActivityResult in your MainActivity with something like this:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Check if the returned data came from myList (requestCode 1) and if
// data was actually received (check if resultCode = RESULT_OK)
if (requestCode == 1) {
if (resultCode == RESULT_OK) {
String text = data.getStringExtra("text_from_list");
// you have to use the same string identifier you used
// when you put the extra in the Intent in myList
TextView tvYearChange = (TextView)findViewById(R.id.tvYearchange);
tvYearChange.setText(text);
}
else {
// Do something if the Activity didn't return data
// (resultCode != RESULT_OK)
}
}
// If you are expecting results from other Activities put more if
// clauses here with the appropriate request code, for example:
// if (requestCode == 2) {
// DoSomething;
// }
// And so on...
}
It shouldn't be hard to modify this Java code to C# code that you can use with Monodroid. Also, take a look at this link from the Android Official Documentation, they have a lot of useful stuff there.
I hope this helps you.
in Receiver Activity get data from intent as below
Intent intent = getIntent();
string text = intent.getStringExtra("t");