From 09ce7b6fb823dc3593a6d0d887e8ec7189cab12e Mon Sep 17 00:00:00 2001 From: Eric Ampire Date: Wed, 8 Sep 2021 00:48:20 +0200 Subject: [PATCH] Sharing animation --- README.md | 11 +- app/build.gradle.kts | 2 +- app/src/main/AndroidManifest.xml | 13 + .../app/initializer/DownloaderInitializer.kt | 19 ++ .../screen/explore/ui/ExploreScreen.kt | 223 +++++++++++------- .../screen/explore/ui/LottieFileItem.kt | 10 +- .../screen/explore/ui/ShareBottomSheet.kt | 201 ++++++++++++++++ .../screen/home/ui/LoginBottomSheet.kt | 14 +- .../presentation/theme/Color.kt | 2 + app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/provider_paths.xml | 6 + buildSrc/src/main/kotlin/Libs.kt | 1 + i18n/src/main/res/values/strings.xml | 1 + util/build.gradle.kts | 1 + .../util/extension/Context.kt | 63 +++++ 15 files changed, 469 insertions(+), 99 deletions(-) create mode 100644 app/src/main/java/com/ericampire/android/androidstudycase/app/initializer/DownloaderInitializer.kt create mode 100644 app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/explore/ui/ShareBottomSheet.kt create mode 100644 app/src/main/res/xml/provider_paths.xml create mode 100644 util/src/main/java/com/ericampire/android/androidstudycase/util/extension/Context.kt diff --git a/README.md b/README.md index 8f2b0f4..a636a0c 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -

Lottiefiles App


+

Lottiefiles App


Lottiefiles App is an Android application built using jetpack compose and Hilt, based on modern Android technology stacks and the MVI architecture. The application is a clone of the [Lottiefiles](https://lottiefiles.com/mobile) mobile application and allows the user to retrieve and save animations, animators and articles locally from an API rest

-
+

License @@ -46,12 +46,9 @@ Go to the [Releases](https://github.com/eric-ampire/lottiefiles-app/releases) to * The API was not complete and the stories and animators returned by the API did not have IDs, I had to find a strategy to assign IDs to data that did not have them to avoid duplication in the database. * Animations didn't come with any information about whether an animation was popular, recent or featured, so I had to add an extra field to make the queries properly. -## Find this repository useful? :heart: -Support it by joining __[stargazers](https://github.com/eric-ampire/lottiefiles-app/stargazers)__ for this repository. :star:
-And __[follow](https://github.com/eric-ampire)__ me for my next creations! 🤩 - # License -```xml + +``` Designed and developed by 2020 ericampire (Eric Ampire) Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4385360..e753c46 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -130,5 +130,5 @@ dependencies { testImplementation(Libs.mockk_core) androidTestImplementation(Libs.mockk_android) - debugImplementation(Libs.code_scanner) + implementation(Libs.code_scanner) } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8b4f311..2b911a9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -39,6 +39,16 @@ + + + + + diff --git a/app/src/main/java/com/ericampire/android/androidstudycase/app/initializer/DownloaderInitializer.kt b/app/src/main/java/com/ericampire/android/androidstudycase/app/initializer/DownloaderInitializer.kt new file mode 100644 index 0000000..b9ae8e7 --- /dev/null +++ b/app/src/main/java/com/ericampire/android/androidstudycase/app/initializer/DownloaderInitializer.kt @@ -0,0 +1,19 @@ +package com.ericampire.android.androidstudycase.app.initializer + +import android.content.Context +import androidx.startup.Initializer +import com.downloader.PRDownloader +import com.downloader.PRDownloaderConfig + +class DownloaderInitializer : Initializer { + override fun create(context: Context) { + val config = PRDownloaderConfig.newBuilder() + .setDatabaseEnabled(true) + .build() + PRDownloader.initialize(context, config) + } + + override fun dependencies(): MutableList>> { + return mutableListOf() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/explore/ui/ExploreScreen.kt b/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/explore/ui/ExploreScreen.kt index 2f8b330..95cbf93 100644 --- a/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/explore/ui/ExploreScreen.kt +++ b/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/explore/ui/ExploreScreen.kt @@ -32,6 +32,8 @@ import com.ericampire.android.androidstudycase.presentation.screen.explore.busin import com.ericampire.android.androidstudycase.presentation.screen.explore.business.ExploreViewModel import com.ericampire.android.androidstudycase.presentation.screen.explore.business.ExploreViewState import com.ericampire.android.androidstudycase.presentation.theme.AppColor +import com.ericampire.android.androidstudycase.util.extension.copyTextToClipboard +import com.ericampire.android.androidstudycase.util.extension.downloadAndShare import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.HorizontalPager import com.google.accompanist.pager.pagerTabIndicatorOffset @@ -55,6 +57,11 @@ fun ExploreScreen( val tabItems = stringArrayResource(id = R.array.explore_item) val pagerState = rememberPagerState(pageCount = tabItems.size) + + val bottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden) + var selectedAnimation by remember { mutableStateOf(null) } + var fileUrl by remember { mutableStateOf(null) } + LaunchedEffect(viewModel) { viewModel.submitAction(ExploreAction.FindRecentFile) } @@ -69,96 +76,144 @@ fun ExploreScreen( } } - Scaffold( - topBar = { - Column( - modifier = Modifier - .fillMaxWidth() - .background(color = AppColor.Black001), - content = { - TopActionBar() - TabRow( - modifier = Modifier.fillMaxWidth(), - selectedTabIndex = pagerState.currentPage, - backgroundColor = Color.Transparent, - indicator = { tabPositions -> - TabRowDefaults.Indicator( - modifier = Modifier - .pagerTabIndicatorOffset(pagerState, tabPositions) - .padding(horizontal = 32.dp) - .clip(RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp)), - height = 3.dp, - color = MaterialTheme.colors.primary - ) - }, - tabs = { - tabItems.forEachIndexed { index, title -> - Tab( - selected = index == pagerState.currentPage, - selectedContentColor = MaterialTheme.colors.primary, - unselectedContentColor = Color.White, - onClick = { - coroutineScope.launch { - pagerState.animateScrollToPage(index) - } - }, - content = { - Text( - modifier = Modifier.padding(vertical = 12.dp), - text = title, - style = MaterialTheme.typography.h6.copy( - fontWeight = FontWeight.Normal - ), - textAlign = TextAlign.Center, - ) - } - ) - } - } - ) + LaunchedEffect(fileUrl) { + if (fileUrl != null) { + context.downloadAndShare( + url = fileUrl!!, + onError = { + Timber.e(it) + }, + onSuccess = { + selectedAnimation = null + fileUrl = null + coroutineScope.launch { + bottomSheetState.hide() + } } ) + } + } + + + ModalBottomSheetLayout( + sheetShape = RoundedCornerShape(topEnd = 24.dp, topStart = 24.dp), + sheetState = bottomSheetState, + sheetContent = { + ShareBottomSheet( + lottiefile = selectedAnimation, + isLoading = fileUrl != null, + onCopyLink = { + context.copyTextToClipboard(it) + coroutineScope.launch { + bottomSheetState.hide() + } + }, + onShareGifFile = { fileUrl = it }, + onShareJsonFile = { fileUrl = it }, + onShareVideoFile = { fileUrl = it } + ) }, - content = { contentPadding -> - Crossfade(modifier = Modifier.padding(contentPadding), targetState = state) { - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center, - content = { - when (it) { - Uninitialized -> { - LoadingAnimation( - waveColor = MaterialTheme.colors.primary.copy(alpha = 0.5f), - arcColor = MaterialTheme.colors.primaryVariant - ) - } - is Loading -> { - LoadingAnimation( - waveColor = MaterialTheme.colors.primary.copy(alpha = 0.5f), - arcColor = MaterialTheme.colors.primaryVariant - ) - } - is Success -> { - val animations = it.invoke() - if (animations.isEmpty()) { - // Todo: Empty instead of Loading View - LoadingAnimation( - waveColor = MaterialTheme.colors.primary.copy(alpha = 0.5f), - arcColor = MaterialTheme.colors.primaryVariant + content = { + Scaffold( + topBar = { + Column( + modifier = Modifier + .fillMaxWidth() + .background(color = AppColor.Black001), + content = { + TopActionBar() + TabRow( + modifier = Modifier.fillMaxWidth(), + selectedTabIndex = pagerState.currentPage, + backgroundColor = Color.Transparent, + indicator = { tabPositions -> + TabRowDefaults.Indicator( + modifier = Modifier + .pagerTabIndicatorOffset(pagerState, tabPositions) + .padding(horizontal = 32.dp) + .clip(RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp)), + height = 3.dp, + color = MaterialTheme.colors.primary ) - } else { - HorizontalPager(state = pagerState) { - ExploreContent(files = animations) + }, + tabs = { + tabItems.forEachIndexed { index, title -> + Tab( + selected = index == pagerState.currentPage, + selectedContentColor = MaterialTheme.colors.primary, + unselectedContentColor = Color.White, + onClick = { + coroutineScope.launch { + pagerState.animateScrollToPage(index) + } + }, + content = { + Text( + modifier = Modifier.padding(vertical = 12.dp), + text = title, + style = MaterialTheme.typography.h6.copy( + fontWeight = FontWeight.Normal + ), + textAlign = TextAlign.Center, + ) + } + ) + } + } + ) + } + ) + }, + content = { contentPadding -> + Crossfade(modifier = Modifier.padding(contentPadding), targetState = state) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + content = { + when (it) { + Uninitialized -> { + LoadingAnimation( + waveColor = MaterialTheme.colors.primary.copy(alpha = 0.5f), + arcColor = MaterialTheme.colors.primaryVariant + ) + } + is Loading -> { + LoadingAnimation( + waveColor = MaterialTheme.colors.primary.copy(alpha = 0.5f), + arcColor = MaterialTheme.colors.primaryVariant + ) + } + is Success -> { + val animations = it.invoke() + if (animations.isEmpty()) { + // Todo: Empty instead of Loading View + LoadingAnimation( + waveColor = MaterialTheme.colors.primary.copy(alpha = 0.5f), + arcColor = MaterialTheme.colors.primaryVariant + ) + } else { + HorizontalPager(state = pagerState) { + ExploreContent( + files = animations, + onShareClick = { + selectedAnimation = it + coroutineScope.launch { + bottomSheetState.animateTo(ModalBottomSheetValue.Expanded) + } + } + ) + } + } + } + is Fail -> { + Timber.e(it.error.localizedMessage) } } } - is Fail -> { - Timber.e(it.error.localizedMessage) - } - } + ) } - ) - } + } + ) } ) } @@ -167,6 +222,7 @@ fun ExploreScreen( @Composable private fun ExploreContent( modifier: Modifier = Modifier, + onShareClick: (Lottiefile) -> Unit, files: List ) { LazyColumn( @@ -184,7 +240,8 @@ private fun ExploreContent( items(items = files, key = { it.toString() }) { lottieFile -> LottieFileItemView( lottiefile = lottieFile, - onClick = {} + onClick = {}, + onShareClick = onShareClick, ) Divider( diff --git a/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/explore/ui/LottieFileItem.kt b/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/explore/ui/LottieFileItem.kt index 3dae165..85926b6 100644 --- a/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/explore/ui/LottieFileItem.kt +++ b/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/explore/ui/LottieFileItem.kt @@ -31,7 +31,8 @@ import com.ericampire.android.androidstudycase.util.LottieFileProvider fun LottieFileItemView( modifier: Modifier = Modifier, lottiefile: Lottiefile, - onClick: (Lottiefile) -> Unit + onClick: (Lottiefile) -> Unit, + onShareClick: (Lottiefile) -> Unit, ) { Card( modifier = modifier, @@ -89,7 +90,9 @@ fun LottieFileItemView( onLikeClick = { /*TODO*/ }, onCommentClick = { /*TODO*/ }, onAddCollectionClick = { /*TODO*/ }, - onShareClick = { } + onShareClick = { + onShareClick(lottiefile) + } ) } ) @@ -203,7 +206,8 @@ fun LottiefileItemViewPreview(@PreviewParameter(LottieFileProvider::class) data: content = { LottieFileItemView( lottiefile = data, - onClick = {} + onClick = {}, + onShareClick = {}, ) } ) diff --git a/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/explore/ui/ShareBottomSheet.kt b/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/explore/ui/ShareBottomSheet.kt new file mode 100644 index 0000000..146d89c --- /dev/null +++ b/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/explore/ui/ShareBottomSheet.kt @@ -0,0 +1,201 @@ +package com.ericampire.android.androidstudycase.presentation.screen.explore.ui + +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.InsertDriveFile +import androidx.compose.material.icons.rounded.Link +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import com.ericampire.android.androidstudycase.R +import com.ericampire.android.androidstudycase.domain.entity.Lottiefile +import com.ericampire.android.androidstudycase.presentation.theme.AndroidStudyCaseTheme +import com.ericampire.android.androidstudycase.presentation.theme.AppColor +import com.ericampire.android.androidstudycase.util.LottieFileProvider + +@Composable +fun ShareBottomSheet( + modifier: Modifier = Modifier, + isLoading: Boolean = false, + lottiefile: Lottiefile?, + onCopyLink: (String) -> Unit, + onShareJsonFile: (String?) -> Unit, + onShareGifFile: (String?) -> Unit, + onShareVideoFile: (String?) -> Unit, +) { + + val progressBarAlpha = if (isLoading) 1f else 0f + Column( + modifier = modifier + .background(AppColor.Black001), + content = { + LinearProgressIndicator( + modifier = Modifier + .fillMaxWidth() + .alpha(progressBarAlpha) + ) + Column( + modifier = modifier + .padding(16.dp) + .fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + content = { + Text( + text = stringResource(id = R.string.txt_share_animation), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.h4.copy( + color = MaterialTheme.colors.onSurface + ), + ) + + Column( + modifier = Modifier + .fillMaxWidth() + .clip(MaterialTheme.shapes.medium) + .background(AppColor.Arsenic) + .clickable { onCopyLink(lottiefile?.lottieUrl ?: "") } + .padding(18.dp), + verticalArrangement = Arrangement.spacedBy(10.dp), + horizontalAlignment = Alignment.CenterHorizontally, + content = { + Icon( + modifier = Modifier.size(48.dp), + tint = AppColor.SlateGray, + imageVector = Icons.Rounded.Link, + contentDescription = null + ) + Text( + textAlign = TextAlign.Center, + text = stringResource(id = R.string.txt_share_animation), + style = MaterialTheme.typography.body1.copy( + color = MaterialTheme.colors.onSurface + ), + ) + } + ) + + Column( + modifier = Modifier + .clip(MaterialTheme.shapes.medium) + .fillMaxWidth(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + content = { + ShareItem( + modifier = Modifier.clickable { + onShareJsonFile(lottiefile?.lottieUrl) + }, + title = "Lottie JSON", + fileExtension = ".json", + icon = Icons.Rounded.InsertDriveFile + ) + Divider(modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray)) + ShareItem( + modifier = Modifier.clickable { + onShareGifFile(lottiefile?.gifUrl) + }, + title = "Animated GIF", + fileExtension = ".gif", + icon = Icons.Rounded.InsertDriveFile + ) + Divider(modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray)) + ShareItem( + modifier = Modifier.clickable { + onShareVideoFile(lottiefile?.videoUrl) + }, + title = "Video MP4", + fileExtension = ".mp4", + icon = Icons.Rounded.InsertDriveFile + ) + } + ) + } + ) + } + ) +} + +@Composable +private fun ShareItem( + modifier: Modifier = Modifier, + title: String, + fileExtension: String, + icon: ImageVector +) { + + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = modifier + .background(AppColor.Arsenic) + .padding(24.dp) + .fillMaxWidth(), + content = { + Row( + horizontalArrangement = Arrangement.spacedBy(24.dp), + verticalAlignment = Alignment.CenterVertically, + content = { + Icon( + imageVector = icon, + tint = AppColor.SlateGray, + contentDescription = null + ) + Text( + text = title, + style = MaterialTheme.typography.h4.copy( + color = MaterialTheme.colors.onSurface + ), + ) + } + ) + + Text( + text = fileExtension, + style = MaterialTheme.typography.body1.copy( + color = Color.Gray + ), + ) + } + ) +} + +@ExperimentalAnimationApi +@ExperimentalMaterialApi +@Preview() +@Composable +fun ShareBottomSheetPreview(@PreviewParameter(LottieFileProvider::class) data: Lottiefile) { + AndroidStudyCaseTheme(darkTheme = true) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + content = { + ShareBottomSheet( + lottiefile = data, + isLoading = true, + onCopyLink = {}, + onShareGifFile = {}, + onShareJsonFile = {}, + onShareVideoFile = {} + ) + } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/home/ui/LoginBottomSheet.kt b/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/home/ui/LoginBottomSheet.kt index 7ad784a..dcb84f4 100644 --- a/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/home/ui/LoginBottomSheet.kt +++ b/app/src/main/java/com/ericampire/android/androidstudycase/presentation/screen/home/ui/LoginBottomSheet.kt @@ -1,6 +1,5 @@ package com.ericampire.android.androidstudycase.presentation.screen.home.ui -import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.* @@ -12,6 +11,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign @@ -32,6 +32,8 @@ fun LoginBottomSheet( onLoginClick: () -> Unit, ) { + val progressBarAlpha = if (isLoading) 1f else 0f + val composition by rememberLottieComposition( spec = LottieCompositionSpec.RawRes(R.raw.people_communicating) ) @@ -45,9 +47,11 @@ fun LoginBottomSheet( .clip(MaterialTheme.shapes.medium) .background(AppColor.Black001), content = { - AnimatedVisibility(isLoading) { - LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) - } + LinearProgressIndicator( + modifier = Modifier + .fillMaxWidth() + .alpha(progressBarAlpha) + ) Column( modifier = Modifier.padding(24.dp), @@ -100,7 +104,7 @@ fun LoginBottomSheet( @ExperimentalAnimationApi @ExperimentalMaterialApi -@Preview() +@Preview @Composable fun LoginDialogViewPreview() { AndroidStudyCaseTheme(darkTheme = true) { diff --git a/app/src/main/java/com/ericampire/android/androidstudycase/presentation/theme/Color.kt b/app/src/main/java/com/ericampire/android/androidstudycase/presentation/theme/Color.kt index fd677b6..86ee1c2 100644 --- a/app/src/main/java/com/ericampire/android/androidstudycase/presentation/theme/Color.kt +++ b/app/src/main/java/com/ericampire/android/androidstudycase/presentation/theme/Color.kt @@ -5,6 +5,8 @@ import androidx.compose.ui.graphics.Color object AppColor { + val SlateGray = Color(0xFF606E7C) + val Arsenic = Color(0xFF3C3C3C) val Teal = Color(0xFF1C7373) val PaleBlue = Color(0xFFD3F6F6) val PrimaryColor = Color(0xFF2BEAED) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e3429fa..b5c1326 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,4 +5,5 @@ 1000s of lottie animations from top creators waiting to be discover, save and share from the palm of your hand Continue with Facebook + Share animation \ No newline at end of file diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml new file mode 100644 index 0000000..fafa14f --- /dev/null +++ b/app/src/main/res/xml/provider_paths.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/Libs.kt b/buildSrc/src/main/kotlin/Libs.kt index 3c1315f..12078b5 100644 --- a/buildSrc/src/main/kotlin/Libs.kt +++ b/buildSrc/src/main/kotlin/Libs.kt @@ -148,4 +148,5 @@ object Libs { const val joda_time = "net.danlew:android.joda:2.10.9" const val code_scanner = "com.budiyev.android:code-scanner:2.1.0" + const val fetch = "com.mindorks.android:prdownloader:0.6.0" } diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 8da56da..f469897 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -23,4 +23,5 @@ Camera permission denied. See this FAQ with information about why we need this permission. Please, grant us access on the Settings screen. Continue with Google Get started for free + Share Lottiefile \ No newline at end of file diff --git a/util/build.gradle.kts b/util/build.gradle.kts index adeabf3..570fbb4 100644 --- a/util/build.gradle.kts +++ b/util/build.gradle.kts @@ -51,4 +51,5 @@ dependencies { kapt(Libs.hilt_android_compiler) api(Libs.timber) + api(Libs.fetch) } \ No newline at end of file diff --git a/util/src/main/java/com/ericampire/android/androidstudycase/util/extension/Context.kt b/util/src/main/java/com/ericampire/android/androidstudycase/util/extension/Context.kt new file mode 100644 index 0000000..c472081 --- /dev/null +++ b/util/src/main/java/com/ericampire/android/androidstudycase/util/extension/Context.kt @@ -0,0 +1,63 @@ +package com.ericampire.android.androidstudycase.util.extension + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.os.Environment +import android.widget.Toast +import androidx.core.app.ShareCompat +import androidx.core.content.FileProvider +import com.downloader.Error +import com.downloader.OnDownloadListener +import com.downloader.PRDownloader +import com.downloader.utils.Utils +import org.zxconnect.android.beserve.util.R +import java.io.File + + +fun Context.downloadAndShare( + url: String, + onSuccess: () -> Unit, + onError: (String?) -> Unit +) { + + val dirPath = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString() + val fileName = url.substringAfterLast("/") + val filePath = Utils.getPath(dirPath, fileName) + val downloadRequest = PRDownloader.download(url, dirPath, fileName).build() + + downloadRequest.start(object : OnDownloadListener { + override fun onDownloadComplete() { + shareFile(filePath) + onSuccess() + } + + override fun onError(error: Error) { + onError(error.serverErrorMessage) + } + }) +} + +fun Context.copyTextToClipboard(text: String) { + val clipboardManager = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clipData = ClipData.newPlainText("text", text) + clipboardManager.setPrimaryClip(clipData) + Toast.makeText(this, "Link copied to clipboard", Toast.LENGTH_LONG).show() +} + +private fun Context.shareFile(filePath: String) { + val file = File(filePath) + val fileUri = FileProvider.getUriForFile( + this, + this.applicationContext.packageName.toString() + ".provider", + file + ) + + val shareIntent = ShareCompat.IntentBuilder(this) + .setChooserTitle(getString(R.string.txt_share_lottie_file)) + .setStream(fileUri) + .setType("application/pdf") + .createChooserIntent() + + startActivity(shareIntent) +} \ No newline at end of file