From ec172b2f48a33fe1b79d80c9c73f46cd67f025dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Font=C3=A1n?= Date: Wed, 9 Nov 2022 21:38:08 +0100 Subject: [PATCH] Optimized the code, added copy link to clipboard and added architectures explanation. --- app/build.gradle.kts | 4 +- .../ui/components/ArchExplanationComponent.kt | 131 ++++++++++++++++++ .../presentation/ui/components/PackageItem.kt | 19 +-- .../ui/components/PackagesListItem.kt | 81 ++++++++++- .../presentation/ui/pages/home/HomePage.kt | 8 +- .../ui/pages/home/HomeViewModel.kt | 19 ++- .../com/bobbyesp/spowlo/util/DownloadUtil.kt | 10 +- .../bobbyesp/spowlo/util/PreferencesUtil.kt | 1 + .../java/com/bobbyesp/spowlo/util/Utils.kt | 2 + app/src/main/res/values/strings.xml | 7 + 10 files changed, 256 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/com/bobbyesp/spowlo/presentation/ui/components/ArchExplanationComponent.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b21afc5e..f8a00899 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -6,7 +6,7 @@ plugins { id("kotlin-android") id("kotlin-kapt") id("org.jetbrains.kotlin.android") - kotlin("plugin.serialization") version "1.7.10" + kotlin("plugin.serialization") version "1.7.20" } apply(plugin = "dagger.hilt.android.plugin") @@ -156,10 +156,12 @@ dependencies { implementation("com.google.accompanist:accompanist-permissions:$accompanistVersion") implementation("com.google.accompanist:accompanist-systemuicontroller:$accompanistVersion") implementation("io.coil-kt:coil-compose:$coilVersion") + implementation("com.holix.android:bottomsheetdialog-compose:1.0.3") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1") implementation("androidx.hilt:hilt-navigation-compose:1.0.0") + implementation("androidx.browser:browser:1.4.0") kapt("androidx.hilt:hilt-compiler:1.0.0") implementation("com.google.dagger:hilt-android:$hiltVersion") kapt("com.google.dagger:hilt-android-compiler:$hiltVersion") diff --git a/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/components/ArchExplanationComponent.kt b/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/components/ArchExplanationComponent.kt new file mode 100644 index 00000000..14be8032 --- /dev/null +++ b/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/components/ArchExplanationComponent.kt @@ -0,0 +1,131 @@ +package com.bobbyesp.spowlo.presentation.ui.components + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.foundation.layout.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.ExpandLess +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.bobbyesp.spowlo.R + +enum class ArchExplType( + val type: String, + val archDescription: String, + val description: Int, + val archType: ArchType = ArchType.Arm64 +) { + Arm64("ARM64-v8a", "64-bit ARM", R.string.arm64_desc, ArchType.Arm64), + Arm("ARMEABI-v7a", "32-bit ARM", R.string.arm32_desc, ArchType.Arm), +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ArchExplanationComponent( + type : ArchExplType, + expanded: Boolean = false +) { + var isExpanded by remember { mutableStateOf(expanded) } + + ElevatedCard( + modifier = Modifier, + onClick = { isExpanded = !isExpanded }, + shape = MaterialTheme.shapes.small + ) { + Box { + Row( + modifier = Modifier + .fillMaxWidth() + .height(IntrinsicSize.Min) + .padding(4.dp) + ) { + Column( + modifier = Modifier + .padding(vertical = 6.dp) + .padding(end = 6.dp) + .padding(start = 2.dp) + .weight(1f) + .fillMaxHeight(), + verticalArrangement = Arrangement.Top + ) { + Text( + text = type.type, + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.onSurface, + maxLines = 2, + overflow = TextOverflow.Ellipsis + ) + Text( + modifier = Modifier.padding(top = 6.dp), + text = type.archDescription, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + overflow = TextOverflow.Ellipsis + ) + Row( + modifier = Modifier.weight(1f), + verticalAlignment = Alignment.CenterVertically + ) { + Spacer( + modifier = Modifier + .weight(1f, true) + ) + + val animatedDegree = + animateFloatAsState(targetValue = if (isExpanded) 0f else -180f) + Box( + modifier = Modifier + .padding(2.dp) + .align(Alignment.Bottom) + ) { + FilledTonalIconButton( + modifier = Modifier + .padding() + .size(24.dp), + onClick = { isExpanded = !isExpanded }) { + Icon( + Icons.Outlined.ExpandLess, + null, + tint = MaterialTheme.colorScheme.onPrimaryContainer, + modifier = Modifier.rotate(animatedDegree.value) + ) + } + } + } + } + } + } + AnimatedVisibility(visible = isExpanded) { + Box(modifier = Modifier + .align(Alignment.CenterHorizontally)) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp, bottom = 4.dp, start = 4.dp, end = 4.dp), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + ArchTag(arch = type.archType) + Text( + text = stringResource(id = type.description), + modifier = Modifier.padding(start = 6.dp), + style = MaterialTheme.typography.bodySmall, + ) + } + } + } + } + } + +@Preview +@Composable +fun previewArchExplanationComponent() { + ArchExplanationComponent(type = ArchExplType.Arm64) +} \ No newline at end of file diff --git a/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/components/PackageItem.kt b/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/components/PackageItem.kt index a202b73a..0d82e6bf 100644 --- a/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/components/PackageItem.kt +++ b/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/components/PackageItem.kt @@ -5,9 +5,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Download import androidx.compose.material.icons.filled.Memory -import androidx.compose.material.icons.outlined.Download -import androidx.compose.material.icons.outlined.ExpandLess -import androidx.compose.material.icons.outlined.MoreHoriz +import androidx.compose.material.icons.outlined.* import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -31,8 +29,10 @@ fun PackageItem( type: ArchType = ArchType.Arm64, link: String, onClick: () -> Unit = {}, + onArchClick: () -> Unit = {}, version: String = "8.7.78.373", - onLongClick: () -> Unit = {} + onLongClick: () -> Unit = {}, + onCopyClick: () -> Unit = {}, ) { Surface(modifier = modifier.clickable(onClick = onClick).padding(6.dp)) { Row( @@ -40,7 +40,7 @@ fun PackageItem( .fillMaxWidth(), verticalAlignment = Alignment.CenterVertically ) { - ArchTag(modifier = Modifier.height(IntrinsicSize.Max).width(IntrinsicSize.Max) ,arch = type) + ArchTag(modifier = Modifier.height(IntrinsicSize.Max).width(IntrinsicSize.Max) ,arch = type, onClick = onArchClick) Text( modifier = Modifier.padding(start = 8.dp).height(IntrinsicSize.Max).width(IntrinsicSize.Min), text = version, @@ -60,7 +60,7 @@ fun PackageItem( .padding(end = 12.dp) .align(Alignment.Bottom) .size(24.dp), - onClick = {} + onClick = onClick ) { Icon( Icons.Filled.Download, @@ -75,13 +75,14 @@ fun PackageItem( .padding() .align(Alignment.Bottom) .size(24.dp), - onClick = {} + onClick = onCopyClick ) { Icon( - Icons.Outlined.MoreHoriz, + Icons.Outlined.ContentCopy, + //Icons.Outlined.MoreHoriz, contentDescription = null, tint = MaterialTheme.colorScheme.onPrimaryContainer, - modifier = Modifier + modifier = Modifier.size(18.dp) ) } } diff --git a/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/components/PackagesListItem.kt b/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/components/PackagesListItem.kt index 9cbf9a21..f5b092a9 100644 --- a/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/components/PackagesListItem.kt +++ b/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/components/PackagesListItem.kt @@ -3,6 +3,7 @@ package com.bobbyesp.spowlo.presentation.ui.components import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.ExpandLess import androidx.compose.material3.* @@ -11,7 +12,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate +import androidx.compose.ui.graphics.Shape import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -20,6 +24,8 @@ import com.bobbyesp.spowlo.domain.spotify.model.PackagesObject import com.bobbyesp.spowlo.presentation.MainActivity import com.bobbyesp.spowlo.presentation.ui.pages.home.HomeViewModel import com.bobbyesp.spowlo.util.DownloadUtil +import com.holix.android.bottomsheetdialog.compose.BottomSheetDialog +import com.holix.android.bottomsheetdialog.compose.BottomSheetDialogProperties enum class PackagesListItemType( val type: Int, @@ -53,8 +59,9 @@ fun PackagesListItem( onClick: () -> Unit = {}, ) { var isExpanded by remember { mutableStateOf(expanded) } - - + var show by remember { + mutableStateOf(false) + } ElevatedCard( modifier = modifier, onClick = { isExpanded = !isExpanded }, @@ -124,20 +131,24 @@ fun PackagesListItem( .padding(horizontal = 6.dp) ) { packages.forEach { version -> + val title = packages[packages.indexOf(version)].Title + val link = packages[packages.indexOf(version)].Link //if the package title has ARM64-V8A, the type is Arm64 - val isArm64: Boolean = packages[packages.indexOf(version)].Title.contains("ARM64-V8A") - val containsArch: Boolean = packages[packages.indexOf(version)].Title.contains("(ARM64-V8A)") || packages[packages.indexOf(version)].Title.contains("ARMEABI-V7A") + val isArm64: Boolean = title.contains("ARM64-V8A") + val containsArch: Boolean = title.contains("(ARM64-V8A)") || title.contains("ARMEABI-V7A") //get just the version name without the architecture - val versionName = if (containsArch) packages[packages.indexOf(version)].Title.substringBefore("(").trim() else packages[packages.indexOf(version)].Title + val versionName = if (containsArch) title.substringBefore("(").trim() else title PackageItem( modifier = Modifier .fillMaxWidth(), //if its Arm64, ArchType is Arm64, else ArchType is Arm type = if (isArm64) ArchType.Arm64 else ArchType.Arm, version = versionName, - link = packages[packages.indexOf(version)].Link, + link = link, //on click open the link in browser - onClick = { DownloadUtil.openLinkInBrowser(packages[packages.indexOf(version)].Link) } + onClick = { DownloadUtil.openLinkInBrowser(link) }, + onArchClick = {show = !show}, + onCopyClick = {DownloadUtil.copyLinkToClipboard(link)} ) if (packages.indexOf(version) != packages.lastIndex) { @@ -152,6 +163,62 @@ fun PackagesListItem( } } } + if (show) { + BottomSheetDialog( + onDismissRequest = { + show = false + }, + properties = BottomSheetDialogProperties( + + ), + ) { + // content + Surface(modifier = Modifier, shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)) { + Column(modifier = Modifier + .fillMaxWidth() + .padding(16.dp)) { + Text( + text = stringResource(id = R.string.architectures), + modifier = Modifier.padding(bottom = 8.dp), + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.Bold, + ) + Text(text = stringResource(id = R.string.archs_desc), + modifier = Modifier, + style = MaterialTheme.typography.bodySmall) + + Divider( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 6.dp) + .width(1f.dp) + ) + Text( + text = stringResource(id = R.string.archs_provided), + modifier = Modifier, + style = MaterialTheme.typography.bodySmall, + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = stringResource(id = R.string.different_archs), + modifier = Modifier, + style = MaterialTheme.typography.bodySmall, + ) + Spacer(modifier = Modifier.height(6.dp)) + ArchExplanationComponent(type = ArchExplType.Arm64) + Spacer(modifier = Modifier.height(8.dp)) + ArchExplanationComponent(type = ArchExplType.Arm) + Spacer(modifier = Modifier.height(6.dp)) + Text( + text = stringResource(id = R.string.check_arch), + modifier = Modifier, + style = MaterialTheme.typography.bodySmall, + ) + + } + } + } + } } @Composable diff --git a/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/pages/home/HomePage.kt b/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/pages/home/HomePage.kt index a747fd52..25a2c1da 100644 --- a/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/pages/home/HomePage.kt +++ b/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/pages/home/HomePage.kt @@ -98,10 +98,10 @@ fun HomePage(navController: NavController, homeViewModel: HomeViewModel = hiltVi style = MaterialTheme.typography.displaySmall ) } - PackagesListItem( type = PackagesListItemType.Regular, expanded = false, onClick = {}, packages = regularVersions.sortedByDescending { it.Title }) - PackagesListItem( type = PackagesListItemType.RegularCloned, expanded = false, onClick = {}, packages = regularClonedVersions.sortedByDescending { it.Title }) - PackagesListItem( type = PackagesListItemType.Amoled, expanded = false, onClick = {}, packages = amoledVersions.sortedByDescending { it.Title }) - PackagesListItem( type = PackagesListItemType.AmoledCloned, expanded = false, onClick = {}, packages = amoledClonedVersions.sortedByDescending { it.Title }) + PackagesListItem( type = PackagesListItemType.Regular, expanded = false, onClick = {}, packages = regularVersions) + PackagesListItem( type = PackagesListItemType.RegularCloned, expanded = false, onClick = {}, packages = regularClonedVersions) + PackagesListItem( type = PackagesListItemType.Amoled, expanded = false, onClick = {}, packages = amoledVersions) + PackagesListItem( type = PackagesListItemType.AmoledCloned, expanded = false, onClick = {}, packages = amoledClonedVersions) Divider(modifier = Modifier.padding(top = 16.dp, bottom = 14.dp)) AnimatedVisibility(visible = loaded) { when(loaded){ diff --git a/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/pages/home/HomeViewModel.kt b/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/pages/home/HomeViewModel.kt index 0551b40e..a9ec43a5 100644 --- a/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/pages/home/HomeViewModel.kt +++ b/app/src/main/java/com/bobbyesp/spowlo/presentation/ui/pages/home/HomeViewModel.kt @@ -43,7 +43,6 @@ class HomeViewModel @Inject constructor( private val _state = mutableStateOf(APICallState()) val state: State = _state - fun setup(){ currentJob?.cancel() currentJob = viewModelScope.launch{ @@ -57,12 +56,13 @@ class HomeViewModel @Inject constructor( callAPI() } } + sortAllPackages() } fun downloadApkFromLink(link: String){ currentJob?.cancel() currentJob = viewModelScope.launch{ - DownloadUtil.downloadSpotify(link, Spowlo.context) + DownloadUtil.openLinkInBrowser(link) } } @@ -135,6 +135,21 @@ class HomeViewModel @Inject constructor( } } + private fun sortPackagesObjectList(list: List): List{ + return list.sortedWith(compareByDescending { it.Title }) + } + + private fun sortAllPackages(){ + mutableStateFlow.update { + it.copy( + regular_versions = sortPackagesObjectList(it.regular_versions), + regular_cloned_versions = sortPackagesObjectList(it.regular_cloned_versions), + amoled_versions = sortPackagesObjectList(it.amoled_versions), + amoled_cloned_versions = sortPackagesObjectList(it.amoled_cloned_versions), + ) + } + } + private fun infoCard(){ mutableStateFlow.update { it.copy( diff --git a/app/src/main/java/com/bobbyesp/spowlo/util/DownloadUtil.kt b/app/src/main/java/com/bobbyesp/spowlo/util/DownloadUtil.kt index c07014e7..5c0aa176 100644 --- a/app/src/main/java/com/bobbyesp/spowlo/util/DownloadUtil.kt +++ b/app/src/main/java/com/bobbyesp/spowlo/util/DownloadUtil.kt @@ -3,6 +3,7 @@ package com.bobbyesp.spowlo.util import android.content.Context import android.content.Intent import android.net.Uri +import androidx.browser.customtabs.CustomTabsIntent import com.bobbyesp.spowlo.Spowlo import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.* @@ -77,14 +78,17 @@ object DownloadUtil { }.flowOn(Dispatchers.IO).distinctUntilChanged() fun openLinkInBrowser(link: String) { - //open a browser with the link - println("--------------------------------------------------------------------------") - println(link) //add flag FLAG_ACTIVITY_NEW_TASK to open the browser in a new task val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) Spowlo.context.startActivity(intent) } + fun copyLinkToClipboard(link: String) { + val clipboard = Spowlo.context.getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager + val clip = android.content.ClipData.newPlainText("Spotify APK Link", link) + clipboard.setPrimaryClip(clip) + } + sealed class DownloadStatus { object NotYet : DownloadStatus() data class Progress(val percent: Int) : DownloadStatus() diff --git a/app/src/main/java/com/bobbyesp/spowlo/util/PreferencesUtil.kt b/app/src/main/java/com/bobbyesp/spowlo/util/PreferencesUtil.kt index 621cd594..cb5ad350 100644 --- a/app/src/main/java/com/bobbyesp/spowlo/util/PreferencesUtil.kt +++ b/app/src/main/java/com/bobbyesp/spowlo/util/PreferencesUtil.kt @@ -37,6 +37,7 @@ object PreferencesUtil { const val THEME_COLOR = "theme_color" const val DYNAMIC_COLOR = "dynamic_color" const val LANGUAGE = "language" + const val SPOTIFY_URL = "spotify_url" private val mutableAppSettingsStateFlow = MutableStateFlow( AppSettings( diff --git a/app/src/main/java/com/bobbyesp/spowlo/util/Utils.kt b/app/src/main/java/com/bobbyesp/spowlo/util/Utils.kt index c375972b..b63ecb77 100644 --- a/app/src/main/java/com/bobbyesp/spowlo/util/Utils.kt +++ b/app/src/main/java/com/bobbyesp/spowlo/util/Utils.kt @@ -1,6 +1,8 @@ package com.bobbyesp.spowlo.util +import android.net.Uri import android.widget.Toast +import androidx.browser.customtabs.CustomTabsIntent import com.bobbyesp.spowlo.Spowlo.Companion.applicationScope import com.bobbyesp.spowlo.Spowlo.Companion.context import com.bobbyesp.spowlo.util.Utils.makeToast diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fd11163f..6eaa5733 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -37,4 +37,11 @@ SPOTIFY VERSIONS App update has failed Update + Architectures + There are two principal types of processor architecture in the majority of devices, 32 and 64 bit. + Spotify provides both types of architectures to us, but you need to know what to choose. + The mobile architectures are the next : + This type of architecture is based on 64 bit. It can run any Spotify package (32 bit ones are emulated) + This type of architecture is based on 32 bit. It can JUST run ARMEABI packages. + To check your processor architecture, scroll down till the end of the main page. \ No newline at end of file