mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-12-22 12:47:54 +01:00
Removed Static Variables, Thorough Code Cleanup, Code Refactoring
This commit is contained in:
parent
9f9a160e88
commit
ff7acc9558
@ -46,6 +46,7 @@ android {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compileSdkVersion(29)
|
||||
buildToolsVersion = "30.0.3"
|
||||
|
||||
@ -104,14 +105,14 @@ dependencies {
|
||||
implementation(Koin.android)
|
||||
implementation(Koin.compose)
|
||||
|
||||
implementation("com.google.accompanist:accompanist-insets:0.7.1")
|
||||
implementation("com.google.accompanist:accompanist-insets:0.8.1")
|
||||
|
||||
// DECOMPOSE
|
||||
implementation(Decompose.decompose)
|
||||
implementation(Decompose.extensionsCompose)
|
||||
|
||||
// Firebase
|
||||
implementation(platform("com.google.firebase:firebase-bom:27.0.0"))
|
||||
implementation(platform("com.google.firebase:firebase-bom:27.1.0"))
|
||||
implementation("com.google.firebase:firebase-analytics-ktx")
|
||||
implementation("com.google.firebase:firebase-crashlytics-ktx")
|
||||
implementation("com.google.firebase:firebase-perf-ktx")
|
||||
|
@ -17,7 +17,6 @@
|
||||
package com.shabinder.spotiflyer
|
||||
|
||||
import android.app.Application
|
||||
import com.shabinder.common.database.appContext
|
||||
import com.shabinder.common.di.initKoin
|
||||
import com.shabinder.spotiflyer.di.appModule
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
@ -29,7 +28,6 @@ class App: Application(), KoinComponent {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
appContext = this
|
||||
val loggingEnabled = true
|
||||
|
||||
initKoin(loggingEnabled) {
|
||||
|
@ -17,10 +17,14 @@
|
||||
package com.shabinder.spotiflyer
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.media.MediaScannerConnection
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.PowerManager
|
||||
@ -31,18 +35,10 @@ import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.SdStorage
|
||||
import androidx.compose.material.icons.rounded.SystemSecurityUpdate
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
@ -57,21 +53,27 @@ import com.google.accompanist.insets.statusBarsHeight
|
||||
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.di.worker.ForegroundService
|
||||
import com.shabinder.common.models.Actions
|
||||
import com.shabinder.common.models.AllPlatforms
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import com.shabinder.common.models.PlatformActions
|
||||
import com.shabinder.common.models.PlatformActions.Companion.SharedPreferencesKey
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.common.root.SpotiFlyerRoot
|
||||
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
|
||||
import com.shabinder.common.uikit.*
|
||||
import com.shabinder.spotiflyer.utils.*
|
||||
import com.shabinder.common.models.Status
|
||||
import com.shabinder.common.models.methods
|
||||
import com.shabinder.spotiflyer.ui.NetworkDialog
|
||||
import com.shabinder.spotiflyer.ui.PermissionDialog
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.json.JSONObject
|
||||
import org.koin.android.ext.android.inject
|
||||
import java.io.File
|
||||
import com.shabinder.common.uikit.showPopUpMessage as uikitShowPopUpMessage
|
||||
|
||||
const val disableDozeCode = 1223
|
||||
|
||||
@ -87,6 +89,7 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
||||
private var permissionGranted = mutableStateOf(true)
|
||||
private lateinit var updateUIReceiver: BroadcastReceiver
|
||||
private lateinit var queryReceiver: BroadcastReceiver
|
||||
private val internetAvailability by lazy { ConnectionLiveData(applicationContext) }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -116,8 +119,12 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
||||
LaunchedEffect(view) {
|
||||
permissionGranted.value = checkPermissions()
|
||||
}
|
||||
NetworkDialog()
|
||||
PermissionDialog()
|
||||
NetworkDialog(isInternetAvailableState())
|
||||
PermissionDialog(
|
||||
permissionGranted.value,
|
||||
{ requestStoragePermission() },
|
||||
{ disableDozeMode(disableDozeCode) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -127,10 +134,13 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
||||
|
||||
private fun initialise() {
|
||||
checkIfLatestVersion()
|
||||
dir.createDirectories()
|
||||
Checkout.preload(applicationContext)
|
||||
handleIntentFromExternalActivity()
|
||||
Log.i("Download Path",dir.defaultDir())
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun isInternetAvailableState(): State<Boolean?> {
|
||||
return internetAvailability.observeAsState()
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@ -140,7 +150,7 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
||||
.withActivity(this)
|
||||
.withFragmentManager(fragmentManager)
|
||||
.withMemoryBar(true)
|
||||
.setTheme(StorageChooser.Theme(appContext).apply {
|
||||
.setTheme(StorageChooser.Theme(applicationContext).apply {
|
||||
scheme = applicationContext.resources.getIntArray(R.array.default_dark)
|
||||
})
|
||||
.setDialogTitle("Set Download Directory")
|
||||
@ -155,11 +165,11 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
||||
if (f.canWrite()) {
|
||||
// hell yeah :)
|
||||
dir.setDownloadDirectory(path)
|
||||
com.shabinder.common.uikit.showPopUpMessage(
|
||||
showPopUpMessage(
|
||||
"Download Directory Set to:\n${dir.defaultDir()} "
|
||||
)
|
||||
}else{
|
||||
com.shabinder.common.uikit.showPopUpMessage(
|
||||
showPopUpMessage(
|
||||
"NO WRITE ACCESS on \n$path ,\nReverting Back to Previous"
|
||||
)
|
||||
}
|
||||
@ -169,6 +179,14 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
||||
chooser.show()
|
||||
}
|
||||
|
||||
private fun showPopUpMessage(string: String, long: Boolean = false) {
|
||||
android.widget.Toast.makeText(
|
||||
applicationContext,
|
||||
string,
|
||||
if(long) android.widget.Toast.LENGTH_LONG else android.widget.Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
permissionGranted.value = checkPermissions()
|
||||
@ -182,9 +200,74 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
||||
override val database = this@MainActivity.dir.db
|
||||
override val fetchPlatformQueryResult = this@MainActivity.fetcher
|
||||
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
|
||||
override val actions = object: Actions {
|
||||
|
||||
override val platformActions = object : PlatformActions {
|
||||
override val imageCacheDir: String = applicationContext.cacheDir.absolutePath + File.separator
|
||||
override val sharedPreferences = applicationContext.getSharedPreferences(SharedPreferencesKey,
|
||||
MODE_PRIVATE
|
||||
)
|
||||
|
||||
override fun addToLibrary(path: String) {
|
||||
MediaScannerConnection.scanFile(
|
||||
applicationContext,
|
||||
listOf(path).toTypedArray(), null, null
|
||||
)
|
||||
}
|
||||
|
||||
override fun sendTracksToService(array: ArrayList<TrackDetails>) {
|
||||
for (list in array.chunked(50)){
|
||||
val serviceIntent = Intent(this@MainActivity, ForegroundService::class.java)
|
||||
serviceIntent.putParcelableArrayListExtra("object", list as ArrayList)
|
||||
ContextCompat.startForegroundService(this@MainActivity, serviceIntent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun showPopUpMessage(string: String, long: Boolean) = this@MainActivity.showPopUpMessage(string,long)
|
||||
|
||||
override fun setDownloadDirectoryAction() = setUpOnPrefClickListener()
|
||||
|
||||
override fun queryActiveTracks() {
|
||||
val serviceIntent = Intent(this@MainActivity, ForegroundService::class.java).apply {
|
||||
action = "query"
|
||||
}
|
||||
ContextCompat.startForegroundService(this@MainActivity, serviceIntent)
|
||||
}
|
||||
|
||||
override fun giveDonation() = startPayment(this@MainActivity)
|
||||
|
||||
override fun shareApp() {
|
||||
val sendIntent: Intent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
putExtra(Intent.EXTRA_TEXT, "Hey, checkout this excellent Music Downloader http://github.com/Shabinder/SpotiFlyer")
|
||||
type = "text/plain"
|
||||
}
|
||||
|
||||
val shareIntent = Intent.createChooser(sendIntent, null)
|
||||
startActivity(shareIntent)
|
||||
}
|
||||
|
||||
override fun openPlatform(packageID: String, platformLink: String) {
|
||||
val manager: PackageManager = applicationContext.packageManager
|
||||
try {
|
||||
val intent = manager.getLaunchIntentForPackage(packageID)
|
||||
?: throw PackageManager.NameNotFoundException()
|
||||
intent.addCategory(Intent.CATEGORY_LAUNCHER)
|
||||
startActivity(intent)
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
val uri: Uri =
|
||||
Uri.parse(platformLink)
|
||||
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
override val isInternetAvailable get() = internetAvailability.value ?: true
|
||||
override val dispatcherIO = Dispatchers.IO
|
||||
override val currentPlatform = AllPlatforms.Jvm
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@ -208,6 +291,9 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Broadcast Handlers
|
||||
* */
|
||||
private fun initializeBroadcast(){
|
||||
val intentFilter = IntentFilter().apply {
|
||||
addAction(Status.QUEUED.name)
|
||||
@ -292,7 +378,7 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
||||
while(!this@MainActivity::root.isInitialized){
|
||||
delay(100)
|
||||
}
|
||||
if(isInternetAvailable)callBacks.searchLink(link)
|
||||
if(methods.isInternetAvailable)callBacks.searchLink(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -301,7 +387,7 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
||||
|
||||
override fun onPaymentError(errorCode: Int, response: String?) {
|
||||
try{
|
||||
uikitShowPopUpMessage("Payment Failed, Response:$response")
|
||||
showPopUpMessage("Payment Failed, Response:$response")
|
||||
}catch (e: Exception){
|
||||
Log.d("Razorpay Payment","Exception in onPaymentSuccess $response")
|
||||
}
|
||||
@ -309,82 +395,39 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
||||
|
||||
override fun onPaymentSuccess(razorpayPaymentId: String?) {
|
||||
try{
|
||||
uikitShowPopUpMessage("Payment Successful, ThankYou!")
|
||||
showPopUpMessage("Payment Successful, ThankYou!")
|
||||
}catch (e: Exception){
|
||||
uikitShowPopUpMessage("Razorpay Payment, Error Occurred.")
|
||||
showPopUpMessage("Razorpay Payment, Error Occurred.")
|
||||
Log.d("Razorpay Payment","Exception in onPaymentSuccess, ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PermissionDialog(){
|
||||
var askForPermission by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(Unit){
|
||||
delay(2000)
|
||||
askForPermission = true
|
||||
/*
|
||||
* RazorPay Payment
|
||||
* */
|
||||
private fun startPayment(mainActivity: Activity) {
|
||||
val co = Checkout().apply {
|
||||
setKeyID("rzp_live_3ZQeoFYOxjmXye")
|
||||
setImage(com.shabinder.common.di.R.drawable.ic_spotiflyer_logo)
|
||||
}
|
||||
AnimatedVisibility(
|
||||
askForPermission && !permissionGranted.value
|
||||
){
|
||||
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),
|
||||
){
|
||||
Text("Grant Permissions",color = Color.Black,fontSize = 18.sp,textAlign = TextAlign.Center)
|
||||
}
|
||||
},title = {Text("Required Permissions:",style = SpotiFlyerTypography.h5,textAlign = TextAlign.Center)},
|
||||
backgroundColor = Color.DarkGray,
|
||||
text = {
|
||||
Column{
|
||||
Spacer(modifier = Modifier.padding(8.dp))
|
||||
Row(verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp)
|
||||
) {
|
||||
Icon(Icons.Rounded.SdStorage,"Storage Permission.")
|
||||
Spacer(modifier = Modifier.padding(start = 16.dp))
|
||||
Column {
|
||||
Text(
|
||||
text = "Storage Permission.",
|
||||
style = SpotiFlyerTypography.h6.copy(fontWeight = FontWeight.SemiBold)
|
||||
)
|
||||
Text(
|
||||
text = "To download your favourite songs to this device.",
|
||||
style = SpotiFlyerTypography.subtitle2,
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 6.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(Icons.Rounded.SystemSecurityUpdate,"Allow Background Running")
|
||||
Spacer(modifier = Modifier.padding(start = 16.dp))
|
||||
Column {
|
||||
Text(
|
||||
text = "Background Running.",
|
||||
style = SpotiFlyerTypography.h6.copy(fontWeight = FontWeight.SemiBold)
|
||||
)
|
||||
Text(
|
||||
text = "To download all songs in background without any System Interruptions",
|
||||
style = SpotiFlyerTypography.subtitle2,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
activityContext = this
|
||||
try {
|
||||
val preFill = JSONObject()
|
||||
|
||||
val options = JSONObject().apply {
|
||||
put("name", "SpotiFlyer")
|
||||
put("description", "Thanks For the Donation!")
|
||||
// You can omit the image option to fetch the image from dashboard
|
||||
// put("image","https://github.com/Shabinder/SpotiFlyer/raw/master/app/SpotifyDownload.png")
|
||||
put("currency", "INR")
|
||||
put("amount", "4900")
|
||||
put("prefill", preFill)
|
||||
}
|
||||
|
||||
co.open(mainActivity, options)
|
||||
} catch (e: Exception) {
|
||||
// showPop("Error in payment: "+ e.message)
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
* * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.shabinder.spotiflyer.utils
|
||||
package com.shabinder.spotiflyer.ui
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.Image
|
||||
@ -30,7 +30,6 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.shabinder.common.di.isInternetAvailableState
|
||||
import com.shabinder.common.uikit.SpotiFlyerShapes
|
||||
import com.shabinder.common.uikit.SpotiFlyerTypography
|
||||
import com.shabinder.common.uikit.colorOffWhite
|
||||
@ -39,7 +38,7 @@ import kotlinx.coroutines.delay
|
||||
@ExperimentalAnimationApi
|
||||
@Composable
|
||||
fun NetworkDialog(
|
||||
networkAvailability: State<Boolean?> = isInternetAvailableState()
|
||||
networkAvailability: State<Boolean?>
|
||||
){
|
||||
var visible by remember { mutableStateOf(false) }
|
||||
|
@ -0,0 +1,107 @@
|
||||
package com.shabinder.spotiflyer.ui
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.AlertDialog
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextButton
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.SdStorage
|
||||
import androidx.compose.material.icons.rounded.SystemSecurityUpdate
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.shabinder.common.uikit.SpotiFlyerShapes
|
||||
import com.shabinder.common.uikit.SpotiFlyerTypography
|
||||
import com.shabinder.common.uikit.colorPrimary
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@Composable
|
||||
fun PermissionDialog(
|
||||
permissionGranted: Boolean,
|
||||
requestStoragePermission:() -> Unit,
|
||||
disableDozeMode:() -> Unit
|
||||
){
|
||||
var askForPermission by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(Unit) {
|
||||
delay(2000)
|
||||
askForPermission = true
|
||||
}
|
||||
AnimatedVisibility(
|
||||
askForPermission && !permissionGranted
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = {},
|
||||
buttons = {
|
||||
TextButton(
|
||||
{
|
||||
requestStoragePermission()
|
||||
disableDozeMode()
|
||||
},
|
||||
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)
|
||||
}
|
||||
},title = { Text("Required Permissions:",style = SpotiFlyerTypography.h5,textAlign = TextAlign.Center) },
|
||||
backgroundColor = Color.DarkGray,
|
||||
text = {
|
||||
Column{
|
||||
Spacer(modifier = Modifier.padding(8.dp))
|
||||
Row(verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp)
|
||||
) {
|
||||
Icon(Icons.Rounded.SdStorage,"Storage Permission.")
|
||||
Spacer(modifier = Modifier.padding(start = 16.dp))
|
||||
Column {
|
||||
Text(
|
||||
text = "Storage Permission.",
|
||||
style = SpotiFlyerTypography.h6.copy(fontWeight = FontWeight.SemiBold)
|
||||
)
|
||||
Text(
|
||||
text = "To download your favourite songs to this device.",
|
||||
style = SpotiFlyerTypography.subtitle2,
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 6.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(Icons.Rounded.SystemSecurityUpdate,"Allow Background Running")
|
||||
Spacer(modifier = Modifier.padding(start = 16.dp))
|
||||
Column {
|
||||
Text(
|
||||
text = "Background Running.",
|
||||
style = SpotiFlyerTypography.h6.copy(fontWeight = FontWeight.SemiBold)
|
||||
)
|
||||
Text(
|
||||
text = "To download all songs in background without any System Interruptions",
|
||||
style = SpotiFlyerTypography.subtitle2,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
@ -36,11 +36,12 @@ object Versions {
|
||||
// Internet
|
||||
const val ktor = "1.5.3"
|
||||
|
||||
const val kotlinxSerialization = "1.1.0"
|
||||
// Database
|
||||
const val sqlDelight = "1.4.4"
|
||||
const val kotlinxSerialization = "1.2.0"
|
||||
|
||||
const val sqliteJdbcDriver = "3.30.1"
|
||||
// Database
|
||||
const val sqlDelight = "1.5.0"
|
||||
|
||||
const val sqliteJdbcDriver = "3.34.0"
|
||||
const val slf4j = "1.7.30"
|
||||
|
||||
// Android
|
||||
|
@ -35,9 +35,8 @@ import androidx.compose.ui.text.font.Font
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import com.shabinder.common.database.R
|
||||
import com.shabinder.common.database.appContext
|
||||
import com.shabinder.common.di.Picture
|
||||
import com.shabinder.common.di.dispatcherIO
|
||||
import com.shabinder.common.models.methods
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@Composable
|
||||
@ -51,7 +50,7 @@ actual fun ImageLoad(
|
||||
var pic by remember(link) { mutableStateOf<ImageBitmap?>(null) }
|
||||
|
||||
LaunchedEffect(link) {
|
||||
withContext(dispatcherIO) {
|
||||
withContext(methods.dispatcherIO) {
|
||||
pic = loader(link).image
|
||||
}
|
||||
}
|
||||
@ -137,7 +136,3 @@ actual fun Toast(
|
||||
) {
|
||||
// We Have Android's Implementation of Toast so its just Empty
|
||||
}
|
||||
|
||||
actual fun showPopUpMessage(text: String) {
|
||||
android.widget.Toast.makeText(appContext, text, android.widget.Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ import com.shabinder.common.di.Picture
|
||||
import com.shabinder.common.list.SpotiFlyerList
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.common.models.methods
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@Composable
|
||||
@ -62,7 +63,7 @@ fun SpotiFlyerListContent(
|
||||
LaunchedEffect(model.errorOccurred) {
|
||||
/*Handle if Any Exception Occurred*/
|
||||
model.errorOccurred?.let {
|
||||
showPopUpMessage(it.message ?: "An Error Occurred, Check your Link / Connection")
|
||||
methods.showPopUpMessage(it.message ?: "An Error Occurred, Check your Link / Connection")
|
||||
component.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
@ -73,12 +73,10 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.shabinder.common.di.Picture
|
||||
import com.shabinder.common.di.giveDonation
|
||||
import com.shabinder.common.di.openPlatform
|
||||
import com.shabinder.common.di.shareApp
|
||||
import com.shabinder.common.main.SpotiFlyerMain
|
||||
import com.shabinder.common.main.SpotiFlyerMain.HomeCategory
|
||||
import com.shabinder.common.models.DownloadRecord
|
||||
import com.shabinder.common.models.methods
|
||||
|
||||
@Composable
|
||||
fun SpotiFlyerMainContent(component: SpotiFlyerMain) {
|
||||
@ -195,7 +193,7 @@ fun SearchPanel(
|
||||
OutlinedButton(
|
||||
modifier = Modifier.padding(12.dp).wrapContentWidth(),
|
||||
onClick = {
|
||||
if (link.isBlank()) showPopUpMessage("Enter A Link!")
|
||||
if (link.isBlank()) methods.showPopUpMessage("Enter A Link!")
|
||||
else {
|
||||
// TODO if(!isOnline(ctx)) showPopUpMessage("Check Your Internet Connection") else
|
||||
onSearch(link)
|
||||
@ -237,7 +235,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
||||
"Open Spotify",
|
||||
tint = Color.Unspecified,
|
||||
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
||||
onClick = { openPlatform("com.spotify.music", "http://open.spotify.com") }
|
||||
onClick = { methods.openPlatform("com.spotify.music", "http://open.spotify.com") }
|
||||
)
|
||||
)
|
||||
Spacer(modifier = modifier.padding(start = 16.dp))
|
||||
@ -246,7 +244,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
||||
"Open Gaana",
|
||||
tint = Color.Unspecified,
|
||||
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
||||
onClick = { openPlatform("com.gaana", "http://gaana.com") }
|
||||
onClick = { methods.openPlatform("com.gaana", "http://gaana.com") }
|
||||
)
|
||||
)
|
||||
Spacer(modifier = modifier.padding(start = 16.dp))
|
||||
@ -255,7 +253,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
||||
"Open Youtube",
|
||||
tint = Color.Unspecified,
|
||||
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
||||
onClick = { openPlatform("com.google.android.youtube", "http://m.youtube.com") }
|
||||
onClick = { methods.openPlatform("com.google.android.youtube", "http://m.youtube.com") }
|
||||
)
|
||||
)
|
||||
Spacer(modifier = modifier.padding(start = 12.dp))
|
||||
@ -264,7 +262,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
||||
"Open Youtube Music",
|
||||
tint = Color.Unspecified,
|
||||
modifier = Modifier.clip(SpotiFlyerShapes.small).clickable(
|
||||
onClick = { openPlatform("com.google.android.apps.youtube.music", "https://music.youtube.com/") }
|
||||
onClick = { methods.openPlatform("com.google.android.apps.youtube.music", "https://music.youtube.com/") }
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -285,7 +283,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth().clickable(
|
||||
onClick = { openPlatform("", "http://github.com/Shabinder/SpotiFlyer") }
|
||||
onClick = { methods.openPlatform("", "http://github.com/Shabinder/SpotiFlyer") }
|
||||
)
|
||||
.padding(vertical = 6.dp)
|
||||
) {
|
||||
@ -304,7 +302,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
||||
}
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth().padding(vertical = 6.dp)
|
||||
.clickable(onClick = { openPlatform("", "http://github.com/Shabinder/SpotiFlyer") }),
|
||||
.clickable(onClick = { methods.openPlatform("", "http://github.com/Shabinder/SpotiFlyer") }),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(Icons.Rounded.Flag, "Help Translate", Modifier.size(32.dp))
|
||||
@ -322,7 +320,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
||||
}
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth().padding(vertical = 6.dp)
|
||||
.clickable(onClick = { giveDonation() }),
|
||||
.clickable(onClick = { methods.giveDonation() }),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(Icons.Rounded.CardGiftcard, "Support Developer")
|
||||
@ -342,7 +340,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
||||
modifier = modifier.fillMaxWidth().padding(vertical = 6.dp)
|
||||
.clickable(
|
||||
onClick = {
|
||||
shareApp()
|
||||
methods.shareApp()
|
||||
}
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
|
@ -24,8 +24,6 @@ enum class ToastDuration(val value: Int) {
|
||||
Short(1000), Long(3000)
|
||||
}
|
||||
|
||||
expect fun showPopUpMessage(text: String)
|
||||
|
||||
@Composable
|
||||
expect fun Toast(
|
||||
text: String,
|
||||
|
@ -28,8 +28,6 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.VectorPainter
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.vectorXmlResource
|
||||
@ -37,7 +35,7 @@ import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.platform.Font
|
||||
import com.shabinder.common.di.Picture
|
||||
import com.shabinder.common.di.dispatcherIO
|
||||
import com.shabinder.common.models.methods
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@Composable
|
||||
@ -50,7 +48,7 @@ actual fun ImageLoad(
|
||||
) {
|
||||
var pic by remember(link) { mutableStateOf<ImageBitmap?>(null) }
|
||||
LaunchedEffect(link) {
|
||||
withContext(dispatcherIO) {
|
||||
withContext(methods.dispatcherIO) {
|
||||
pic = loader(link).image
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ import kotlinx.coroutines.launch
|
||||
private val message: MutableState<String> = mutableStateOf("")
|
||||
private val state: MutableState<Boolean> = mutableStateOf(false)
|
||||
|
||||
actual fun showPopUpMessage(text: String) {
|
||||
fun showToast(text: String) {
|
||||
message.value = text
|
||||
state.value = true
|
||||
}
|
||||
|
@ -25,10 +25,15 @@ kotlin {
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
api("dev.icerock.moko:parcelize:0.6.0")
|
||||
api("dev.icerock.moko:parcelize:0.6.1")
|
||||
api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0")
|
||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3-native-mt")
|
||||
}
|
||||
}
|
||||
if(HostOS.isMac){
|
||||
val iosMain by getting {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,18 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
import android.content.SharedPreferences
|
||||
|
||||
actual interface PlatformActions {
|
||||
|
||||
companion object {
|
||||
const val SharedPreferencesKey = "configurations"
|
||||
}
|
||||
|
||||
val imageCacheDir: String
|
||||
|
||||
val sharedPreferences: SharedPreferences
|
||||
|
||||
fun addToLibrary(path: String)
|
||||
|
||||
fun sendTracksToService(array: ArrayList<TrackDetails>)
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
|
||||
/*
|
||||
* Holder to call platform actions from anywhere
|
||||
* */
|
||||
lateinit var methods: Actions
|
||||
|
||||
/*
|
||||
* Interface Having All Platform Dependent Functions
|
||||
* */
|
||||
interface Actions {
|
||||
|
||||
// Platform Specific Actions
|
||||
val platformActions: PlatformActions
|
||||
|
||||
// Show Toast
|
||||
fun showPopUpMessage(string: String, long: Boolean = false)
|
||||
|
||||
// Change Download Directory
|
||||
fun setDownloadDirectoryAction()
|
||||
|
||||
/*
|
||||
* Query Downloading Tracks
|
||||
* ex- Get Tracks from android service etc
|
||||
* */
|
||||
fun queryActiveTracks()
|
||||
|
||||
// Donate Money
|
||||
fun giveDonation()
|
||||
|
||||
// Share SpotiFlyer App
|
||||
fun shareApp()
|
||||
|
||||
// Open / Redirect to another Platform
|
||||
fun openPlatform(packageID: String, platformLink: String)
|
||||
|
||||
// IO-Dispatcher
|
||||
val dispatcherIO: CoroutineDispatcher
|
||||
|
||||
// Internet Connectivity Check
|
||||
val isInternetAvailable: Boolean
|
||||
|
||||
// Current Platform Info
|
||||
val currentPlatform: AllPlatforms
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Shabinder Singh
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.shabinder.common.models
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Optional<T>(val value: T?)
|
@ -0,0 +1,3 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
expect interface PlatformActions
|
@ -0,0 +1,4 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
actual interface PlatformActions {
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
package com.shabinder.common.models
|
||||
|
||||
actual interface PlatformActions {
|
||||
}
|
@ -35,6 +35,9 @@ kotlin {
|
||||
implementation(SqlDelight.runtime)
|
||||
implementation(SqlDelight.coroutineExtensions)
|
||||
api(Extras.kermit)
|
||||
// koin
|
||||
api(Koin.core)
|
||||
api(Koin.test)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,24 +16,17 @@
|
||||
|
||||
package com.shabinder.common.database
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import co.touchlab.kermit.LogcatLogger
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.shabinder.database.Database
|
||||
import com.squareup.sqldelight.android.AndroidSqliteDriver
|
||||
|
||||
lateinit var appContext: Context
|
||||
|
||||
/*
|
||||
* As MainActivity is God Activity , hence its active almost throughout App's lifetime
|
||||
* */
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
lateinit var activityContext: Context
|
||||
import org.koin.dsl.module
|
||||
|
||||
@Suppress("RedundantNullableReturnType")
|
||||
actual fun createDatabase(): Database? {
|
||||
val driver = AndroidSqliteDriver(Database.Schema, appContext, "Database.db")
|
||||
return Database(driver)
|
||||
actual fun databaseModule() = module {
|
||||
single {
|
||||
val driver = AndroidSqliteDriver(Database.Schema, get(), "Database.db")
|
||||
SpotiFlyerDatabase(Database(driver))
|
||||
}
|
||||
}
|
||||
actual fun getLogger(): Logger = LogcatLogger()
|
||||
|
@ -17,7 +17,7 @@
|
||||
package com.shabinder.common.database
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.shabinder.database.Database
|
||||
import org.koin.core.module.Module
|
||||
|
||||
expect fun createDatabase(): Database?
|
||||
expect fun databaseModule(): Module
|
||||
expect fun getLogger(): Logger
|
||||
|
@ -0,0 +1,6 @@
|
||||
package com.shabinder.common.database
|
||||
|
||||
import com.shabinder.database.Database
|
||||
|
||||
/* Database Wrapper */
|
||||
class SpotiFlyerDatabase(val instance: Database?)
|
@ -20,13 +20,17 @@ import co.touchlab.kermit.CommonLogger
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.shabinder.database.Database
|
||||
import com.squareup.sqldelight.sqlite.driver.JdbcSqliteDriver
|
||||
import org.koin.dsl.module
|
||||
import java.io.File
|
||||
|
||||
@Suppress("RedundantNullableReturnType")
|
||||
actual fun createDatabase(): Database? {
|
||||
val databasePath = File(System.getProperty("java.io.tmpdir"), "Database.db")
|
||||
val driver = JdbcSqliteDriver(url = "jdbc:sqlite:${databasePath.absolutePath}")
|
||||
.also { Database.Schema.create(it) }
|
||||
return Database(driver)
|
||||
actual fun databaseModule() = module {
|
||||
single {
|
||||
val databasePath = File(System.getProperty("user.home") + File.separator + "SpotiFlyer" + File.separator + "Database", "Database.db")
|
||||
databasePath.parentFile.mkdirs()
|
||||
val driver = JdbcSqliteDriver(url = "jdbc:sqlite:${databasePath.absolutePath}")
|
||||
.also { Database.Schema.create(it) }
|
||||
SpotiFlyerDatabase(Database(driver))
|
||||
}
|
||||
}
|
||||
actual fun getLogger(): Logger = CommonLogger()
|
||||
|
@ -3,12 +3,15 @@ package com.shabinder.common.database
|
||||
import co.touchlab.kermit.Logger
|
||||
import co.touchlab.kermit.NSLogLogger
|
||||
import com.shabinder.database.Database
|
||||
import org.koin.dsl.module
|
||||
import com.squareup.sqldelight.drivers.native.NativeSqliteDriver
|
||||
|
||||
@Suppress("RedundantNullableReturnType")
|
||||
actual fun createDatabase(): Database? {
|
||||
val driver = NativeSqliteDriver(Database.Schema, "Database.db")
|
||||
return Database(driver)
|
||||
actual fun databaseModule(): Module {
|
||||
single {
|
||||
val driver = NativeSqliteDriver(Database.Schema, "Database.db")
|
||||
SpotiFlyerDatabase(Database(driver))
|
||||
}
|
||||
}
|
||||
|
||||
actual fun getLogger(): Logger = NSLogLogger()
|
@ -19,6 +19,7 @@ package com.shabinder.common.database
|
||||
import co.touchlab.kermit.CommonLogger
|
||||
import co.touchlab.kermit.Logger
|
||||
import com.shabinder.database.Database
|
||||
import org.koin.dsl.module
|
||||
|
||||
actual fun createDatabase(): Database? = null
|
||||
actual fun databaseModule() = module { single { SpotiFlyerDatabase(null) } }
|
||||
actual fun getLogger(): Logger = CommonLogger()
|
||||
|
@ -52,24 +52,21 @@ kotlin {
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.2.0")
|
||||
implementation("com.shabinder.fuzzywuzzy:fuzzywuzzy:1.0")
|
||||
implementation(Ktor.clientCore)
|
||||
implementation(Ktor.clientSerialization)
|
||||
implementation(Ktor.clientLogging)
|
||||
implementation(Ktor.clientJson)
|
||||
implementation(Ktor.auth)
|
||||
api(Ktor.clientCore)
|
||||
api(Ktor.clientSerialization)
|
||||
api(Ktor.clientLogging)
|
||||
api(Ktor.clientJson)
|
||||
api(Ktor.auth)
|
||||
api(Extras.youtubeDownloader)
|
||||
// koin
|
||||
api(Koin.core)
|
||||
api(Koin.test)
|
||||
api(Extras.kermit)
|
||||
}
|
||||
}
|
||||
androidMain {
|
||||
dependencies {
|
||||
implementation(compose.materialIconsExtended)
|
||||
implementation(Koin.android)
|
||||
implementation(Ktor.clientAndroid)
|
||||
implementation(Extras.Android.razorpay)
|
||||
api(Koin.android)
|
||||
api(Ktor.clientAndroid)
|
||||
api(Extras.Android.razorpay)
|
||||
api(Extras.mp3agic)
|
||||
api(Extras.jaudioTagger)
|
||||
api("com.github.shabinder:storage-chooser:2.0.4.45")
|
||||
@ -79,18 +76,18 @@ kotlin {
|
||||
desktopMain {
|
||||
dependencies {
|
||||
implementation(compose.materialIconsExtended)
|
||||
implementation(Ktor.clientApache)
|
||||
implementation(Ktor.slf4j)
|
||||
api(Ktor.clientApache)
|
||||
api(Ktor.slf4j)
|
||||
api(Extras.mp3agic)
|
||||
api(Extras.jaudioTagger)
|
||||
}
|
||||
}
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation(project(":common:data-models"))
|
||||
implementation(Ktor.clientJs)
|
||||
implementation(npm("browser-id3-writer", "4.4.0"))
|
||||
implementation(npm("file-saver", "2.0.4"))
|
||||
implementation(project(":common:data-models"))
|
||||
api(Ktor.clientJs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,90 +16,8 @@
|
||||
|
||||
package com.shabinder.common.di
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.razorpay.Checkout
|
||||
import com.shabinder.common.database.activityContext
|
||||
import com.shabinder.common.di.worker.ForegroundService
|
||||
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
|
||||
try {
|
||||
val intent = manager.getLaunchIntentForPackage(packageID)
|
||||
?: throw PackageManager.NameNotFoundException()
|
||||
intent.addCategory(Intent.CATEGORY_LAUNCHER)
|
||||
activityContext.startActivity(intent)
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
val uri: Uri =
|
||||
Uri.parse(platformLink)
|
||||
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||
activityContext.startActivity(intent)
|
||||
}
|
||||
}
|
||||
actual val dispatcherIO = Dispatchers.IO
|
||||
actual val currentPlatform: AllPlatforms = AllPlatforms.Jvm
|
||||
|
||||
actual val isInternetAvailable: Boolean
|
||||
get() = internetAvailability.value ?: true
|
||||
|
||||
actual fun shareApp() {
|
||||
val sendIntent: Intent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
putExtra(Intent.EXTRA_TEXT, "Hey, checkout this excellent Music Downloader http://github.com/Shabinder/SpotiFlyer")
|
||||
type = "text/plain"
|
||||
}
|
||||
|
||||
val shareIntent = Intent.createChooser(sendIntent, null)
|
||||
activityContext.startActivity(shareIntent)
|
||||
}
|
||||
|
||||
actual fun giveDonation() = startPayment()
|
||||
|
||||
private fun startPayment(mainActivity: Activity = activityContext as Activity) {
|
||||
/*
|
||||
* You need to pass current activity in order to let Razorpay create CheckoutActivity
|
||||
* */
|
||||
val co = Checkout().apply {
|
||||
setKeyID("rzp_live_3ZQeoFYOxjmXye")
|
||||
setImage(R.drawable.ic_spotiflyer_logo)
|
||||
}
|
||||
|
||||
try {
|
||||
val preFill = JSONObject()
|
||||
|
||||
val options = JSONObject().apply {
|
||||
put("name", "SpotiFlyer")
|
||||
put("description", "Thanks For the Donation!")
|
||||
// You can omit the image option to fetch the image from dashboard
|
||||
// put("image","https://github.com/Shabinder/SpotiFlyer/raw/master/app/SpotifyDownload.png")
|
||||
put("currency", "INR")
|
||||
put("amount", "4900")
|
||||
put("prefill", preFill)
|
||||
}
|
||||
|
||||
co.open(mainActivity, options)
|
||||
} catch (e: Exception) {
|
||||
// showPop("Error in payment: "+ e.message)
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
actual fun queryActiveTracks() {
|
||||
val serviceIntent = Intent(activityContext, ForegroundService::class.java).apply {
|
||||
action = "query"
|
||||
}
|
||||
ContextCompat.startForegroundService(activityContext, serviceIntent)
|
||||
}
|
||||
import com.shabinder.common.models.methods
|
||||
|
||||
actual suspend fun downloadTracks(
|
||||
list: List<TrackDetails>,
|
||||
@ -107,8 +25,6 @@ actual suspend fun downloadTracks(
|
||||
dir: Dir
|
||||
) {
|
||||
if (!list.isNullOrEmpty()) {
|
||||
val serviceIntent = Intent(activityContext, ForegroundService::class.java)
|
||||
serviceIntent.putParcelableArrayListExtra("object", ArrayList<TrackDetails>(list))
|
||||
activityContext.let { ContextCompat.startForegroundService(it, serviceIntent) }
|
||||
methods.platformActions.sendTracksToService(ArrayList(list))
|
||||
}
|
||||
}
|
@ -16,19 +16,16 @@
|
||||
|
||||
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
|
||||
import android.os.Environment
|
||||
import android.widget.Toast
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import co.touchlab.kermit.Kermit
|
||||
import com.mpatric.mp3agic.Mp3File
|
||||
import com.shabinder.common.database.appContext
|
||||
import com.shabinder.common.database.SpotiFlyerDatabase
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.common.models.methods
|
||||
import com.shabinder.database.Database
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.File
|
||||
@ -40,19 +37,13 @@ import java.net.URL
|
||||
|
||||
actual class Dir actual constructor(
|
||||
private val logger: Kermit,
|
||||
private val database: Database?,
|
||||
private val spotiFlyerDatabase: SpotiFlyerDatabase,
|
||||
) {
|
||||
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)
|
||||
}
|
||||
private val sharedPreferences:SharedPreferences by lazy { methods.platformActions.sharedPreferences }
|
||||
|
||||
fun setDownloadDirectory(newBasePath:String){
|
||||
sharedPreferences.edit().putString(DirKey,newBasePath).apply()
|
||||
@ -63,7 +54,7 @@ actual class Dir actual constructor(
|
||||
|
||||
actual fun fileSeparator(): String = File.separator
|
||||
|
||||
actual fun imageCacheDir(): String = context.cacheDir.absolutePath + File.separator
|
||||
actual fun imageCacheDir(): String = methods.platformActions.imageCacheDir
|
||||
|
||||
// fun call in order to always access Updated Value
|
||||
actual fun defaultDir(): String = sharedPreferences.getString(DirKey,defaultBaseDir)!! + File.separator +
|
||||
@ -158,13 +149,7 @@ actual class Dir actual constructor(
|
||||
}
|
||||
}
|
||||
|
||||
actual fun addToLibrary(path: String) {
|
||||
logger.d { "Scanning File" }
|
||||
MediaScannerConnection.scanFile(
|
||||
appContext,
|
||||
listOf(path).toTypedArray(), null, null
|
||||
)
|
||||
}
|
||||
actual fun addToLibrary(path: String) = methods.platformActions.addToLibrary(path)
|
||||
|
||||
actual suspend fun loadImage(url: String): Picture = withContext(Dispatchers.IO){
|
||||
val cachePath = imageCacheDir() + getNameURL(url)
|
||||
@ -214,5 +199,5 @@ actual class Dir actual constructor(
|
||||
}
|
||||
}
|
||||
|
||||
actual val db: Database? = database
|
||||
actual val db: Database? = spotiFlyerDatabase.instance
|
||||
}
|
||||
|
@ -23,10 +23,7 @@ import android.net.Network
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
|
||||
import android.net.NetworkRequest
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.shabinder.common.database.appContext
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.IOException
|
||||
import java.lang.Exception
|
||||
@ -34,13 +31,6 @@ import java.net.InetSocketAddress
|
||||
|
||||
const val TAG = "C-Manager"
|
||||
|
||||
val internetAvailability by lazy { ConnectionLiveData(appContext) }
|
||||
|
||||
@Composable
|
||||
fun isInternetAvailableState(): State<Boolean?> {
|
||||
return internetAvailability.observeAsState()
|
||||
}
|
||||
|
||||
/**
|
||||
* Save all available networks with an internet connection to a set (@validNetworks).
|
||||
* As long as the size of the set > 0, this LiveData emits true.
|
||||
@ -49,7 +39,7 @@ fun isInternetAvailableState(): State<Boolean?> {
|
||||
* Inspired by:
|
||||
* https://github.com/AlexSheva-mason/Rick-Morty-Database/blob/master/app/src/main/java/com/shevaalex/android/rickmortydatabase/utils/networking/ConnectionLiveData.kt
|
||||
*/
|
||||
class ConnectionLiveData(context: Context = appContext) : LiveData<Boolean>() {
|
||||
class ConnectionLiveData(context: Context) : LiveData<Boolean>() {
|
||||
|
||||
private lateinit var networkCallback: ConnectivityManager.NetworkCallback
|
||||
private val cm = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
|
@ -17,7 +17,7 @@
|
||||
package com.shabinder.common.di
|
||||
|
||||
import co.touchlab.kermit.Kermit
|
||||
import com.shabinder.common.database.createDatabase
|
||||
import com.shabinder.common.database.databaseModule
|
||||
import com.shabinder.common.database.getLogger
|
||||
import com.shabinder.common.di.providers.GaanaProvider
|
||||
import com.shabinder.common.di.providers.SpotifyProvider
|
||||
@ -39,12 +39,12 @@ import org.koin.dsl.module
|
||||
fun initKoin(enableNetworkLogs: Boolean = false, appDeclaration: KoinAppDeclaration = {}) =
|
||||
startKoin {
|
||||
appDeclaration()
|
||||
modules(commonModule(enableNetworkLogs = enableNetworkLogs))
|
||||
modules(commonModule(enableNetworkLogs = enableNetworkLogs), databaseModule())
|
||||
}
|
||||
|
||||
fun commonModule(enableNetworkLogs: Boolean) = module {
|
||||
single { createHttpClient(enableNetworkLogs = enableNetworkLogs) }
|
||||
single { Dir(get(), createDatabase()) }
|
||||
single { Dir(get(), get()) }
|
||||
single { Kermit(getLogger()) }
|
||||
single { TokenStore(get(), get()) }
|
||||
single { YoutubeMusic(get(), get()) }
|
||||
|
@ -17,26 +17,22 @@
|
||||
package com.shabinder.common.di
|
||||
|
||||
import co.touchlab.kermit.Kermit
|
||||
import com.shabinder.common.database.createDatabase
|
||||
import com.shabinder.common.database.SpotiFlyerDatabase
|
||||
import com.shabinder.common.di.utils.removeIllegalChars
|
||||
import com.shabinder.common.models.DownloadResult
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.database.Database
|
||||
import com.shabinder.downloader.exceptions.YoutubeException
|
||||
import io.ktor.client.*
|
||||
import io.ktor.client.features.*
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.statement.HttpStatement
|
||||
import io.ktor.http.contentLength
|
||||
import io.ktor.http.isSuccess
|
||||
import io.ktor.utils.io.errors.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
expect class Dir (
|
||||
logger: Kermit,
|
||||
database: Database? = createDatabase(),
|
||||
spotiFlyerDatabase: SpotiFlyerDatabase,
|
||||
) {
|
||||
val db: Database?
|
||||
fun isPresent(path: String): Boolean
|
||||
|
@ -16,37 +16,19 @@
|
||||
|
||||
package com.shabinder.common.di
|
||||
|
||||
import com.shabinder.common.models.AllPlatforms
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.common.models.methods
|
||||
import io.ktor.client.request.*
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
expect fun openPlatform(packageID: String, platformLink: String)
|
||||
|
||||
expect fun shareApp()
|
||||
|
||||
expect fun giveDonation()
|
||||
|
||||
expect val dispatcherIO: CoroutineDispatcher
|
||||
|
||||
expect val isInternetAvailable: Boolean
|
||||
|
||||
expect val currentPlatform: AllPlatforms
|
||||
|
||||
expect suspend fun downloadTracks(
|
||||
list: List<TrackDetails>,
|
||||
fetcher: FetchPlatformQueryResult,
|
||||
dir: Dir
|
||||
)
|
||||
|
||||
expect fun queryActiveTracks()
|
||||
|
||||
/*
|
||||
* Refactor This
|
||||
* */
|
||||
suspend fun isInternetAccessible(): Boolean {
|
||||
return withContext(dispatcherIO) {
|
||||
return withContext(methods.dispatcherIO) {
|
||||
try {
|
||||
ktorHttpClient.head<String>("http://google.com")
|
||||
true
|
||||
|
@ -26,12 +26,12 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class FetchPlatformQueryResult(
|
||||
private val gaanaProvider: GaanaProvider,
|
||||
private val spotifyProvider: SpotifyProvider,
|
||||
val gaanaProvider: GaanaProvider,
|
||||
val spotifyProvider: SpotifyProvider,
|
||||
val youtubeProvider: YoutubeProvider,
|
||||
val youtubeMusic: YoutubeMusic,
|
||||
val youtubeMp3: YoutubeMp3,
|
||||
private val dir: Dir
|
||||
val dir: Dir
|
||||
) {
|
||||
private val db: DownloadRecordDatabaseQueries?
|
||||
get() = dir.db?.downloadRecordDatabaseQueries
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
package com.shabinder.common.di.gaana
|
||||
|
||||
import com.shabinder.common.di.currentPlatform
|
||||
import com.shabinder.common.models.AllPlatforms
|
||||
import com.shabinder.common.models.corsProxy
|
||||
import com.shabinder.common.models.gaana.GaanaAlbum
|
||||
@ -24,10 +23,11 @@ import com.shabinder.common.models.gaana.GaanaArtistDetails
|
||||
import com.shabinder.common.models.gaana.GaanaArtistTracks
|
||||
import com.shabinder.common.models.gaana.GaanaPlaylist
|
||||
import com.shabinder.common.models.gaana.GaanaSong
|
||||
import com.shabinder.common.models.methods
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.request.get
|
||||
|
||||
val corsApi get() = if (currentPlatform is AllPlatforms.Js) {
|
||||
val corsApi get() = if (methods.currentPlatform is AllPlatforms.Js) {
|
||||
corsProxy.url
|
||||
} // "https://spotiflyer-cors.azurewebsites.net/" //"https://spotiflyer-cors.herokuapp.com/"//"https://cors.bridged.cc/"
|
||||
else ""
|
||||
|
@ -19,7 +19,6 @@ package com.shabinder.common.di.providers
|
||||
import co.touchlab.kermit.Kermit
|
||||
import com.shabinder.common.di.Dir
|
||||
import com.shabinder.common.di.TokenStore
|
||||
import com.shabinder.common.di.currentPlatform
|
||||
import com.shabinder.common.di.finalOutputDir
|
||||
import com.shabinder.common.di.kotlinxSerializer
|
||||
import com.shabinder.common.di.spotify.SpotifyRequests
|
||||
@ -27,6 +26,7 @@ import com.shabinder.common.di.spotify.authenticateSpotify
|
||||
import com.shabinder.common.models.AllPlatforms
|
||||
import com.shabinder.common.models.PlatformQueryResult
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.common.models.methods
|
||||
import com.shabinder.common.models.spotify.Album
|
||||
import com.shabinder.common.models.spotify.Image
|
||||
import com.shabinder.common.models.spotify.Source
|
||||
@ -45,14 +45,14 @@ class SpotifyProvider(
|
||||
private val dir: Dir,
|
||||
) : SpotifyRequests {
|
||||
|
||||
init {
|
||||
/* init {
|
||||
logger.d { "Creating Spotify Provider" }
|
||||
GlobalScope.launch(Dispatchers.Default) {
|
||||
if (currentPlatform is AllPlatforms.Js) {
|
||||
if (methods.currentPlatform is AllPlatforms.Js) {
|
||||
authenticateSpotifyClient(override = true)
|
||||
} else authenticateSpotifyClient()
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
override suspend fun authenticateSpotifyClient(override: Boolean): HttpClient? {
|
||||
val token = if (override) authenticateSpotify() else tokenStore.getToken()
|
||||
|
@ -18,9 +18,9 @@ package com.shabinder.common.di.providers
|
||||
|
||||
import co.touchlab.kermit.Kermit
|
||||
import com.shabinder.common.di.Dir
|
||||
import com.shabinder.common.di.currentPlatform
|
||||
import com.shabinder.common.di.youtubeMp3.Yt1sMp3
|
||||
import com.shabinder.common.models.AllPlatforms
|
||||
import com.shabinder.common.models.methods
|
||||
import io.ktor.client.HttpClient
|
||||
|
||||
class YoutubeMp3(
|
||||
@ -31,7 +31,7 @@ class YoutubeMp3(
|
||||
suspend fun getMp3DownloadLink(videoID: String): String? = try {
|
||||
getLinkFromYt1sMp3(videoID)?.let {
|
||||
logger.i { "Download Link: $it" }
|
||||
if (currentPlatform is AllPlatforms.Js/* && corsProxy !is CorsProxy.PublicProxyWithExtension*/)
|
||||
if (methods.currentPlatform is AllPlatforms.Js/* && corsProxy !is CorsProxy.PublicProxyWithExtension*/)
|
||||
"https://kind-grasshopper-73.telebit.io/cors/$it"
|
||||
// "https://spotiflyer.azurewebsites.net/$it" // Data OUT Limit issue
|
||||
else it
|
||||
|
@ -16,8 +16,8 @@
|
||||
|
||||
package com.shabinder.common.di.spotify
|
||||
|
||||
import com.shabinder.common.di.isInternetAvailable
|
||||
import com.shabinder.common.di.kotlinxSerializer
|
||||
import com.shabinder.common.models.methods
|
||||
import com.shabinder.common.models.spotify.TokenData
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.features.auth.Auth
|
||||
@ -29,7 +29,7 @@ import io.ktor.http.Parameters
|
||||
|
||||
suspend fun authenticateSpotify(): TokenData? {
|
||||
return try {
|
||||
if (isInternetAvailable) spotifyAuthClient.post("https://accounts.spotify.com/api/token") {
|
||||
if (methods.isInternetAvailable) spotifyAuthClient.post("https://accounts.spotify.com/api/token") {
|
||||
body = FormDataContent(Parameters.build { append("grant_type", "client_credentials") })
|
||||
} else null
|
||||
}catch (e:Exception) {
|
||||
|
@ -21,7 +21,7 @@ package com.shabinder.common.di.utils
|
||||
// implementation("org.jetbrains.kotlinx:atomicfu:0.14.4")
|
||||
// Gist: https://gist.github.com/fluidsonic/ba32de21c156bbe8424c8d5fc20dcd8e
|
||||
|
||||
import com.shabinder.common.di.dispatcherIO
|
||||
import com.shabinder.common.models.methods
|
||||
import io.ktor.utils.io.core.Closeable
|
||||
import kotlinx.atomicfu.atomic
|
||||
import kotlinx.coroutines.CancellationException
|
||||
@ -37,7 +37,7 @@ import kotlinx.coroutines.withContext
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class ParallelExecutor(
|
||||
parentContext: CoroutineContext = dispatcherIO,
|
||||
parentContext: CoroutineContext = methods.dispatcherIO,
|
||||
) : Closeable {
|
||||
|
||||
private val concurrentOperationLimit = atomic(4)
|
||||
|
@ -17,43 +17,13 @@
|
||||
package com.shabinder.common.di
|
||||
|
||||
import com.shabinder.common.di.utils.ParallelExecutor
|
||||
import com.shabinder.common.models.AllPlatforms
|
||||
import com.shabinder.common.models.DownloadResult
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.downloader.YoutubeDownloader
|
||||
import io.ktor.client.request.head
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
actual fun openPlatform(packageID: String, platformLink: String) {
|
||||
// TODO
|
||||
}
|
||||
actual val currentPlatform: AllPlatforms = AllPlatforms.Jvm
|
||||
|
||||
actual val dispatcherIO = Dispatchers.IO
|
||||
|
||||
actual fun shareApp() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
actual fun giveDonation() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
actual fun queryActiveTracks() {}
|
||||
|
||||
actual val isInternetAvailable: Boolean
|
||||
get() {
|
||||
var result = false
|
||||
val job = GlobalScope.launch { result = isInternetAccessible() }
|
||||
while (job.isActive) {}
|
||||
return result
|
||||
}
|
||||
|
||||
val DownloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>> = MutableSharedFlow(1)
|
||||
|
||||
|
@ -20,6 +20,7 @@ import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import co.touchlab.kermit.Kermit
|
||||
import com.mpatric.mp3agic.Mp3File
|
||||
import com.shabinder.common.database.SpotiFlyerDatabase
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.database.Database
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@ -38,7 +39,7 @@ import javax.imageio.ImageIO
|
||||
|
||||
actual class Dir actual constructor(
|
||||
private val logger: Kermit,
|
||||
private val database: Database?,
|
||||
private val spotiFlyerDatabase: SpotiFlyerDatabase,
|
||||
) {
|
||||
|
||||
init {
|
||||
@ -137,9 +138,9 @@ actual class Dir actual constructor(
|
||||
}
|
||||
}
|
||||
|
||||
actual val db: Database?
|
||||
get() = database
|
||||
actual val db: Database? get() = spotiFlyerDatabase.instance
|
||||
}
|
||||
|
||||
fun BufferedImage.toImageBitmap() = Image.makeFromEncoded(
|
||||
toByteArray(this)
|
||||
).asImageBitmap()
|
||||
|
@ -12,7 +12,7 @@ import cocoapods.TagLibIOS.TLAudio
|
||||
|
||||
actual class Dir actual constructor(
|
||||
private val logger: Kermit,
|
||||
private val database: Database?
|
||||
private val spotiFlyerDatabase: SpotiFlyerDatabase,
|
||||
) {
|
||||
|
||||
init {
|
||||
@ -142,5 +142,5 @@ actual class Dir actual constructor(
|
||||
// TODO
|
||||
}
|
||||
|
||||
actual val db: Database? = database
|
||||
actual val db: Database? = spotiFlyerDatabase.instance
|
||||
}
|
@ -16,55 +16,14 @@
|
||||
|
||||
package com.shabinder.common.di
|
||||
|
||||
import com.shabinder.common.models.AllPlatforms
|
||||
import com.shabinder.common.models.DownloadResult
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import io.ktor.client.request.head
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import com.shabinder.common.models.methods
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
actual val currentPlatform: AllPlatforms = AllPlatforms.Js
|
||||
|
||||
actual fun openPlatform(packageID: String, platformLink: String) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
actual fun shareApp() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
actual fun giveDonation() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
actual fun queryActiveTracks() {}
|
||||
|
||||
actual val dispatcherIO: CoroutineDispatcher = Dispatchers.Default
|
||||
|
||||
/*
|
||||
* Refactor This
|
||||
* */
|
||||
private suspend fun isInternetAvailable(): Boolean {
|
||||
return withContext(dispatcherIO) {
|
||||
try {
|
||||
ktorHttpClient.head<String>("http://google.com")
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
println(e.message)
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
actual val isInternetAvailable: Boolean
|
||||
get() {
|
||||
return true
|
||||
}
|
||||
|
||||
val DownloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>> = MutableSharedFlow(1)
|
||||
// Error:https://github.com/Kotlin/kotlinx.atomicfu/issues/182
|
||||
// val DownloadScope = ParallelExecutor(Dispatchers.Default) //Download Pool of 4 parallel
|
||||
@ -76,7 +35,7 @@ actual suspend fun downloadTracks(
|
||||
dir: Dir
|
||||
) {
|
||||
list.forEach {
|
||||
withContext(dispatcherIO) {
|
||||
withContext(methods.dispatcherIO) {
|
||||
allTracksStatus[it.title] = DownloadStatus.Queued
|
||||
if (!it.videoID.isNullOrBlank()) { // Video ID already known!
|
||||
downloadTrack(it.videoID!!, it, fetcher, dir)
|
||||
|
@ -17,6 +17,7 @@
|
||||
package com.shabinder.common.di
|
||||
|
||||
import co.touchlab.kermit.Kermit
|
||||
import com.shabinder.common.database.SpotiFlyerDatabase
|
||||
import com.shabinder.common.di.gaana.corsApi
|
||||
import com.shabinder.common.di.utils.removeIllegalChars
|
||||
import com.shabinder.common.models.DownloadResult
|
||||
@ -32,7 +33,7 @@ import org.w3c.dom.ImageBitmap
|
||||
|
||||
actual class Dir actual constructor(
|
||||
private val logger: Kermit,
|
||||
private val database: Database?,
|
||||
private val spotiFlyerDatabase: SpotiFlyerDatabase,
|
||||
) {
|
||||
|
||||
/*init {
|
||||
@ -112,8 +113,7 @@ actual class Dir actual constructor(
|
||||
|
||||
private suspend fun freshImage(url: String): ImageBitmap? = null
|
||||
|
||||
actual val db: Database?
|
||||
get() = database
|
||||
actual val db: Database? get() = spotiFlyerDatabase.instance
|
||||
}
|
||||
|
||||
fun ByteArray.toArrayBuffer(): ArrayBuffer {
|
||||
|
@ -64,7 +64,6 @@ interface SpotiFlyerList {
|
||||
val dir: Dir
|
||||
val link: String
|
||||
val listOutput: Consumer<Output>
|
||||
val showPopUpMessage: (String) -> Unit
|
||||
val downloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>>
|
||||
}
|
||||
sealed class Output {
|
||||
|
@ -40,8 +40,7 @@ internal class SpotiFlyerListImpl(
|
||||
storeFactory = storeFactory,
|
||||
fetchQuery = fetchQuery,
|
||||
downloadProgressFlow = downloadProgressFlow,
|
||||
link = link,
|
||||
showPopUpMessage = showPopUpMessage
|
||||
link = link
|
||||
).provide()
|
||||
}
|
||||
|
||||
|
@ -25,12 +25,12 @@ import com.shabinder.common.database.getLogger
|
||||
import com.shabinder.common.di.Dir
|
||||
import com.shabinder.common.di.FetchPlatformQueryResult
|
||||
import com.shabinder.common.di.downloadTracks
|
||||
import com.shabinder.common.di.queryActiveTracks
|
||||
import com.shabinder.common.list.SpotiFlyerList.State
|
||||
import com.shabinder.common.list.store.SpotiFlyerListStore.Intent
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import com.shabinder.common.models.PlatformQueryResult
|
||||
import com.shabinder.common.models.TrackDetails
|
||||
import com.shabinder.common.models.methods
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
||||
@ -39,7 +39,6 @@ internal class SpotiFlyerListStoreProvider(
|
||||
private val storeFactory: StoreFactory,
|
||||
private val fetchQuery: FetchPlatformQueryResult,
|
||||
private val link: String,
|
||||
private val showPopUpMessage: (String) -> Unit,
|
||||
private val downloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>>
|
||||
) {
|
||||
val logger = getLogger()
|
||||
@ -93,7 +92,7 @@ internal class SpotiFlyerListStoreProvider(
|
||||
is Intent.StartDownloadAll -> {
|
||||
val finalList =
|
||||
intent.trackList.filter { it.downloaded == DownloadStatus.NotDownloaded }
|
||||
if (finalList.isNullOrEmpty()) showPopUpMessage("All Songs are Processed")
|
||||
if (finalList.isNullOrEmpty()) methods.showPopUpMessage("All Songs are Processed")
|
||||
else downloadTracks(finalList, fetchQuery, dir)
|
||||
|
||||
val list = intent.trackList.map {
|
||||
@ -107,7 +106,7 @@ internal class SpotiFlyerListStoreProvider(
|
||||
downloadTracks(listOf(intent.track), fetchQuery, dir)
|
||||
dispatch(Result.UpdateTrackItem(intent.track.copy(downloaded = DownloadStatus.Queued)))
|
||||
}
|
||||
is Intent.RefreshTracksStatuses -> queryActiveTracks()
|
||||
is Intent.RefreshTracksStatuses -> methods.queryActiveTracks()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,6 @@ interface SpotiFlyerMain {
|
||||
val storeFactory: StoreFactory
|
||||
val database: Database?
|
||||
val dir: Dir
|
||||
val showPopUpMessage: (String) -> Unit
|
||||
}
|
||||
|
||||
sealed class Output {
|
||||
|
@ -19,7 +19,6 @@ package com.shabinder.common.main.integration
|
||||
import com.arkivanov.decompose.ComponentContext
|
||||
import com.arkivanov.mvikotlin.extensions.coroutines.states
|
||||
import com.shabinder.common.di.Picture
|
||||
import com.shabinder.common.di.isInternetAvailable
|
||||
import com.shabinder.common.main.SpotiFlyerMain
|
||||
import com.shabinder.common.main.SpotiFlyerMain.Dependencies
|
||||
import com.shabinder.common.main.SpotiFlyerMain.HomeCategory
|
||||
@ -28,6 +27,7 @@ import com.shabinder.common.main.SpotiFlyerMain.State
|
||||
import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
|
||||
import com.shabinder.common.main.store.SpotiFlyerMainStoreProvider
|
||||
import com.shabinder.common.main.store.getStore
|
||||
import com.shabinder.common.models.methods
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
internal class SpotiFlyerMainImpl(
|
||||
@ -39,16 +39,15 @@ internal class SpotiFlyerMainImpl(
|
||||
instanceKeeper.getStore {
|
||||
SpotiFlyerMainStoreProvider(
|
||||
storeFactory = storeFactory,
|
||||
database = database,
|
||||
showPopUpMessage = showPopUpMessage
|
||||
database = database
|
||||
).provide()
|
||||
}
|
||||
|
||||
override val models: Flow<State> = store.states
|
||||
|
||||
override fun onLinkSearch(link: String) {
|
||||
if (isInternetAvailable) mainOutput.callback(Output.Search(link = link))
|
||||
else showPopUpMessage("Check Network Connection Please")
|
||||
if (methods.isInternetAvailable) mainOutput.callback(Output.Search(link = link))
|
||||
else methods.showPopUpMessage("Check Network Connection Please")
|
||||
}
|
||||
|
||||
override fun onInputLinkChanged(link: String) {
|
||||
|
@ -21,13 +21,11 @@ import com.arkivanov.mvikotlin.core.store.SimpleBootstrapper
|
||||
import com.arkivanov.mvikotlin.core.store.Store
|
||||
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
||||
import com.arkivanov.mvikotlin.extensions.coroutines.SuspendExecutor
|
||||
import com.shabinder.common.di.giveDonation
|
||||
import com.shabinder.common.di.openPlatform
|
||||
import com.shabinder.common.di.shareApp
|
||||
import com.shabinder.common.main.SpotiFlyerMain
|
||||
import com.shabinder.common.main.SpotiFlyerMain.State
|
||||
import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
|
||||
import com.shabinder.common.models.DownloadRecord
|
||||
import com.shabinder.common.models.methods
|
||||
import com.shabinder.database.Database
|
||||
import com.squareup.sqldelight.runtime.coroutines.asFlow
|
||||
import com.squareup.sqldelight.runtime.coroutines.mapToList
|
||||
@ -38,7 +36,6 @@ import kotlinx.coroutines.flow.map
|
||||
|
||||
internal class SpotiFlyerMainStoreProvider(
|
||||
private val storeFactory: StoreFactory,
|
||||
private val showPopUpMessage: (String) -> Unit,
|
||||
private val database: Database?
|
||||
) {
|
||||
|
||||
@ -81,9 +78,9 @@ internal class SpotiFlyerMainStoreProvider(
|
||||
|
||||
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
|
||||
when (intent) {
|
||||
is Intent.OpenPlatform -> openPlatform(intent.platformID, intent.platformLink)
|
||||
is Intent.GiveDonation -> giveDonation()
|
||||
is Intent.ShareApp -> shareApp()
|
||||
is Intent.OpenPlatform -> methods.openPlatform(intent.platformID, intent.platformLink)
|
||||
is Intent.GiveDonation -> methods.giveDonation()
|
||||
is Intent.ShareApp -> methods.shareApp()
|
||||
is Intent.SetLink -> dispatch(Result.LinkChanged(link = intent.link))
|
||||
is Intent.SelectCategory -> dispatch(Result.CategoryChanged(intent.category))
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import com.shabinder.common.di.Dir
|
||||
import com.shabinder.common.di.FetchPlatformQueryResult
|
||||
import com.shabinder.common.list.SpotiFlyerList
|
||||
import com.shabinder.common.main.SpotiFlyerMain
|
||||
import com.shabinder.common.models.Actions
|
||||
import com.shabinder.common.models.DownloadStatus
|
||||
import com.shabinder.common.root.SpotiFlyerRoot.Dependencies
|
||||
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
|
||||
@ -47,9 +48,8 @@ interface SpotiFlyerRoot {
|
||||
val database: Database?
|
||||
val fetchPlatformQueryResult: FetchPlatformQueryResult
|
||||
val directories: Dir
|
||||
val showPopUpMessage: (String) -> Unit
|
||||
val downloadProgressReport: MutableSharedFlow<HashMap<String, DownloadStatus>>
|
||||
val setDownloadDirectoryAction:()->Unit
|
||||
val actions:Actions
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,16 +28,31 @@ import com.arkivanov.decompose.value.Value
|
||||
import com.shabinder.common.di.Dir
|
||||
import com.shabinder.common.list.SpotiFlyerList
|
||||
import com.shabinder.common.main.SpotiFlyerMain
|
||||
import com.shabinder.common.models.Actions
|
||||
import com.shabinder.common.models.AllPlatforms
|
||||
import com.shabinder.common.models.Consumer
|
||||
import com.shabinder.common.models.methods
|
||||
import com.shabinder.common.root.SpotiFlyerRoot
|
||||
import com.shabinder.common.root.SpotiFlyerRoot.Child
|
||||
import com.shabinder.common.root.SpotiFlyerRoot.Dependencies
|
||||
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal class SpotiFlyerRootImpl(
|
||||
componentContext: ComponentContext,
|
||||
dependencies: Dependencies,
|
||||
) : SpotiFlyerRoot, ComponentContext by componentContext, Dependencies by dependencies {
|
||||
) : SpotiFlyerRoot, ComponentContext by componentContext, Dependencies by dependencies, Actions by dependencies.actions {
|
||||
|
||||
init {
|
||||
methods = actions
|
||||
GlobalScope.launch {
|
||||
/*Authenticate Spotify Client*/
|
||||
if (methods.currentPlatform is AllPlatforms.Js) {
|
||||
fetchPlatformQueryResult.spotifyProvider.authenticateSpotifyClient(override = true)
|
||||
} else fetchPlatformQueryResult.spotifyProvider.authenticateSpotifyClient()
|
||||
}
|
||||
}
|
||||
|
||||
private val router =
|
||||
router<Configuration, Child>(
|
||||
|
@ -38,6 +38,7 @@ kotlin {
|
||||
implementation(project(":common:database"))
|
||||
implementation(project(":common:dependency-injection"))
|
||||
implementation(project(":common:compose"))
|
||||
implementation(project(":common:data-models"))
|
||||
implementation(project(":common:root"))
|
||||
implementation(Decompose.decompose)
|
||||
implementation(Decompose.extensionsCompose)
|
||||
|
@ -29,14 +29,21 @@ import com.shabinder.common.di.Dir
|
||||
import com.shabinder.common.di.DownloadProgressFlow
|
||||
import com.shabinder.common.di.FetchPlatformQueryResult
|
||||
import com.shabinder.common.di.initKoin
|
||||
import com.shabinder.common.di.isInternetAccessible
|
||||
import com.shabinder.common.models.Actions
|
||||
import com.shabinder.common.models.AllPlatforms
|
||||
import com.shabinder.common.models.PlatformActions
|
||||
import com.shabinder.common.root.SpotiFlyerRoot
|
||||
import com.shabinder.common.uikit.SpotiFlyerColors
|
||||
import com.shabinder.common.uikit.SpotiFlyerRootContent
|
||||
import com.shabinder.common.uikit.SpotiFlyerShapes
|
||||
import com.shabinder.common.uikit.SpotiFlyerTypography
|
||||
import com.shabinder.common.uikit.colorOffWhite
|
||||
import com.shabinder.common.uikit.showToast
|
||||
import com.shabinder.database.Database
|
||||
import com.shabinder.common.uikit.showPopUpMessage as uikitShowPopUpMessage
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
private val koin = initKoin(enableNetworkLogs = true).koin
|
||||
|
||||
@ -70,7 +77,33 @@ private fun spotiFlyerRoot(componentContext: ComponentContext): SpotiFlyerRoot =
|
||||
override val fetchPlatformQueryResult: FetchPlatformQueryResult = koin.get()
|
||||
override val directories: Dir = koin.get()
|
||||
override val database: Database? = directories.db
|
||||
override val showPopUpMessage: (String) -> Unit = ::uikitShowPopUpMessage
|
||||
override val downloadProgressReport = DownloadProgressFlow
|
||||
override val actions = object: Actions {
|
||||
override val platformActions = object : PlatformActions {}
|
||||
|
||||
override fun showPopUpMessage(string: String, long: Boolean) = showToast(string)
|
||||
|
||||
override fun setDownloadDirectoryAction() {}
|
||||
|
||||
override fun queryActiveTracks() {}
|
||||
|
||||
override fun giveDonation() {}
|
||||
|
||||
override fun shareApp() {}
|
||||
|
||||
override fun openPlatform(packageID: String, platformLink: String) {}
|
||||
|
||||
override val dispatcherIO = Dispatchers.IO
|
||||
|
||||
override val isInternetAvailable: Boolean
|
||||
get() {
|
||||
var result = false
|
||||
val job = GlobalScope.launch { result = isInternetAccessible() }
|
||||
while (job.isActive) {/*TODO Better Way*/}
|
||||
return result
|
||||
}
|
||||
|
||||
override val currentPlatform = AllPlatforms.Jvm
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -22,9 +22,13 @@ import com.arkivanov.mvikotlin.core.store.StoreFactory
|
||||
import com.arkivanov.mvikotlin.logging.store.LoggingStoreFactory
|
||||
import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
|
||||
import com.shabinder.common.di.DownloadProgressFlow
|
||||
import com.shabinder.common.models.Actions
|
||||
import com.shabinder.common.models.AllPlatforms
|
||||
import com.shabinder.common.models.PlatformActions
|
||||
import com.shabinder.common.root.SpotiFlyerRoot
|
||||
import com.shabinder.database.Database
|
||||
import extras.renderableChild
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import react.RBuilder
|
||||
import react.RComponent
|
||||
import react.RProps
|
||||
@ -50,14 +54,33 @@ class App(props: AppProps): RComponent<AppProps, RState>(props) {
|
||||
private val dependencies = props.dependencies
|
||||
|
||||
private val root = SpotiFlyerRoot(ctx,
|
||||
object : SpotiFlyerRoot.Dependencies{
|
||||
object : SpotiFlyerRoot.Dependencies {
|
||||
override val storeFactory: StoreFactory = LoggingStoreFactory(DefaultStoreFactory)
|
||||
override val fetchPlatformQueryResult = dependencies.fetchPlatformQueryResult
|
||||
override val directories = dependencies.directories
|
||||
override val database: Database? = directories.db
|
||||
override val showPopUpMessage: (String) -> Unit = {}//TODO
|
||||
override val downloadProgressReport = DownloadProgressFlow
|
||||
override val actions = object : Actions {
|
||||
override val platformActions = object : PlatformActions {}
|
||||
|
||||
override fun showPopUpMessage(string: String, long: Boolean) {
|
||||
/*TODO("Not yet implemented")*/
|
||||
}
|
||||
|
||||
override fun setDownloadDirectoryAction() {}
|
||||
|
||||
override fun queryActiveTracks() {}
|
||||
|
||||
override fun giveDonation() {}
|
||||
|
||||
override fun shareApp() {}
|
||||
|
||||
override fun openPlatform(packageID: String, platformLink: String) {}
|
||||
|
||||
override val dispatcherIO = Dispatchers.Default
|
||||
override val isInternetAvailable: Boolean = true
|
||||
override val currentPlatform = AllPlatforms.Js
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user