From 49562de50302d79649e56fea81ac905f892daf92 Mon Sep 17 00:00:00 2001 From: shabinder Date: Mon, 19 Apr 2021 23:54:48 +0530 Subject: [PATCH] Custom Download Directory Func. --- android/src/main/AndroidManifest.xml | 3 + .../com/shabinder/spotiflyer/MainActivity.kt | 54 ++++++++++++++-- .../spotiflyer/utils/UtilFunctions.kt | 7 ++- android/src/main/res/values/res.xml | 62 +++++++++++++++++++ .../common/uikit/SpotiFlyerRootUi.kt | 14 +++-- .../common/models/gaana/GaanaAlbum.kt | 2 +- common/dependency-injection/build.gradle.kts | 1 + .../com/shabinder/common/di/AndroidActual.kt | 4 ++ .../com/shabinder/common/di/AndroidDir.kt | 26 ++++++-- .../kotlin/com/shabinder/common/di/Dir.kt | 2 +- .../common/di/providers/GaanaProvider.kt | 4 +- .../common/di/providers/SpotifyProvider.kt | 23 +++---- .../shabinder/common/root/SpotiFlyerRoot.kt | 1 + .../root/callbacks/SpotiFlyerRootCallBacks.kt | 1 + .../root/integration/SpotiFlyerRootImpl.kt | 1 + 15 files changed, 169 insertions(+), 36 deletions(-) create mode 100644 android/src/main/res/values/res.xml diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 7bc64e5f..7ec80d6d 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -35,6 +35,8 @@ + @@ -48,6 +50,7 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" + android:configChanges="orientation|screenSize" android:forceDarkAllowed="true" android:requestLegacyExternalStorage="true" tools:targetApi="q"> diff --git a/android/src/main/java/com/shabinder/spotiflyer/MainActivity.kt b/android/src/main/java/com/shabinder/spotiflyer/MainActivity.kt index 7555dc80..daa45ccf 100644 --- a/android/src/main/java/com/shabinder/spotiflyer/MainActivity.kt +++ b/android/src/main/java/com/shabinder/spotiflyer/MainActivity.kt @@ -50,6 +50,8 @@ import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.extensions.compose.jetbrains.rememberRootComponent import com.arkivanov.mvikotlin.logging.store.LoggingStoreFactory import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory +import com.codekidlabs.storagechooser.R +import com.codekidlabs.storagechooser.StorageChooser import com.google.accompanist.insets.ProvideWindowInsets import com.google.accompanist.insets.navigationBarsPadding import com.google.accompanist.insets.statusBarsHeight @@ -57,6 +59,7 @@ import com.google.accompanist.insets.statusBarsPadding import com.razorpay.Checkout import com.razorpay.PaymentResultListener import com.shabinder.common.database.activityContext +import com.shabinder.common.database.appContext import com.shabinder.common.di.* import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.TrackDetails @@ -69,6 +72,7 @@ import com.tonyodev.fetch2.Status import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import org.koin.android.ext.android.inject +import java.io.File import com.shabinder.common.uikit.showPopUpMessage as uikitShowPopUpMessage const val disableDozeCode = 1223 @@ -128,6 +132,43 @@ class MainActivity : ComponentActivity(), PaymentResultListener { dir.createDirectories() Checkout.preload(applicationContext) handleIntentFromExternalActivity() + Log.i("Download Path",dir.defaultDir()) + } + + @Suppress("DEPRECATION") + private fun setUpOnPrefClickListener() { + // Initialize Builder + val chooser = StorageChooser.Builder() + .withActivity(this) + .withFragmentManager(fragmentManager) + .withMemoryBar(true) + .setTheme(StorageChooser.Theme(appContext).apply { + scheme = applicationContext.resources.getIntArray(R.array.default_dark) + }) + .setDialogTitle("Set Download Directory") + .allowCustomPath(true) + .setType(StorageChooser.DIRECTORY_CHOOSER) + .build() + + // get path that the user has chosen + chooser.setOnSelectListener { path -> + Log.d("Setting Base Path", path) + val f = File(path) + if (f.canWrite()) { + // hell yeah :) + dir.setDownloadDirectory(path) + com.shabinder.common.uikit.showPopUpMessage( + "Download Directory Set to:\n${dir.defaultDir()} " + ) + }else{ + com.shabinder.common.uikit.showPopUpMessage( + "NO WRITE ACCESS on \n$path ,\nReverting Back to Previous" + ) + } + } + + // Show dialog whenever you want by + chooser.show() } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { @@ -145,6 +186,7 @@ class MainActivity : ComponentActivity(), PaymentResultListener { override val directories: Dir = this@MainActivity.dir override val showPopUpMessage: (String) -> Unit = ::uikitShowPopUpMessage override val downloadProgressReport: MutableSharedFlow> = trackStatusFlow + override val setDownloadDirectoryAction: () -> Unit = ::setUpOnPrefClickListener } ) @@ -289,10 +331,14 @@ class MainActivity : ComponentActivity(), PaymentResultListener { AlertDialog( onDismissRequest = {}, buttons = { - TextButton({ - requestStoragePermission() - disableDozeMode(disableDozeCode) - },Modifier.padding(bottom = 16.dp,start = 16.dp,end = 16.dp).fillMaxWidth().background(colorPrimary,shape = SpotiFlyerShapes.medium).padding(horizontal = 8.dp), + TextButton( + { + requestStoragePermission() + disableDozeMode(disableDozeCode) + }, + Modifier.padding(bottom = 16.dp, start = 16.dp, end = 16.dp).fillMaxWidth() + .background(colorPrimary, shape = SpotiFlyerShapes.medium) + .padding(horizontal = 8.dp), ){ Text("Grant Permissions",color = Color.Black,fontSize = 18.sp,textAlign = TextAlign.Center) } diff --git a/android/src/main/java/com/shabinder/spotiflyer/utils/UtilFunctions.kt b/android/src/main/java/com/shabinder/spotiflyer/utils/UtilFunctions.kt index f1afdc64..38af2bd1 100644 --- a/android/src/main/java/com/shabinder/spotiflyer/utils/UtilFunctions.kt +++ b/android/src/main/java/com/shabinder/spotiflyer/utils/UtilFunctions.kt @@ -75,4 +75,9 @@ fun Activity.requestStoragePermission() { 786 ) } -} \ No newline at end of file +} +/* +fun Activity.requestBroaderStoragePermission() { + val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) + startActivity(intent) +}*/ diff --git a/android/src/main/res/values/res.xml b/android/src/main/res/values/res.xml new file mode 100644 index 00000000..81dab6b7 --- /dev/null +++ b/android/src/main/res/values/res.xml @@ -0,0 +1,62 @@ + + + + + + + @color/colorPrimary + @android:color/white + @android:color/white + @android:color/black + @color/colorPrimary + @color/colorAccent + + + @color/colorPrimary + @android:color/white + @android:color/black + @android:color/white + @color/chevronBgColor + #da6c6c + #da6c6c + #da6c6c + @color/colorPrimary + + + + + @color/colorPrimary + @android:color/white + @android:color/black + @android:color/white + #da6c6c + @color/colorPrimary + + + @color/colorPrimary + @android:color/black + @android:color/white + @android:color/white + @color/grey + #da6c6c + #da6c6c + #da6c6c + #da6c6c + + \ No newline at end of file diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerRootUi.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerRootUi.kt index 6dbfcfa3..7a54699f 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerRootUi.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerRootUi.kt @@ -34,9 +34,9 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.material.TopAppBar +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Settings import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember @@ -114,6 +114,7 @@ fun MainScreen(modifier: Modifier = Modifier, alpha: Float,topPadding: Dp = 0.dp AppBar( backgroundColor = appBarColor, + setDownloadDirectory = component.callBacks::setDownloadDirectory, modifier = Modifier.fillMaxWidth() ) Spacer(Modifier.padding(top = topPadding)) @@ -132,6 +133,7 @@ fun MainScreen(modifier: Modifier = Modifier, alpha: Float,topPadding: Dp = 0.dp @Composable fun AppBar( backgroundColor: Color, + setDownloadDirectory:()->Unit, modifier: Modifier = Modifier ) { TopAppBar( @@ -149,14 +151,14 @@ fun AppBar( style = appNameStyle ) } - }, /* + }, actions = { IconButton( - onClick = { *//*TODO: Open Preferences*//* } + onClick = { setDownloadDirectory() } ) { Icon(Icons.Filled.Settings,"Preferences", tint = Color.Gray) } - },*/ + }, modifier = modifier, elevation = 0.dp ) diff --git a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaAlbum.kt b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaAlbum.kt index b050acf9..f8d49aa2 100644 --- a/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaAlbum.kt +++ b/common/data-models/src/commonMain/kotlin/com/shabinder/common/models/gaana/GaanaAlbum.kt @@ -20,7 +20,7 @@ import kotlinx.serialization.Serializable @Serializable data class GaanaAlbum( - val tracks: List, + val tracks: List?, val count: Int, val custom_artworks: CustomArtworks, val release_year: Int, diff --git a/common/dependency-injection/build.gradle.kts b/common/dependency-injection/build.gradle.kts index 376b63d6..5c12b433 100644 --- a/common/dependency-injection/build.gradle.kts +++ b/common/dependency-injection/build.gradle.kts @@ -53,6 +53,7 @@ kotlin { implementation(Extras.Android.razorpay) api(Extras.mp3agic) api(Extras.jaudioTagger) + api("com.github.shabinder:storage-chooser:2.0.4.45") // api(files("$rootDir/libs/mobile-ffmpeg.aar")) } } diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidActual.kt b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidActual.kt index 8ea44d9e..07d4eba7 100644 --- a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidActual.kt +++ b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidActual.kt @@ -28,6 +28,10 @@ import com.shabinder.common.models.AllPlatforms import com.shabinder.common.models.TrackDetails import kotlinx.coroutines.Dispatchers import org.json.JSONObject +import com.codekidlabs.storagechooser.StorageChooser +import com.codekidlabs.storagechooser.StorageChooser.OnSelectListener + + actual fun openPlatform(packageID: String, platformLink: String) { val manager: PackageManager = activityContext.packageManager diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidDir.kt b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidDir.kt index 0e023ad9..a46591b7 100644 --- a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidDir.kt +++ b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidDir.kt @@ -17,6 +17,8 @@ package com.shabinder.common.di import android.content.Context +import android.content.Context.MODE_PRIVATE +import android.content.SharedPreferences import android.graphics.Bitmap import android.graphics.BitmapFactory import android.media.MediaScannerConnection @@ -37,21 +39,33 @@ import java.net.URL actual class Dir actual constructor( private val logger: Kermit, - private val database: Database? + private val database: Database?, ) { - private val scope = CoroutineScope(Dispatchers.IO) + companion object { + const val SharedPreferencesKey = "configurations" + const val DirKey = "downloadDir" + } private val context: Context get() = appContext + private val sharedPreferences:SharedPreferences by lazy { + context.getSharedPreferences(SharedPreferencesKey,MODE_PRIVATE) + } + + fun setDownloadDirectory(newBasePath:String){ + sharedPreferences.edit().putString(DirKey,newBasePath).apply() + } + + @Suppress("DEPRECATION") + private val defaultBaseDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).toString() + actual fun fileSeparator(): String = File.separator actual fun imageCacheDir(): String = context.cacheDir.absolutePath + File.separator - @Suppress("DEPRECATION") - actual fun defaultDir(): String = - Environment.getExternalStorageDirectory().toString() + File.separator + - Environment.DIRECTORY_MUSIC + File.separator + + // fun call in order to always access Updated Value + actual fun defaultDir(): String = sharedPreferences.getString(DirKey,defaultBaseDir)!! + File.separator + "SpotiFlyer" + File.separator actual fun isPresent(path: String): Boolean = File(path).exists() diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Dir.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Dir.kt index a7f0f6e4..ecae893c 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Dir.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Dir.kt @@ -36,7 +36,7 @@ import kotlin.math.roundToInt expect class Dir( logger: Kermit, - database: Database? = createDatabase() + database: Database? = createDatabase(), ) { val db: Database? fun isPresent(path: String): Boolean diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/GaanaProvider.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/GaanaProvider.kt index 9bf63da0..d5da8daa 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/GaanaProvider.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/GaanaProvider.kt @@ -81,10 +81,10 @@ class GaanaProvider( getGaanaAlbum(seokey = link).also { folderType = "Albums" subFolder = link - it.tracks.forEach { track -> + it.tracks?.forEach { track -> track.updateStatusIfPresent(folderType, subFolder) } - trackList = it.tracks.toTrackDetailsList(folderType, subFolder) + trackList = it.tracks?.toTrackDetailsList(folderType, subFolder) ?: emptyList() title = link coverUrl = it.custom_artworks.size_480p } diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/SpotifyProvider.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/SpotifyProvider.kt index 95fd198a..071ba1d7 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/SpotifyProvider.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/providers/SpotifyProvider.kt @@ -68,7 +68,7 @@ class SpotifyProvider( install(JsonFeature) { serializer = kotlinxSerializer } - }?.also { httpClient = it } + }.also { httpClient = it } } } @@ -127,10 +127,7 @@ class SpotifyProvider( it.updateStatusIfPresent(folderType, subFolder) trackList = listOf(it).toTrackDetailsList(folderType, subFolder) title = it.name.toString() - coverUrl = ( - it.album?.images?.elementAtOrNull(1)?.url - ?: it.album?.images?.elementAtOrNull(0)?.url - ).toString() + coverUrl = it.album?.images?.elementAtOrNull(0)?.url.toString() } } @@ -143,22 +140,19 @@ class SpotifyProvider( it.album = Album( images = listOf( Image( - url = albumObject.images?.elementAtOrNull(1)?.url - ?: albumObject.images?.elementAtOrNull(0)?.url + url = albumObject.images?.elementAtOrNull(0)?.url ) ) ) } + albumObject.tracks?.items?.toTrackDetailsList(folderType, subFolder).let { if (it.isNullOrEmpty()) { // TODO Handle Error } else { trackList = it title = albumObject.name.toString() - coverUrl = ( - albumObject.images?.elementAtOrNull(1)?.url - ?: albumObject.images?.elementAtOrNull(0)?.url - ).toString() + coverUrl = albumObject.images?.elementAtOrNull(0)?.url.toString() } } } @@ -189,8 +183,7 @@ class SpotifyProvider( // log("Total Tracks Fetched", tempTrackList.size.toString()) trackList = tempTrackList.toTrackDetailsList(folderType, subFolder) title = playlistObject.name.toString() - coverUrl = playlistObject.images?.elementAtOrNull(1)?.url - ?: playlistObject.images?.firstOrNull()?.url.toString() + coverUrl = playlistObject.images?.firstOrNull()?.url.toString() } "episode" -> { // TODO } @@ -221,14 +214,14 @@ class SpotifyProvider( title = it.name.toString(), artists = it.artists?.map { artist -> artist?.name.toString() } ?: listOf(), durationSec = (it.duration_ms / 1000).toInt(), - albumArtPath = dir.imageCacheDir() + (it.album?.images?.elementAtOrNull(1)?.url ?: it.album?.images?.firstOrNull()?.url.toString()).substringAfterLast('/') + ".jpeg", + albumArtPath = dir.imageCacheDir() + (it.album?.images?.firstOrNull()?.url.toString()).substringAfterLast('/') + ".jpeg", albumName = it.album?.name, year = it.album?.release_date, comment = "Genres:${it.album?.genres?.joinToString()}", trackUrl = it.href, downloaded = it.downloaded, source = Source.Spotify, - albumArtURL = it.album?.images?.elementAtOrNull(1)?.url ?: it.album?.images?.firstOrNull()?.url.toString(), + albumArtURL = it.album?.images?.firstOrNull()?.url.toString(), outputFilePath = dir.finalOutputDir(it.name.toString(), type, subFolder, dir.defaultDir()/*,".m4a"*/) ) } diff --git a/common/root/src/commonMain/kotlin/com/shabinder/common/root/SpotiFlyerRoot.kt b/common/root/src/commonMain/kotlin/com/shabinder/common/root/SpotiFlyerRoot.kt index 7277ef82..9cbd80c3 100644 --- a/common/root/src/commonMain/kotlin/com/shabinder/common/root/SpotiFlyerRoot.kt +++ b/common/root/src/commonMain/kotlin/com/shabinder/common/root/SpotiFlyerRoot.kt @@ -49,6 +49,7 @@ interface SpotiFlyerRoot { val directories: Dir val showPopUpMessage: (String) -> Unit val downloadProgressReport: MutableSharedFlow> + val setDownloadDirectoryAction:()->Unit } } diff --git a/common/root/src/commonMain/kotlin/com/shabinder/common/root/callbacks/SpotiFlyerRootCallBacks.kt b/common/root/src/commonMain/kotlin/com/shabinder/common/root/callbacks/SpotiFlyerRootCallBacks.kt index 5183b950..14fbbef9 100644 --- a/common/root/src/commonMain/kotlin/com/shabinder/common/root/callbacks/SpotiFlyerRootCallBacks.kt +++ b/common/root/src/commonMain/kotlin/com/shabinder/common/root/callbacks/SpotiFlyerRootCallBacks.kt @@ -19,4 +19,5 @@ package com.shabinder.common.root.callbacks interface SpotiFlyerRootCallBacks { fun searchLink(link: String) fun popBackToHomeScreen() + fun setDownloadDirectory() } diff --git a/common/root/src/commonMain/kotlin/com/shabinder/common/root/integration/SpotiFlyerRootImpl.kt b/common/root/src/commonMain/kotlin/com/shabinder/common/root/integration/SpotiFlyerRootImpl.kt index 773b4ae2..30bdf51d 100644 --- a/common/root/src/commonMain/kotlin/com/shabinder/common/root/integration/SpotiFlyerRootImpl.kt +++ b/common/root/src/commonMain/kotlin/com/shabinder/common/root/integration/SpotiFlyerRootImpl.kt @@ -55,6 +55,7 @@ internal class SpotiFlyerRootImpl( it !is Configuration.Main } } + override fun setDownloadDirectory() { setDownloadDirectoryAction() } } private fun createChild(configuration: Configuration, componentContext: ComponentContext): Child =