Our Goal
1) Users access https://calvin.my/test from their mobile phones.
2) If they have our mobile app installed, the mobile app automatically launches and shows the content.
3) Otherwise, the link is opened in the default browser.
Preparing the app
1) Create a new Android app.
2) Edit the AndroidManifest.xml to include an intent filter for the App Link.
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="@style/Theme.LinkOne">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
<data android:host="calvin.my" />
<data android:pathPrefix="/test" />
</intent-filter>
</activity>
3) We will print an ID from the URL for testing purposes.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
val eventId = extractEventId(intent)
setContent {
LinkOneTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
EventDisplay(
eventId = eventId,
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
val eventId = extractEventId(intent)
recreate()
}
private fun extractEventId(intent: Intent): String? {
return if (intent.action == Intent.ACTION_VIEW) {
intent.data?.getQueryParameter("id")
} else {
null
}
}
}
@Composable
fun EventDisplay(eventId: String?, modifier: Modifier = Modifier) {
Column(
modifier = modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = "LinkOne App",
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(bottom = 16.dp)
)
if (eventId != null) {
Text(
text = "Event ID:",
fontSize = 18.sp,
modifier = Modifier.padding(bottom = 8.dp)
)
Text(
text = eventId,
fontSize = 20.sp,
fontWeight = FontWeight.Medium
)
} else {
Text(
text = "No event ID found",
fontSize = 16.sp
)
}
}
}
Preparing the URL
1) To associate the mobile app with the URL, we create and host an assetlinks.json
file.
2) The requirements include:
- It has to be publicly accessible and located at https://target-domain-name/.well-known/assetlinks.json
- It must not be redirected (E.g., 301 or 302)
- It has to be served as
application/json
type
3) The content must include 2 important values - the package name of the mobile app, and the SHA256 fingerprint of the signing cert.
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "xx.yy.zz",
"sha256_cert_fingerprints": [
"XX:XX:XX:XX..."
]
}
}
]
4) You can get the fingerprint using this command:
$ keytool -list -v -keystore your-cert
Testing
1) Create a QR code of the URL you want to test. For example, https://calvin.my/test?id=111
2) Scan the QR code, and it opens up the default browser to the URL.
3) Build a signed copy of your mobile app and install it.
4) Scan the same QR code again, and it should now open the mobile app.