diff --git a/dynamiclinks/app/build.gradle b/dynamiclinks/app/build.gradle
index 437e8a3c4..879444de1 100644
--- a/dynamiclinks/app/build.gradle
+++ b/dynamiclinks/app/build.gradle
@@ -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'
diff --git a/dynamiclinks/app/src/main/AndroidManifest.xml b/dynamiclinks/app/src/main/AndroidManifest.xml
index 615a7f5e9..b22caa9f6 100644
--- a/dynamiclinks/app/src/main/AndroidManifest.xml
+++ b/dynamiclinks/app/src/main/AndroidManifest.xml
@@ -19,7 +19,6 @@
-
@@ -28,7 +27,6 @@
android:host="example.com"
android:scheme="https"/>
-
+
+
+
+
+
+
+
+
+
diff --git a/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/EntryChoiceActivity.kt b/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/EntryChoiceActivity.kt
index bf71091a9..d2cc39708 100644
--- a/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/EntryChoiceActivity.kt
+++ b/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/EntryChoiceActivity.kt
@@ -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))
)
}
}
diff --git a/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/java/MainActivity.java b/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/java/MainActivity.java
index bde97fc68..3758392f6 100644
--- a/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/java/MainActivity.java
+++ b/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/java/MainActivity.java
@@ -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());
@@ -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() {
@@ -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),
@@ -119,7 +114,6 @@ public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
} else {
Log.d(TAG, "getDynamicLink: no link found");
}
- // [END_EXCLUDE]
}
})
.addOnFailureListener(this, new OnFailureListener() {
@@ -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.
@@ -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)
@@ -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();
diff --git a/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/DynamicLinksViewModel.kt b/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/DynamicLinksViewModel.kt
new file mode 100644
index 000000000..f3031fd41
--- /dev/null
+++ b/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/DynamicLinksViewModel.kt
@@ -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 = _deepLink
+
+ private val _shortLink = MutableStateFlow("")
+ val shortLink: StateFlow = _shortLink
+
+ private val _validUriPrefix = MutableStateFlow(true)
+ val validUriPrefix: StateFlow = _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 create(
+ modelClass: Class,
+ extras: CreationExtras
+ ): T {
+ // Get Remote Config instance.
+ val dynamicLinks = Firebase.dynamicLinks
+ return DynamicLinksViewModel(dynamicLinks) as T
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/MainActivity.kt b/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/MainActivity.kt
index fb4e50ec7..33170baa4 100644
--- a/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/MainActivity.kt
+++ b/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/MainActivity.kt
@@ -3,143 +3,83 @@ package com.google.firebase.quickstart.deeplinks.kotlin
import android.content.Intent
import android.net.Uri
import android.os.Bundle
-import androidx.annotation.VisibleForTesting
-import com.google.android.material.snackbar.Snackbar
+import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
-import android.util.Log
-import android.widget.TextView
-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.dynamiclinks.ktx.component1
-import com.google.firebase.dynamiclinks.ktx.component2
-import com.google.firebase.ktx.Firebase
+import androidx.lifecycle.lifecycleScope
+import com.google.android.material.snackbar.Snackbar
import com.google.firebase.quickstart.deeplinks.R
import com.google.firebase.quickstart.deeplinks.databinding.ActivityMainBinding
+import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
- // [START on_create]
+ private val viewModel: DynamicLinksViewModel by viewModels { DynamicLinksViewModel.Factory }
+
override fun onCreate(savedInstanceState: Bundle?) {
- // [START_EXCLUDE]
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val linkSendTextView = binding.linkViewSend
val linkReceiveTextView = binding.linkViewReceive
+ val shortLinkTextView = binding.shortLinkViewSend
+
+ val uriPrefix = getString(R.string.dynamic_links_uri_prefix)
// Validate that the developer has set the app code.
- validateAppCode()
+ viewModel.validateAppCode(uriPrefix)
// Create a deep link and display it in the UI
- val newDeepLink = buildDeepLink(Uri.parse(DEEP_LINK_URL), 0)
+
+ val newDeepLink = viewModel.buildDeepLink(uriPrefix, Uri.parse(DEEP_LINK_URL), 0)
linkSendTextView.text = newDeepLink.toString()
// Share button click listener
binding.buttonShare.setOnClickListener { shareDeepLink(newDeepLink.toString()) }
- // [END_EXCLUDE]
binding.buttonShareShortLink.setOnClickListener {
- val shortLinkTextView = findViewById(R.id.shortLinkViewSend);
- val shortDynamicLink = shortLinkTextView.text;
- shareDeepLink(shortDynamicLink.toString());
+ val shortDynamicLink = shortLinkTextView.text
+ shareDeepLink(shortDynamicLink.toString())
}
binding.buttonGenerateShortLink.setOnClickListener {
- val deepLink = Uri.parse(DEEP_LINK_URL);
- buildShortLinkFromParams(deepLink, 0);
+ val deepLink = Uri.parse(DEEP_LINK_URL)
+ viewModel.buildShortLinkFromParams(uriPrefix, deepLink, 0)
}
- // [START get_deep_link]
- Firebase.dynamicLinks
- .getDynamicLink(intent)
- .addOnSuccessListener(this) { pendingDynamicLinkData: PendingDynamicLinkData? ->
- // Get deep link from result (may be null if no link is found)
- var deepLink: Uri? = null
- if (pendingDynamicLinkData != null) {
- deepLink = pendingDynamicLinkData.link
- }
-
- // Handle the deep link. For example, open the linked
- // content, or apply promotional credit to the user's
- // account.
- // ...
-
- // [START_EXCLUDE]
- // Display deep link in the UI
- if (deepLink != null) {
- Snackbar.make(findViewById(android.R.id.content),
- "Found deep link!", Snackbar.LENGTH_LONG).show()
-
- linkReceiveTextView.text = deepLink.toString()
- } else {
- Log.d(TAG, "getDynamicLink: no link found")
- }
- // [END_EXCLUDE]
- }
- .addOnFailureListener(this) { e -> Log.w(TAG, "getDynamicLink:onFailure", e) }
- // [END get_deep_link]
- }
- // [END on_create]
-
- /**
- * 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(deepLink: Uri, minVersion: Int): Uri {
- val uriPrefix = getString(R.string.dynamic_links_uri_prefix)
+ viewModel.getDynamicLink(intent)
- // Set dynamic link parameters:
- // * URI prefix (required)
- // * Android Parameters (required)
- // * Deep link
- // [START build_dynamic_link]
- // Build the dynamic link
- val link = Firebase.dynamicLinks.dynamicLink {
- domainUriPrefix = uriPrefix
- androidParameters {
- minimumVersion = minVersion
+ lifecycleScope.launch {
+ viewModel.deepLink.collect { deepLink ->
+ if(deepLink.isNotEmpty()){
+ linkReceiveTextView.text = deepLink
+ Snackbar.make(findViewById(android.R.id.content),
+ "Found deep link!", Snackbar.LENGTH_LONG).show()
+ }
}
- link = deepLink
}
- // [END build_dynamic_link]
-
- // Return the dynamic link as a URI
- return link.uri
- }
- @VisibleForTesting
- fun buildShortLinkFromParams(deepLink: Uri, minVersion: Int) {
- val uriPrefix = getString(R.string.dynamic_links_uri_prefix)
+ lifecycleScope.launch {
+ viewModel.shortLink.collect { shortLink ->
+ if(shortLink.isNotEmpty()){
+ shortLinkTextView.text = shortLink
+ }
+ }
+ }
- // Set dynamic link parameters:
- // * URI prefix (required)
- // * Android Parameters (required)
- // * Deep link
- Firebase.dynamicLinks.shortLinkAsync {
- link = deepLink
- domainUriPrefix = uriPrefix
- androidParameters {
- minimumVersion = minVersion
+ lifecycleScope.launch {
+ viewModel.validUriPrefix.collect { flag ->
+ if(!flag){
+ AlertDialog.Builder(applicationContext)
+ .setTitle("Invalid Configuration")
+ .setMessage("Please set your Dynamic Links domain in app/build.gradle")
+ .setPositiveButton(android.R.string.ok, null)
+ .create().show()
+ }
}
- }.addOnSuccessListener { (shortLink, flowchartLink) ->
- val shortLinkTextView = findViewById(R.id.shortLinkViewSend);
- shortLinkTextView.text = shortLink.toString();
- }.addOnFailureListener(this) { e ->
- Log.e(TAG, e.toString());
}
+
+
}
private fun shareDeepLink(deepLink: String) {
@@ -151,17 +91,6 @@ class MainActivity : AppCompatActivity() {
startActivity(intent)
}
- private fun validateAppCode() {
- val uriPrefix = getString(R.string.dynamic_links_uri_prefix)
- if (uriPrefix.contains("YOUR_APP")) {
- AlertDialog.Builder(this)
- .setTitle("Invalid Configuration")
- .setMessage("Please set your Dynamic Links domain in app/build.gradle")
- .setPositiveButton(android.R.string.ok, null)
- .create().show()
- }
- }
-
companion object {
private const val TAG = "MainActivity"
diff --git a/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/MainComposeActivity.kt b/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/MainComposeActivity.kt
new file mode 100644
index 000000000..8dcc7e54c
--- /dev/null
+++ b/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/MainComposeActivity.kt
@@ -0,0 +1,315 @@
+package com.google.firebase.quickstart.deeplinks.kotlin
+
+import android.app.Activity
+import android.content.Context
+import android.content.ContextWrapper
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.AlertDialog
+import androidx.compose.material.Button
+import androidx.compose.material.ButtonDefaults
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Scaffold
+import androidx.compose.material.SnackbarHostState
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
+import androidx.compose.material.TopAppBar
+import androidx.compose.material.rememberScaffoldState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.State
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.google.firebase.quickstart.deeplinks.R
+import com.google.firebase.quickstart.deeplinks.kotlin.ui.theme.DynamicLinksTheme
+import kotlinx.coroutines.channels.Channel
+
+private const val DEEP_LINK_URL = "https://www.youtube.com/deeplinks"
+
+class MainComposeActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContent {
+ DynamicLinksTheme {
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = MaterialTheme.colors.background
+ ) {
+ MainAppView()
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun MainAppView(
+ lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
+ dynamicLinksViewModel: DynamicLinksViewModel = viewModel(factory = DynamicLinksViewModel.Factory)
+) {
+ val context = LocalContext.current
+ val activity = context.findActivity()
+ val intent = activity?.intent
+ val uriPrefix = stringResource(R.string.dynamic_links_uri_prefix)
+ var newDeepLink by remember { mutableStateOf("") }
+ var openDialog by remember { mutableStateOf(false) }
+
+ DisposableEffect(lifecycleOwner) {
+ val observer = LifecycleEventObserver { _, event ->
+ // Configure Firebase Dynamic Links when the screen is created
+ if (event == Lifecycle.Event.ON_CREATE) {
+ // Validate that the developer has set the app code.
+ dynamicLinksViewModel.validateAppCode(uriPrefix)
+
+ newDeepLink = dynamicLinksViewModel.buildDeepLink(uriPrefix, Uri.parse(DEEP_LINK_URL), 0).toString()
+
+ intent?.let {
+ dynamicLinksViewModel.getDynamicLink(it)
+ }
+ }
+ }
+
+ lifecycleOwner.lifecycle.addObserver(observer)
+
+ onDispose {
+ lifecycleOwner.lifecycle.removeObserver(observer)
+ }
+ }
+
+ val snackbarHostState = remember { SnackbarHostState() }
+ val channel = remember { Channel(Channel.Factory.CONFLATED) }
+ val scaffoldState = rememberScaffoldState(snackbarHostState = snackbarHostState)
+
+ // Checks if a valid uri has been updated, show only once
+ LaunchedEffect(key1 = true) {
+ dynamicLinksViewModel.validUriPrefix.collect { flag ->
+ if (!flag) {
+ openDialog = true
+ }
+ }
+ }
+
+ // Checks if a deep link is used to open app
+ LaunchedEffect(key1 = channel) {
+ dynamicLinksViewModel.deepLink.collect { deepLink ->
+ if (deepLink.isNotEmpty()) {
+ snackbarHostState.showSnackbar("Found deep link!")
+ }
+ }
+ }
+ Scaffold(
+ scaffoldState = scaffoldState,
+ topBar = {
+ TopAppBar(
+ backgroundColor = colorResource(R.color.colorPrimary)
+ ) {
+ Text(
+ text = stringResource(R.string.app_name),
+ style = MaterialTheme.typography.h6,
+ textAlign = TextAlign.Center,
+ modifier = Modifier.padding(8.dp),
+ color = Color.White
+ )
+ }
+ },
+ content = { it ->
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(it)
+ ) {
+ MainContent(
+ _openDialog = openDialog,
+ _linkReceiveTextView = dynamicLinksViewModel.deepLink.collectAsState(),
+ _shortLinkTextView = dynamicLinksViewModel.shortLink.collectAsState(),
+ newDeepLink = newDeepLink,
+ buildDeepLink = {
+ dynamicLinksViewModel.buildDeepLink(uriPrefix, Uri.parse(DEEP_LINK_URL), 0).toString()
+ },
+ buildShortLinkFromParams = {
+ dynamicLinksViewModel.buildShortLinkFromParams(uriPrefix, it, 0)
+ }
+ )
+ }
+ }
+ )
+}
+
+@Composable
+fun MainContent(
+ _openDialog: Boolean = false,
+ _linkReceiveTextView: State = mutableStateOf(""),
+ _shortLinkTextView: State = mutableStateOf(""),
+ newDeepLink: String = "",
+ buildDeepLink: () -> String = { "" },
+ buildShortLinkFromParams: (Uri) -> Unit = {},
+){
+ val context = LocalContext.current
+ var openDialog by remember { mutableStateOf(_openDialog) }
+ val linkReceiveTextView by _linkReceiveTextView
+ val shortLinkTextView by _shortLinkTextView
+
+ Column(
+ modifier = Modifier
+ .padding(16.dp)
+ .fillMaxWidth(),
+ ) {
+ if(openDialog) {
+ AlertDialog(
+ onDismissRequest = { openDialog = false },
+ title = { Text("Invalid Configuration") },
+ text = { Text("Please set your Dynamic Links domain in app/build.gradle") },
+ confirmButton = {
+ Button(onClick = {
+ openDialog = false
+ }) {
+ Text(stringResource(android.R.string.ok))
+ }
+ }
+ )
+ }
+
+ Image(
+ painter = painterResource(R.drawable.firebase_lockup_400),
+ contentDescription = "",
+ modifier = Modifier.fillMaxWidth(),
+ alignment = Alignment.Center
+ )
+
+ Text(
+ text = stringResource(R.string.title_receive),
+ fontSize = 20.sp,
+ style = MaterialTheme.typography.h6,
+ modifier = Modifier.padding(top = 8.dp)
+ )
+
+ Text(
+ text = linkReceiveTextView.ifEmpty {
+ stringResource(R.string.msg_no_deep_link)
+ },
+ fontSize = 16.sp,
+ modifier = Modifier.padding(top = 8.dp)
+ )
+
+ Text(
+ text = stringResource(R.string.dynamic_link),
+ fontSize = 20.sp,
+ style = MaterialTheme.typography.h6,
+ modifier = Modifier.padding(top = 32.dp)
+ )
+
+ Text(
+ text = buildDeepLink().ifEmpty { "https://abc.xyz/foo" },
+ fontSize = 16.sp,
+ modifier = Modifier.padding(top = 8.dp)
+ )
+
+ Button(
+ modifier = Modifier.fillMaxWidth(),
+ colors = ButtonDefaults.buttonColors(backgroundColor = colorResource(R.color.colorPrimary)),
+ onClick = {
+ shareDeepLink(context, newDeepLink)
+ }
+ ) {
+ Text(
+ text = stringResource(R.string.share_dynamic_link).uppercase(),
+ color = Color.White,
+ )
+ }
+
+ Text(
+ text = stringResource(R.string.short_dynamic_link),
+ fontSize = 20.sp,
+ style = MaterialTheme.typography.h6,
+ modifier = Modifier.padding(top = 32.dp)
+ )
+
+ Text(
+ text = shortLinkTextView.ifEmpty {
+ "https://abc.xyz/foo"
+ },
+ fontSize = 16.sp,
+ modifier = Modifier.padding(top = 8.dp)
+ )
+
+ Button(
+ modifier = Modifier.fillMaxWidth(),
+ colors = ButtonDefaults.buttonColors(backgroundColor = colorResource(R.color.colorPrimary)),
+ onClick = {
+ val deepLink = Uri.parse(DEEP_LINK_URL)
+ buildShortLinkFromParams(deepLink)
+ }
+ ) {
+ Text(
+ text = stringResource(R.string.generate_short_link).uppercase(),
+ color = Color.White
+ )
+ }
+
+ Button(
+ modifier = Modifier.fillMaxWidth(),
+ colors = ButtonDefaults.buttonColors(backgroundColor = colorResource(R.color.colorPrimary)),
+ onClick = {
+ shareDeepLink(context, shortLinkTextView)
+ }
+ ) {
+ Text(
+ text = stringResource(R.string.share_short_link).uppercase(),
+ color = Color.White
+ )
+ }
+ }
+}
+
+@Composable
+@Preview(showBackground = true)
+fun MainContentPreview(){
+ DynamicLinksTheme {
+ MainContent()
+ }
+}
+
+fun shareDeepLink(context: Context, deepLink: String) {
+ val intent = Intent(Intent.ACTION_SEND)
+ intent.type = "text/plain"
+ intent.putExtra(Intent.EXTRA_SUBJECT, "Firebase Deep Link")
+ intent.putExtra(Intent.EXTRA_TEXT, deepLink)
+
+ context.startActivity(intent)
+}
+
+fun Context.findActivity(): Activity? = when (this) {
+ is Activity -> this
+ is ContextWrapper -> baseContext.findActivity()
+ else -> null
+}
\ No newline at end of file
diff --git a/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/ui/theme/Color.kt b/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/ui/theme/Color.kt
new file mode 100644
index 000000000..b500a555a
--- /dev/null
+++ b/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/ui/theme/Color.kt
@@ -0,0 +1,11 @@
+package com.google.firebase.quickstart.deeplinks.kotlin.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple80 = Color(0xFFD0BCFF)
+val PurpleGrey80 = Color(0xFFCCC2DC)
+val Pink80 = Color(0xFFEFB8C8)
+
+val FirebaseBlue = Color(0xFF0288D1) // copied from colors.xml
+val FirebaseBannerBlue = Color(0xFF039BE5) // copied from colors.xml
+val FirebaseOrange = Color(0xFFFFA000) // copied from colors.xml
\ No newline at end of file
diff --git a/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/ui/theme/Shape.kt b/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/ui/theme/Shape.kt
new file mode 100644
index 000000000..b273cfb63
--- /dev/null
+++ b/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/ui/theme/Shape.kt
@@ -0,0 +1,11 @@
+package com.google.firebase.quickstart.deeplinks.kotlin.ui.theme
+
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Shapes
+import androidx.compose.ui.unit.dp
+
+val Shapes = Shapes(
+ small = RoundedCornerShape(16.dp),
+ medium = RoundedCornerShape(2.dp),
+ large = RoundedCornerShape(0.dp)
+)
\ No newline at end of file
diff --git a/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/ui/theme/Theme.kt b/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/ui/theme/Theme.kt
new file mode 100644
index 000000000..f5aa458dd
--- /dev/null
+++ b/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/ui/theme/Theme.kt
@@ -0,0 +1,48 @@
+package com.google.firebase.quickstart.deeplinks.kotlin.ui.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.darkColors
+import androidx.compose.material.lightColors
+import androidx.compose.runtime.Composable
+
+private val DarkColorPalette = darkColors(
+ primary = Purple80,
+ primaryVariant = PurpleGrey80,
+ secondary = Pink80
+)
+
+private val LightColorPalette = lightColors(
+ primary = FirebaseBlue,
+ primaryVariant = FirebaseBannerBlue,
+ secondary = FirebaseOrange
+
+ /* Other default colors to override
+ background = Color(0xFFFFFBFE),
+ surface = Color(0xFFFFFBFE),
+ onPrimary = Color.White,
+ onSecondary = Color.White,
+ onTertiary = Color.White,
+ onBackground = Color(0xFF1C1B1F),
+ onSurface = Color(0xFF1C1B1F),
+ */
+)
+
+@Composable
+fun DynamicLinksTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ content: @Composable () -> Unit
+) {
+ val colors = if (darkTheme) {
+ DarkColorPalette
+ } else {
+ LightColorPalette
+ }
+
+ MaterialTheme(
+ colors = colors,
+ typography = Typography,
+ shapes = Shapes,
+ content = content
+ )
+}
\ No newline at end of file
diff --git a/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/ui/theme/Type.kt b/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/ui/theme/Type.kt
new file mode 100644
index 000000000..67f305a2f
--- /dev/null
+++ b/dynamiclinks/app/src/main/java/com/google/firebase/quickstart/deeplinks/kotlin/ui/theme/Type.kt
@@ -0,0 +1,34 @@
+package com.google.firebase.quickstart.deeplinks.kotlin.ui.theme
+
+import androidx.compose.material.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ body1 = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.5.sp
+ )
+ /* Other default text styles to override
+ titleLarge = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 22.sp,
+ lineHeight = 28.sp,
+ letterSpacing = 0.sp
+ ),
+ labelSmall = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Medium,
+ fontSize = 11.sp,
+ lineHeight = 16.sp,
+ letterSpacing = 0.5.sp
+ )
+ */
+)
\ No newline at end of file
diff --git a/dynamiclinks/gradle.properties b/dynamiclinks/gradle.properties
index aac7c9b46..29b531a1d 100644
--- a/dynamiclinks/gradle.properties
+++ b/dynamiclinks/gradle.properties
@@ -10,7 +10,7 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
-
+android.useAndroidX=true
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects