Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] Navigating to ViewModel from Android notification using PendingIntent #2418

Open
1 of 7 tasks
tbalcom opened this issue Nov 28, 2017 · 10 comments
Open
1 of 7 tasks
Labels
p/android Android platform

Comments

@tbalcom
Copy link
Contributor

tbalcom commented Nov 28, 2017

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.

Steps to reproduce 📜

  1. Create an Android notification using Notification.Builder or NotificationCompat.Builder.

  2. Create an MvxViewModelRequest for the ViewModel.

  3. Get a PendingIntent from the MvxViewModelRequest.

  4. Associate the PendingIntent with the Android notification using SetContentIntent.

  5. Click on the notification. The activity is displayed but the new MvvmCross 5.x lifecycle is not fully implemented. Specifically Prepare is not called.

Expected behavior 🤔

Prepare should be called.

Actual behavior 🐛

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

Configuration 🔧

Version: 5.5

Platform:

  • 📱 iOS
  • 🤖 Android
  • 🏁 WPF
  • 🌎 UWP
  • 🍎 MacOS
  • 📺 tvOS
  • 🐒 Xamarin.Forms
@nmilcoff nmilcoff added the p/android Android platform label Dec 5, 2017
@nmilcoff
Copy link
Contributor

nmilcoff commented Dec 5, 2017

@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.

@tbalcom
Copy link
Contributor Author

tbalcom commented Dec 5, 2017

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).

@nmilcoff nmilcoff added this to the 6.0.0 milestone Dec 8, 2017
@martijn00 martijn00 removed this from the 6.0.0 milestone Mar 5, 2018
@tbalcom
Copy link
Contributor Author

tbalcom commented Mar 6, 2018

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?

@nmilcoff
Copy link
Contributor

nmilcoff commented Mar 6, 2018

So... in order to fix this issue, we will need to:

  1. Provide a new convention to store the provided parameter inside the Intent bundle.
  2. Check if the parameter content is serializable, throw an exception if it isn't. Also document this.
  3. Read the parameter from the bundle, deserialize and find a way to call 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.

@nickrandolph
Copy link
Contributor

@nmilcoff would you be able to generate a PR that demonstrates this issue in the playground?

@nickrandolph nickrandolph added this to the 6.x milestone Jun 6, 2018
@Nickolas-
Copy link
Contributor

@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 ?

@nmilcoff
Copy link
Contributor

@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

@Nickolas-
Copy link
Contributor

I’m 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.

@sillerjp
Copy link
Contributor

sillerjp commented Jan 22, 2019

@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(data) but my activity/vm doesn't contain fragments or isn't a fragment. When the navigate is called the VM initialized is called but after that I get the logs:
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.

@tbalcom
Copy link
Contributor Author

tbalcom commented Mar 5, 2019

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:

  1. 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.
    }
}
  1. 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.
    }
}

@martijn00 martijn00 modified the milestones: 6.x, 7.0.0 May 27, 2019
@Cheesebaron Cheesebaron removed this from the 7.0.0 milestone Jan 14, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
p/android Android platform
Development

No branches or pull requests

7 participants