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
-
+
@@ -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