Skip to content

Compose Firebase Dynamic Links #1477

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dynamiclinks/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ dependencies {
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.activity:activity-compose:1.5.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1'

androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation 'androidx.test:rules:1.4.0'
Expand Down
14 changes: 12 additions & 2 deletions dynamiclinks/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

<activity android:name=".java.MainActivity"
android:exported="true">
<!-- [START link_intent_filter] -->
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
Expand All @@ -28,7 +27,6 @@
android:host="example.com"
android:scheme="https"/>
</intent-filter>
<!-- [END link_intent_filter] -->
</activity>

<activity android:name=".kotlin.MainActivity"
Expand All @@ -42,6 +40,18 @@
android:scheme="https"/>
</intent-filter>
</activity>

<activity android:name=".kotlin.MainComposeActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:host="kotlin.example.com"
android:scheme="https"/>
</intent-filter>
</activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ class EntryChoiceActivity : BaseEntryChoiceActivity() {
Choice(
"Kotlin",
"Run the Firebase Dynamic Links quickstart written in Kotlin.",
Intent(this, com.google.firebase.quickstart.deeplinks.kotlin.MainActivity::class.java))
Intent(this, com.google.firebase.quickstart.deeplinks.kotlin.MainActivity::class.java)),
Choice(
"Compose",
"Run the Firebase Dynamic Links quickstart written in Compose.",
Intent(this, com.google.firebase.quickstart.deeplinks.kotlin.MainComposeActivity::class.java))
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,8 @@ public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final String DEEP_LINK_URL = "https://example.com/deeplinks";

// [START on_create]
@Override
protected void onCreate(Bundle savedInstanceState) {
// [START_EXCLUDE]
super.onCreate(savedInstanceState);
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
Expand Down Expand Up @@ -89,9 +87,7 @@ public void onClick(View v) {
buildShortLinkFromParams(deepLink, 0);
}
});
// [END_EXCLUDE]

// [START get_deep_link]
FirebaseDynamicLinks.getInstance()
.getDynamicLink(getIntent())
.addOnSuccessListener(this, new OnSuccessListener<PendingDynamicLinkData>() {
Expand All @@ -109,7 +105,6 @@ public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
// account.
// ...

// [START_EXCLUDE]
// Display deep link in the UI
if (deepLink != null) {
Snackbar.make(findViewById(android.R.id.content),
Expand All @@ -119,7 +114,6 @@ public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
} else {
Log.d(TAG, "getDynamicLink: no link found");
}
// [END_EXCLUDE]
}
})
.addOnFailureListener(this, new OnFailureListener() {
Expand All @@ -128,9 +122,7 @@ public void onFailure(@NonNull Exception e) {
Log.w(TAG, "getDynamicLink:onFailure", e);
}
});
// [END get_deep_link]
}
// [END on_create]

/**
* Build a Firebase Dynamic Link.
Expand All @@ -152,7 +144,6 @@ public Uri buildDeepLink(@NonNull Uri deepLink, int minVersion) {
// * URI prefix (required)
// * Android Parameters (required)
// * Deep link
// [START build_dynamic_link]
DynamicLink.Builder builder = FirebaseDynamicLinks.getInstance()
.createDynamicLink()
.setDomainUriPrefix(uriPrefix)
Expand All @@ -163,7 +154,6 @@ public Uri buildDeepLink(@NonNull Uri deepLink, int minVersion) {

// Build the dynamic link
DynamicLink link = builder.buildDynamicLink();
// [END build_dynamic_link]

// Return the dynamic link as a URI
return link.getUri();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package com.google.firebase.quickstart.deeplinks.kotlin

import android.content.Intent
import android.net.Uri
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks
import com.google.firebase.dynamiclinks.PendingDynamicLinkData
import com.google.firebase.dynamiclinks.ktx.androidParameters
import com.google.firebase.dynamiclinks.ktx.dynamicLink
import com.google.firebase.dynamiclinks.ktx.dynamicLinks
import com.google.firebase.dynamiclinks.ktx.shortLinkAsync
import com.google.firebase.ktx.Firebase
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.tasks.await

class DynamicLinksViewModel(
private val dynamicLinks: FirebaseDynamicLinks
): ViewModel() {

private val _deepLink = MutableStateFlow("")
val deepLink: StateFlow<String> = _deepLink

private val _shortLink = MutableStateFlow("")
val shortLink: StateFlow<String> = _shortLink

private val _validUriPrefix = MutableStateFlow(true)
val validUriPrefix: StateFlow<Boolean> = _validUriPrefix

fun getDynamicLink(intent: Intent) {
viewModelScope.launch {
try {
val pendingDynamicLinkData: PendingDynamicLinkData = dynamicLinks
.getDynamicLink(intent)
.await()

val deepLink: Uri? = pendingDynamicLinkData.link

// Handle the deep link. For example, open the linked
// content, or apply promotional credit to the user's
// account.
// ...

// Display deep link in the UI
if (deepLink != null) {
_deepLink.value = deepLink.toString()
} else {
Log.d(TAG, "getDynamicLink: no link found")
}
} catch (e: Exception) {
Log.w(TAG, "getDynamicLink:onFailure", e)
}
}
}


/**
* Build a Firebase Dynamic Link.
* https://firebase.google.com/docs/dynamic-links/android/create#create-a-dynamic-link-from-parameters
*
* @param deepLink the deep link your app will open. This link must be a valid URL and use the
* HTTP or HTTPS scheme.
* @param minVersion the `versionCode` of the minimum version of your app that can open
* the deep link. If the installed app is an older version, the user is taken
* to the Play store to upgrade the app. Pass 0 if you do not
* require a minimum version.
* @return a [Uri] representing a properly formed deep link.
*/
// @VisibleForTesting
fun buildDeepLink(uriPrefix: String, deepLink: Uri, minVersion: Int): Uri {
// Set dynamic link parameters:
// * URI prefix (required)
// * Android Parameters (required)
// * Deep link
// Build the dynamic link
val link = Firebase.dynamicLinks.dynamicLink {
domainUriPrefix = uriPrefix
androidParameters {
minimumVersion = minVersion
}
link = deepLink
}

// Return the dynamic link as a URI
return link.uri
}

// @VisibleForTesting
fun buildShortLinkFromParams(uriPrefix: String, deepLink: Uri, minVersion: Int) {
// Set dynamic link parameters:
// * URI prefix (required)
// * Android Parameters (required)
// * Deep link

try {
viewModelScope.launch {
val shortDynamicLinks = Firebase.dynamicLinks.shortLinkAsync {
link = deepLink
domainUriPrefix = uriPrefix
androidParameters {
minimumVersion = minVersion
}
}.await()

val shortLinks = shortDynamicLinks.shortLink
// val flowChartLink = shortDynamicLinks.previewLink

_shortLink.value = shortLinks.toString()
}
} catch (e: Exception) {
Log.e(TAG, e.toString())
}
}

fun validateAppCode(uriPrefix: String) {
if (uriPrefix.contains("YOUR_APP")) {
_validUriPrefix.value = false
}
}

companion object {
const val TAG = "DynamicLinksViewModel"

// Used to inject this ViewModel's dependencies
// See also: https://developer.android.com/topic/libraries/architecture/viewmodel/viewmodel-factories
val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
extras: CreationExtras
): T {
// Get Remote Config instance.
val dynamicLinks = Firebase.dynamicLinks
return DynamicLinksViewModel(dynamicLinks) as T
}
}
}
}
Loading