I've made a simple Android project demonstrating the problem.
I asked a question on StackOverflow and was asked to create an issue here.
The problem I have is I can't figure out how to navigate to a MvxViewModel using a PendingIntent. This was doable in MvvmCross 4.x. The same MvvmCross 4.x techniques work in MvvmCross 5.x, but the documentation states it's not good to mix the two navigation schemes together.
Create an Android notification using Notification.Builder or NotificationCompat.Builder.
Create an MvxViewModelRequest for the ViewModel.
Get a PendingIntent from the MvxViewModelRequest.
Associate the PendingIntent with the Android notification using SetContentIntent.
Click on the notification. The activity is displayed but the new MvvmCross 5.x lifecycle is not fully implemented. Specifically Prepare is not called.
Prepare should be called.
Prepare is not called, so the ViewModel is essentially uninitialized. The following output can be observed:
2017-11-28 04:10:00 [TRACE] (MvxNotificationNavigation.Core.ViewModels.NewViewModel) NewViewModel.ctor called
11-28 16:10:00.373 I/mvx ( 4616): 34.28 Missing parameter for call to NewViewModel - missing parameter parameter - asssuming null - this may fail for value types!
[0:] mvx:Diagnostic: 34.28 Missing parameter for call to NewViewModel - missing parameter parameter - asssuming null - this may fail for value types!
2017-11-28 04:10:00 [TRACE] (MvxNotificationNavigation.Core.ViewModels.NewViewModel) NewViewModel.Initialize called
Version: 5.5
Platform:
@tbalcom Did you really see this issue in v5.5? Because this PR: #2359 should have fixed the fact that Prepare / Initialize were not being called.
The generic public override void Prepare is called in Mvx 5.5 but not the override with the navigation parameters public override void Prepare(NotificationModel parameter).
I've found I can use a PendingIntent which triggers a BroadcastReceiver which then uses the IMvxNavigationService to navigate to the ViewModel when an Android notification is clicked. It's not ideal at all but it works.
Is there any MvvmCross 5 or 6 sample showing how to navigate to a ViewModel when an Android notification is clicked using the IMvxNavigationService?
So... in order to fix this issue, we will need to:
Prepare<T>.Btw thanks for the input and update!
Regarding your question, I don't think there's an example unfortunately... But you should be able to create a custom dependency service (interface & implementation at Core level) that handles incoming notifications. You just need to:
1) Ensure Mvx is running.
2) Mvx.Resolve your service (or something like that) and pass the notification information to it.
3) Inside your service, navigate to wherever you want.
@nmilcoff would you be able to generate a PR that demonstrates this issue in the playground?
@nmilcoff I did another trick, but have a different issue. I created a custom BroadcastReceiver and generated a pending intent from GCMService. When I press on notification, my BroadcastReceiver intercepts it and navigates to specific viewModel. It works fine until I update to latest MvvmCross 6.2.1. Right now BroadcastReceiver does not understand which activity is the top one.
I found a breaking change in MvxApplicationCallbacksCurrentTopActivity implementation. What do you think would be the best workaround? If I create a custom one , could it cause any other issues when activity in background is still top activity ?
@Nickolas- What version of Mvx were you using before? I would probably start by trying to remove the dependency on IMvxCurrentTopActivity in the BroadcastReceiver, as you can actually use IMvxNavigationService outside a ViewModel
I鈥檓 not using IMvxCurrentTopActivity in the BroadcastReceiver.
NavigationService.Navigate<SomeVM> (data);
There is no top current activity when the app is in the background state.
But I found the problem in my logic. I was trying to present a MvxFragmentViewModel, but with no top current activity the presenter could not understand how to show it. Then I added the logic back. Now it shows the correct activity even in the background state. But it will not bring the activity to the front. I need to use Activity.LaunchMode = SingleTop + NewIntentHandle.
You should document this one too, maybe someone will have a similar problem.
@Nickolas- I have the exact same issue as you. When the app is in the background I cannot get it to resume the app and display the activity. Only when the app is opened then it navigates correctly from the broadcast receiver. I'm also just using NavigationService.Navigate
2019-01-22 02:25:13 [WARN] (MvvmCross.Logging.MvxLog) Cannot Resolve current top activity
and the app is not opened
How were you able to resume the app when calling the navigate from the broad cast receiver?
I ended up implementing my own workaround. Since navigation will work in onresume in an activity what I do is I call a different activity with a regular pending intent and navigate from there into the activity I really want to go. Simple, not perfect but works for what I need to do. Looking forward for the top activity fix though.
Here's a working solution for an Android app using one activity and multiple fragments:
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 places to handle this Intent from MainActivity while still allowing the use of MvvmCross navigation service:
OnCreate - 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.
}
}
OnNewIntent - 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.
}
}
Most helpful comment
I鈥檓 not using IMvxCurrentTopActivity in the BroadcastReceiver.
NavigationService.Navigate<SomeVM> (data);There is no top current activity when the app is in the background state.
But I found the problem in my logic. I was trying to present a MvxFragmentViewModel, but with no top current activity the presenter could not understand how to show it. Then I added the logic back. Now it shows the correct activity even in the background state. But it will not bring the activity to the front. I need to use Activity.LaunchMode = SingleTop + NewIntentHandle.
You should document this one too, maybe someone will have a similar problem.