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