mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-22 17:14:32 +01:00
SpotiFlyerList and more porting
This commit is contained in:
parent
97f9606863
commit
c7c61e51d6
@ -22,6 +22,7 @@ android {
|
|||||||
versionCode = Versions.versionCode
|
versionCode = Versions.versionCode
|
||||||
versionName = Versions.versionName
|
versionName = Versions.versionName
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
getByName("release") {
|
getByName("release") {
|
||||||
isMinifyEnabled = false
|
isMinifyEnabled = false
|
||||||
@ -29,6 +30,7 @@ android {
|
|||||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
// Flag to enable support for the new language APIs
|
// Flag to enable support for the new language APIs
|
||||||
//coreLibraryDesugaringEnabled = true
|
//coreLibraryDesugaringEnabled = true
|
||||||
|
@ -18,6 +18,12 @@ kotlin {
|
|||||||
//implementation(Badoo.Reaktive.reaktive)
|
//implementation(Badoo.Reaktive.reaktive)
|
||||||
implementation(Decompose.decompose)
|
implementation(Decompose.decompose)
|
||||||
implementation(Decompose.extensionsCompose)
|
implementation(Decompose.extensionsCompose)
|
||||||
|
|
||||||
|
//Coil-Image Loading
|
||||||
|
Versions.coilVersion.let{
|
||||||
|
implementation("dev.chrisbanes.accompanist:accompanist-coil:$it")
|
||||||
|
implementation("dev.chrisbanes.accompanist:accompanist-insets:$it")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.shabinder.common.ui
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.layout.preferredHeight
|
||||||
|
import androidx.compose.foundation.layout.preferredWidth
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
import dev.chrisbanes.accompanist.coil.CoilImage
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
actual fun ImageLoad(
|
||||||
|
url:String,
|
||||||
|
loadingResource:ImageBitmap?,
|
||||||
|
errorResource:ImageBitmap?,
|
||||||
|
modifier: Modifier
|
||||||
|
){
|
||||||
|
val imgUri = url.toUri().buildUpon().scheme("https").build()
|
||||||
|
CoilImage(
|
||||||
|
data = imgUri,
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
loading = { loadingResource?.let { Image(it,"loading image") } },
|
||||||
|
error = { errorResource?.let { it1 -> Image(it1,"Error Image") } },
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
}
|
@ -2,9 +2,11 @@ package com.shabinder.common.list
|
|||||||
|
|
||||||
import com.arkivanov.decompose.ComponentContext
|
import com.arkivanov.decompose.ComponentContext
|
||||||
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
||||||
|
import com.shabinder.common.FetchPlatformQueryResult
|
||||||
import com.shabinder.common.PlatformQueryResult
|
import com.shabinder.common.PlatformQueryResult
|
||||||
import com.shabinder.common.TrackDetails
|
import com.shabinder.common.TrackDetails
|
||||||
import com.shabinder.common.list.integration.SpotiFlyerListImpl
|
import com.shabinder.common.list.integration.SpotiFlyerListImpl
|
||||||
|
import com.shabinder.common.utils.Consumer
|
||||||
import com.shabinder.database.Database
|
import com.shabinder.database.Database
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@ -13,10 +15,13 @@ interface SpotiFlyerList {
|
|||||||
val models: Flow<State>
|
val models: Flow<State>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For Single Track Download -> list(that track)
|
* Download All Tracks(after filtering already Downloaded)
|
||||||
* For Download All -> Model.tracks
|
|
||||||
* */
|
* */
|
||||||
fun onDownloadClicked(trackList:List<TrackDetails>)
|
fun onDownloadAllClicked(trackList:List<TrackDetails>)
|
||||||
|
/*
|
||||||
|
* Download All Tracks(after filtering already Downloaded)
|
||||||
|
* */
|
||||||
|
fun onDownloadClicked(wholeTrackList:List<TrackDetails>,trackIndex:Int)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To Pop and return back to Main Screen
|
* To Pop and return back to Main Screen
|
||||||
@ -25,15 +30,15 @@ interface SpotiFlyerList {
|
|||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
val storeFactory: StoreFactory
|
val storeFactory: StoreFactory
|
||||||
val database: Database
|
val fetchQuery: FetchPlatformQueryResult
|
||||||
val link: String
|
val link: String
|
||||||
fun listOutput(finished: Output.Finished)
|
fun listOutput(finished: Output.Finished): Consumer<Output>
|
||||||
}
|
}
|
||||||
sealed class Output {
|
sealed class Output {
|
||||||
object Finished : Output()
|
object Finished : Output()
|
||||||
}
|
}
|
||||||
data class State(
|
data class State(
|
||||||
val result:PlatformQueryResult? = null,
|
val queryResult:PlatformQueryResult? = null,
|
||||||
val link:String = ""
|
val link:String = ""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,151 @@
|
|||||||
|
package com.shabinder.common.list
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.material.CircularProgressIndicator
|
||||||
|
import androidx.compose.material.ExtendedFloatingActionButton
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.shabinder.common.DownloadStatus
|
||||||
|
import com.shabinder.common.TrackDetails
|
||||||
|
import com.shabinder.common.ui.ImageLoad
|
||||||
|
import com.shabinder.spotiflyer.ui.SpotiFlyerTypography
|
||||||
|
import com.shabinder.spotiflyer.ui.colorAccent
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SpotiFlyerListContent(
|
||||||
|
component: SpotiFlyerList,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
val model by component.models.collectAsState(SpotiFlyerList.State())
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
Box(modifier = modifier.fillMaxSize()) {
|
||||||
|
//TODO Null Handling
|
||||||
|
val result = model.queryResult!!
|
||||||
|
|
||||||
|
LazyColumn(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
content = {
|
||||||
|
item {
|
||||||
|
CoverImage(result.title, result.coverUrl, coroutineScope)
|
||||||
|
}
|
||||||
|
itemsIndexed(result.trackList) { index, item ->
|
||||||
|
TrackCard(
|
||||||
|
track = item,
|
||||||
|
downloadTrack = { component.onDownloadClicked(result.trackList,index) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
)
|
||||||
|
DownloadAllButton(
|
||||||
|
onClick = {component.onDownloadAllClicked(result.trackList)},
|
||||||
|
modifier = Modifier.padding(bottom = 24.dp).align(Alignment.BottomCenter)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TrackCard(
|
||||||
|
track: TrackDetails,
|
||||||
|
downloadTrack:()->Unit,
|
||||||
|
) {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)) {
|
||||||
|
ImageLoad(
|
||||||
|
url = track.albumArtURL,
|
||||||
|
modifier = Modifier
|
||||||
|
.preferredWidth(75.dp)
|
||||||
|
.preferredHeight(90.dp)
|
||||||
|
.clip(MaterialTheme.shapes.medium)
|
||||||
|
)
|
||||||
|
Column(modifier = Modifier.padding(horizontal = 8.dp).preferredHeight(60.dp).weight(1f),verticalArrangement = Arrangement.SpaceEvenly) {
|
||||||
|
Text(track.title,maxLines = 1,overflow = TextOverflow.Ellipsis,style = SpotiFlyerTypography.h6,color = colorAccent)
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.Bottom,
|
||||||
|
modifier = Modifier.padding(horizontal = 8.dp).fillMaxSize()
|
||||||
|
){
|
||||||
|
Text("${track.artists.firstOrNull()}...",fontSize = 12.sp,maxLines = 1)
|
||||||
|
Text("${track.durationSec/60} min, ${track.durationSec%60} sec",fontSize = 12.sp,maxLines = 1,overflow = TextOverflow.Ellipsis)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
when(track.downloaded){
|
||||||
|
DownloadStatus.Downloaded -> {
|
||||||
|
//Image(vectorResource(id = R.drawable.ic_tick))
|
||||||
|
}
|
||||||
|
DownloadStatus.Queued -> {
|
||||||
|
CircularProgressIndicator()
|
||||||
|
}
|
||||||
|
DownloadStatus.Failed -> {
|
||||||
|
//Image(vectorResource(id = R.drawable.ic_error))
|
||||||
|
}
|
||||||
|
DownloadStatus.Downloading -> {
|
||||||
|
CircularProgressIndicator(progress = track.progress.toFloat()/100f)
|
||||||
|
}
|
||||||
|
DownloadStatus.Converting -> {
|
||||||
|
CircularProgressIndicator(progress = 100f,color = colorAccent)
|
||||||
|
}
|
||||||
|
DownloadStatus.NotDownloaded -> {
|
||||||
|
/*Image(vectorResource(id = R.drawable.ic_arrow), Modifier.clickable(onClick = {
|
||||||
|
downloadTrack()
|
||||||
|
}))*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CoverImage(
|
||||||
|
title: String,
|
||||||
|
coverURL: String,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier.padding(vertical = 8.dp).fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
ImageLoad(
|
||||||
|
url = coverURL,
|
||||||
|
modifier = Modifier
|
||||||
|
.preferredWidth(210.dp)
|
||||||
|
.preferredHeight(230.dp)
|
||||||
|
.clip(MaterialTheme.shapes.medium)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = SpotiFlyerTypography.h5,
|
||||||
|
maxLines = 2,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
//color = colorAccent,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/*scope.launch {
|
||||||
|
updateGradient(coverURL, ctx)
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DownloadAllButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||||
|
ExtendedFloatingActionButton(
|
||||||
|
text = { Text("Download All") },
|
||||||
|
onClick = onClick,
|
||||||
|
//icon = { Icon(imageVector = Image(R.drawable.ic_download_arrow),tint = Color.Black) },
|
||||||
|
backgroundColor = colorAccent,
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
}
|
@ -20,15 +20,19 @@ internal class SpotiFlyerListImpl(
|
|||||||
instanceKeeper.getStore {
|
instanceKeeper.getStore {
|
||||||
SpotiFlyerListStoreProvider(
|
SpotiFlyerListStoreProvider(
|
||||||
storeFactory = storeFactory,
|
storeFactory = storeFactory,
|
||||||
database = database,
|
fetchQuery = fetchQuery,
|
||||||
link = link
|
link = link
|
||||||
).provide()
|
).provide()
|
||||||
}
|
}
|
||||||
|
|
||||||
override val models: Flow<State> = store.states
|
override val models: Flow<State> = store.states
|
||||||
|
|
||||||
override fun onDownloadClicked(trackList: List<TrackDetails>) {
|
override fun onDownloadAllClicked(trackList: List<TrackDetails>) {
|
||||||
store.accept(Intent.StartDownload(trackList))
|
store.accept(Intent.StartDownloadAll(trackList))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDownloadClicked(wholeTrackList: List<TrackDetails>, trackIndex: Int) {
|
||||||
|
store.accept(Intent.StartDownload(wholeTrackList,trackIndex))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed(){
|
override fun onBackPressed(){
|
||||||
|
@ -9,7 +9,8 @@ import com.shabinder.common.list.store.SpotiFlyerListStore.*
|
|||||||
|
|
||||||
internal interface SpotiFlyerListStore: Store<Intent, State, Nothing> {
|
internal interface SpotiFlyerListStore: Store<Intent, State, Nothing> {
|
||||||
sealed class Intent {
|
sealed class Intent {
|
||||||
data class StartDownload(val trackList: List<TrackDetails>): Intent()
|
data class StartDownloadAll(val trackList: List<TrackDetails>): Intent()
|
||||||
|
data class StartDownload(val wholeTrackList: List<TrackDetails>, val trackIndex:Int): Intent()
|
||||||
data class SearchLink(val link: String): Intent()
|
data class SearchLink(val link: String): Intent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,18 +2,13 @@ package com.shabinder.common.list.store
|
|||||||
|
|
||||||
import com.arkivanov.mvikotlin.core.store.*
|
import com.arkivanov.mvikotlin.core.store.*
|
||||||
import com.arkivanov.mvikotlin.extensions.coroutines.SuspendExecutor
|
import com.arkivanov.mvikotlin.extensions.coroutines.SuspendExecutor
|
||||||
import com.shabinder.common.FetchPlatformQueryResult
|
import com.shabinder.common.*
|
||||||
import com.shabinder.common.PlatformQueryResult
|
|
||||||
import com.shabinder.common.list.SpotiFlyerList.State
|
import com.shabinder.common.list.SpotiFlyerList.State
|
||||||
import com.shabinder.common.list.store.SpotiFlyerListStore.Intent
|
import com.shabinder.common.list.store.SpotiFlyerListStore.Intent
|
||||||
import com.shabinder.database.Database
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
internal class SpotiFlyerListStoreProvider(
|
internal class SpotiFlyerListStoreProvider(
|
||||||
private val storeFactory: StoreFactory,
|
private val storeFactory: StoreFactory,
|
||||||
private val database: Database,
|
private val fetchQuery: FetchPlatformQueryResult,
|
||||||
private val link: String
|
private val link: String
|
||||||
) {
|
) {
|
||||||
fun provide(): SpotiFlyerListStore =
|
fun provide(): SpotiFlyerListStore =
|
||||||
@ -28,21 +23,45 @@ internal class SpotiFlyerListStoreProvider(
|
|||||||
private sealed class Result {
|
private sealed class Result {
|
||||||
data class ResultFetched(val result: PlatformQueryResult) : Result()
|
data class ResultFetched(val result: PlatformQueryResult) : Result()
|
||||||
data class SearchLink(val link: String) : Result()
|
data class SearchLink(val link: String) : Result()
|
||||||
|
data class UpdateTrackList(val list:List<TrackDetails>): Result()
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class ExecutorImpl : SuspendExecutor<Intent, Unit, State, Result, Nothing>() {
|
private inner class ExecutorImpl : SuspendExecutor<Intent, Unit, State, Result, Nothing>() {
|
||||||
override suspend fun executeAction(action: Unit, getState: () -> State) {
|
override suspend fun executeAction(action: Unit, getState: () -> State) {
|
||||||
FetchPlatformQueryResult().query(link)?.let{
|
fetchQuery.query(link)?.let{
|
||||||
dispatch(Result.ResultFetched(it))
|
dispatch(Result.ResultFetched(it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
|
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
|
||||||
when (intent) {//TODO: Add Dispatchers where needed
|
when (intent) {//TODO: Add Dispatchers where needed
|
||||||
is Intent.StartDownload -> {}//TODO()
|
is Intent.SearchLink -> fetchQuery.query(link)?.let{
|
||||||
is Intent.SearchLink -> FetchPlatformQueryResult().query(link)?.let{
|
|
||||||
dispatch((Result.ResultFetched(it)))
|
dispatch((Result.ResultFetched(it)))
|
||||||
}
|
}
|
||||||
|
is Intent.StartDownloadAll -> {
|
||||||
|
val finalList =
|
||||||
|
intent.trackList.filter { it.downloaded == DownloadStatus.NotDownloaded }
|
||||||
|
if (finalList.isNullOrEmpty()) //TODO showDialog("All Songs are Processed")
|
||||||
|
else downloadTracks(finalList)
|
||||||
|
|
||||||
|
val list = intent.trackList.map {
|
||||||
|
if (it.downloaded == DownloadStatus.NotDownloaded) {
|
||||||
|
it.downloaded = DownloadStatus.Queued
|
||||||
|
}
|
||||||
|
it
|
||||||
|
}
|
||||||
|
dispatch(Result.UpdateTrackList(list))
|
||||||
|
}
|
||||||
|
is Intent.StartDownload -> {
|
||||||
|
val trackList = intent.wholeTrackList.toMutableList()
|
||||||
|
val track = trackList.getOrNull(intent.trackIndex)
|
||||||
|
?.apply { downloaded = DownloadStatus.Queued }
|
||||||
|
track?.let {
|
||||||
|
trackList[intent.trackIndex] = it
|
||||||
|
dispatch(Result.UpdateTrackList(trackList))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,8 +69,9 @@ internal class SpotiFlyerListStoreProvider(
|
|||||||
private object ReducerImpl : Reducer<State, Result> {
|
private object ReducerImpl : Reducer<State, Result> {
|
||||||
override fun State.reduce(result: Result): State =
|
override fun State.reduce(result: Result): State =
|
||||||
when (result) {
|
when (result) {
|
||||||
is Result.ResultFetched -> copy(result = result.result)
|
is Result.ResultFetched -> copy(queryResult = result.result)
|
||||||
is Result.SearchLink -> copy(link = result.link)
|
is Result.SearchLink -> copy(link = result.link)
|
||||||
}
|
is Result.UpdateTrackList -> copy(queryResult = this.queryResult?.apply { trackList = result.list })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.shabinder.common.main
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SpotiFlyerMainContent(component: SpotiFlyerMain){
|
||||||
|
val model by component.models.collectAsState(SpotiFlyerMain.State())
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.shabinder.common.root
|
||||||
|
|
||||||
|
import com.arkivanov.decompose.ComponentContext
|
||||||
|
import com.arkivanov.decompose.RouterState
|
||||||
|
import com.arkivanov.decompose.value.Value
|
||||||
|
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
||||||
|
import com.shabinder.common.FetchPlatformQueryResult
|
||||||
|
import com.shabinder.common.list.SpotiFlyerList
|
||||||
|
import com.shabinder.common.main.SpotiFlyerMain
|
||||||
|
import com.shabinder.common.root.SpotiFlyerRoot.Dependencies
|
||||||
|
import com.shabinder.database.Database
|
||||||
|
import com.shabinder.common.root.integration.SpotiFlyerRootImpl
|
||||||
|
|
||||||
|
interface SpotiFlyerRoot {
|
||||||
|
|
||||||
|
val routerState: Value<RouterState<*, Child>>
|
||||||
|
|
||||||
|
sealed class Child {
|
||||||
|
data class Main(val component: SpotiFlyerMain) : Child()
|
||||||
|
data class List(val component: SpotiFlyerList) : Child()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
val storeFactory: StoreFactory
|
||||||
|
val database: Database
|
||||||
|
val fetchPlatformQueryResult: FetchPlatformQueryResult
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("FunctionName") // Factory function
|
||||||
|
fun SpotiFlyerRoot(componentContext: ComponentContext, dependencies: Dependencies): SpotiFlyerRoot =
|
||||||
|
SpotiFlyerRootImpl(componentContext, dependencies)
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.shabinder.common.root
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import com.arkivanov.decompose.extensions.compose.jetbrains.Children
|
||||||
|
import com.shabinder.common.list.SpotiFlyerListContent
|
||||||
|
import com.shabinder.common.main.SpotiFlyerMainContent
|
||||||
|
import com.shabinder.common.root.SpotiFlyerRoot.Child
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SpotiFlyerRootContent(component: SpotiFlyerRoot) {
|
||||||
|
Children(
|
||||||
|
routerState = component.routerState,
|
||||||
|
//TODO animation = crossfade()
|
||||||
|
) { child, _ ->
|
||||||
|
when (child) {
|
||||||
|
is Child.Main -> SpotiFlyerMainContent(component = child.component)
|
||||||
|
is Child.List -> SpotiFlyerListContent(component = child.component)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
package com.shabinder.common.root.integration
|
||||||
|
|
||||||
|
import com.arkivanov.decompose.ComponentContext
|
||||||
|
import com.arkivanov.decompose.RouterState
|
||||||
|
import com.arkivanov.decompose.pop
|
||||||
|
import com.arkivanov.decompose.push
|
||||||
|
import com.arkivanov.decompose.router
|
||||||
|
import com.arkivanov.decompose.statekeeper.Parcelable
|
||||||
|
import com.arkivanov.decompose.statekeeper.Parcelize
|
||||||
|
import com.arkivanov.decompose.value.Value
|
||||||
|
import com.shabinder.common.list.SpotiFlyerList
|
||||||
|
import com.shabinder.common.main.SpotiFlyerMain
|
||||||
|
import com.shabinder.common.root.SpotiFlyerRoot
|
||||||
|
import com.shabinder.common.root.SpotiFlyerRoot.Child
|
||||||
|
import com.shabinder.common.root.SpotiFlyerRoot.Dependencies
|
||||||
|
import com.shabinder.common.utils.Consumer
|
||||||
|
|
||||||
|
internal class SpotiFlyerRootImpl(
|
||||||
|
componentContext: ComponentContext,
|
||||||
|
dependencies: Dependencies
|
||||||
|
) : SpotiFlyerRoot, ComponentContext by componentContext, Dependencies by dependencies {
|
||||||
|
|
||||||
|
private val router =
|
||||||
|
router<Configuration, Child>(
|
||||||
|
initialConfiguration = Configuration.Main,
|
||||||
|
handleBackButton = true,
|
||||||
|
componentFactory = ::createChild
|
||||||
|
)
|
||||||
|
|
||||||
|
override val routerState: Value<RouterState<*, Child>> = router.state
|
||||||
|
|
||||||
|
private fun createChild(configuration: Configuration, componentContext: ComponentContext): Child =
|
||||||
|
when (configuration) {
|
||||||
|
is Configuration.Main -> Child.Main(spotiFlyerMain(componentContext))
|
||||||
|
is Configuration.Edit -> Child.List(spotiFlyerList(componentContext, link = configuration.link))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun spotiFlyerMain(componentContext: ComponentContext): SpotiFlyerMain =
|
||||||
|
SpotiFlyerMain(
|
||||||
|
componentContext = componentContext,
|
||||||
|
dependencies = object : SpotiFlyerMain.Dependencies, Dependencies by this {
|
||||||
|
override fun mainOutput(searched: SpotiFlyerMain.Output): Consumer<SpotiFlyerMain.Output> = Consumer(::onMainOutput)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun spotiFlyerList(componentContext: ComponentContext, link: String): SpotiFlyerList =
|
||||||
|
SpotiFlyerList(
|
||||||
|
componentContext = componentContext,
|
||||||
|
dependencies = object : SpotiFlyerList.Dependencies, Dependencies by this {
|
||||||
|
override val fetchQuery = fetchPlatformQueryResult
|
||||||
|
override val link: String = link
|
||||||
|
|
||||||
|
override fun listOutput(finished: SpotiFlyerList.Output.Finished): Consumer<SpotiFlyerList.Output> =
|
||||||
|
Consumer(::onListOutput)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun onMainOutput(output: SpotiFlyerMain.Output): Unit =
|
||||||
|
when (output) {
|
||||||
|
is SpotiFlyerMain.Output.Search -> router.push(Configuration.Edit(link = output.link))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onListOutput(output: SpotiFlyerList.Output): Unit =
|
||||||
|
when (output) {
|
||||||
|
is SpotiFlyerList.Output.Finished -> router.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class Configuration : Parcelable {
|
||||||
|
@Parcelize
|
||||||
|
object Main : Configuration()
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class Edit(val link: String) : Configuration()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.shabinder.common.ui
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
expect fun ImageLoad(
|
||||||
|
url:String,
|
||||||
|
loadingResource: ImageBitmap? = null,
|
||||||
|
errorResource: ImageBitmap? = null,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
)
|
@ -1,257 +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.spotiflyer.ui.tracklist
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import androidx.compose.foundation.Image
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.*
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.material.*
|
|
||||||
import androidx.compose.runtime.*
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.layout.ContentScale
|
|
||||||
import androidx.compose.ui.platform.AmbientContext
|
|
||||||
import androidx.compose.ui.res.vectorResource
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.net.toUri
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import com.shabinder.spotiflyer.R
|
|
||||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
|
||||||
import com.shabinder.spotiflyer.models.PlatformQueryResult
|
|
||||||
import com.shabinder.spotiflyer.models.TrackDetails
|
|
||||||
import com.shabinder.spotiflyer.providers.GaanaProvider
|
|
||||||
import com.shabinder.spotiflyer.providers.SpotifyProvider
|
|
||||||
import com.shabinder.spotiflyer.providers.YoutubeProvider
|
|
||||||
import com.shabinder.spotiflyer.ui.SpotiFlyerTypography
|
|
||||||
import com.shabinder.spotiflyer.ui.colorAccent
|
|
||||||
import com.shabinder.spotiflyer.ui.utils.calculateDominantColor
|
|
||||||
import com.shabinder.spotiflyer.utils.downloadTracks
|
|
||||||
import com.shabinder.spotiflyer.utils.sharedViewModel
|
|
||||||
import com.shabinder.spotiflyer.utils.showDialog
|
|
||||||
import com.shabinder.spotiflyer.worker.ForegroundService
|
|
||||||
import dev.chrisbanes.accompanist.coil.CoilImage
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
|
|
||||||
/*
|
|
||||||
* UI for List of Tracks to be universally used.
|
|
||||||
**/
|
|
||||||
@Composable
|
|
||||||
fun TrackList(
|
|
||||||
fullLink: String,
|
|
||||||
navController: NavController,
|
|
||||||
spotifyProvider: SpotifyProvider,
|
|
||||||
gaanaProvider: GaanaProvider,
|
|
||||||
youtubeProvider: YoutubeProvider,
|
|
||||||
modifier: Modifier = Modifier
|
|
||||||
){
|
|
||||||
val context = AmbientContext.current
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
var result by remember(fullLink) { mutableStateOf<PlatformQueryResult?>(null) }
|
|
||||||
|
|
||||||
coroutineScope.launch(Dispatchers.Default) {
|
|
||||||
@Suppress("UnusedEquals")//Add Delay if result is not Initialized yet.
|
|
||||||
try{result == null}catch(e:java.lang.IllegalStateException){delay(100)}
|
|
||||||
if(result == null){
|
|
||||||
result = when{
|
|
||||||
/*
|
|
||||||
* Using SharedViewModel's Link as NAVIGATION's Arg is buggy for links.
|
|
||||||
* */
|
|
||||||
//SPOTIFY
|
|
||||||
sharedViewModel.link.contains("spotify",true) ->
|
|
||||||
spotifyProvider.query(sharedViewModel.link)
|
|
||||||
|
|
||||||
//YOUTUBE
|
|
||||||
sharedViewModel.link.contains("youtube.com",true) || sharedViewModel.link.contains("youtu.be",true) ->
|
|
||||||
youtubeProvider.query(sharedViewModel.link)
|
|
||||||
|
|
||||||
//GAANA
|
|
||||||
sharedViewModel.link.contains("gaana",true) ->
|
|
||||||
gaanaProvider.query(sharedViewModel.link)
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
showDialog("Link is Not Valid")
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
withContext(Dispatchers.Main){
|
|
||||||
//Error Occurred And Has Been Shown to User
|
|
||||||
if(result == null) navController.popBackStack()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sharedViewModel.updateTrackList(result?.trackList ?: listOf())
|
|
||||||
queryActiveTracks(context)
|
|
||||||
|
|
||||||
result?.let{
|
|
||||||
val ctx = AmbientContext.current
|
|
||||||
Box(modifier = modifier.fillMaxSize()){
|
|
||||||
LazyColumn(
|
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
|
||||||
content = {
|
|
||||||
item {
|
|
||||||
CoverImage(it.title,it.coverUrl,coroutineScope)
|
|
||||||
}
|
|
||||||
itemsIndexed(sharedViewModel.trackList) { index, item ->
|
|
||||||
TrackCard(
|
|
||||||
track = item,
|
|
||||||
onDownload = {
|
|
||||||
downloadTracks(arrayListOf(item),ctx)
|
|
||||||
sharedViewModel.updateTrackStatus(index,DownloadStatus.Queued)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
)
|
|
||||||
DownloadAllButton(
|
|
||||||
onClick = {
|
|
||||||
val finalList = sharedViewModel.trackList.filter{it.downloaded == DownloadStatus.NotDownloaded}
|
|
||||||
if (finalList.isNullOrEmpty()) showDialog("All Songs are Processed")
|
|
||||||
else downloadTracks(finalList as ArrayList<TrackDetails>,ctx)
|
|
||||||
val list = sharedViewModel.trackList.map {
|
|
||||||
if(it.downloaded == DownloadStatus.NotDownloaded){
|
|
||||||
it.downloaded = DownloadStatus.Queued
|
|
||||||
}
|
|
||||||
it
|
|
||||||
}
|
|
||||||
sharedViewModel.updateTrackList(list)
|
|
||||||
},
|
|
||||||
modifier = Modifier.padding(bottom = 24.dp).align(Alignment.BottomCenter)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun CoverImage(
|
|
||||||
title: String,
|
|
||||||
coverURL: String,
|
|
||||||
scope: CoroutineScope,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
val ctx = AmbientContext.current
|
|
||||||
Column(
|
|
||||||
modifier.padding(vertical = 8.dp).fillMaxWidth(),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
|
||||||
) {
|
|
||||||
val imgUri = coverURL.toUri().buildUpon().scheme("https").build()
|
|
||||||
CoilImage(
|
|
||||||
data = imgUri,
|
|
||||||
contentScale = ContentScale.Crop,
|
|
||||||
loading = { Image(vectorResource(id = R.drawable.ic_musicplaceholder)) },
|
|
||||||
modifier = Modifier
|
|
||||||
.preferredWidth(210.dp)
|
|
||||||
.preferredHeight(230.dp)
|
|
||||||
.clip(MaterialTheme.shapes.medium)
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = title,
|
|
||||||
style = SpotiFlyerTypography.h5,
|
|
||||||
maxLines = 2,
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
//color = colorAccent,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
scope.launch {
|
|
||||||
updateGradient(coverURL, ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun DownloadAllButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
|
|
||||||
ExtendedFloatingActionButton(
|
|
||||||
text = { Text("Download All") },
|
|
||||||
onClick = onClick,
|
|
||||||
icon = { Icon(imageVector = vectorResource(R.drawable.ic_download_arrow),tint = Color.Black) },
|
|
||||||
backgroundColor = colorAccent,
|
|
||||||
modifier = modifier
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun TrackCard(
|
|
||||||
track:TrackDetails,
|
|
||||||
onDownload:(TrackDetails)->Unit,
|
|
||||||
) {
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)) {
|
|
||||||
val imgUri = track.albumArtURL.toUri().buildUpon().scheme("https").build()
|
|
||||||
CoilImage(
|
|
||||||
data = imgUri,
|
|
||||||
//Loading Placeholder Makes Scrolling very stuttery
|
|
||||||
// loading = { Image(vectorResource(id = R.drawable.ic_song_placeholder)) },
|
|
||||||
error = { Image(vectorResource(id = R.drawable.ic_musicplaceholder)) },
|
|
||||||
contentScale = ContentScale.Inside,
|
|
||||||
// fadeIn = true,
|
|
||||||
modifier = Modifier.preferredHeight(75.dp).preferredWidth(90.dp)
|
|
||||||
)
|
|
||||||
Column(modifier = Modifier.padding(horizontal = 8.dp).preferredHeight(60.dp).weight(1f),verticalArrangement = Arrangement.SpaceEvenly) {
|
|
||||||
Text(track.title,maxLines = 1,overflow = TextOverflow.Ellipsis,style = SpotiFlyerTypography.h6,color = colorAccent)
|
|
||||||
Row(
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
verticalAlignment = Alignment.Bottom,
|
|
||||||
modifier = Modifier.padding(horizontal = 8.dp).fillMaxSize()
|
|
||||||
){
|
|
||||||
Text("${track.artists.firstOrNull()}...",fontSize = 12.sp,maxLines = 1)
|
|
||||||
Text("${track.durationSec/60} min, ${track.durationSec%60} sec",fontSize = 12.sp,maxLines = 1,overflow = TextOverflow.Ellipsis)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
when(track.downloaded){
|
|
||||||
DownloadStatus.Downloaded -> {
|
|
||||||
Image(vectorResource(id = R.drawable.ic_tick))
|
|
||||||
}
|
|
||||||
DownloadStatus.Queued -> {
|
|
||||||
CircularProgressIndicator()
|
|
||||||
}
|
|
||||||
DownloadStatus.Failed -> {
|
|
||||||
Image(vectorResource(id = R.drawable.ic_error))
|
|
||||||
}
|
|
||||||
DownloadStatus.Downloading -> {
|
|
||||||
CircularProgressIndicator(progress = track.progress.toFloat()/100f)
|
|
||||||
}
|
|
||||||
DownloadStatus.Converting -> {
|
|
||||||
CircularProgressIndicator(progress = 100f,color = colorAccent)
|
|
||||||
}
|
|
||||||
DownloadStatus.NotDownloaded -> {
|
|
||||||
Image(vectorResource(id = R.drawable.ic_arrow), Modifier.clickable(onClick = {
|
|
||||||
onDownload(track)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private fun queryActiveTracks(context:Context?) {
|
|
||||||
val serviceIntent = Intent(context, ForegroundService::class.java).apply {
|
|
||||||
action = "query"
|
|
||||||
}
|
|
||||||
context?.let { ContextCompat.startForegroundService(it, serviceIntent) }
|
|
||||||
}
|
|
||||||
suspend fun updateGradient(imageURL:String,ctx:Context){
|
|
||||||
calculateDominantColor(imageURL,ctx)?.color
|
|
||||||
?.let { sharedViewModel.updateGradientColor(it) }
|
|
||||||
}
|
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.shabinder.common.ui
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
actual fun ImageLoad(
|
||||||
|
url:String,
|
||||||
|
loadingResource: ImageBitmap?,
|
||||||
|
errorResource: ImageBitmap?,
|
||||||
|
modifier: Modifier
|
||||||
|
){
|
||||||
|
|
||||||
|
}
|
@ -6,6 +6,22 @@ import co.touchlab.kermit.Kermit
|
|||||||
import com.shabinder.common.database.appContext
|
import com.shabinder.common.database.appContext
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
actual fun openPlatform(platformID:String ,platformLink:String){
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun shareApp(){
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun giveDonation(){
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun downloadTracks(list: List<TrackDetails>){
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
actual open class Dir actual constructor(logger: Kermit) {
|
actual open class Dir actual constructor(logger: Kermit) {
|
||||||
|
|
||||||
private val context:Context
|
private val context:Context
|
@ -10,6 +10,8 @@ import io.ktor.client.*
|
|||||||
import io.ktor.client.features.json.*
|
import io.ktor.client.features.json.*
|
||||||
import io.ktor.client.features.json.serializer.*
|
import io.ktor.client.features.json.serializer.*
|
||||||
import io.ktor.client.features.logging.*
|
import io.ktor.client.features.logging.*
|
||||||
|
import io.ktor.client.request.*
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import org.koin.core.context.startKoin
|
import org.koin.core.context.startKoin
|
||||||
import org.koin.dsl.KoinAppDeclaration
|
import org.koin.dsl.KoinAppDeclaration
|
||||||
@ -38,6 +40,17 @@ val kotlinxSerializer = KotlinxSerializer( Json {
|
|||||||
ignoreUnknownKeys = true
|
ignoreUnknownKeys = true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
fun isInternetAvailable(): Boolean {
|
||||||
|
return runBlocking {
|
||||||
|
try {
|
||||||
|
ktorHttpClient.head<String>("http://google.com")
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println(e.message)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
fun createHttpClient(enableNetworkLogs: Boolean,serializer: KotlinxSerializer = kotlinxSerializer) = HttpClient {
|
fun createHttpClient(enableNetworkLogs: Boolean,serializer: KotlinxSerializer = kotlinxSerializer) = HttpClient {
|
||||||
install(JsonFeature) {
|
install(JsonFeature) {
|
||||||
this.serializer = serializer
|
this.serializer = serializer
|
||||||
@ -49,3 +62,4 @@ fun createHttpClient(enableNetworkLogs: Boolean,serializer: KotlinxSerializer =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val ktorHttpClient = HttpClient {}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.shabinder.common
|
package com.shabinder.common
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
import co.touchlab.kermit.Kermit
|
import co.touchlab.kermit.Kermit
|
||||||
import com.shabinder.common.utils.removeIllegalChars
|
import com.shabinder.common.utils.removeIllegalChars
|
||||||
|
|
||||||
@ -9,6 +10,7 @@ expect fun shareApp()
|
|||||||
|
|
||||||
expect fun giveDonation()
|
expect fun giveDonation()
|
||||||
|
|
||||||
|
expect fun downloadTracks(list: List<TrackDetails>)
|
||||||
|
|
||||||
expect open class Dir(
|
expect open class Dir(
|
||||||
logger: Kermit
|
logger: Kermit
|
||||||
|
@ -1,8 +1,46 @@
|
|||||||
package com.shabinder.common
|
package com.shabinder.common
|
||||||
|
|
||||||
//TODO
|
import com.shabinder.common.database.DownloadRecordDatabaseQueries
|
||||||
class FetchPlatformQueryResult {
|
import com.shabinder.common.providers.GaanaProvider
|
||||||
|
import com.shabinder.common.providers.SpotifyProvider
|
||||||
|
import com.shabinder.database.Database
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
class FetchPlatformQueryResult(
|
||||||
|
private val gaanaProvider: GaanaProvider,
|
||||||
|
private val spotifyProvider: SpotifyProvider,
|
||||||
|
private val youtubeProvider: YoutubeProvider,
|
||||||
|
private val database: Database
|
||||||
|
) {
|
||||||
|
private val db:DownloadRecordDatabaseQueries
|
||||||
|
get() = database.downloadRecordDatabaseQueries
|
||||||
|
|
||||||
suspend fun query(link:String): PlatformQueryResult?{
|
suspend fun query(link:String): PlatformQueryResult?{
|
||||||
return null
|
val result = when{
|
||||||
|
//SPOTIFY
|
||||||
|
link.contains("spotify",true) ->
|
||||||
|
spotifyProvider.query(link)
|
||||||
|
|
||||||
|
//YOUTUBE
|
||||||
|
link.contains("youtube.com",true) || link.contains("youtu.be",true) ->
|
||||||
|
youtubeProvider.query(link)
|
||||||
|
|
||||||
|
//GAANA
|
||||||
|
link.contains("gaana",true) ->
|
||||||
|
gaanaProvider.query(link)
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result?.run {
|
||||||
|
withContext(Dispatchers.Default){
|
||||||
|
db.add(
|
||||||
|
folderType, title, link, coverUrl, trackList.size.toLong()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,6 +3,22 @@ package com.shabinder.common
|
|||||||
import co.touchlab.kermit.Kermit
|
import co.touchlab.kermit.Kermit
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
actual fun openPlatform(platformID:String ,platformLink:String){
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun shareApp(){
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun giveDonation(){
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun downloadTracks(list: List<TrackDetails>){
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
actual open class Dir actual constructor(private val logger: Kermit) {
|
actual open class Dir actual constructor(private val logger: Kermit) {
|
||||||
|
|
||||||
actual fun fileSeparator(): String = File.separator
|
actual fun fileSeparator(): String = File.separator
|
Loading…
Reference in New Issue
Block a user