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 =