Fixes & Refactors

This commit is contained in:
shabinder 2021-02-10 01:12:01 +05:30
parent 85a8dfec68
commit f9120a1fb4
32 changed files with 157 additions and 282 deletions

View File

@ -9,6 +9,7 @@ import com.arkivanov.mvikotlin.logging.store.LoggingStoreFactory
import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
import com.shabinder.common.di.Dir import com.shabinder.common.di.Dir
import com.shabinder.common.di.FetchPlatformQueryResult import com.shabinder.common.di.FetchPlatformQueryResult
import com.shabinder.common.di.createDirectories
import com.shabinder.common.root.SpotiFlyerRoot import com.shabinder.common.root.SpotiFlyerRoot
import com.shabinder.common.root.SpotiFlyerRootContent import com.shabinder.common.root.SpotiFlyerRootContent
import com.shabinder.common.ui.SpotiFlyerTheme import com.shabinder.common.ui.SpotiFlyerTheme
@ -28,6 +29,7 @@ class MainActivity : AppCompatActivity() {
SpotiFlyerRootContent(rootComponent(::spotiFlyerRoot)) SpotiFlyerRootContent(rootComponent(::spotiFlyerRoot))
} }
} }
dir.createDirectories()
} }

View File

@ -13,6 +13,7 @@ kotlin {
implementation(compose.materialIconsExtended) implementation(compose.materialIconsExtended)
//implementation("org.jetbrains.compose.material:material-icons-extended:0.3.0-build150") //implementation("org.jetbrains.compose.material:material-icons-extended:0.3.0-build150")
implementation(project(":common:dependency-injection")) implementation(project(":common:dependency-injection"))
//implementation("com.alialbaali.kamel:kamel-image:0.0.7")
implementation(project(":common:data-models")) implementation(project(":common:data-models"))
implementation(project(":common:database")) implementation(project(":common:database"))
implementation(SqlDelight.coroutineExtensions) implementation(SqlDelight.coroutineExtensions)

View File

@ -1,48 +1,9 @@
package com.shabinder.common.ui package com.shabinder.common.ui
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import com.shabinder.common.di.Picture
import com.shabinder.common.database.appContext import com.shabinder.common.database.appContext
@Composable
actual fun ImageLoad(
pic: Picture?,
modifier: Modifier
){
Image(pic?.image?.asImageBitmap(), vectorResource(R.drawable.music) ,"Image",modifier)
}
@Composable
fun Image(pic: ImageBitmap?, placeholder:ImageVector, desc: String,modifier:Modifier = Modifier) {
if(pic == null) Image(placeholder,desc,modifier) else Image(pic,desc,modifier)
}
/*
@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
)
}
*/
@Composable @Composable
actual fun Toast( actual fun Toast(
text: String, text: String,

View File

@ -34,6 +34,9 @@ actual fun DownloadImageArrow(modifier: Modifier){
@Composable @Composable
actual fun DownloadAllImage() = vectorResource(R.drawable.ic_download_arrow) actual fun DownloadAllImage() = vectorResource(R.drawable.ic_download_arrow)
@Composable
actual fun PlaceHolderImage() = vectorResource(R.drawable.music)
@Composable @Composable
actual fun SpotiFlyerLogo() = vectorResource(R.drawable.ic_spotiflyer_logo) actual fun SpotiFlyerLogo() = vectorResource(R.drawable.ic_spotiflyer_logo)
@ -51,5 +54,6 @@ actual fun YoutubeLogo() = vectorResource(R.drawable.ic_youtube)
@Composable @Composable
actual fun YoutubeMusicLogo() = vectorResource(R.drawable.ic_youtube_music_logo) actual fun YoutubeMusicLogo() = vectorResource(R.drawable.ic_youtube_music_logo)
@Composable @Composable
actual fun GithubLogo() = vectorResource(R.drawable.ic_github) actual fun GithubLogo() = vectorResource(R.drawable.ic_github)

View File

@ -1,10 +1,10 @@
package com.shabinder.common.list package com.shabinder.common.list
import androidx.compose.ui.graphics.ImageBitmap
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.di.Dir import com.shabinder.common.di.Dir
import com.shabinder.common.di.FetchPlatformQueryResult import com.shabinder.common.di.FetchPlatformQueryResult
import com.shabinder.common.di.Picture
import com.shabinder.common.list.integration.SpotiFlyerListImpl import com.shabinder.common.list.integration.SpotiFlyerListImpl
import com.shabinder.common.models.spotify.Source import com.shabinder.common.models.spotify.Source
import com.shabinder.common.utils.Consumer import com.shabinder.common.utils.Consumer
@ -33,14 +33,14 @@ interface SpotiFlyerList {
/* /*
* Load Image from cache/Internet and cache it * Load Image from cache/Internet and cache it
* */ * */
fun loadImage(url:String): Picture? suspend fun loadImage(url:String): ImageBitmap?
interface Dependencies { interface Dependencies {
val storeFactory: StoreFactory val storeFactory: StoreFactory
val fetchQuery: FetchPlatformQueryResult val fetchQuery: FetchPlatformQueryResult
val dir: Dir val dir: Dir
val link: String val link: String
fun listOutput(finished: Output.Finished): Consumer<Output> val listOutput: Consumer<Output>
} }
sealed class Output { sealed class Output {
object Finished : Output() object Finished : Output()

View File

@ -5,25 +5,23 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.*
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.DownloadStatus
import com.shabinder.common.di.Picture
import com.shabinder.common.models.TrackDetails import com.shabinder.common.models.TrackDetails
import com.shabinder.common.ui.* import com.shabinder.common.ui.*
import com.shabinder.common.ui.SpotiFlyerTypography import com.shabinder.common.ui.SpotiFlyerTypography
import com.shabinder.common.ui.colorAccent import com.shabinder.common.ui.colorAccent
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@Composable @Composable
fun SpotiFlyerListContent( fun SpotiFlyerListContent(
@ -64,12 +62,17 @@ fun SpotiFlyerListContent(
fun TrackCard( fun TrackCard(
track: TrackDetails, track: TrackDetails,
downloadTrack:()->Unit, downloadTrack:()->Unit,
loadImage:(String)-> Picture? loadImage:suspend (String)-> ImageBitmap?
) { ) {
Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)) { Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)) {
val pic: Picture? = loadImage(track.albumArtURL) var pic by mutableStateOf<ImageBitmap?>(null)
val scope = rememberCoroutineScope()
scope.launch {
pic = loadImage(track.albumArtURL)
}
ImageLoad( ImageLoad(
pic = pic, pic = pic,
"Album Art",
modifier = Modifier modifier = Modifier
.preferredWidth(75.dp) .preferredWidth(75.dp)
.preferredHeight(90.dp) .preferredHeight(90.dp)
@ -116,16 +119,20 @@ fun CoverImage(
title: String, title: String,
coverURL: String, coverURL: String,
scope: CoroutineScope, scope: CoroutineScope,
loadImage: (String) -> Picture?, loadImage: suspend (String) -> ImageBitmap?,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
Column( Column(
modifier.padding(vertical = 8.dp).fillMaxWidth(), modifier.padding(vertical = 8.dp).fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
val pic = loadImage(coverURL) var pic by mutableStateOf<ImageBitmap?>(null)
scope.launch {
pic = loadImage(coverURL)
}
ImageLoad( ImageLoad(
pic, pic,
"Cover Image",
modifier = Modifier modifier = Modifier
.preferredWidth(210.dp) .preferredWidth(210.dp)
.preferredHeight(230.dp) .preferredHeight(230.dp)

View File

@ -1,8 +1,8 @@
package com.shabinder.common.list.integration package com.shabinder.common.list.integration
import androidx.compose.ui.graphics.ImageBitmap
import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.ComponentContext
import com.arkivanov.mvikotlin.extensions.coroutines.states import com.arkivanov.mvikotlin.extensions.coroutines.states
import com.shabinder.common.di.Picture
import com.shabinder.common.models.TrackDetails import com.shabinder.common.models.TrackDetails
import com.shabinder.common.list.SpotiFlyerList import com.shabinder.common.list.SpotiFlyerList
import com.shabinder.common.list.SpotiFlyerList.Dependencies import com.shabinder.common.list.SpotiFlyerList.Dependencies
@ -37,8 +37,8 @@ internal class SpotiFlyerListImpl(
} }
override fun onBackPressed(){ override fun onBackPressed(){
listOutput(SpotiFlyerList.Output.Finished) listOutput.callback(SpotiFlyerList.Output.Finished)
} }
override fun loadImage(url: String): Picture? = dir.loadImage(url) override suspend fun loadImage(url: String): ImageBitmap? = dir.loadImage(url)
} }

View File

@ -1,10 +1,10 @@
package com.shabinder.common.main package com.shabinder.common.main
import androidx.compose.ui.graphics.ImageBitmap
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.di.Dir import com.shabinder.common.di.Dir
import com.shabinder.common.models.DownloadRecord import com.shabinder.common.models.DownloadRecord
import com.shabinder.common.di.Picture
import com.shabinder.common.main.integration.SpotiFlyerMainImpl import com.shabinder.common.main.integration.SpotiFlyerMainImpl
import com.shabinder.common.utils.Consumer import com.shabinder.common.utils.Consumer
import com.shabinder.database.Database import com.shabinder.database.Database
@ -33,10 +33,10 @@ interface SpotiFlyerMain {
/* /*
* Load Image from cache/Internet and cache it * Load Image from cache/Internet and cache it
* */ * */
fun loadImage(url:String): Picture? suspend fun loadImage(url:String): ImageBitmap?
interface Dependencies { interface Dependencies {
fun mainOutput(searched: Output): Consumer<Output> val mainOutput: Consumer<Output>
val storeFactory: StoreFactory val storeFactory: StoreFactory
val database: Database val database: Database
val dir: Dir val dir: Dir

View File

@ -14,23 +14,24 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.History import androidx.compose.material.icons.outlined.History
import androidx.compose.material.icons.outlined.Info import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.rounded.* import androidx.compose.material.icons.rounded.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.*
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.shabinder.common.models.DownloadRecord import com.shabinder.common.models.DownloadRecord
import com.shabinder.common.di.Picture
import com.shabinder.common.main.SpotiFlyerMain.HomeCategory import com.shabinder.common.main.SpotiFlyerMain.HomeCategory
import com.shabinder.common.di.openPlatform import com.shabinder.common.di.openPlatform
import com.shabinder.common.ui.* import com.shabinder.common.ui.*
import com.shabinder.common.ui.SpotiFlyerTypography import com.shabinder.common.ui.SpotiFlyerTypography
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@Composable @Composable
fun SpotiFlyerMainContent(component: SpotiFlyerMain){ fun SpotiFlyerMainContent(component: SpotiFlyerMain){
@ -120,18 +121,18 @@ fun SearchPanel(
value = link, value = link,
onValueChange = updateLink , onValueChange = updateLink ,
leadingIcon = { leadingIcon = {
Icon(Icons.Rounded.Edit,"Link Text Box",tint = Color(0xFFCCCCCC))//LightGray Icon(Icons.Rounded.Edit,"Link Text Box",tint = Color.LightGray)
}, },
label = { Text(text = "Paste Link Here...",color = Color(0xFFCCCCCC)) }, label = { Text(text = "Paste Link Here...",color = Color.LightGray) },
singleLine = true, singleLine = true,
//textStyle = LocalTextStyle.current.merge(TextStyle(fontSize = 18.sp,color = Color.White)), textStyle = TextStyle.Default.merge(TextStyle(fontSize = 18.sp,color = Color.White)),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Uri), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Uri),
modifier = modifier.padding(12.dp).fillMaxWidth() modifier = modifier.padding(12.dp).fillMaxWidth()
.border( .border(
BorderStroke(2.dp, Brush.horizontalGradient(listOf(colorPrimary, colorAccent))), BorderStroke(2.dp, Brush.horizontalGradient(listOf(colorPrimary, colorAccent))),
RoundedCornerShape(30.dp) RoundedCornerShape(30.dp)
), ),
backgroundColor = Color(0xFF000000), backgroundColor = Color.Black,
shape = RoundedCornerShape(size = 30.dp), shape = RoundedCornerShape(size = 30.dp),
activeColor = transparent, activeColor = transparent,
inactiveColor = transparent, inactiveColor = transparent,
@ -158,7 +159,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
Column(modifier.fillMaxSize().verticalScroll(rememberScrollState())) { Column(modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
Card( Card(
modifier = modifier.fillMaxWidth(), modifier = modifier.fillMaxWidth(),
border = BorderStroke(1.dp,Color(0xFF888888))//Gray border = BorderStroke(1.dp,Color.Gray)
) { ) {
Column(modifier.padding(12.dp)) { Column(modifier.padding(12.dp)) {
Text( Text(
@ -171,28 +172,28 @@ fun AboutColumn(modifier: Modifier = Modifier) {
Icon( Icon(
imageVector = SpotifyLogo(), imageVector = SpotifyLogo(),
"Open Spotify", "Open Spotify",
tint = unspecifiedColor, tint = Color.Unspecified,
modifier = Modifier.clickable( modifier = Modifier.clickable(
onClick = { openPlatform("com.spotify.music","http://open.spotify.com") }) onClick = { openPlatform("com.spotify.music","http://open.spotify.com") })
) )
Spacer(modifier = modifier.padding(start = 16.dp)) Spacer(modifier = modifier.padding(start = 16.dp))
Icon(imageVector = GaanaLogo(), Icon(imageVector = GaanaLogo(),
"Open Gaana", "Open Gaana",
tint = unspecifiedColor, tint = Color.Unspecified,
modifier = Modifier.clickable( modifier = Modifier.clickable(
onClick = { openPlatform("com.gaana","http://gaana.com") }) onClick = { openPlatform("com.gaana","http://gaana.com") })
) )
Spacer(modifier = modifier.padding(start = 16.dp)) Spacer(modifier = modifier.padding(start = 16.dp))
Icon(imageVector = YoutubeLogo(), Icon(imageVector = YoutubeLogo(),
"Open Youtube", "Open Youtube",
tint = unspecifiedColor, tint = Color.Unspecified,
modifier = Modifier.clickable( modifier = Modifier.clickable(
onClick = { openPlatform("com.google.android.youtube","http://m.youtube.com") }) onClick = { openPlatform("com.google.android.youtube","http://m.youtube.com") })
) )
Spacer(modifier = modifier.padding(start = 12.dp)) Spacer(modifier = modifier.padding(start = 12.dp))
Icon(imageVector = YoutubeMusicLogo(), Icon(imageVector = YoutubeMusicLogo(),
"Open Youtube Music", "Open Youtube Music",
tint = unspecifiedColor, tint = Color.Unspecified,
modifier = Modifier.clickable( modifier = Modifier.clickable(
onClick = { openPlatform("com.google.android.apps.youtube.music","https://music.youtube.com/") }) onClick = { openPlatform("com.google.android.apps.youtube.music","https://music.youtube.com/") })
) )
@ -202,7 +203,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
Spacer(modifier = Modifier.padding(top = 8.dp)) Spacer(modifier = Modifier.padding(top = 8.dp))
Card( Card(
modifier = modifier.fillMaxWidth(), modifier = modifier.fillMaxWidth(),
border = BorderStroke(1.dp,Color(0xFF888888))//Gray border = BorderStroke(1.dp,Color.Gray)//Gray
) { ) {
Column(modifier.padding(12.dp)) { Column(modifier.padding(12.dp)) {
Text( Text(
@ -300,7 +301,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
@Composable @Composable
fun HistoryColumn( fun HistoryColumn(
list: List<DownloadRecord>, list: List<DownloadRecord>,
loadImage:(String)-> Picture?, loadImage:suspend (String)-> ImageBitmap?,
onItemClicked: (String) -> Unit onItemClicked: (String) -> Unit
) { ) {
LazyColumn( LazyColumn(
@ -321,13 +322,18 @@ fun HistoryColumn(
@Composable @Composable
fun DownloadRecordItem( fun DownloadRecordItem(
item: DownloadRecord, item: DownloadRecord,
loadImage:(String)-> Picture?, loadImage:suspend (String)-> ImageBitmap?,
onItemClicked:(String)->Unit onItemClicked:(String)->Unit
) { ) {
Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(end = 8.dp)) { Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(end = 8.dp)) {
val pic = loadImage(item.coverUrl) val scope = rememberCoroutineScope()
var pic by mutableStateOf<ImageBitmap?>(null)
scope.launch(Dispatchers.Unconfined) {
pic = loadImage(item.coverUrl)
}
ImageLoad( ImageLoad(
pic, pic,
"Album Art",
modifier = Modifier.preferredHeight(75.dp).preferredWidth(90.dp) modifier = Modifier.preferredHeight(75.dp).preferredWidth(90.dp)
) )
Column(modifier = Modifier.padding(horizontal = 8.dp).preferredHeight(60.dp).weight(1f),verticalArrangement = Arrangement.SpaceEvenly) { Column(modifier = Modifier.padding(horizontal = 8.dp).preferredHeight(60.dp).weight(1f),verticalArrangement = Arrangement.SpaceEvenly) {
@ -343,7 +349,7 @@ fun DownloadRecordItem(
} }
Image( Image(
imageVector = Icons.Rounded.Share, imageVector = Icons.Rounded.Share,
"Share App", "Research",
modifier = Modifier.clickable(onClick = { modifier = Modifier.clickable(onClick = {
//if(!isOnline(ctx)) showDialog("Check Your Internet Connection") else //if(!isOnline(ctx)) showDialog("Check Your Internet Connection") else
onItemClicked(item.link) onItemClicked(item.link)

View File

@ -1,8 +1,8 @@
package com.shabinder.common.main.integration package com.shabinder.common.main.integration
import androidx.compose.ui.graphics.ImageBitmap
import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.ComponentContext
import com.arkivanov.mvikotlin.extensions.coroutines.states import com.arkivanov.mvikotlin.extensions.coroutines.states
import com.shabinder.common.di.Picture
import com.shabinder.common.main.SpotiFlyerMain import com.shabinder.common.main.SpotiFlyerMain
import com.shabinder.common.main.SpotiFlyerMain.* import com.shabinder.common.main.SpotiFlyerMain.*
import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
@ -26,7 +26,7 @@ internal class SpotiFlyerMainImpl(
override val models: Flow<State> = store.states override val models: Flow<State> = store.states
override fun onLinkSearch(link: String) { override fun onLinkSearch(link: String) {
mainOutput(Output.Search(link = link)) mainOutput.callback(Output.Search(link = link))
} }
override fun onInputLinkChanged(link: String) { override fun onInputLinkChanged(link: String) {
@ -37,5 +37,5 @@ internal class SpotiFlyerMainImpl(
store.accept(Intent.SelectCategory(category)) store.accept(Intent.SelectCategory(category))
} }
override fun loadImage(url: String): Picture? = dir.loadImage(url) override suspend fun loadImage(url: String): ImageBitmap? = dir.loadImage(url)
} }

View File

@ -14,6 +14,7 @@ import com.shabinder.common.main.SpotiFlyerMain
import com.shabinder.common.root.SpotiFlyerRoot import com.shabinder.common.root.SpotiFlyerRoot
import com.shabinder.common.root.SpotiFlyerRoot.Child import com.shabinder.common.root.SpotiFlyerRoot.Child
import com.shabinder.common.root.SpotiFlyerRoot.Dependencies import com.shabinder.common.root.SpotiFlyerRoot.Dependencies
import com.shabinder.common.ui.showPopUpMessage
import com.shabinder.common.utils.Consumer import com.shabinder.common.utils.Consumer
internal class SpotiFlyerRootImpl( internal class SpotiFlyerRootImpl(
@ -40,7 +41,7 @@ internal class SpotiFlyerRootImpl(
SpotiFlyerMain( SpotiFlyerMain(
componentContext = componentContext, componentContext = componentContext,
dependencies = object : SpotiFlyerMain.Dependencies, Dependencies by this { dependencies = object : SpotiFlyerMain.Dependencies, Dependencies by this {
override fun mainOutput(searched: SpotiFlyerMain.Output): Consumer<SpotiFlyerMain.Output> = Consumer(::onMainOutput) override val mainOutput: Consumer<SpotiFlyerMain.Output> = Consumer(::onMainOutput)
override val dir: Dir = directories override val dir: Dir = directories
} }
) )
@ -52,16 +53,16 @@ internal class SpotiFlyerRootImpl(
override val fetchQuery = fetchPlatformQueryResult override val fetchQuery = fetchPlatformQueryResult
override val dir: Dir = directories override val dir: Dir = directories
override val link: String = link override val link: String = link
override val listOutput : Consumer<SpotiFlyerList.Output> = Consumer(::onListOutput)
override fun listOutput(finished: SpotiFlyerList.Output.Finished): Consumer<SpotiFlyerList.Output> =
Consumer(::onListOutput)
} }
) )
private fun onMainOutput(output: SpotiFlyerMain.Output): Unit = private fun onMainOutput(output: SpotiFlyerMain.Output){
showPopUpMessage("Button Pressed")
when (output) { when (output) {
is SpotiFlyerMain.Output.Search -> router.push(Configuration.List(link = output.link)) is SpotiFlyerMain.Output.Search -> router.push(Configuration.List(link = output.link))
} }
}
private fun onListOutput(output: SpotiFlyerList.Output): Unit = private fun onListOutput(output: SpotiFlyerList.Output): Unit =
when (output) { when (output) {

View File

@ -27,7 +27,6 @@ val colorSuccessGreen = Color(0xFF59C351)
val darkBackgroundColor = Color(0xFF000000) val darkBackgroundColor = Color(0xFF000000)
val colorOffWhite = Color(0xFFE7E7E7) val colorOffWhite = Color(0xFFE7E7E7)
val transparent = Color(0x00000000) val transparent = Color(0x00000000)
val unspecifiedColor =Color(0f, 0f, 0f, 0f)
val black = Color(0xFF000000) val black = Color(0xFF000000)
val lightGray = Color(0xFFCCCCCC) val lightGray = Color(0xFFCCCCCC)

View File

@ -1,13 +1,3 @@
package com.shabinder.common.ui package com.shabinder.common.ui
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.shabinder.common.di.Picture
@Composable
expect fun ImageLoad(
pic: Picture?,
modifier: Modifier = Modifier
)
expect fun showPopUpMessage(text: String) expect fun showPopUpMessage(text: String)

View File

@ -1,15 +1,25 @@
package com.shabinder.common.ui package com.shabinder.common.ui
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
@Composable
fun ImageLoad(pic: ImageBitmap?, desc: String, modifier:Modifier = Modifier, placeholder:ImageVector = PlaceHolderImage()) {
if(pic == null) Image(placeholder, desc, modifier) else Image(pic, desc, modifier)
}
@Composable @Composable
expect fun DownloadImageTick() expect fun DownloadImageTick()
@Composable @Composable
expect fun DownloadAllImage():ImageVector expect fun DownloadAllImage():ImageVector
@Composable
expect fun PlaceHolderImage():ImageVector
@Composable @Composable
expect fun SpotiFlyerLogo():ImageVector expect fun SpotiFlyerLogo():ImageVector

View File

@ -20,12 +20,15 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.BrokenImage
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.shabinder.common.ui.* import com.shabinder.common.ui.*
@ -43,7 +46,7 @@ fun Splash(modifier: Modifier = Modifier, onTimeout: () -> Unit) {
delay(SplashWaitTime) delay(SplashWaitTime)
currentOnTimeout() currentOnTimeout()
} }
Image(imageVector = SpotiFlyerLogo(),"SpotiFlyer Logo") Image(imageVector = Icons.Default.BrokenImage/*SpotiFlyerLogo()*/,"SpotiFlyer Logo")
MadeInIndia(Modifier.align(Alignment.BottomCenter)) MadeInIndia(Modifier.align(Alignment.BottomCenter))
} }
} }
@ -66,7 +69,7 @@ fun MadeInIndia(
fontSize = 22.sp fontSize = 22.sp
) )
Spacer(modifier = Modifier.padding(start = 4.dp)) Spacer(modifier = Modifier.padding(start = 4.dp))
Icon(HeartIcon(),"Love",tint = unspecifiedColor) //Icon(HeartIcon(),"Love",tint = Color.Unspecified)
Spacer(modifier = Modifier.padding(start = 4.dp)) Spacer(modifier = Modifier.padding(start = 4.dp))
Text( Text(
text = " in India", text = " in India",

View File

@ -4,13 +4,13 @@ package com.shabinder.common.utils
* Callback Utility * Callback Utility
* */ * */
interface Consumer<in T> { interface Consumer<in T> {
fun onCall(value: T) fun callback(value: T)
} }
@Suppress("FunctionName") // Factory function @Suppress("FunctionName") // Factory function
inline fun <T> Consumer(crossinline block: (T) -> Unit): Consumer<T> = inline fun <T> Consumer(crossinline block: (T) -> Unit): Consumer<T> =
object : Consumer<T> { object : Consumer<T> {
override fun onCall(value: T) { override fun callback(value: T) {
block(value) block(value)
} }
} }

View File

@ -9,31 +9,3 @@ import com.shabinder.common.di.Picture
import java.awt.image.BufferedImage import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import javax.imageio.ImageIO import javax.imageio.ImageIO
@Composable
actual fun ImageLoad(
pic: Picture?,
modifier: Modifier
){
if(pic == null) {
Image(
vectorXmlResource("common/compose-ui/src/main/res/drawable/music.xml"),
"",
modifier
)
}
else{
Image(
org.jetbrains.skija.Image.makeFromEncoded(
toByteArray(pic.image)
).asImageBitmap(),
"Image",
modifier = modifier
)
}
}
fun toByteArray(bitmap: BufferedImage) : ByteArray {
val baOs = ByteArrayOutputStream()
ImageIO.write(bitmap, "png", baOs)
return baOs.toByteArray()
}

View File

@ -34,6 +34,10 @@ actual fun DownloadImageArrow(modifier: Modifier){
@Composable @Composable
actual fun DownloadAllImage():ImageVector = vectorXmlResource("common/compose-ui/src/main/res/drawable/ic_download_arrow.xml") actual fun DownloadAllImage():ImageVector = vectorXmlResource("common/compose-ui/src/main/res/drawable/ic_download_arrow.xml")
@Composable
actual fun PlaceHolderImage():ImageVector = vectorXmlResource("common/compose-ui/src/main/res/drawable/music.xml")
@Composable @Composable
actual fun SpotiFlyerLogo():ImageVector = vectorXmlResource("common/compose-ui/src/main/res/drawable/ic_spotiflyer_logo.xml") actual fun SpotiFlyerLogo():ImageVector = vectorXmlResource("common/compose-ui/src/main/res/drawable/ic_spotiflyer_logo.xml")

View File

@ -24,5 +24,5 @@ data class Artist (
val popularity : Int, val popularity : Int,
val seokey : String, val seokey : String,
val name : String, val name : String,
@SerialName("artwork_175x175")var artworkLink :String? @SerialName("artwork_175x175")var artworkLink :String? = null
) )

View File

@ -18,6 +18,9 @@ package com.shabinder.common.models.gaana
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class CustomArtworks ( data class CustomArtworks (
@SerialName("40x40") val size_40p : String, @SerialName("40x40") val size_40p : String,
@SerialName("80x80") val size_80p : String, @SerialName("80x80") val size_80p : String,

View File

@ -16,6 +16,9 @@
package com.shabinder.common.models.gaana package com.shabinder.common.models.gaana
import kotlinx.serialization.Serializable
@Serializable
data class GaanaAlbum ( data class GaanaAlbum (
val tracks : List<GaanaTrack>, val tracks : List<GaanaTrack>,
val count : Int, val count : Int,

View File

@ -16,6 +16,9 @@
package com.shabinder.common.models.gaana package com.shabinder.common.models.gaana
import kotlinx.serialization.Serializable
@Serializable
data class GaanaArtistDetails( data class GaanaArtistDetails(
val artist : List<Artist>, val artist : List<Artist>,
val count : Int, val count : Int,

View File

@ -16,6 +16,9 @@
package com.shabinder.common.models.gaana package com.shabinder.common.models.gaana
import kotlinx.serialization.Serializable
@Serializable
data class GaanaArtistTracks( data class GaanaArtistTracks(
val count : Int, val count : Int,
val tracks : List<GaanaTrack> val tracks : List<GaanaTrack>

View File

@ -16,6 +16,9 @@
package com.shabinder.common.models.gaana package com.shabinder.common.models.gaana
import kotlinx.serialization.Serializable
@Serializable
data class GaanaPlaylist ( data class GaanaPlaylist (
val modified_on : String, val modified_on : String,
val count : Int, val count : Int,

View File

@ -16,6 +16,9 @@
package com.shabinder.common.models.gaana package com.shabinder.common.models.gaana
import kotlinx.serialization.Serializable
@Serializable
data class GaanaSong( data class GaanaSong(
val tracks : List<GaanaTrack> val tracks : List<GaanaTrack>
) )

View File

@ -22,21 +22,21 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
data class GaanaTrack ( data class GaanaTrack (
val tags : List<Tags?>?, val tags : List<Tags?>? = null,
val seokey : String, val seokey : String,
val albumseokey : String?, val albumseokey : String? = null,
val track_title : String, val track_title : String,
val album_title : String?, val album_title : String? = null,
val language : String?, val language : String? = null,
val duration: Int, val duration: Int,
@SerialName("artwork_large") val artworkLink : String, @SerialName("artwork_large") val artworkLink : String,
val artist : List<Artist?>, val artist : List<Artist?> = emptyList(),
@SerialName("gener") val genre : List<Genre?>?, @SerialName("gener") val genre : List<Genre?>? = null,
val lyrics_url : String?, val lyrics_url : String? = null,
val youtube_id : String?, val youtube_id : String? = null,
val total_favourite_count : Int?, val total_favourite_count : Int? = null,
val release_date : String?, val release_date : String? = null,
val play_ct : String?, val play_ct : String? = null,
val secondary_language : String?, val secondary_language : String? = null,
var downloaded: DownloadStatus? = DownloadStatus.NotDownloaded var downloaded: DownloadStatus? = DownloadStatus.NotDownloaded
) )

View File

@ -4,6 +4,8 @@ import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.os.Environment import android.os.Environment
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import co.touchlab.kermit.Kermit import co.touchlab.kermit.Kermit
import com.mpatric.mp3agic.Mp3File import com.mpatric.mp3agic.Mp3File
import com.shabinder.common.models.TrackDetails import com.shabinder.common.models.TrackDetails
@ -57,25 +59,11 @@ actual class Dir actual constructor(
File(imageCacheDir()).deleteRecursively() File(imageCacheDir()).deleteRecursively()
} }
actual suspend fun cacheImage(picture: Picture) { actual suspend fun cacheImage(image: Any,path:String) {
try { try {
val path = imageCacheDir() + picture.name
FileOutputStream(path).use { out -> FileOutputStream(path).use { out ->
picture.image.compress(Bitmap.CompressFormat.JPEG, 100, out) (image as? Bitmap)?.compress(Bitmap.CompressFormat.JPEG, 100, out)
} }
val bw =
BufferedWriter(
OutputStreamWriter(
FileOutputStream(path + cacheImagePostfix()), StandardCharsets.UTF_8
)
)
bw.write(picture.source)
bw.write("\r\n${picture.width}")
bw.write("\r\n${picture.height}")
bw.close()
} catch (e: IOException) { } catch (e: IOException) {
e.printStackTrace() e.printStackTrace()
} }
@ -96,45 +84,20 @@ actual class Dir actual constructor(
.setId3v2TagsAndSaveFile(trackDetails,path) .setId3v2TagsAndSaveFile(trackDetails,path)
} }
actual fun loadImage(url: String): Picture? { actual suspend fun loadImage(url: String): ImageBitmap? {
val cachePath = imageCacheDir() + getNameURL(url) val cachePath = imageCacheDir() + getNameURL(url)
var picture: Picture? = loadCachedImage(cachePath) return (loadCachedImage(cachePath) ?: freshImage(url))?.asImageBitmap()
if (picture == null) picture = freshImage(url)
return picture
} }
private fun loadCachedImage(cachePath: String): Picture? { private fun loadCachedImage(cachePath: String): Bitmap? {
return try { return try {
val read = BufferedReader( BitmapFactory.decodeFile(cachePath)
InputStreamReader(
FileInputStream(cachePath + cacheImagePostfix()),
StandardCharsets.UTF_8
)
)
val source = read.readLine()
val width = read.readLine().toInt()
val height = read.readLine().toInt()
read.close()
val result: Bitmap? = BitmapFactory.decodeFile(cachePath)
if (result != null) {
com.shabinder.common.di.Picture(
source,
getNameURL(source),
result,
width,
height
)
}else null
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
null null
} }
} }
private fun freshImage(url:String): Picture?{ private suspend fun freshImage(url:String): Bitmap?{
return try { return try {
val source = URL(url) val source = URL(url)
val connection: HttpURLConnection = source.openConnection() as HttpURLConnection val connection: HttpURLConnection = source.openConnection() as HttpURLConnection
@ -145,17 +108,10 @@ actual class Dir actual constructor(
val result: Bitmap? = BitmapFactory.decodeStream(input) val result: Bitmap? = BitmapFactory.decodeStream(input)
if (result != null) { if (result != null) {
val picture = com.shabinder.common.di.Picture(
url,
getNameURL(url),
result,
result.width,
result.height
)
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
cacheImage(picture) cacheImage(result,imageCacheDir() + getNameURL(url))
} }
picture result
} else null } else null
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()

View File

@ -1,12 +0,0 @@
package com.shabinder.common.di
import android.graphics.Bitmap
actual data class Picture(
var source: String = "",
var name: String = "",
var image: Bitmap,
var width: Int = 0,
var height: Int = 0,
var id: Int = 0
)

View File

@ -1,5 +1,6 @@
package com.shabinder.common.di package com.shabinder.common.di
import androidx.compose.ui.graphics.ImageBitmap
import co.touchlab.kermit.Kermit import co.touchlab.kermit.Kermit
import com.shabinder.common.models.DownloadResult import com.shabinder.common.models.DownloadResult
import com.shabinder.common.models.TrackDetails import com.shabinder.common.models.TrackDetails
@ -18,8 +19,8 @@ expect class Dir(
fun defaultDir(): String fun defaultDir(): String
fun imageCacheDir(): String fun imageCacheDir(): String
fun createDirectory(dirPath:String) fun createDirectory(dirPath:String)
suspend fun cacheImage(picture: Picture) suspend fun cacheImage(image: Any,path: String) // in Android = ImageBitmap, Desktop = BufferedImage
fun loadImage(url:String): Picture? suspend fun loadImage(url:String): ImageBitmap?
suspend fun clearCache() suspend fun clearCache()
suspend fun saveFileWithMetadata(mp3ByteArray: ByteArray, path: String, trackDetails: TrackDetails) suspend fun saveFileWithMetadata(mp3ByteArray: ByteArray, path: String, trackDetails: TrackDetails)
} }
@ -44,8 +45,6 @@ suspend fun downloadFile(url: String): Flow<DownloadResult> {
client.close() client.close()
} }
} }
fun Dir.cacheImagePostfix():String = "info"
fun getNameURL(url: String): String { fun getNameURL(url: String): String {
return url.substring(url.lastIndexOf('/') + 1, url.length) return url.substring(url.lastIndexOf('/') + 1, url.length)
} }

View File

@ -2,8 +2,6 @@ package com.shabinder.common.di
import com.shabinder.common.models.TrackDetails import com.shabinder.common.models.TrackDetails
expect class Picture
expect fun openPlatform(platformID:String ,platformLink:String) expect fun openPlatform(platformID:String ,platformLink:String)
expect fun shareApp() expect fun shareApp()

View File

@ -1,16 +1,18 @@
package com.shabinder.common.di package com.shabinder.common.di
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import co.touchlab.kermit.Kermit import co.touchlab.kermit.Kermit
import com.mpatric.mp3agic.Mp3File import com.mpatric.mp3agic.Mp3File
import com.shabinder.common.models.TrackDetails import com.shabinder.common.models.TrackDetails
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jetbrains.skija.Image
import java.awt.image.BufferedImage import java.awt.image.BufferedImage
import java.io.* import java.io.*
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL import java.net.URL
import java.nio.charset.StandardCharsets
import javax.imageio.ImageIO import javax.imageio.ImageIO
actual class Dir actual constructor(private val logger: Kermit) { actual class Dir actual constructor(private val logger: Kermit) {
@ -46,25 +48,11 @@ actual class Dir actual constructor(private val logger: Kermit) {
File(imageCacheDir()).deleteRecursively() File(imageCacheDir()).deleteRecursively()
} }
actual suspend fun cacheImage(picture: Picture) { actual suspend fun cacheImage(image: Any,path:String) {
try { try {
val path = imageCacheDir() + picture.name (image as? BufferedImage)?.let {
ImageIO.write(it,"jpeg", File(path))
ImageIO.write(picture.image, "jpeg", File(path)) }
val bw =
BufferedWriter(
OutputStreamWriter(
FileOutputStream(path + cacheImagePostfix()),
StandardCharsets.UTF_8
)
)
bw.write(picture.source)
bw.write("\r\n${picture.width}")
bw.write("\r\n${picture.height}")
bw.close()
} catch (e: IOException) { } catch (e: IOException) {
e.printStackTrace() e.printStackTrace()
} }
@ -85,45 +73,23 @@ actual class Dir actual constructor(private val logger: Kermit) {
.setId3v2TagsAndSaveFile(trackDetails,path) .setId3v2TagsAndSaveFile(trackDetails,path)
} }
actual fun loadImage(url: String): Picture? { actual suspend fun loadImage(url: String): ImageBitmap? {
val cachePath = imageCacheDir() + getNameURL(url) val cachePath = imageCacheDir() + getNameURL(url)
var picture: Picture? = loadCachedImage(cachePath) var picture: ImageBitmap? = loadCachedImage(cachePath)
if (picture == null) picture = freshImage(url,cachePath) if (picture == null) picture = freshImage(url)
return picture return picture
} }
private fun loadCachedImage(cachePath: String): Picture? { private fun loadCachedImage(cachePath: String): ImageBitmap? {
return try { return try {
val read = BufferedReader( ImageIO.read(File(cachePath))?.toImageBitmap()
InputStreamReader(
FileInputStream(cachePath + cacheImagePostfix()),
StandardCharsets.UTF_8
)
)
val source = read.readLine()
val width = read.readLine().toInt()
val height = read.readLine().toInt()
read.close()
val result: BufferedImage? = ImageIO.read(File(cachePath))
if (result != null) {
com.shabinder.common.di.Picture(
source,
getNameURL(source),
result,
width,
height
)
}else null
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() //e.printStackTrace()
null null
} }
} }
private fun freshImage(url:String,cachePath: String): Picture?{
private suspend fun freshImage(url:String): ImageBitmap?{
return try { return try {
val source = URL(url) val source = URL(url)
val connection: HttpURLConnection = source.openConnection() as HttpURLConnection val connection: HttpURLConnection = source.openConnection() as HttpURLConnection
@ -134,17 +100,10 @@ actual class Dir actual constructor(private val logger: Kermit) {
val result: BufferedImage? = ImageIO.read(input) val result: BufferedImage? = ImageIO.read(input)
if (result != null) { if (result != null) {
val picture = com.shabinder.common.di.Picture(
url,
getNameURL(url),
result,
result.width,
result.height
)
GlobalScope.launch(Dispatchers.IO) { //TODO Refactor GlobalScope.launch(Dispatchers.IO) { //TODO Refactor
cacheImage(picture) cacheImage(result,imageCacheDir() + getNameURL(url))
} }
picture result.toImageBitmap()
} else null } else null
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
@ -152,3 +111,12 @@ actual class Dir actual constructor(private val logger: Kermit) {
} }
} }
} }
fun BufferedImage.toImageBitmap() = Image.makeFromEncoded(
toByteArray(this)
).asImageBitmap()
private fun toByteArray(bitmap: BufferedImage) : ByteArray {
val baOs = ByteArrayOutputStream()
ImageIO.write(bitmap, "png", baOs)
return baOs.toByteArray()
}

View File

@ -1,12 +0,0 @@
package com.shabinder.common.di
import java.awt.image.BufferedImage
actual data class Picture(
var source: String = "",
var name: String = "",
var image: BufferedImage,
var width: Int = 0,
var height: Int = 0,
var id: Int = 0
)