William Kennedy
Posted on May 16, 2023
Photo by Tyler Lastovich on Unsplash
Turbo Android - Part 1 Getting Setup
In previous blog posts, I’ve covered getting set up with Turbo iOS and implementing native functionality.
However, Android is a massive part of the mobile market, and there are too few resources on getting up and running with Turbo Android.
This post aims to rectify that.
You will need Android Studio for this tutorial.
The Basics
First of all, we will assume you have a Turbo-enabled app. I’ll be using this app for a talk I’m giving.
The app is called CRM ME. It’s a simple CRM app. You can add, delete and update contacts—all your basic functionality.
You can create your own using the following Rails command.
rails new crm_me -j esbuild
Note that we’re using esbuild. This is because import maps are not supported on the Android browser yet.
With your Rails app installed, you can run rails g scaffold Contacts name:string
followed by rails db:migrate
, and you’re good to go.
Let’s run your app on the following port.
bin/dev -p 3000
Setting up Turbo Android and Installing the Library
Open up Android Studio, click New Project, then click ‘No Activity’.
On the next screen, you can name the app whatever you want and click Finish.
To get Turbo up and running, we first need the Hotwire library.
Open up build.gradle(Module:app)
At the bottom of this file, you’ll see the following:
dependencies {
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
Add in the Turbo Android library.
dependencies {
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
implementation "dev.hotwire:turbo:7.0.0-rc18"
}
You will be asked to sync your Gradle file. Ensure that you do as this installs all the dependencies.
Now, we need to get our boilerplate up and running.
Creating the MainActivity and MainSessionNavHost
Android is made up of activities. An activity is a single screen with a user interface. We will then add this activity to the Android Manifest file.
Let’s dive into the code:
First, create a kotlin file called MainActivity
under the java/com.example.yourappname
Enter the following code:
package com.example.yourappname
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import dev.hotwire.turbo.activities.TurboActivity
import dev.hotwire.turbo.delegates.TurboActivityDelegate
class MainActivity : AppCompatActivity(), TurboActivity {
override lateinit var delegate: TurboActivityDelegate
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
delegate = TurboActivityDelegate(this, R.id.main_nav_host)
}
}
You’ll notice that activity_main
and main_nav_host
are currently red. We’ll rectify that soon.
We must first create our MainSessionNavHost
, the main class that handles the flow of our application.
Create a kotlin file called MainSessionNavHost
under java/com.example.yourappname
Enter the following:
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import dev.hotwire.turbo.config.TurboPathConfiguration
import dev.hotwire.turbo.session.TurboSessionNavHostFragment
import kotlin.reflect.KClass
class MainSessionNavHost : TurboSessionNavHostFragment() {
override var sessionName = "main"
override var startLocation = "http://10.0.2.2:3000"
override val registeredFragments: List<KClass<out Fragment>>
get() = listOf()
override val registeredActivities: List<KClass<out AppCompatActivity>>
get() = listOf()
override val pathConfigurationLocation: TurboPathConfiguration.Location
get() = TurboPathConfiguration.Location( assetFilePath = "json/configuration.json")
}
Now we have three more files to create; a small change to our Android manifest file, and we should be up and running. We need to make three resource files, a json/configuration.json
asset file, a main_activity
layout and a WebFragment.
Let’s start with our JSON file.
Right-click on the app and go to New > Folder > Assets Folder.
Next, right-click on the assets directory, New > Directory and create the JSON directory.
Finally, right-click on the json directory and add a file called configuration.json
.
Add the following to the file.
{
"settings": {
"screenshots_enabled": true
},
"rules": [
{
"patterns": [
".*"
],
"properties": {
"context": "default",
"uri": "turbo://fragment/web",
"pull_to_refresh_enabled": true
}
}
]
}
This is the most important file as it determines the navigation on your Turbo-enabled app. We will cover Path Configuration in a future article, but I highly recommend reading this documentation.
Next, we will create a WebFragment
file.
Place this in the same directory as your MainActivity
It will have the following:
import dev.hotwire.turbo.fragments.TurboWebFragment
import dev.hotwire.turbo.nav.TurboNavDestination
import dev.hotwire.turbo.nav.TurboNavGraphDestination
@TurboNavGraphDestination(uri = "turbo://fragment/web")
open class WebFragment : TurboWebFragment(), TurboNavDestination {}
We add this to the list of registered fragments in our MainSessionNavHost
override val registeredFragments: List<KClass<out Fragment>>
get() = listOf(
WebFragment::class
)
We are so close to getting up and running. There are just three little things to do.
First, we create our layout_main
file.
Click Resource Manager > Layout > + Icon > Layout Resource File
Mine looks like this:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/main_nav_host"
android:name="com.example.thirdturbo.MainSessionNavHost"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="false" />
</androidx.constraintlayout.widget.ConstraintLayout>
Finally, we have to edit our Android Manifest(found in the top-level directory) file to ensure we can access the internet and localhost.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:usesCleartextTraffic="true"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ThirdTurbo"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Now you should be able to click the Run button and watch it build your app.
Congratulations, you now have a Turbo-enabled Android app.
In the next part, we’ll take the app further by adding more Turbo-enabled fragments, such as modals and native screens.
Posted on May 16, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.