Track List Implemented

This commit is contained in:
shabinder 2020-12-30 20:12:10 +05:30
parent 86431dbc1c
commit 66064c631b
14 changed files with 187 additions and 77 deletions

View File

@ -79,6 +79,7 @@ dependencies {
//Compose
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.ui:ui-tooling:$compose_version"
implementation "androidx.compose.animation:animation:$compose_version"
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.material:material-icons-extended:$compose_version"
@ -90,6 +91,7 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
//Coroutines
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
@ -98,13 +100,13 @@ dependencies {
//Room
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version"
implementation "androidx.room:room-runtime:$room_version"
//Hilt
kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
//FFmpeg

View File

@ -25,11 +25,16 @@ import androidx.compose.ui.res.vectorResource
import androidx.core.view.WindowCompat
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import com.shabinder.spotiflyer.navigation.ComposeNavigation
import com.shabinder.spotiflyer.navigation.navigateToPlatform
import com.shabinder.spotiflyer.networking.SpotifyService
import com.shabinder.spotiflyer.networking.SpotifyServiceTokenRequest
import com.shabinder.spotiflyer.ui.ComposeLearnTheme
import com.shabinder.spotiflyer.ui.appNameStyle
import com.shabinder.spotiflyer.ui.colorOffWhite
import com.shabinder.spotiflyer.utils.*
import com.squareup.moshi.Moshi
import dagger.hilt.android.AndroidEntryPoint
@ -51,6 +56,7 @@ import com.shabinder.spotiflyer.utils.showDialog as showDialog1
class MainActivity : AppCompatActivity() {
private var spotifyService : SpotifyService? = null
lateinit var navController: NavHostController
@Inject lateinit var moshi: Moshi
@Inject lateinit var spotifyServiceTokenRequest: SpotifyServiceTokenRequest
@ -62,19 +68,24 @@ class MainActivity : AppCompatActivity() {
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
ComposeLearnTheme {
ProvideWindowInsets {
Column {
val appBarColor = MaterialTheme.colors.surface.copy(alpha = 0.87f)
Providers(AmbientContentColor provides colorOffWhite) {
ProvideWindowInsets {
Column {
val appBarColor = MaterialTheme.colors.surface.copy(alpha = 0.87f)
// Draw a scrim over the status bar which matches the app bar
Spacer(Modifier.background(appBarColor).fillMaxWidth().statusBarsHeight())
// Draw a scrim over the status bar which matches the app bar
Spacer(
Modifier.background(appBarColor).fillMaxWidth().statusBarsHeight()
)
AppBar(
backgroundColor = appBarColor,
modifier = Modifier.fillMaxWidth()
)
AppBar(
backgroundColor = appBarColor,
modifier = Modifier.fillMaxWidth()
)
navController = rememberNavController()
ComposeNavigation()
ComposeNavigation(navController)
}
}
}
}
@ -93,7 +104,6 @@ class MainActivity : AppCompatActivity() {
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
//Return to MainFragment For Further Processing of this Intent
handleIntentFromExternalActivity(intent)
}
@ -172,7 +182,7 @@ class MainActivity : AppCompatActivity() {
if ("text/plain" == intent.type) {
intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
log("Intent Received", it)
sharedViewModel.intentString.value = it
navController.navigateToPlatform(it)
}
}
}
@ -238,7 +248,7 @@ fun DefaultPreview() {
modifier = Modifier.fillMaxWidth()
)
ComposeNavigation()
//ComposeNavigation()
}
}
}

View File

@ -22,9 +22,11 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.github.kiulian.downloader.YoutubeDownloader
import com.shabinder.spotiflyer.database.DatabaseDAO
import com.shabinder.spotiflyer.models.PlatformQueryResult
import com.shabinder.spotiflyer.networking.GaanaInterface
import com.shabinder.spotiflyer.networking.SpotifyService
import dagger.hilt.android.scopes.ActivityRetainedScoped
import kotlinx.coroutines.flow.Flow
@ActivityRetainedScoped
class SharedViewModel @ViewModelInject constructor(

View File

@ -2,6 +2,7 @@ package com.shabinder.spotiflyer.navigation
import androidx.compose.runtime.Composable
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.*
import androidx.navigation.compose.popUpTo
@ -14,8 +15,7 @@ import com.shabinder.spotiflyer.utils.sharedViewModel
import com.shabinder.spotiflyer.utils.showDialog
@Composable
fun ComposeNavigation() {
val navController = rememberNavController()
fun ComposeNavigation(navController: NavHostController) {
NavHost(
navController = navController,
startDestination = "home"

View File

@ -12,6 +12,7 @@ val colorAccent = Color(0xFF9AB3FF)
val colorRedError = Color(0xFFFF9494)
val colorSuccessGreen = Color(0xFF59C351)
val darkBackgroundColor = Color(0xFF000000)
val colorOffWhite = Color(0xFFE7E7E7)
val SpotiFlyerColors = darkColors(
primary = colorPrimary,

View File

@ -80,7 +80,6 @@ val SpotiFlyerTypography = Typography(
fontWeight = FontWeight.Medium,
lineHeight = 20.sp,
letterSpacing = 0.15.sp,
color = colorAccent
),
body2 = TextStyle(
fontFamily = Montserrat,

View File

@ -1,40 +0,0 @@
/*
* Copyright (C) 2020 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.base
import androidx.lifecycle.ViewModel
import com.shabinder.spotiflyer.models.TrackDetails
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
abstract class TrackListViewModel:ViewModel() {
abstract var folderType:String
abstract var subFolder:String
private val _trackList = MutableStateFlow<List<TrackDetails>>(mutableListOf())
open val trackList:StateFlow<List<TrackDetails>>
get() = _trackList
fun updateTrackList(list:List<TrackDetails>){
_trackList.value = list
}
private val loading = "Loading!"
open var title = MutableStateFlow(loading)
open var coverUrl = MutableStateFlow(loading)
}

View File

@ -85,7 +85,8 @@ fun AboutColumn(modifier: Modifier = Modifier) {
Column(modifier.padding(12.dp)) {
Text(
text = stringResource(R.string.supported_platform),
style = SpotiFlyerTypography.body1
style = SpotiFlyerTypography.body1,
color = colorAccent
)
Spacer(modifier = Modifier.padding(top = 12.dp))
Row(horizontalArrangement = Arrangement.Center,modifier = modifier.fillMaxWidth()) {
@ -115,7 +116,8 @@ fun AboutColumn(modifier: Modifier = Modifier) {
Column(modifier.padding(12.dp)) {
Text(
text = stringResource(R.string.support_development),
style = SpotiFlyerTypography.body1
style = SpotiFlyerTypography.body1,
color = colorAccent
)
Spacer(modifier = Modifier.padding(top = 6.dp))
Row(verticalAlignment = Alignment.CenterVertically,

View File

@ -1,8 +1,15 @@
package com.shabinder.spotiflyer.ui.platforms.gaana
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.navigation.NavController
import com.shabinder.spotiflyer.models.PlatformQueryResult
import com.shabinder.spotiflyer.models.spotify.Source
import com.shabinder.spotiflyer.ui.tracklist.TrackList
import com.shabinder.spotiflyer.utils.*
import kotlinx.coroutines.launch
@ -11,6 +18,9 @@ fun Gaana(
fullLink: String,
navController: NavController
) {
val source = Source.Gaana
var result by remember { mutableStateOf<PlatformQueryResult?>(null) }
//Coroutine Scope Active till this Composable is Active
val coroutineScope = rememberCoroutineScope()
@ -29,17 +39,17 @@ fun Gaana(
}
coroutineScope.launch {
val result = gaanaSearch(
result = gaanaSearch(
type,
link,
sharedViewModel.gaanaInterface,
sharedViewModel.databaseDAO,
)
log("Gaana",result.toString())
log("Gaana Tracks",result.trackList.size.toString())
}
result?.let {
TrackList(
result = it,
source = source
)
}
}

View File

@ -1,12 +1,15 @@
package com.shabinder.spotiflyer.ui.platforms.spotify
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.viewinterop.viewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavController
import com.shabinder.spotiflyer.models.PlatformQueryResult
import com.shabinder.spotiflyer.models.spotify.Source
import com.shabinder.spotiflyer.networking.SpotifyService
import com.shabinder.spotiflyer.ui.tracklist.TrackList
import com.shabinder.spotiflyer.utils.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -14,8 +17,8 @@ import kotlinx.coroutines.launch
@Composable
fun Spotify(fullLink: String, navController: NavController,) {
val source: Source = Source.Spotify
val coroutineScope = rememberCoroutineScope()
var result by remember { mutableStateOf<PlatformQueryResult?>(null) }
var spotifyLink =
"https://" + fullLink.substringAfterLast("https://").substringBefore(" ").trim()
@ -57,7 +60,7 @@ fun Spotify(fullLink: String, navController: NavController,) {
showDialog("Authentication Failed")
navController.popBackStack()
}else{
val result = spotifySearch(
result = spotifySearch(
type,
link,
sharedViewModel.spotifyService.value!!,
@ -66,4 +69,11 @@ fun Spotify(fullLink: String, navController: NavController,) {
}
}
}
result?.let {
log("Spotify",it.trackList.size.toString())
TrackList(
result = it,
source = source
)
}
}

View File

@ -1,9 +1,15 @@
package com.shabinder.spotiflyer.ui.platforms.youtube
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.navigation.NavController
import com.shabinder.spotiflyer.models.PlatformQueryResult
import com.shabinder.spotiflyer.models.spotify.Source
import com.shabinder.spotiflyer.ui.tracklist.TrackList
import com.shabinder.spotiflyer.utils.sharedViewModel
import com.shabinder.spotiflyer.utils.showDialog
import kotlinx.coroutines.launch
@ -15,7 +21,7 @@ private const val sampleDomain1 = "youtube.com"
@Composable
fun Youtube(fullLink: String, navController: NavController,) {
val source = Source.YouTube
var result by remember { mutableStateOf<PlatformQueryResult?>(null) }
//Coroutine Scope Active till this Composable is Active
val coroutineScope = rememberCoroutineScope()
@ -38,7 +44,7 @@ fun Youtube(fullLink: String, navController: NavController,) {
searchId = link.substringAfterLast("/","error")
}
if(searchId != "error") {
getYTTrack(
result = getYTTrack(
searchId,
sharedViewModel.ytDownloader,
sharedViewModel.databaseDAO
@ -49,4 +55,10 @@ fun Youtube(fullLink: String, navController: NavController,) {
}
}
}
result?.let {
TrackList(
result = it,
source = source
)
}
}

View File

@ -1,14 +1,69 @@
package com.shabinder.spotiflyer.ui.tracklist
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.viewModel
import androidx.navigation.NavController
import com.bumptech.glide.RequestManager
import com.shabinder.spotiflyer.R
import com.shabinder.spotiflyer.models.PlatformQueryResult
import com.shabinder.spotiflyer.models.TrackDetails
import com.shabinder.spotiflyer.models.spotify.Source
import com.shabinder.spotiflyer.ui.SpotiFlyerTypography
import com.shabinder.spotiflyer.ui.colorAccent
import dev.chrisbanes.accompanist.glide.GlideImage
/*
* UI for List of Tracks to be universally used.
* */
@Composable
fun TrackList(modifier: Modifier = Modifier){
fun TrackList(
result: PlatformQueryResult,
source: Source,
modifier: Modifier = Modifier
){
LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp),
content = {
items(result.trackList) {
TrackCard(track = it)
}
},
modifier = Modifier.fillMaxSize()
)
}
@Composable
fun TrackCard(track:TrackDetails) {
Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)) {
GlideImage(
data = track.albumArtURL,
loading = { Image(vectorResource(id = R.drawable.ic_song_placeholder)) },
error = { Image(vectorResource(id = R.drawable.ic_musicplaceholder)) },
contentScale = ContentScale.Inside,
modifier = Modifier.preferredHeight(75.dp).preferredWidth(90.dp)
)
Column(modifier = Modifier.padding(horizontal = 8.dp).fillMaxHeight().weight(1f),verticalArrangement = Arrangement.SpaceBetween) {
Text(track.title,maxLines = 1,overflow = TextOverflow.Ellipsis,style = SpotiFlyerTypography.h6,color = colorAccent)
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.padding(horizontal = 12.dp).fillMaxSize()
){
Text("${track.artists.firstOrNull()}...",fontSize = 13.sp)
Text("${track.durationSec/60} minutes, ${track.durationSec%60} sec",fontSize = 13.sp)
}
}
Image(vectorResource(id = R.drawable.ic_arrow))
}
}

View File

@ -2,17 +2,38 @@ package com.shabinder.spotiflyer.utils
import android.content.Context
import android.content.Intent
import android.graphics.drawable.Drawable
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Build
import android.util.Log
import android.widget.ImageView
import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.vectorResource
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toBitmap
import androidx.core.net.toUri
import com.bumptech.glide.Glide
import com.bumptech.glide.RequestManager
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.Target
import com.shabinder.spotiflyer.MainActivity
import com.shabinder.spotiflyer.R
import com.shabinder.spotiflyer.models.TrackDetails
import com.shabinder.spotiflyer.models.spotify.Source
import com.shabinder.spotiflyer.worker.ForegroundService
import dev.chrisbanes.accompanist.glide.GlideImage
import dev.chrisbanes.accompanist.glide.GlideImageDefaults
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File
import java.io.IOException
/**
* mainActivity Instance to use whereEver Needed , as Its God Activity.
@ -54,7 +75,6 @@ fun finalOutputDir(itemName:String ,type:String, subFolder:String,extension:Stri
removeIllegalChars(itemName) + extension
}
/**
* Util. Function To Check Connection Status
* */

View File

@ -0,0 +1,27 @@
<!--
~ Copyright (C) 2020 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/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="36dp"
android:height="32dp" android:viewportWidth="512" android:viewportHeight="512">
<path android:fillColor="#516AEC" android:pathData="m296,288 l60,-60c7.73,-7.73 17.86,-11.6 28,-11.6 10.055,0 20.101,3.806 27.806,11.407 15.612,15.402 15.207,41.18 -0.3,56.687l-134.293,134.293c-11.716,11.716 -30.711,11.716 -42.426,0l-134.787,-134.787c-7.73,-7.73 -11.6,-17.86 -11.6,-28 0,-10.055 3.806,-20.101 11.407,-27.806 15.402,-15.612 41.18,-15.207 56.687,0.3l59.506,59.506v-232c0,-22.091 17.909,-40 40,-40 22.091,0 40,17.909 40,40z"/>
<path android:fillColor="#EC7EBA" android:pathData="m411.51,284.49 l-134.3,134.3c-11.71,11.71 -30.71,11.71 -42.42,0l-12.74,-12.74c10.69,4.06 23.23,1.77 31.84,-6.84l134.29,-134.29c12.51,-12.51 15.19,-31.7 7.57,-46.74 5.86,1.81 11.39,5.03 16.06,9.63 15.61,15.4 15.2,41.18 -0.3,56.68z"/>
<path android:fillColor="#EC7EBA" android:pathData="m251.88,27.72c-3.46,-3.46 -7.55,-6.29 -12.08,-8.3 4.95,-2.2 10.43,-3.42 16.2,-3.42 11.04,0 21.04,4.48 28.28,11.72s11.72,17.24 11.72,28.28v232l-15.329,15.329c-6.3,6.3 -17.071,1.838 -17.071,-7.071v-240.258c0,-11.04 -4.48,-21.04 -11.72,-28.28z"/>
<path android:fillColor="#6A82FB" android:pathData="m496,512h-24c-8.836,0 -16,-7.164 -16,-16s7.164,-16 16,-16h24c8.836,0 16,7.164 16,16s-7.164,16 -16,16z"/>
<path android:fillColor="#6A82FB" android:pathData="m40,512h-24c-8.836,0 -16,-7.164 -16,-16s7.164,-16 16,-16h24c8.836,0 16,7.164 16,16s-7.164,16 -16,16z"/>
<path android:fillColor="#FC5C7D" android:pathData="m416,512h-320c-8.836,0 -16,-7.164 -16,-16s7.164,-16 16,-16h320c8.836,0 16,7.164 16,16s-7.164,16 -16,16z"/>
<path android:fillColor="#4AFC5C7D" android:pathData="m256,443.552c-11.78,0 -23.56,-4.484 -32.527,-13.452l-134.786,-134.787c-10.503,-10.502 -16.287,-24.463 -16.287,-39.313 0,-14.708 5.688,-28.573 16.017,-39.042 10.31,-10.451 24.214,-16.233 39.151,-16.284h0.189c14.966,0 29.552,6.009 40.05,16.507l32.193,32.192v-193.373c0,-30.878 25.122,-56 56,-56s56,25.122 56,56v193.373l32.687,-32.687c10.501,-10.502 24.463,-16.286 39.313,-16.286 14.708,0 28.573,5.688 39.042,16.017 10.45,10.31 16.233,24.214 16.284,39.151 0.051,15.032 -5.966,29.698 -16.507,40.24l-134.292,134.292c-8.967,8.968 -20.747,13.452 -32.527,13.452zM127.761,232.673c-0.028,0 -0.056,0 -0.084,0 -6.349,0.021 -12.202,2.421 -16.479,6.758 -4.383,4.443 -6.797,10.327 -6.797,16.569 0,6.302 2.456,12.228 6.914,16.686l134.785,134.787c5.459,5.459 14.341,5.459 19.8,0l134.292,-134.292c4.556,-4.557 7.157,-10.937 7.134,-17.504 -0.021,-6.349 -2.421,-12.202 -6.758,-16.479 -4.443,-4.383 -10.327,-6.797 -16.569,-6.797 -6.302,0 -12.228,2.456 -16.687,6.914l-60,60c-4.575,4.577 -11.456,5.945 -17.437,3.469 -5.977,-2.478 -9.875,-8.313 -9.875,-14.784v-232c0,-13.234 -10.767,-24 -24,-24 -13.234,0 -24,10.766 -24,24v232c0,6.471 -3.898,12.306 -9.877,14.782 -5.978,2.476 -12.861,1.108 -17.437,-3.469l-59.506,-59.505c-4.536,-4.537 -10.882,-7.135 -17.419,-7.135z"/>
</vector>