Skip to content

Commit

Permalink
Merge pull request #1214 from joreilly/stylianos/conference-deep-link
Browse files Browse the repository at this point in the history
Conference deep link for Android
  • Loading branch information
joreilly authored Apr 7, 2024
2 parents 5958657 + cd9cbb4 commit f6e277e
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 10 deletions.
9 changes: 9 additions & 0 deletions androidApp/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@
<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"
android:host="confetti-app.dev" />
</intent-filter>
</activity>

<activity android:name=".car.signin.SignInWithGoogleActivity" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

package dev.johnoreilly.confetti

import android.net.Uri
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
Expand Down Expand Up @@ -32,6 +33,8 @@ import org.koin.android.ext.android.inject

class MainActivity : ComponentActivity() {

private var isDeepLinkHandledPreviously = false

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

Expand All @@ -54,9 +57,18 @@ class MainActivity : ComponentActivity() {
// including IME animations
WindowCompat.setDecorFitsSystemWindows(window, false)

isDeepLinkHandledPreviously = savedInstanceState?.getBoolean(KEY_DEEP_LINK_HANDLED) ?: false
val initialConferenceId = intent.data?.extractConferenceIdOrNull(isDeepLinkHandledPreviously)
if (initialConferenceId != null) {
intent.setData(null)
isDeepLinkHandledPreviously = true
}
val appComponent =
DefaultAppComponent(
componentContext = defaultComponentContext(),
componentContext = defaultComponentContext(
discardSavedState = initialConferenceId != null,
),
initialConferenceId = initialConferenceId,
onSignOut = {
lifecycleScope.launch {
credentialManager.clearCredentialState(ClearCredentialStateRequest())
Expand Down Expand Up @@ -86,6 +98,33 @@ class MainActivity : ComponentActivity() {
}
}
}

/**
* From a deep link like `https://confetti-app.dev/conference/devfeststockholm2023` extracts `devfeststockholm2023`
*/
private fun Uri.extractConferenceIdOrNull(isDeepLinkHandledPreviously: Boolean): String? {
if (isDeepLinkHandledPreviously) {
return null
}
if (host != "confetti-app.dev") return null
val path = path ?: return null
if (path.firstOrNull() != '/') return null
val parts = path.substring(1).split('/')
if (parts.size != 2) return null
if (parts[0] != "conference") return null
val conferenceId = parts[1]
if (!conferenceId.all { it.isLetterOrDigit() }) return null
return conferenceId
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(KEY_DEEP_LINK_HANDLED, isDeepLinkHandledPreviously)
}

companion object {
const val KEY_DEEP_LINK_HANDLED: String = "dev.johnoreilly.confetti.KEY_DEEP_LINK_HANDLED"
}
}

@Composable
Expand Down
3 changes: 2 additions & 1 deletion iosApp/iosApp/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class AppDelegate : NSObject, UIApplicationDelegate {
componentContext: DefaultComponentContext(lifecycle: ApplicationLifecycle()),
onSignOut: {},
onSignIn: {},
isMultiPane: UIDevice.current.userInterfaceIdiom != UIUserInterfaceIdiom.phone
isMultiPane: UIDevice.current.userInterfaceIdiom != UIUserInterfaceIdiom.phone,
initialConferenceId: nil
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class DefaultAppComponent(
private val onSignOut: () -> Unit,
private val onSignIn: () -> Unit,
private val isMultiPane: Boolean = false,
initialConferenceId: String? = null,
) : AppComponent, KoinComponent, ComponentContext by componentContext {

private val coroutineScope = coroutineScope()
Expand All @@ -53,15 +54,22 @@ class DefaultAppComponent(

init {
coroutineScope.launch {
val conference: String = repository.getConference()
if (conference == AppSettings.CONFERENCE_NOT_SET) {
showConferences()
if (initialConferenceId != null) {
// todo, consider changing how conference theme colors are decided so that only knowing the conference
// ID is enough to also get the right color
repository.setConference(initialConferenceId, null)
showConference(conference = initialConferenceId, conferenceThemeColor = null)
} else {
val conferenceThemeColor = repository.getConferenceThemeColor()
showConference(conference = conference, conferenceThemeColor = conferenceThemeColor)

// Take the opportunity to update any listeners of the conference
repository.updateConfenceListeners(conference, conferenceThemeColor)
val conference: String = repository.getConference()
if (conference == AppSettings.CONFERENCE_NOT_SET) {
showConferences()
} else {
val conferenceThemeColor = repository.getConferenceThemeColor()
showConference(conference = conference, conferenceThemeColor = conferenceThemeColor)

// Take the opportunity to update any listeners of the conference
repository.updateConfenceListeners(conference, conferenceThemeColor)
}
}
}

Expand Down

0 comments on commit f6e277e

Please sign in to comment.