Ui Fixes and ImageLoading fun improvised.

This commit is contained in:
shabinder 2021-02-12 04:47:46 +05:30
parent a186a1a1e7
commit 7602fa3f23
12 changed files with 72 additions and 36 deletions

View File

@ -8,6 +8,7 @@ import android.os.Bundle
import android.os.PowerManager import android.os.PowerManager
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.material.Surface
import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.extensions.compose.jetbrains.rootComponent import com.arkivanov.decompose.extensions.compose.jetbrains.rootComponent
import com.arkivanov.mvikotlin.logging.store.LoggingStoreFactory import com.arkivanov.mvikotlin.logging.store.LoggingStoreFactory
@ -22,6 +23,7 @@ import com.shabinder.common.root.SpotiFlyerRoot
import com.shabinder.common.root.SpotiFlyerRootContent import com.shabinder.common.root.SpotiFlyerRootContent
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
import com.shabinder.common.ui.SpotiFlyerTheme import com.shabinder.common.ui.SpotiFlyerTheme
import com.shabinder.common.ui.colorOffWhite
import com.shabinder.database.Database import com.shabinder.database.Database
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
@ -41,7 +43,9 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
SpotiFlyerTheme { SpotiFlyerTheme {
Surface(contentColor = colorOffWhite) {
root = SpotiFlyerRootContent(rootComponent(::spotiFlyerRoot)) root = SpotiFlyerRootContent(rootComponent(::spotiFlyerRoot))
}
} }
} }
initialise() initialise()

View File

@ -24,6 +24,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@Composable @Composable
fun SpotiFlyerListContent( fun SpotiFlyerListContent(
@ -67,13 +68,8 @@ fun TrackCard(
loadImage:suspend (String)-> ImageBitmap? 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)) {
var pic by mutableStateOf<ImageBitmap?>(null)
val scope = rememberCoroutineScope()
LaunchedEffect((1..10).random()){
pic = loadImage(track.albumArtURL)
}
ImageLoad( ImageLoad(
pic = pic, {loadImage(track.albumArtURL)},
"Album Art", "Album Art",
modifier = Modifier modifier = Modifier
.width(75.dp) .width(75.dp)
@ -128,14 +124,8 @@ fun CoverImage(
modifier.padding(vertical = 8.dp).fillMaxWidth(), modifier.padding(vertical = 8.dp).fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
var pic by mutableStateOf<ImageBitmap?>(null)
LaunchedEffect(true){
scope.launch(dispatcherIO) {
pic = loadImage(coverURL)
}
}
ImageLoad( ImageLoad(
pic, { loadImage(coverURL) },
"Cover Image", "Cover Image",
modifier = Modifier modifier = Modifier
.width(210.dp) .width(210.dp)

View File

@ -30,8 +30,6 @@ 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){
@ -326,13 +324,15 @@ fun DownloadRecordItem(
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 scope = rememberCoroutineScope() /*KamelImage(
var pic by mutableStateOf<ImageBitmap?>(null) lazyImageResource(item.coverUrl),
scope.launch(dispatcherIO) { "Album Art",
pic = loadImage(item.coverUrl) modifier = Modifier.height(75.dp).width(90.dp),
} crossfade = true,
onLoading = { PlaceHolderImage() }
)*/
ImageLoad( ImageLoad(
pic, { loadImage(item.coverUrl) },
"Album Art", "Album Art",
modifier = Modifier.height(75.dp).width(90.dp) modifier = Modifier.height(75.dp).width(90.dp)
) )

View File

@ -1,15 +1,25 @@
@file:Suppress("FunctionName") @file:Suppress("FunctionName")
package com.shabinder.common.ui package com.shabinder.common.ui
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import kotlinx.coroutines.withContext
@Composable @Composable
fun ImageLoad(pic: ImageBitmap?, desc: String, modifier:Modifier = Modifier, placeholder:ImageVector = PlaceHolderImage()) { fun ImageLoad(loader: suspend () -> ImageBitmap?, desc: String = "Album Art", modifier:Modifier = Modifier, placeholder:ImageVector = PlaceHolderImage()) {
if(pic == null) Image(placeholder, desc, modifier) else Image(pic, desc, modifier) var pic by remember { mutableStateOf<ImageBitmap?>(null) }
Crossfade(pic){
if(pic == null) Image(placeholder, desc, modifier) else Image(pic!!, desc, modifier)
}
LaunchedEffect(loader){
withContext(dispatcherIO) {
pic = loader()
}
}
} }
@Composable @Composable

View File

@ -21,5 +21,5 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
data class GaanaArtistTracks( data class GaanaArtistTracks(
val count : Int, val count : Int,
val tracks : List<GaanaTrack> val tracks : List<GaanaTrack>? = null
) )

View File

@ -16,7 +16,7 @@ class TokenStore(
private val db: TokenDBQueries private val db: TokenDBQueries
get() = tokenDB.tokenDBQueries get() = tokenDB.tokenDBQueries
private suspend fun save(token: TokenData){ private fun save(token: TokenData){
if(!token.access_token.isNullOrBlank() && token.expiry != null) if(!token.access_token.isNullOrBlank() && token.expiry != null)
db.add(token.access_token!!, token.expiry!! + Clock.System.now().epochSeconds) db.add(token.access_token!!, token.expiry!! + Clock.System.now().epochSeconds)
} }
@ -25,6 +25,7 @@ class TokenStore(
var token: TokenData? = db.select().executeAsOneOrNull()?.let { var token: TokenData? = db.select().executeAsOneOrNull()?.let {
TokenData(it.accessToken,null,it.expiry) TokenData(it.accessToken,null,it.expiry)
} }
logger.d{"System Time:${Clock.System.now().epochSeconds} , Token Expiry:${token?.expiry}"}
if(Clock.System.now().epochSeconds > token?.expiry ?:0 || token == null){ if(Clock.System.now().epochSeconds > token?.expiry ?:0 || token == null){
logger.d{"Requesting New Token"} logger.d{"Requesting New Token"}
token = authenticateSpotify() token = authenticateSpotify()

View File

@ -85,7 +85,7 @@ class GaanaProvider(
dir.defaultDir() dir.defaultDir()
) )
)) {//Download Already Present!! )) {//Download Already Present!!
it.downloaded = com.shabinder.common.models.DownloadStatus.Downloaded it.downloaded = DownloadStatus.Downloaded
} }
trackList = listOf(it).toTrackDetailsList(folderType, subFolder) trackList = listOf(it).toTrackDetailsList(folderType, subFolder)
title = it.track_title title = it.track_title
@ -115,7 +115,7 @@ class GaanaProvider(
) )
) )
) {//Download Already Present!! ) {//Download Already Present!!
track.downloaded = com.shabinder.common.models.DownloadStatus.Downloaded track.downloaded = DownloadStatus.Downloaded
} }
} }
trackList = it.tracks.toTrackDetailsList(folderType, subFolder) trackList = it.tracks.toTrackDetailsList(folderType, subFolder)
@ -146,7 +146,7 @@ class GaanaProvider(
) )
) )
) {//Download Already Present!! ) {//Download Already Present!!
track.downloaded = com.shabinder.common.models.DownloadStatus.Downloaded track.downloaded = DownloadStatus.Downloaded
} }
} }
trackList = it.tracks.toTrackDetailsList(folderType, subFolder) trackList = it.tracks.toTrackDetailsList(folderType, subFolder)
@ -175,7 +175,7 @@ class GaanaProvider(
coverUrl = it.artworkLink ?: gaanaPlaceholderImageUrl coverUrl = it.artworkLink ?: gaanaPlaceholderImageUrl
} }
getGaanaArtistTracks(seokey = link).also { getGaanaArtistTracks(seokey = link).also {
it.tracks.forEach { track -> it.tracks?.forEach { track ->
if (dir.isPresent( if (dir.isPresent(
dir.finalOutputDir( dir.finalOutputDir(
track.track_title, track.track_title,
@ -185,10 +185,10 @@ class GaanaProvider(
) )
) )
) {//Download Already Present!! ) {//Download Already Present!!
track.downloaded = com.shabinder.common.models.DownloadStatus.Downloaded track.downloaded = DownloadStatus.Downloaded
} }
} }
trackList = it.tracks.toTrackDetailsList(folderType, subFolder) trackList = it.tracks?.toTrackDetailsList(folderType, subFolder) ?: emptyList()
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
db.add( db.add(
type = "Artist", type = "Artist",

View File

@ -19,8 +19,11 @@ package com.shabinder.common.di.providers
import co.touchlab.kermit.Kermit import co.touchlab.kermit.Kermit
import com.shabinder.common.database.DownloadRecordDatabaseQueries import com.shabinder.common.database.DownloadRecordDatabaseQueries
import com.shabinder.common.di.Dir import com.shabinder.common.di.Dir
import com.shabinder.common.di.TokenStore
import com.shabinder.common.di.finalOutputDir import com.shabinder.common.di.finalOutputDir
import com.shabinder.common.di.kotlinxSerializer
import com.shabinder.common.di.spotify.SpotifyRequests import com.shabinder.common.di.spotify.SpotifyRequests
import com.shabinder.common.di.spotify.authenticateSpotify
import com.shabinder.common.models.PlatformQueryResult import com.shabinder.common.models.PlatformQueryResult
import com.shabinder.common.models.TrackDetails import com.shabinder.common.models.TrackDetails
import com.shabinder.common.models.spotify.Album import com.shabinder.common.models.spotify.Album
@ -29,16 +32,40 @@ import com.shabinder.common.models.spotify.Source
import com.shabinder.common.models.spotify.Track import com.shabinder.common.models.spotify.Track
import com.shabinder.database.Database import com.shabinder.database.Database
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.features.*
import io.ktor.client.features.json.*
import io.ktor.client.request.*
import io.ktor.http.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class SpotifyProvider( class SpotifyProvider(
override val httpClient: HttpClient, private val tokenStore: TokenStore,
private val database: Database, private val database: Database,
private val logger: Kermit, private val logger: Kermit,
private val dir: Dir, private val dir: Dir,
) : SpotifyRequests { ) : SpotifyRequests {
init {
logger.d { "Creating Spotify Provider" }
GlobalScope.launch(Dispatchers.Default) {
val token = tokenStore.getToken()
httpClient = HttpClient {
defaultRequest {
header("Authorization","Bearer ${token.access_token}")
}
install(JsonFeature) {
serializer = kotlinxSerializer
}
}
logger.d { "Spotify Provider Created with $token" }
}
}
override lateinit var httpClient: HttpClient
private val db:DownloadRecordDatabaseQueries private val db:DownloadRecordDatabaseQueries
get() = database.downloadRecordDatabaseQueries get() = database.downloadRecordDatabaseQueries

View File

@ -2,6 +2,7 @@ package com.shabinder.common.di.spotify
import com.shabinder.common.di.kotlinxSerializer import com.shabinder.common.di.kotlinxSerializer
import com.shabinder.common.models.spotify.TokenData import com.shabinder.common.models.spotify.TokenData
import com.shabinder.database.Database
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.features.auth.* import io.ktor.client.features.auth.*
import io.ktor.client.features.auth.providers.* import io.ktor.client.features.auth.providers.*
@ -9,6 +10,7 @@ import io.ktor.client.features.json.*
import io.ktor.client.request.* import io.ktor.client.request.*
import io.ktor.client.request.forms.* import io.ktor.client.request.forms.*
import io.ktor.http.* import io.ktor.http.*
import kotlinx.datetime.Clock
suspend fun authenticateSpotify(): TokenData { suspend fun authenticateSpotify(): TokenData {
return spotifyAuthClient.post("https://accounts.spotify.com/api/token"){ return spotifyAuthClient.post("https://accounts.spotify.com/api/token"){

View File

@ -17,6 +17,7 @@ import com.shabinder.common.root.SpotiFlyerRootContent
import com.shabinder.common.ui.SpotiFlyerColors import com.shabinder.common.ui.SpotiFlyerColors
import com.shabinder.common.ui.SpotiFlyerShapes import com.shabinder.common.ui.SpotiFlyerShapes
import com.shabinder.common.ui.SpotiFlyerTypography import com.shabinder.common.ui.SpotiFlyerTypography
import com.shabinder.common.ui.colorOffWhite
import com.shabinder.database.Database import com.shabinder.database.Database
private val koin = initKoin(enableNetworkLogs = true).koin private val koin = initKoin(enableNetworkLogs = true).koin
@ -29,7 +30,8 @@ fun main(){
Window("SpotiFlyer") { Window("SpotiFlyer") {
Surface( Surface(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
color = Color.Black color = Color.Black,
contentColor = colorOffWhite
) { ) {
DesktopMaterialTheme( DesktopMaterialTheme(
colors = SpotiFlyerColors, colors = SpotiFlyerColors,