Custom Download Directory Func.

This commit is contained in:
shabinder 2021-04-19 23:54:48 +05:30
parent 289ada76b0
commit 49562de503
15 changed files with 169 additions and 36 deletions

View File

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

View File

@ -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)
}

View File

@ -75,4 +75,9 @@ fun Activity.requestStoragePermission() {
786
)
}
}
}
/*
fun Activity.requestBroaderStoragePermission() {
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
startActivity(intent)
}*/

View 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>
&lt;!&ndash; dialog colors &ndash;&gt;
<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>

View File

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

View File

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

View File

@ -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"))
}
}

View File

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

View File

@ -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()

View File

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

View File

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

View File

@ -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"*/)
)
}

View File

@ -49,6 +49,7 @@ interface SpotiFlyerRoot {
val directories: Dir
val showPopUpMessage: (String) -> Unit
val downloadProgressReport: MutableSharedFlow<HashMap<String, DownloadStatus>>
val setDownloadDirectoryAction:()->Unit
}
}

View File

@ -19,4 +19,5 @@ package com.shabinder.common.root.callbacks
interface SpotiFlyerRootCallBacks {
fun searchLink(link: String)
fun popBackToHomeScreen()
fun setDownloadDirectory()
}

View File

@ -55,6 +55,7 @@ internal class SpotiFlyerRootImpl(
it !is Configuration.Main
}
}
override fun setDownloadDirectory() { setDownloadDirectoryAction() }
}
private fun createChild(configuration: Configuration, componentContext: ComponentContext): Child =