mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-12-22 20:57:54 +01:00
Custom Download Directory Func.
This commit is contained in:
parent
289ada76b0
commit
49562de503
@ -35,6 +35,8 @@
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission android:name="android.permission.READ_STORAGE_PERMISSION" />
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
@ -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">
|
||||
|
@ -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<out String>, 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<HashMap<String, DownloadStatus>> = 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)
|
||||
}
|
||||
|
@ -75,4 +75,9 @@ fun Activity.requestStoragePermission() {
|
||||
786
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
fun Activity.requestBroaderStoragePermission() {
|
||||
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
|
||||
startActivity(intent)
|
||||
}*/
|
||||
|
62
android/src/main/res/values/res.xml
Normal file
62
android/src/main/res/values/res.xml
Normal file
@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- <color name="colorPrimary">#2d6c55</color>
|
||||
<color name="colorPrimaryDark">#235644</color>
|
||||
<color name="colorAccent">#ff9c40</color>
|
||||
|
||||
<color name="colorCyanListClick">#d6c8f5f3</color>
|
||||
<color name="colorListDivider">#89606060</color>
|
||||
|
||||
<color name="pathLayoutBgColor">#70ffffff</color>
|
||||
<color name="chevronBgColor">#50ffffff</color>
|
||||
<color name="inactiveGradientColor">#53000000</color>
|
||||
|
||||
<!– dialog colors –>
|
||||
<color name="memory_status_color">#de6565</color>
|
||||
<color name="memory_bar_color">#7bde65</color>
|
||||
<color name="new_folder_color">#c14b84</color>
|
||||
<color name="select_color">#6b3fa1</color>
|
||||
<color name="cancel_color">#3fa19f</color>-->
|
||||
|
||||
<array name="default_light">
|
||||
<!-- Overview -->
|
||||
<item>@color/colorPrimary</item> <!-- Top Header bg -->
|
||||
<item>@android:color/white</item> <!-- header text -->
|
||||
<item>@android:color/white</item> <!-- list bg -->
|
||||
<item>@android:color/black</item> <!-- storage list name text -->
|
||||
<item>@color/colorPrimary</item> <!-- free space text -->
|
||||
<item>@color/colorAccent</item> <!-- memory bar -->
|
||||
|
||||
<!-- secondary dialog colors -->
|
||||
<item>@color/colorPrimary</item> <!-- address bar bg -->
|
||||
<item>@android:color/white</item> <!-- list bg -->
|
||||
<item>@android:color/black</item> <!-- list text -->
|
||||
<item>@android:color/white</item> <!-- address bar tint -->
|
||||
<item>@color/chevronBgColor</item> <!-- new folder hint tint -->
|
||||
<item>#da6c6c</item> <!-- select button color -->
|
||||
<item>#da6c6c</item> <!-- new folder layour bg -->
|
||||
<item>#da6c6c</item> <!-- new folder layour bg -->
|
||||
<item>@color/colorPrimary</item> <!-- new folder layour bg -->
|
||||
</array>
|
||||
|
||||
<array name="default_dark">
|
||||
<!-- Overview -->
|
||||
<item>@color/colorPrimary</item> <!-- Top Header bg -->
|
||||
<item>@android:color/white</item> <!-- header text -->
|
||||
<item>@android:color/black</item> <!-- list bg -->
|
||||
<item>@android:color/white</item> <!-- storage list name text -->
|
||||
<item>#da6c6c</item> <!-- free space text -->
|
||||
<item>@color/colorPrimary</item> <!-- memory bar -->
|
||||
|
||||
<!-- secondary dialog colors -->
|
||||
<item>@color/colorPrimary</item> <!-- address bar bg -->
|
||||
<item>@android:color/black</item> <!-- list bg -->
|
||||
<item>@android:color/white</item> <!-- list text -->
|
||||
<item>@android:color/white</item> <!-- address bar tint -->
|
||||
<item>@color/grey</item> <!-- new folder hint tint -->
|
||||
<item>#da6c6c</item> <!-- select button color -->
|
||||
<item>#da6c6c</item> <!-- new folder layour bg -->
|
||||
<item>#da6c6c</item> <!-- new multi fab -->
|
||||
<item>#da6c6c</item> <!-- new multi fab -->
|
||||
</array>
|
||||
</resources>
|
@ -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
|
||||
)
|
||||
|
@ -20,7 +20,7 @@ import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class GaanaAlbum(
|
||||
val tracks: List<GaanaTrack>,
|
||||
val tracks: List<GaanaTrack>?,
|
||||
val count: Int,
|
||||
val custom_artworks: CustomArtworks,
|
||||
val release_year: Int,
|
||||
|
@ -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"))
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"*/)
|
||||
)
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ interface SpotiFlyerRoot {
|
||||
val directories: Dir
|
||||
val showPopUpMessage: (String) -> Unit
|
||||
val downloadProgressReport: MutableSharedFlow<HashMap<String, DownloadStatus>>
|
||||
val setDownloadDirectoryAction:()->Unit
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,4 +19,5 @@ package com.shabinder.common.root.callbacks
|
||||
interface SpotiFlyerRootCallBacks {
|
||||
fun searchLink(link: String)
|
||||
fun popBackToHomeScreen()
|
||||
fun setDownloadDirectory()
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ internal class SpotiFlyerRootImpl(
|
||||
it !is Configuration.Main
|
||||
}
|
||||
}
|
||||
override fun setDownloadDirectory() { setDownloadDirectoryAction() }
|
||||
}
|
||||
|
||||
private fun createChild(configuration: Configuration, componentContext: ComponentContext): Child =
|
||||
|
Loading…
Reference in New Issue
Block a user