mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-26 18:34:32 +01:00
Dynamic Gradient.
This commit is contained in:
parent
66064c631b
commit
df5f7dd2c5
@ -15,19 +15,19 @@ import androidx.compose.foundation.layout.*
|
|||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.Providers
|
|
||||||
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.graphics.Color
|
||||||
import androidx.compose.ui.platform.setContent
|
import androidx.compose.ui.platform.setContent
|
||||||
import androidx.compose.ui.res.vectorResource
|
import androidx.compose.ui.res.vectorResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.navigation.NavController
|
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import com.example.jetcaster.util.verticalGradientScrim
|
||||||
import com.shabinder.spotiflyer.navigation.ComposeNavigation
|
import com.shabinder.spotiflyer.navigation.ComposeNavigation
|
||||||
import com.shabinder.spotiflyer.navigation.navigateToPlatform
|
import com.shabinder.spotiflyer.navigation.navigateToPlatform
|
||||||
import com.shabinder.spotiflyer.networking.SpotifyService
|
import com.shabinder.spotiflyer.networking.SpotifyService
|
||||||
@ -55,8 +55,7 @@ import com.shabinder.spotiflyer.utils.showDialog as showDialog1
|
|||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private var spotifyService : SpotifyService? = null
|
private lateinit var navController: NavHostController
|
||||||
lateinit var navController: NavHostController
|
|
||||||
@Inject lateinit var moshi: Moshi
|
@Inject lateinit var moshi: Moshi
|
||||||
@Inject lateinit var spotifyServiceTokenRequest: SpotifyServiceTokenRequest
|
@Inject lateinit var spotifyServiceTokenRequest: SpotifyServiceTokenRequest
|
||||||
|
|
||||||
@ -70,20 +69,28 @@ class MainActivity : AppCompatActivity() {
|
|||||||
ComposeLearnTheme {
|
ComposeLearnTheme {
|
||||||
Providers(AmbientContentColor provides colorOffWhite) {
|
Providers(AmbientContentColor provides colorOffWhite) {
|
||||||
ProvideWindowInsets {
|
ProvideWindowInsets {
|
||||||
Column {
|
val appBarColor = MaterialTheme.colors.surface.copy(alpha = 0.6f)
|
||||||
val appBarColor = MaterialTheme.colors.surface.copy(alpha = 0.87f)
|
navController = rememberNavController()
|
||||||
|
|
||||||
|
val gradientColor by sharedViewModel.gradientColor.collectAsState()
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize().verticalGradientScrim(
|
||||||
|
color = gradientColor.copy(alpha = 0.38f),
|
||||||
|
startYPercentage = 1f,
|
||||||
|
endYPercentage = 0f,
|
||||||
|
fixedHeight = 700f,
|
||||||
|
)
|
||||||
|
) {
|
||||||
// Draw a scrim over the status bar which matches the app bar
|
// Draw a scrim over the status bar which matches the app bar
|
||||||
Spacer(
|
Spacer(
|
||||||
Modifier.background(appBarColor).fillMaxWidth().statusBarsHeight()
|
Modifier.background(appBarColor).fillMaxWidth()
|
||||||
|
.statusBarsHeight()
|
||||||
)
|
)
|
||||||
|
|
||||||
AppBar(
|
AppBar(
|
||||||
backgroundColor = appBarColor,
|
backgroundColor = appBarColor,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
navController = rememberNavController()
|
|
||||||
|
|
||||||
ComposeNavigation(navController)
|
ComposeNavigation(navController)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,7 +148,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adding my own new Spotify Web Api Requests!
|
* Adding my own Spotify Web Api Requests!
|
||||||
* */
|
* */
|
||||||
private fun implementSpotifyService(token: String) {
|
private fun implementSpotifyService(token: String) {
|
||||||
val httpClient: OkHttpClient.Builder = OkHttpClient.Builder()
|
val httpClient: OkHttpClient.Builder = OkHttpClient.Builder()
|
||||||
@ -154,17 +161,17 @@ class MainActivity : AppCompatActivity() {
|
|||||||
chain.proceed(request)
|
chain.proceed(request)
|
||||||
}).addInterceptor(NetworkInterceptor())
|
}).addInterceptor(NetworkInterceptor())
|
||||||
|
|
||||||
val retrofit = Retrofit.Builder()
|
val retrofit = Retrofit.Builder().run{
|
||||||
.baseUrl("https://api.spotify.com/v1/")
|
baseUrl("https://api.spotify.com/v1/")
|
||||||
.client(httpClient.build())
|
client(httpClient.build())
|
||||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||||
.build()
|
build()
|
||||||
|
}
|
||||||
spotifyService = retrofit.create(SpotifyService::class.java)
|
sharedViewModel.spotifyService.value = retrofit.create(SpotifyService::class.java)
|
||||||
sharedViewModel.spotifyService.value = spotifyService
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun authenticateSpotify() {
|
fun authenticateSpotify() {
|
||||||
|
if(sharedViewModel.spotifyService.value == null){
|
||||||
sharedViewModel.viewModelScope.launch {
|
sharedViewModel.viewModelScope.launch {
|
||||||
log("Spotify Authentication","Started")
|
log("Spotify Authentication","Started")
|
||||||
val token = spotifyServiceTokenRequest.getToken()
|
val token = spotifyServiceTokenRequest.getToken()
|
||||||
@ -175,6 +182,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
log("Spotify Token", token.value.toString())
|
log("Spotify Token", token.value.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun handleIntentFromExternalActivity(intent: Intent? = getIntent()) {
|
private fun handleIntentFromExternalActivity(intent: Intent? = getIntent()) {
|
||||||
@ -227,7 +235,8 @@ fun AppBar(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = modifier
|
modifier = modifier,
|
||||||
|
elevation = 0.dp
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
package com.shabinder.spotiflyer
|
package com.shabinder.spotiflyer
|
||||||
|
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.hilt.lifecycle.ViewModelInject
|
import androidx.hilt.lifecycle.ViewModelInject
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
@ -25,8 +27,13 @@ import com.shabinder.spotiflyer.database.DatabaseDAO
|
|||||||
import com.shabinder.spotiflyer.models.PlatformQueryResult
|
import com.shabinder.spotiflyer.models.PlatformQueryResult
|
||||||
import com.shabinder.spotiflyer.networking.GaanaInterface
|
import com.shabinder.spotiflyer.networking.GaanaInterface
|
||||||
import com.shabinder.spotiflyer.networking.SpotifyService
|
import com.shabinder.spotiflyer.networking.SpotifyService
|
||||||
|
import com.shabinder.spotiflyer.ui.colorPrimary
|
||||||
|
import com.shabinder.spotiflyer.ui.colorPrimaryDark
|
||||||
|
import com.shabinder.spotiflyer.ui.home.HomeCategory
|
||||||
import dagger.hilt.android.scopes.ActivityRetainedScoped
|
import dagger.hilt.android.scopes.ActivityRetainedScoped
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
@ActivityRetainedScoped
|
@ActivityRetainedScoped
|
||||||
class SharedViewModel @ViewModelInject constructor(
|
class SharedViewModel @ViewModelInject constructor(
|
||||||
@ -34,6 +41,17 @@ class SharedViewModel @ViewModelInject constructor(
|
|||||||
val gaanaInterface : GaanaInterface,
|
val gaanaInterface : GaanaInterface,
|
||||||
val ytDownloader: YoutubeDownloader
|
val ytDownloader: YoutubeDownloader
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
var intentString = MutableLiveData<String>()
|
var spotifyService = MutableStateFlow<SpotifyService?>(null)
|
||||||
var spotifyService = MutableLiveData<SpotifyService>()
|
|
||||||
|
private val _gradientColor = MutableStateFlow(colorPrimaryDark)
|
||||||
|
val gradientColor : StateFlow<Color>
|
||||||
|
get() = _gradientColor
|
||||||
|
|
||||||
|
fun updateGradientColor(color: Color) {
|
||||||
|
_gradientColor.value = color
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetGradient() {
|
||||||
|
_gradientColor.value = colorPrimaryDark
|
||||||
|
}
|
||||||
}
|
}
|
@ -2,6 +2,7 @@ package com.shabinder.spotiflyer.ui.home
|
|||||||
|
|
||||||
import androidx.compose.foundation.*
|
import androidx.compose.foundation.*
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
@ -21,24 +22,26 @@ 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.layout.ContentScale
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.res.vectorResource
|
import androidx.compose.ui.res.vectorResource
|
||||||
import androidx.compose.ui.text.TextStyle
|
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.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.core.net.toUri
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.compose.navigate
|
|
||||||
import com.shabinder.spotiflyer.MainActivity
|
|
||||||
import com.shabinder.spotiflyer.R
|
import com.shabinder.spotiflyer.R
|
||||||
|
import com.shabinder.spotiflyer.database.DownloadRecord
|
||||||
import com.shabinder.spotiflyer.navigation.navigateToPlatform
|
import com.shabinder.spotiflyer.navigation.navigateToPlatform
|
||||||
import com.shabinder.spotiflyer.ui.SpotiFlyerTypography
|
import com.shabinder.spotiflyer.ui.SpotiFlyerTypography
|
||||||
import com.shabinder.spotiflyer.ui.colorAccent
|
import com.shabinder.spotiflyer.ui.colorAccent
|
||||||
import com.shabinder.spotiflyer.ui.colorPrimary
|
import com.shabinder.spotiflyer.ui.colorPrimary
|
||||||
import com.shabinder.spotiflyer.utils.mainActivity
|
|
||||||
import com.shabinder.spotiflyer.utils.openPlatform
|
import com.shabinder.spotiflyer.utils.openPlatform
|
||||||
import com.shabinder.spotiflyer.utils.sharedViewModel
|
import com.shabinder.spotiflyer.utils.sharedViewModel
|
||||||
import com.shabinder.spotiflyer.utils.showDialog
|
import dev.chrisbanes.accompanist.glide.GlideImage
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Home(navController: NavController, modifier: Modifier = Modifier) {
|
fun Home(navController: NavController, modifier: Modifier = Modifier) {
|
||||||
@ -47,7 +50,6 @@ fun Home(navController: NavController, modifier: Modifier = Modifier) {
|
|||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
|
|
||||||
val link by viewModel.link.collectAsState()
|
val link by viewModel.link.collectAsState()
|
||||||
val selectedCategory by viewModel.selectedCategory.collectAsState()
|
|
||||||
|
|
||||||
AuthenticationBanner(viewModel,modifier)
|
AuthenticationBanner(viewModel,modifier)
|
||||||
|
|
||||||
@ -57,7 +59,7 @@ fun Home(navController: NavController, modifier: Modifier = Modifier) {
|
|||||||
navController,
|
navController,
|
||||||
modifier
|
modifier
|
||||||
)
|
)
|
||||||
|
val selectedCategory by viewModel.selectedCategory.collectAsState()
|
||||||
|
|
||||||
HomeTabBar(
|
HomeTabBar(
|
||||||
selectedCategory,
|
selectedCategory,
|
||||||
@ -68,10 +70,13 @@ fun Home(navController: NavController, modifier: Modifier = Modifier) {
|
|||||||
|
|
||||||
when(selectedCategory){
|
when(selectedCategory){
|
||||||
HomeCategory.About -> AboutColumn()
|
HomeCategory.About -> AboutColumn()
|
||||||
HomeCategory.History -> HistoryColumn()
|
HomeCategory.History -> HistoryColumn(viewModel.downloadRecordList,navController)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
//Update Download List
|
||||||
|
viewModel.getDownloadRecordList()
|
||||||
|
//reset Gradient
|
||||||
|
sharedViewModel.resetGradient()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -186,7 +191,7 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
|||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.Center,
|
horizontalArrangement = Arrangement.Center,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = modifier.fillMaxWidth().padding(8.dp)
|
modifier = modifier.fillMaxSize().padding(8.dp).weight(1f)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.made_with_love),
|
text = stringResource(id = R.string.made_with_love),
|
||||||
@ -206,8 +211,52 @@ fun AboutColumn(modifier: Modifier = Modifier) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HistoryColumn() {
|
fun HistoryColumn(
|
||||||
//TODO("Not yet implemented")
|
downloadRecordList: StateFlow<List<DownloadRecord>>,
|
||||||
|
navController: NavController
|
||||||
|
) {
|
||||||
|
val list by downloadRecordList.collectAsState()
|
||||||
|
|
||||||
|
LazyColumn(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
content = {
|
||||||
|
items(list) {
|
||||||
|
DownloadRecordItem(item = it,navController = navController)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.padding(top = 8.dp).fillMaxSize()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DownloadRecordItem(item: DownloadRecord,navController: NavController) {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(end = 8.dp)) {
|
||||||
|
val imgUri = item.coverUrl.toUri().buildUpon().scheme("https").build()
|
||||||
|
GlideImage(
|
||||||
|
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(item.name,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(item.type,fontSize = 13.sp)
|
||||||
|
Text("Tracks: ${item.totalFiles}",fontSize = 13.sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Image(
|
||||||
|
imageVector = vectorResource(id = R.drawable.ic_share_open),
|
||||||
|
modifier = Modifier.clickable(onClick = { navController.navigateToPlatform(item.link) })
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -236,7 +285,7 @@ fun HomeTabBar(
|
|||||||
TabRow(
|
TabRow(
|
||||||
selectedTabIndex = selectedIndex,
|
selectedTabIndex = selectedIndex,
|
||||||
indicator = indicator,
|
indicator = indicator,
|
||||||
modifier = modifier
|
modifier = modifier,
|
||||||
) {
|
) {
|
||||||
categories.forEachIndexed { index, category ->
|
categories.forEachIndexed { index, category ->
|
||||||
Tab(
|
Tab(
|
||||||
@ -289,14 +338,16 @@ fun SearchPanel(
|
|||||||
RoundedCornerShape(30.dp)
|
RoundedCornerShape(30.dp)
|
||||||
),
|
),
|
||||||
backgroundColor = Color.Black,
|
backgroundColor = Color.Black,
|
||||||
textStyle = AmbientTextStyle.current.merge(TextStyle(fontSize = 20.sp,color = Color.White)),
|
textStyle = AmbientTextStyle.current.merge(TextStyle(fontSize = 18.sp,color = Color.White)),
|
||||||
shape = RoundedCornerShape(size = 30.dp),
|
shape = RoundedCornerShape(size = 30.dp),
|
||||||
activeColor = Color.Transparent,
|
activeColor = Color.Transparent,
|
||||||
inactiveColor = Color.Transparent
|
inactiveColor = Color.Transparent
|
||||||
)
|
)
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
modifier = Modifier.padding(12.dp).wrapContentWidth(),
|
modifier = Modifier.padding(12.dp).wrapContentWidth(),
|
||||||
onClick = {navController.navigateToPlatform(link)},
|
onClick = {
|
||||||
|
navController.navigateToPlatform(link)
|
||||||
|
},
|
||||||
border = BorderStroke(1.dp, Brush.horizontalGradient(listOf(colorPrimary, colorAccent)))
|
border = BorderStroke(1.dp, Brush.horizontalGradient(listOf(colorPrimary, colorAccent)))
|
||||||
){
|
){
|
||||||
Text(text = "Search",style = SpotiFlyerTypography.h6,modifier = Modifier.padding(4.dp))
|
Text(text = "Search",style = SpotiFlyerTypography.h6,modifier = Modifier.padding(4.dp))
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
package com.shabinder.spotiflyer.ui.home
|
package com.shabinder.spotiflyer.ui.home
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.shabinder.spotiflyer.database.DatabaseDAO
|
||||||
|
import com.shabinder.spotiflyer.database.DownloadRecord
|
||||||
|
import com.shabinder.spotiflyer.utils.sharedViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class HomeViewModel : ViewModel() {
|
class HomeViewModel : ViewModel() {
|
||||||
|
|
||||||
@ -30,6 +37,21 @@ class HomeViewModel: ViewModel() {
|
|||||||
_selectedCategory.value = s
|
_selectedCategory.value = s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val _downloadRecordList = MutableStateFlow<List<DownloadRecord>>(listOf())
|
||||||
|
val downloadRecordList: StateFlow<List<DownloadRecord>>
|
||||||
|
get() = _downloadRecordList
|
||||||
|
|
||||||
|
fun getDownloadRecordList() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
_downloadRecordList.value = sharedViewModel.databaseDAO.getRecord().toMutableList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
getDownloadRecordList()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class HomeCategory {
|
enum class HomeCategory {
|
||||||
|
@ -3,25 +3,33 @@ package com.shabinder.spotiflyer.ui.tracklist
|
|||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.material.ExtendedFloatingActionButton
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
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.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.res.vectorResource
|
import androidx.compose.ui.res.vectorResource
|
||||||
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 androidx.compose.ui.viewinterop.viewModel
|
import androidx.core.net.toUri
|
||||||
import androidx.navigation.NavController
|
|
||||||
import com.bumptech.glide.RequestManager
|
|
||||||
import com.shabinder.spotiflyer.R
|
import com.shabinder.spotiflyer.R
|
||||||
import com.shabinder.spotiflyer.models.PlatformQueryResult
|
import com.shabinder.spotiflyer.models.PlatformQueryResult
|
||||||
import com.shabinder.spotiflyer.models.TrackDetails
|
import com.shabinder.spotiflyer.models.TrackDetails
|
||||||
import com.shabinder.spotiflyer.models.spotify.Source
|
import com.shabinder.spotiflyer.models.spotify.Source
|
||||||
import com.shabinder.spotiflyer.ui.SpotiFlyerTypography
|
import com.shabinder.spotiflyer.ui.SpotiFlyerTypography
|
||||||
import com.shabinder.spotiflyer.ui.colorAccent
|
import com.shabinder.spotiflyer.ui.colorAccent
|
||||||
import dev.chrisbanes.accompanist.glide.GlideImage
|
import com.shabinder.spotiflyer.ui.utils.calculateDominantColor
|
||||||
|
import com.shabinder.spotiflyer.utils.sharedViewModel
|
||||||
|
import dev.chrisbanes.accompanist.coil.CoilImage
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* UI for List of Tracks to be universally used.
|
* UI for List of Tracks to be universally used.
|
||||||
@ -32,33 +40,86 @@ fun TrackList(
|
|||||||
source: Source,
|
source: Source,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
){
|
){
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
Box(modifier = modifier.fillMaxSize()){
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
content = {
|
content = {
|
||||||
|
item {
|
||||||
|
CoverImage(result.title,result.coverUrl,coroutineScope)
|
||||||
|
}
|
||||||
items(result.trackList) {
|
items(result.trackList) {
|
||||||
TrackCard(track = it)
|
TrackCard(track = it)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
)
|
||||||
|
DownloadAllButton(
|
||||||
|
onClick = {},
|
||||||
|
modifier = Modifier.padding(bottom = 24.dp).align(Alignment.BottomCenter)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CoverImage(
|
||||||
|
title: String,
|
||||||
|
coverURL: String,
|
||||||
|
scope: CoroutineScope,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier.padding(vertical = 8.dp).fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
CoilImage(
|
||||||
|
data = coverURL,
|
||||||
|
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,
|
||||||
|
//color = colorAccent,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
scope.launch { updateGradient(coverURL) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
@Composable
|
||||||
fun TrackCard(track:TrackDetails) {
|
fun TrackCard(track:TrackDetails) {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)) {
|
Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)) {
|
||||||
GlideImage(
|
val imgUri = track.albumArtURL.toUri().buildUpon().scheme("https").build()
|
||||||
data = track.albumArtURL,
|
CoilImage(
|
||||||
loading = { Image(vectorResource(id = R.drawable.ic_song_placeholder)) },
|
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)) },
|
error = { Image(vectorResource(id = R.drawable.ic_musicplaceholder)) },
|
||||||
contentScale = ContentScale.Inside,
|
contentScale = ContentScale.Inside,
|
||||||
|
// fadeIn = true,
|
||||||
modifier = Modifier.preferredHeight(75.dp).preferredWidth(90.dp)
|
modifier = Modifier.preferredHeight(75.dp).preferredWidth(90.dp)
|
||||||
)
|
)
|
||||||
Column(modifier = Modifier.padding(horizontal = 8.dp).fillMaxHeight().weight(1f),verticalArrangement = Arrangement.SpaceBetween) {
|
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)
|
Text(track.title,maxLines = 1,overflow = TextOverflow.Ellipsis,style = SpotiFlyerTypography.h6,color = colorAccent)
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
modifier = Modifier.padding(horizontal = 12.dp).fillMaxSize()
|
verticalAlignment = Alignment.Bottom,
|
||||||
|
modifier = Modifier.padding(horizontal = 8.dp).fillMaxSize()
|
||||||
){
|
){
|
||||||
Text("${track.artists.firstOrNull()}...",fontSize = 13.sp)
|
Text("${track.artists.firstOrNull()}...",fontSize = 13.sp)
|
||||||
Text("${track.durationSec/60} minutes, ${track.durationSec%60} sec",fontSize = 13.sp)
|
Text("${track.durationSec/60} minutes, ${track.durationSec%60} sec",fontSize = 13.sp)
|
||||||
@ -67,3 +128,8 @@ fun TrackCard(track:TrackDetails) {
|
|||||||
Image(vectorResource(id = R.drawable.ic_arrow))
|
Image(vectorResource(id = R.drawable.ic_arrow))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun updateGradient(imageURL:String){
|
||||||
|
calculateDominantColor(imageURL)?.color
|
||||||
|
?.let { sharedViewModel.updateGradientColor(it) }
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.shabinder.spotiflyer.ui.utils
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.compositeOver
|
||||||
|
import androidx.compose.ui.graphics.luminance
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
fun Color.contrastAgainst(background: Color): Float {
|
||||||
|
val fg = if (alpha < 1f) compositeOver(background) else this
|
||||||
|
|
||||||
|
val fgLuminance = fg.luminance() + 0.05f
|
||||||
|
val bgLuminance = background.luminance() + 0.05f
|
||||||
|
|
||||||
|
return max(fgLuminance, bgLuminance) / min(fgLuminance, bgLuminance)
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.shabinder.spotiflyer.ui.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.collection.LruCache
|
||||||
|
import androidx.compose.animation.animate
|
||||||
|
import androidx.compose.animation.core.Spring
|
||||||
|
import androidx.compose.animation.core.spring
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.AmbientContext
|
||||||
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
|
import androidx.palette.graphics.Palette
|
||||||
|
import coil.Coil
|
||||||
|
import coil.request.ImageRequest
|
||||||
|
import coil.request.SuccessResult
|
||||||
|
import coil.size.Scale
|
||||||
|
import com.shabinder.spotiflyer.utils.mainActivity
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class DominantColors(val color: Color, val onColor: Color)
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun calculateDominantColor(url: String): DominantColors? {
|
||||||
|
// we calculate the swatches in the image, and return the first valid color
|
||||||
|
return calculateSwatchesInImage(mainActivity, url)
|
||||||
|
// First we want to sort the list by the color's population
|
||||||
|
.sortedByDescending { swatch -> swatch.population }
|
||||||
|
// Then we want to find the first valid color
|
||||||
|
.firstOrNull { swatch -> Color(swatch.rgb).contrastAgainst(Color.Black) >= 3f }
|
||||||
|
// If we found a valid swatch, wrap it in a [DominantColors]
|
||||||
|
?.let { swatch ->
|
||||||
|
DominantColors(
|
||||||
|
color = Color(swatch.rgb),
|
||||||
|
onColor = Color(swatch.bodyTextColor).copy(alpha = 1f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the given [imageUrl] with [Coil], then uses [Palette] to calculate the dominant color.
|
||||||
|
*/
|
||||||
|
suspend fun calculateSwatchesInImage(
|
||||||
|
context: Context,
|
||||||
|
imageUrl: String
|
||||||
|
): List<Palette.Swatch> {
|
||||||
|
val r = ImageRequest.Builder(context)
|
||||||
|
.data(imageUrl)
|
||||||
|
// We scale the image to cover 128px x 128px (i.e. min dimension == 128px)
|
||||||
|
.size(128).scale(Scale.FILL)
|
||||||
|
// Disable hardware bitmaps, since Palette uses Bitmap.getPixels()
|
||||||
|
.allowHardware(false)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val bitmap = when (val result = Coil.execute(r)) {
|
||||||
|
is SuccessResult -> result.drawable.toBitmap()
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitmap?.let {
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
val palette = Palette.Builder(bitmap)
|
||||||
|
// Disable any bitmap resizing in Palette. We've already loaded an appropriately
|
||||||
|
// sized bitmap through Coil
|
||||||
|
.resizeBitmapArea(0)
|
||||||
|
// Clear any built-in filters. We want the unfiltered dominant color
|
||||||
|
.clearFilters()
|
||||||
|
// We reduce the maximum color count down to 8
|
||||||
|
.maximumColorCount(8)
|
||||||
|
.generate()
|
||||||
|
|
||||||
|
palette.swatches
|
||||||
|
}
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.example.jetcaster.util
|
||||||
|
|
||||||
|
import androidx.annotation.FloatRange
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.composed
|
||||||
|
import androidx.compose.ui.draw.drawBehind
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import com.shabinder.spotiflyer.utils.log
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a vertical gradient scrim in the foreground.
|
||||||
|
*
|
||||||
|
* @param color The color of the gradient scrim.
|
||||||
|
* @param startYPercentage The start y value, in percentage of the layout's height (0f to 1f)
|
||||||
|
* @param endYPercentage The end y value, in percentage of the layout's height (0f to 1f)
|
||||||
|
* @param decay The exponential decay to apply to the gradient. Defaults to `1.0f` which is
|
||||||
|
* a linear gradient.
|
||||||
|
* @param numStops The number of color stops to draw in the gradient. Higher numbers result in
|
||||||
|
* the higher visual quality at the cost of draw performance. Defaults to `16`.
|
||||||
|
*/
|
||||||
|
fun Modifier.verticalGradientScrim(
|
||||||
|
color: Color,
|
||||||
|
@FloatRange(from = 0.0, to = 1.0) startYPercentage: Float = 0f,
|
||||||
|
@FloatRange(from = 0.0, to = 1.0) endYPercentage: Float = 1f,
|
||||||
|
decay: Float = 1.0f,
|
||||||
|
numStops: Int = 16,
|
||||||
|
fixedHeight: Float? = null
|
||||||
|
): Modifier = composed {
|
||||||
|
val colors = remember(color, numStops) {
|
||||||
|
if (decay != 1f) {
|
||||||
|
// If we have a non-linear decay, we need to create the color gradient steps
|
||||||
|
// manually
|
||||||
|
val baseAlpha = color.alpha
|
||||||
|
List(numStops) { i ->
|
||||||
|
val x = i * 1f / (numStops - 1)
|
||||||
|
val opacity = x.pow(decay)
|
||||||
|
color.copy(alpha = baseAlpha * opacity)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If we have a linear decay, we just create a simple list of start + end colors
|
||||||
|
listOf(color.copy(alpha = 0f), color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var height by remember { mutableStateOf(fixedHeight ?: 0f) }
|
||||||
|
val brush = remember(color, numStops, startYPercentage, endYPercentage, height) {
|
||||||
|
Brush.verticalGradient(
|
||||||
|
colors = colors,
|
||||||
|
startY = height * startYPercentage,
|
||||||
|
endY = height * endYPercentage
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
drawBehind {
|
||||||
|
height = fixedHeight ?: size.height
|
||||||
|
// log("Height",size.height.toString())
|
||||||
|
drawRect(brush = brush)
|
||||||
|
}
|
||||||
|
}
|
@ -17,13 +17,13 @@
|
|||||||
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="300dp"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="300dp"
|
||||||
android:height="300dp" android:viewportWidth="512" android:viewportHeight="512">
|
android:height="300dp" android:viewportWidth="512" android:viewportHeight="512">
|
||||||
<path android:fillColor="#A3FFFFFF" android:pathData="m256,80a48.054,48.054 0,0 1,48 48v32h12a19.991,19.991 0,0 0,3.524 -39.671,63.984 63.984,0 0,0 -127.048,0 19.991,19.991 0,0 0,3.524 39.671h12v-32a48.054,48.054 0,0 1,48 -48z"/>
|
<path android:fillColor="#A3787878" android:pathData="m256,80a48.054,48.054 0,0 1,48 48v32h12a19.991,19.991 0,0 0,3.524 -39.671,63.984 63.984,0 0,0 -127.048,0 19.991,19.991 0,0 0,3.524 39.671h12v-32a48.054,48.054 0,0 1,48 -48z"/>
|
||||||
<path android:fillColor="#A3FFFFFF" android:pathData="m48,152a24.027,24.027 0,0 0,24 -24v-74.234l42.53,-14.176 -5.06,-15.18 -48,16a8,8 0,0 0,-5.47 7.59v57.376a24,24 0,1 0,-8 46.624zM48,120a8,8 0,1 1,-8 8,8.009 8.009,0 0,1 8,-8z"/>
|
<path android:fillColor="#A3787878" android:pathData="m48,152a24.027,24.027 0,0 0,24 -24v-74.234l42.53,-14.176 -5.06,-15.18 -48,16a8,8 0,0 0,-5.47 7.59v57.376a24,24 0,1 0,-8 46.624zM48,120a8,8 0,1 1,-8 8,8.009 8.009,0 0,1 8,-8z"/>
|
||||||
<path android:fillColor="#A3FFFFFF" android:pathData="m485.006,17.76a7.993,7.993 0,0 0,-6.741 -1.569l-72,16a8,8 0,0 0,-6.265 7.809v57.376a24,24 0,1 0,16 22.624v-73.583l56,-12.444v47.4a24,24 0,1 0,16 22.627v-80a8,8 0,0 0,-2.994 -6.24zM392,128a8,8 0,1 1,8 -8,8.009 8.009,0 0,1 -8,8zM464,112a8,8 0,1 1,8 -8,8.009 8.009,0 0,1 -8,8z"/>
|
<path android:fillColor="#A3787878" android:pathData="m485.006,17.76a7.993,7.993 0,0 0,-6.741 -1.569l-72,16a8,8 0,0 0,-6.265 7.809v57.376a24,24 0,1 0,16 22.624v-73.583l56,-12.444v47.4a24,24 0,1 0,16 22.627v-80a8,8 0,0 0,-2.994 -6.24zM392,128a8,8 0,1 1,8 -8,8.009 8.009,0 0,1 -8,8zM464,112a8,8 0,1 1,8 -8,8.009 8.009,0 0,1 -8,8z"/>
|
||||||
<path android:fillColor="#A3FFFFFF" android:pathData="m48,456h416v40h-416z"/>
|
<path android:fillColor="#A3787878" android:pathData="m48,456h416v40h-416z"/>
|
||||||
<path android:fillColor="#A3FFFFFF" android:pathData="m64,376a16,16 0,0 0,-16 16v7h48v-7a16,16 0,0 0,-16 -16z"/>
|
<path android:fillColor="#A3787878" android:pathData="m64,376a16,16 0,0 0,-16 16v7h48v-7a16,16 0,0 0,-16 -16z"/>
|
||||||
<path android:fillColor="#A3FFFFFF" android:pathData="m24,416h464v24h-464z"/>
|
<path android:fillColor="#A3787878" android:pathData="m24,416h464v24h-464z"/>
|
||||||
<path android:fillColor="#A3FFFFFF" android:pathData="M256,144m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0"/>
|
<path android:fillColor="#A3787878" android:pathData="M256,144m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0"/>
|
||||||
<path android:fillColor="#A3FFFFFF" android:pathData="m368,400 l16,-160h-256l16,160zM256,296a24,24 0,1 1,-24 24,24 24,0 0,1 24,-24z"/>
|
<path android:fillColor="#A3787878" android:pathData="m368,400 l16,-160h-256l16,160zM256,296a24,24 0,1 1,-24 24,24 24,0 0,1 24,-24z"/>
|
||||||
<path android:fillColor="#A3FFFFFF" android:pathData="m168,224h176a32,32 0,0 0,-32 -32h-112a32,32 0,0 0,-32 32z"/>
|
<path android:fillColor="#A3787878" android:pathData="m168,224h176a32,32 0,0 0,-32 -32h-112a32,32 0,0 0,-32 32z"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="30dp"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="32dp"
|
||||||
android:height="30dp" android:viewportWidth="512" android:viewportHeight="512">
|
android:height="32dp" android:viewportWidth="512" android:viewportHeight="512">
|
||||||
<path android:fillColor="#FF3C64" android:pathData="m304,232a24,24 0,0 1,-16.971 -40.971l160,-160a24,24 0,0 1,33.942 33.942l-160,160a23.926,23.926 0,0 1,-16.971 7.029z"/>
|
<path android:fillColor="#FF3C64" android:pathData="m304,232a24,24 0,0 1,-16.971 -40.971l160,-160a24,24 0,0 1,33.942 33.942l-160,160a23.926,23.926 0,0 1,-16.971 7.029z"/>
|
||||||
<path android:fillColor="#FF3B63" android:pathData="m464,200a24,24 0,0 1,-24 -24v-104h-104a24,24 0,0 1,0 -48h128a24,24 0,0 1,24 24v128a24,24 0,0 1,-24 24z"/>
|
<path android:fillColor="#FF3B63" android:pathData="m464,200a24,24 0,0 1,-24 -24v-104h-104a24,24 0,0 1,0 -48h128a24,24 0,0 1,24 24v128a24,24 0,0 1,-24 24z"/>
|
||||||
<path android:fillColor="#CE1CFF" android:pathData="m464,488h-416a24,24 0,0 1,-24 -24v-416a24,24 0,0 1,24 -24h176a24,24 0,0 1,0 48h-152v368h368v-152a24,24 0,0 1,48 0v176a24,24 0,0 1,-24 24z"/>
|
<path android:fillColor="#CE1CFF" android:pathData="m464,488h-416a24,24 0,0 1,-24 -24v-416a24,24 0,0 1,24 -24h176a24,24 0,0 1,0 48h-152v368h368v-152a24,24 0,0 1,48 0v176a24,24 0,0 1,-24 24z"/>
|
||||||
|
@ -17,6 +17,6 @@
|
|||||||
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="42dp"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="42dp"
|
||||||
android:height="42dp" android:viewportWidth="512" android:viewportHeight="512">
|
android:height="42dp" android:viewportWidth="512" android:viewportHeight="512">
|
||||||
<path android:fillColor="#A3FFFFFF" android:pathData="m511.739,103.734 l-257,50.947v233.725c-10.733,-7.199 -23.633,-11.406 -37.5,-11.406 -37.22,0 -67.5,30.28 -67.5,67.5s30.28,67.5 67.5,67.5c34.684,0 63.329,-26.299 67.073,-60h0.427v-182.682l197,-39.053v98.141c-10.733,-7.199 -23.633,-11.406 -37.5,-11.406 -37.22,0 -67.5,30.28 -67.5,67.5s30.28,67.5 67.5,67.5c39.927,0 71.547,-34.762 67.073,-75h0.427zM217.239,482c-20.678,0 -37.5,-16.822 -37.5,-37.5s16.822,-37.5 37.5,-37.5 37.5,16.822 37.5,37.5 -16.822,37.5 -37.5,37.5zM444.239,422c-20.678,0 -37.5,-16.822 -37.5,-37.5s16.822,-37.5 37.5,-37.5 37.5,16.822 37.5,37.5 -16.822,37.5 -37.5,37.5zM481.739,199.682 L284.739,238.735v-59.416l197,-39.053z"/>
|
<path android:fillColor="#A3787878" android:pathData="m511.739,103.734 l-257,50.947v233.725c-10.733,-7.199 -23.633,-11.406 -37.5,-11.406 -37.22,0 -67.5,30.28 -67.5,67.5s30.28,67.5 67.5,67.5c34.684,0 63.329,-26.299 67.073,-60h0.427v-182.682l197,-39.053v98.141c-10.733,-7.199 -23.633,-11.406 -37.5,-11.406 -37.22,0 -67.5,30.28 -67.5,67.5s30.28,67.5 67.5,67.5c39.927,0 71.547,-34.762 67.073,-75h0.427zM217.239,482c-20.678,0 -37.5,-16.822 -37.5,-37.5s16.822,-37.5 37.5,-37.5 37.5,16.822 37.5,37.5 -16.822,37.5 -37.5,37.5zM444.239,422c-20.678,0 -37.5,-16.822 -37.5,-37.5s16.822,-37.5 37.5,-37.5 37.5,16.822 37.5,37.5 -16.822,37.5 -37.5,37.5zM481.739,199.682 L284.739,238.735v-59.416l197,-39.053z"/>
|
||||||
<path android:fillColor="#A3FFFFFF" android:pathData="m182.179,159.75h30c0,-31.002 4.415,-66.799 -24.144,-95.356 -8.968,-8.968 -17.455,-16.07 -24.942,-22.336 -19.798,-16.57 -27.832,-24.012 -27.832,-42.058h-30v221.406c-10.734,-7.199 -23.634,-11.406 -37.5,-11.406 -37.22,0 -67.5,30.28 -67.5,67.5s30.28,67.5 67.5,67.5c34.684,0 63.329,-26.299 67.073,-60h0.427v-227.219c9.458,8.262 20.077,16.341 31.562,27.825 19.029,19.031 15.356,44.009 15.356,74.144zM67.761,315c-20.678,0 -37.5,-16.822 -37.5,-37.5s16.822,-37.5 37.5,-37.5 37.5,16.822 37.5,37.5 -16.823,37.5 -37.5,37.5z"/>
|
<path android:fillColor="#A3787878" android:pathData="m182.179,159.75h30c0,-31.002 4.415,-66.799 -24.144,-95.356 -8.968,-8.968 -17.455,-16.07 -24.942,-22.336 -19.798,-16.57 -27.832,-24.012 -27.832,-42.058h-30v221.406c-10.734,-7.199 -23.634,-11.406 -37.5,-11.406 -37.22,0 -67.5,30.28 -67.5,67.5s30.28,67.5 67.5,67.5c34.684,0 63.329,-26.299 67.073,-60h0.427v-227.219c9.458,8.262 20.077,16.341 31.562,27.825 19.029,19.031 15.356,44.009 15.356,74.144zM67.761,315c-20.678,0 -37.5,-16.822 -37.5,-37.5s16.822,-37.5 37.5,-37.5 37.5,16.822 37.5,37.5 -16.823,37.5 -37.5,37.5z"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
Loading…
Reference in New Issue
Block a user