xamarin/xamarin-android

Problems with Predictive Back Gesture

gmck opened this issue · 4 comments

gmck commented

Android application type

Android for .NET (net6.0-android, etc.)

Affected platform version

VS 2022 17.5.0 Prev 1

Description

For those that don’t know, Predictive Back Gesture helps in preventing a user from accidentally exiting an app by momentarily animating and shrinking the app size when they swipe from either left or right when the app is on the StartDestination fragment of the app. This is meant to indicate that the app will be exited and end up in the Launcher window from where it was opened if they continue the swipe. By cancelling the swipe, the app resumes full screen again. If they continue the swipe, the app exits as normal.

As of Android 13, this is still a future feature, that is only available via Settings > System -> Developer Options -> Predictive Back Animations. Once set on in Developer Settings, the predictive animation will be displayed for several Google Apps. By opting in developers can now prepare their apps to include the Predictive Back Gesture to test their apps for Android 14.
The following Google apps already support this feature for Android 13 devices e.g. Phone, Google TV, Google News and possibly others.

Google provides the following documentation https://developer.android.com/guide/navigation/predictive-back-gesture.
For our apps to support Predictive Back Gesture, we are required to make the following changes.

  1. Add the following to the Application tag of the AndroidManifest.xml.
    android:enableOnBackInvokedCallback="true"
  2. Replace Activity’s OnBackPressed (deprecated) by using an OnBackPressedCallback from Xamarin.AndroidX.Activity >=1.6.0 which is contained in the NuGet package Xamarin.AndroidX.AppCompat 1.5.1.1
  3. OnBackPressedCallback is an abstract class, so we have to create a class and override the single method HandleOnBackPressed().

My apps have been using Google’s NavigationComponent for at least the last 12 months and I’ve made extensive use of OnBackPressedCallback in every fragment. One problem I found very early on was that my HomeFragment or the StartDestination Fragment required different processing than all the other fragments in that I had to call Activity.Finish() in its HandleOnBackPressed() method. This allowed the OnDestroy of the MainActivity to be able to test for IsFinishing and if true shut down a bound service running in the background, but not shut down the service when the device was rotated during a normal configuration change.

The problem:
I thought that because I was already using an OnBackPressedCallback in the HomeFragment of the linked test app, all I would have to do would be to include the new enableOnBackInvokedCallback = true in the manifest. However, that didn’t produce the Predictive Back Gesture animation when exiting the app.

All the examples I had seen were using the MainActivity for the onBackPressedCallback, so I commented out the OnBackPressedCallback code in the HomeFragment and created a new OnBackPressedCallback with a different class name for use in the MainActivity. Again that also failed to show the animation when attempting to exit the app.

In frustration, I thought I’d set the MainActivity’s OnBackPressedCallback.Enabled = false and go back to the code in the HomeFragment and recheck it. However, I ran it once more as was, with Enable = false, and the BackGesture then appeared when trying to exit.

Ok that worked, but obviously, it would never be able to execute the HandleOnBackPressed of that class if it was disabled, therefore that wouldn’t be of any help in a production app, because Finish would never be executed and therefore IsFinishing would always be false in the OnDestroy().

So again I reversed everything around and went back to the HomeFragment. I deliberately set it to enabled = false and it also showed the Predictive BackGesture animation when attempting to exit.

I’ve left the MainActivity’s OnBackPressedCallback commented, so I am using the HomeFragment, but I’ve set enabled to false, so it at least shows the Predictive Back Gesture.

I’m interested if anyone has any ideas about how to make it work correctly like those Google Apps.

Link to test app - https://github.com/gmck/NavigationGraph7Net7

Steps to Reproduce

See above link

Did you find any workaround?

No

Relevant log output

N/A

For reference, the Add support for the predictive back gesture docs state:

Supporting the predictive back gesture requires updating your app, using the OnBackPressedCallback AppCompat 1.6.0-alpha05 (AndroidX) or higher API, or using the new OnBackInvokedCallback platform API.

Which you mentioned in your "following changes" list. Unfortunately:

  1. Replace Activity’s OnBackPressed (deprecated) by using an OnBackPressedCallback from Xamarin.AndroidX.Activity >=1.6.0 which is contained in the NuGet package Xamarin.AndroidX.AppCompat 1.5.1.1

this is incorrect. Specifically, the Xamarin.AndroidX.AppCompat 1.5.1.1 NuGet package does not bind androidx.appcompat.appcompat 1.6.0; Xamarin.AndroidX.AppCompat 1.5.1 binds androidx.appcompat 1.5.1, which is mentioned in the <tags/> in the NuGet metadata:

<tags>.NET Android Xamarin AndroidX Xamarin.AndroidX Support Google appcompat artifact=androidx.appcompat:appcompat artifact_versioned=androidx.appcompat:appcompat:1.5.1</tags> 

In general, we do not bind preview AndroidX packages, and thus do not bind androidx.appcompat.appcompat 1.6.0-alpha05. Furthermore, as of this writing, there is no stable version of the androidx.appcompat.appcompat package with version 1.6 or later; currently, the only versions >= -alpha05 are:

  • 1.6.0-alpha05
  • 1.6.0-beta01
  • 1.6.0-rc01
  • 1.7.0-alpha01

None of these are stable, and thus none of these have been or will be bound by us. :-(

That leaves "the new OnBackInvokedCallback platform API", which was introduced in API-33. It has been bound, and exists in Xamarin.Android 13.0 and net6.0-android33.0 or later as Android.Window.IOnBackInvokedCallback.

Could you please try using the IOnBackInvokedCallback interface? Unfortunately that will require runtime version-checking code to ensure that IOnBackInvokedCallback is not used on pre-Android 13 devices.

gmck commented

@jonpryor

Thanks for your feedback, I hope I didn’t mislead too many people.

However, I’m still confused, if I look at OnBackPressedCallback it says C:\Users\gmcke.nuget\packages\xamarin.androidx.activity\1.6.0.1\lib\net6.0-android31.0\Xamarin.AndroidX.Activity.dll and that is contained within Xamarin.AndroidX.AppCompat 1.5.1.1.

I thought the crucial package was Xamarin.AndroidX.Activity. I obviously missed the significance of OnBackPressedCallback AppCompat 1.6.0-alpha05 even though that is what I wrote.

That leaves "the new OnBackInvokedCallback platform API", which was introduced in API-33.

I tried adding the IOnBackInvokedCallback to the MainActivity, but the generated OnBackInvoked() is never called. I still have to have onBackPressedCallback = new NavFragmentOnBackPressedCallback(this, false); (note the false) in the HomeFragment just to ensure that I get the predictive back gesture and therefore IsFinishing will still be false in the MainActivity’s OnDestroy.
I was hoping I could use OnBackInvoked at that point to test for IsFinishing. I’ve updated NavigationGraph7Net7 if you want to check it.

One other question. How do you conditionally set up an Interface such as IOnBackInvokedCallback for devices less than 13?