Hello 🙂
Lately we've been talking about Turbo and iOS, and about making PWAs installable.
This article is the continuation of those two articles (well, more the one on iOS), so I invite you to skim it, because even if the two systems are quite different, the principle is the same.
The application in production
As always, the app we're going to talk about is the one I use every time I want to test new technologies. As I write these lines, it's available on the Google Play Store.
How do I get started with Turbo Native Android?
JavasScript side
As with iOS, you'll need to make Turbo accessible directly from "window", like this:
If you've never worked with Android, or if like me you have, but it's been years, I think the best thing to do is to grab the Turbo Native Android project from Github.
In the demo project, you'll already be able to understand a few things, and more importantly, you'll be able to compile it.
A few basics on how it works
The route file
Yes, there's one here too, but I find that on Android it's even more important than on the other platform, so here's an example:
Already in this file, the "screenshots_enabled" allows you to simply manage transitions, if you want a screenshot of the "view" (the page, basically) to be displayed when you go back, or if you simply want to set a loading spinner.
Next, there's the routing, where we choose which configuration will be loaded via a regex.
In the configuration, we choose :
The context: "default" or "modal", note that here, unlike iOS, when you click on a link in a modal, even a link in a turbo frame, then turbo native will open another modal on top of the modal.
the URI: Here, we choose the fragment to be used for this view. To simplify, a fragment is the class that will be attached to the view; fragments are elements native to Android.
Presentation: Here you can see what this corresponds to, you can decide to reset the navigation stack, for example.
The most important thing in this file is the choice of the URI to be used, as you'll see later.
In the "onSessionCreated" method, we add a line "session.webView.addJavascriptInterface", which lets us define a class that will become accessible from javascript, in our case the AndroidNativeInterface class, and we can see that we're injecting an activity with "requireActivity".
Sharing a URL
And yes, as on the application on the other platform, we'll need to share URLs, to share the invitation with other guests.
// ...classAndroidNativeInterface(privatevalactivity:Activity){@JavascriptInterfacefuncomposeEmail(emailAddress:String?){if(emailAddress==null)return// return if the address string is nullvalemailIntent=Intent(Intent.ACTION_SENDTO).apply{data=Uri.parse("mailto:$emailAddress")}activity.runOnUiThread{try{activity.startActivity(Intent.createChooser(emailIntent,"Send email using:"))}catch(e:ActivityNotFoundException){//Log.e("EmailError", "No email clients installed!")}}}}
And there you have it, the native code is pretty straightforward.
I won't go into any more detail of the same kind, as I think you've understood the principle, and above all, remember to look at the iOS article, because even if the language is different, it's the same logic.
Controlling the Android back button
During development, I had a problem: once I had taken a bank loan by stripe, when I arrived at the reservation confirmation view, for some as yet unknown reason, I couldn't set the view to "replace_root" to prevent the user from pressing return and therefore behaving strangely.
Remember the fragments you can associate with a url in route configuration? Well, I've created a specific fragment for the "complete-" route:
package...importandroid.content.Intentimportandroid.content.Intent.FLAG_ACTIVITY_NEW_TASKimportandroid.os.Bundleimportandroid.view.Viewimportandroidx.activity.OnBackPressedCallbackimportfr.lfmarchand.reservation.app.Rimportfr.lfmarchand.reservation.app.base.NavDestinationimportdev.hotwire.turbo.fragments.TurboWebFragmentimportdev.hotwire.turbo.nav.TurboNavGraphDestination@TurboNavGraphDestination(uri="turbo://fragment/web/complete")openclassWebCompleteFragment:TurboWebFragment(),NavDestination{overridefunonViewCreated(view:View,savedInstanceState:Bundle?){super.onViewCreated(view,savedInstanceState)setupMenu()disableBackButton()}overridefunonResume(){super.onResume()disableBackButton()}privatefunsetupMenu(){toolbarForNavigation()?.inflateMenu(R.menu.web)}privatefundisableBackButton(){requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner,object: OnBackPressedCallback(true){overridefunhandleOnBackPressed(){// Intent to start the home screenvalhomeIntent=Intent(Intent.ACTION_MAIN).apply{addCategory(Intent.CATEGORY_HOME)flags=FLAG_ACTIVITY_NEW_TASK}startActivity(homeIntent)}})}}
Simply a method that changes the behavior of the back button and returns the user to the device's home screen when he clicks on it, this method is called in "onViewCreated" and "onResume", when the user returns from the home screen to the application.
Conclusion
This article is more concise than the one on iOS, quite simply, because the logic is the same, the platform and programming language change, but the logic remains the same.
Have a great week 😁.