diff --git a/android/build.gradle.kts b/android/build.gradle.kts index d3620fc1..09047061 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -91,6 +91,13 @@ dependencies { implementation(fetch) } + implementation(MVIKotlin.mvikotlin) + implementation(MVIKotlin.mvikotlinMain) + implementation(MVIKotlin.mvikotlinLogging) + implementation(MVIKotlin.mvikotlinTimeTravel) + implementation(Decompose.decompose) + implementation(Decompose.extensionsCompose) + //Test testImplementation("junit:junit:4.13.1") androidTestImplementation(Androidx.junit) diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index d829873d..13fbb871 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,16 +1,59 @@ - + + + + + + + + + + + + + + + + + + + - + android:name=".App" + android:allowBackup="false" + android:supportsRtl="true" + android:usesCleartextTraffic="true" + android:theme="@style/Theme.AppCompat.Light.NoActionBar" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:roundIcon="@mipmap/ic_launcher_round" + android:forceDarkAllowed="true" + android:requestLegacyExternalStorage="true" + tools:targetApi="q"> + - - + + + + + + + + + \ No newline at end of file diff --git a/android/src/main/java/com/shabinder/android/MainActivity.kt b/android/src/main/java/com/shabinder/android/MainActivity.kt index a9f4b513..c0f9e6d3 100644 --- a/android/src/main/java/com/shabinder/android/MainActivity.kt +++ b/android/src/main/java/com/shabinder/android/MainActivity.kt @@ -2,21 +2,43 @@ package com.shabinder.android import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.platform.setContent -import com.shabinder.android.di.appModule -import com.shabinder.common.database.appContext -import com.shabinder.common.initKoin -import com.shabinder.common.ui.SpotiFlyerMain -import org.koin.android.ext.koin.androidLogger +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.extensions.compose.jetbrains.rootComponent +import com.arkivanov.mvikotlin.logging.store.LoggingStoreFactory +import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory +import com.shabinder.common.Dir +import com.shabinder.common.FetchPlatformQueryResult +import com.shabinder.common.root.SpotiFlyerRoot +import com.shabinder.common.root.SpotiFlyerRootContent +import com.shabinder.common.ui.SpotiFlyerTheme +import com.shabinder.database.Database +import org.koin.android.ext.android.inject class MainActivity : AppCompatActivity() { + + val database: Database by inject() + val fetcher: FetchPlatformQueryResult by inject() + val dir: Dir by inject() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { - val scope = rememberCoroutineScope() - SpotiFlyerMain() - + SpotiFlyerTheme { + SpotiFlyerRootContent(rootComponent(::spotiFlyerRoot)) + } } } -} \ No newline at end of file + + + private fun spotiFlyerRoot(componentContext: ComponentContext): SpotiFlyerRoot = + SpotiFlyerRoot( + componentContext, + dependencies = object : SpotiFlyerRoot.Dependencies{ + override val storeFactory = LoggingStoreFactory(DefaultStoreFactory) + override val database = this@MainActivity.database + override val fetchPlatformQueryResult = this@MainActivity.fetcher + override val directories: Dir = this@MainActivity.dir + } + ) +} diff --git a/android/src/main/java/com/shabinder/android/SharedViewModel.kt b/android/src/main/java/com/shabinder/android/SharedViewModel.kt deleted file mode 100644 index e0fa5edd..00000000 --- a/android/src/main/java/com/shabinder/android/SharedViewModel.kt +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2021 Shabinder Singh - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.shabinder.android - -import android.content.Intent -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateListOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.compose.ui.graphics.Color -import androidx.lifecycle.ViewModel -import co.touchlab.kermit.Kermit -import com.shabinder.common.DownloadStatus -import com.shabinder.common.TrackDetails -import com.shabinder.common.YoutubeProvider -import com.shabinder.common.providers.GaanaProvider -import com.shabinder.common.providers.SpotifyProvider -import com.shabinder.database.Database -import com.shabinder.spotiflyer.ui.colorPrimaryDark -import com.tonyodev.fetch2.Status - -class SharedViewModel( - val database: Database, - val logger: Kermit, - val spotifyProvider: SpotifyProvider, - val gaanaProvider : GaanaProvider, - val youtubeProvider: YoutubeProvider -) : ViewModel() { - var isAuthenticated by mutableStateOf(false) - private set - - fun authenticated(s:Boolean) { - isAuthenticated = s - } - - /* - * Nav Gives Error on YT links with ? sign - * */ - var link by mutableStateOf("") - private set - - fun updateLink(s:String) { - link = s - } - - - val trackList = mutableStateListOf() - - fun updateTrackList(list:List){ - trackList.clear() - trackList.addAll(list) - } - fun updateTrackStatus(position:Int, status: DownloadStatus){ - if(position != -1){ - val track = trackList[position].apply { downloaded = status } - trackList[position] = track - } - } - - fun updateTrackStatus(intent: Intent){ - val trackDetails = intent.getSerializableExtra("track") as TrackDetails? - trackDetails?.let { - val position: Int = - trackList.map { trackState -> trackState.title }.indexOf(it.title) - logger.d{"$position, ${intent.action} , ${it.title}"} - if (position != -1) { - trackList.getOrNull(position)?.let{ track -> - when (intent.action) { - Status.QUEUED.name -> { - track.downloaded = DownloadStatus.Queued - } - Status.FAILED.name -> { - track.downloaded = DownloadStatus.Failed - } - Status.DOWNLOADING.name -> { - track.downloaded = DownloadStatus.Downloading - } - "Progress" -> { - //Progress Update - track.progress = intent.getIntExtra("progress", 0) - track.downloaded = DownloadStatus.Downloading - } - "Converting" -> { - //Progress Update - track.downloaded = DownloadStatus.Converting - } - "track_download_completed" -> { - track.downloaded = DownloadStatus.Downloaded - } - } - trackList[position] = track - logger.d{"TrackListUpdated"} - } - } - } - } - - var gradientColor by mutableStateOf(Color.Transparent) - private set - - fun updateGradientColor(color: Color) { - gradientColor = color - } - - fun resetGradient() { - gradientColor = colorPrimaryDark - } -} \ No newline at end of file diff --git a/android/src/main/java/com/shabinder/android/di/AppModule.kt b/android/src/main/java/com/shabinder/android/di/AppModule.kt index 4a5a2101..c94c29cb 100644 --- a/android/src/main/java/com/shabinder/android/di/AppModule.kt +++ b/android/src/main/java/com/shabinder/android/di/AppModule.kt @@ -1,9 +1,7 @@ package com.shabinder.android.di -import com.shabinder.android.SharedViewModel -import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.dsl.module val appModule = module { - viewModel { SharedViewModel(get(),get(),get(),get(),get()) } + } diff --git a/android/src/main/java/com/shabinder/android/navigation/ComposeNavigation.kt b/android/src/main/java/com/shabinder/android/navigation/ComposeNavigation.kt deleted file mode 100644 index 978defc0..00000000 --- a/android/src/main/java/com/shabinder/android/navigation/ComposeNavigation.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2021 Shabinder Singh - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.shabinder.android.navigation - -import androidx.compose.runtime.Composable -import androidx.navigation.NavController -import androidx.navigation.NavHostController -import androidx.navigation.NavType -import androidx.navigation.compose.* -import com.shabinder.spotiflyer.MainActivity -import com.shabinder.spotiflyer.providers.GaanaProvider -import com.shabinder.spotiflyer.providers.SpotifyProvider -import com.shabinder.spotiflyer.providers.YoutubeProvider -import com.shabinder.common.ui.home.Home -import com.shabinder.spotiflyer.ui.tracklist.TrackList -import com.shabinder.spotiflyer.utils.sharedViewModel - -@Composable -fun ComposeNavigation( - mainActivity: MainActivity, - navController: NavHostController, - spotifyProvider: SpotifyProvider, - gaanaProvider: GaanaProvider, - youtubeProvider: YoutubeProvider, - ) { - NavHost( - navController = navController, - startDestination = "home" - ) { - - //HomeScreen - Starting Point - composable("home") { - Home( - navController = navController, - mainActivity, - ) - } - - //Spotify Screen - //Argument `link` = Link of Track/Album/Playlist - composable( - "track_list/{link}", - arguments = listOf(navArgument("link") { type = NavType.StringType }) - ) { - TrackList( - fullLink = it.arguments?.getString("link") ?: "error", - navController = navController, - spotifyProvider, - gaanaProvider, - youtubeProvider - ) - } - } -} - -fun NavController.navigateToTrackList(link:String, singleInstance: Boolean = true, inclusive:Boolean = false) { - sharedViewModel.updateLink(link) - navigate("track_list/$link") { - launchSingleTop = singleInstance - popUpTo(route = "home") { - this.inclusive = inclusive - } - } -} \ No newline at end of file diff --git a/android/src/main/res/drawable/ic_launcher_foreground.xml b/android/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 00000000..b41fc74d --- /dev/null +++ b/android/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..b9eb3da5 --- /dev/null +++ b/android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..b9eb3da5 --- /dev/null +++ b/android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/android/src/main/res/values/colors.xml b/android/src/main/res/values/colors.xml new file mode 100644 index 00000000..31fa7b96 --- /dev/null +++ b/android/src/main/res/values/colors.xml @@ -0,0 +1,29 @@ + + + + + #FC5C7D + #CE1CFF + #9AB3FF + #FFFFFF + #99FFFFFF + #E6333333 + #000000 + #121212 + #59C351 + #FF9494 + \ No newline at end of file diff --git a/android/src/main/res/values/ic_launcher_background.xml b/android/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 00000000..0d48aff9 --- /dev/null +++ b/android/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,20 @@ + + + + + #000000 + \ No newline at end of file diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml new file mode 100644 index 00000000..4867d84c --- /dev/null +++ b/android/src/main/res/values/strings.xml @@ -0,0 +1,33 @@ + + + + SpotiFlyer + About + History + Supported Platforms + Support Development + Star / Fork the project on Github. + GitHub + Translate + Help us translate this app in your local language. + Donate + If you think I deserve to get paid for my work, you can leave me some money here. + Share + Share this app with your friends and family. + Made with + in India + \ No newline at end of file diff --git a/android/src/main/res/values/themes.xml b/android/src/main/res/values/themes.xml new file mode 100644 index 00000000..1948e03e --- /dev/null +++ b/android/src/main/res/values/themes.xml @@ -0,0 +1,29 @@ + + + + + + \ No newline at end of file diff --git a/common/compose-ui/src/androidMain/kotlin/com/shabinder/common/ui/Images.kt b/common/compose-ui/src/androidMain/kotlin/com/shabinder/common/ui/Images.kt index d72bc715..1d4b599a 100644 --- a/common/compose-ui/src/androidMain/kotlin/com/shabinder/common/ui/Images.kt +++ b/common/compose-ui/src/androidMain/kotlin/com/shabinder/common/ui/Images.kt @@ -33,4 +33,24 @@ actual fun DownloadImageArrow(modifier: Modifier){ } @Composable -actual fun DownloadAllImage() = vectorResource(R.drawable.ic_download_arrow) \ No newline at end of file +actual fun DownloadAllImage() = vectorResource(R.drawable.ic_download_arrow) + +@Composable +actual fun SpotiFlyerLogo() = vectorResource(R.drawable.ic_spotiflyer_logo) + +@Composable +actual fun HeartIcon() = vectorResource(R.drawable.ic_heart) + +@Composable +actual fun SpotifyLogo() = vectorResource(R.drawable.ic_spotify_logo) + +@Composable +actual fun GaanaLogo() = vectorResource(R.drawable.ic_gaana) + +@Composable +actual fun YoutubeLogo() = vectorResource(R.drawable.ic_youtube) + +@Composable +actual fun YoutubeMusicLogo() = vectorResource(R.drawable.ic_youtube_music_logo) +@Composable +actual fun GithubLogo() = vectorResource(R.drawable.ic_github) \ No newline at end of file diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/list/SpotiFlyerListUi.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/list/SpotiFlyerListUi.kt index 184f5d0f..c487a2cc 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/list/SpotiFlyerListUi.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/list/SpotiFlyerListUi.kt @@ -20,8 +20,8 @@ import com.shabinder.common.DownloadStatus import com.shabinder.common.Picture import com.shabinder.common.TrackDetails import com.shabinder.common.ui.* -import com.shabinder.spotiflyer.ui.SpotiFlyerTypography -import com.shabinder.spotiflyer.ui.colorAccent +import com.shabinder.common.ui.SpotiFlyerTypography +import com.shabinder.common.ui.colorAccent import kotlinx.coroutines.CoroutineScope @Composable diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt index 3bc9acb9..e8d3dfa4 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt @@ -2,7 +2,9 @@ package com.shabinder.common.main import com.arkivanov.decompose.ComponentContext import com.arkivanov.mvikotlin.core.store.StoreFactory +import com.shabinder.common.Dir import com.shabinder.common.DownloadRecord +import com.shabinder.common.Picture import com.shabinder.common.main.integration.SpotiFlyerMainImpl import com.shabinder.common.utils.Consumer import com.shabinder.database.Database @@ -23,10 +25,21 @@ interface SpotiFlyerMain { * */ fun onInputLinkChanged(link: String) + /* + * change TabBar Selected Category + * */ + fun selectCategory(category: HomeCategory) + + /* + * Load Image from cache/Internet and cache it + * */ + fun loadImage(url:String):Picture? + interface Dependencies { fun mainOutput(searched: Output): Consumer val storeFactory: StoreFactory val database: Database + val dir: Dir } sealed class Output { data class Search(val link: String) : Output() @@ -34,8 +47,12 @@ interface SpotiFlyerMain { data class State( val records: List = emptyList(), - val link: String = "" + val link: String = "", + val selectedCategory: HomeCategory = HomeCategory.About ) + enum class HomeCategory { + About, History + } } @Suppress("FunctionName") // Factory function diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMainUi.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMainUi.kt index 03cbf629..8ef2b797 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMainUi.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMainUi.kt @@ -1,10 +1,359 @@ package com.shabinder.common.main +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.* +import androidx.compose.material.AmbientTextStyle +import androidx.compose.material.Icon +import androidx.compose.material.TabDefaults.tabIndicatorOffset +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.DateRange +import androidx.compose.material.icons.outlined.Info +import androidx.compose.material.icons.rounded.Edit +import androidx.compose.material.icons.rounded.Info +import androidx.compose.material.icons.rounded.Share import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +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.sp +import com.shabinder.common.DownloadRecord +import com.shabinder.common.Picture +import com.shabinder.common.main.SpotiFlyerMain.HomeCategory +import com.shabinder.common.openPlatform +import com.shabinder.common.ui.* +import com.shabinder.common.ui.SpotiFlyerTypography @Composable fun SpotiFlyerMainContent(component: SpotiFlyerMain){ val model by component.models.collectAsState(SpotiFlyerMain.State()) + + Column { + + SearchPanel( + model.link, + component::onInputLinkChanged, + component::onLinkSearch + ) + + HomeTabBar( + model.selectedCategory, + HomeCategory.values(), + component::selectCategory + ) + + when(model.selectedCategory){ + HomeCategory.About -> AboutColumn() + HomeCategory.History -> HistoryColumn( + model.records, + component::loadImage, + component::onLinkSearch + ) + } + } +} + +@Composable +fun HomeTabBar( + selectedCategory: HomeCategory, + categories: Array, + selectCategory: (HomeCategory) -> Unit, + modifier: Modifier = Modifier +) { + val selectedIndex =categories.indexOfFirst { it == selectedCategory } + val indicator = @Composable { tabPositions: List -> + HomeCategoryTabIndicator( + Modifier.tabIndicatorOffset(tabPositions[selectedIndex]) + ) + } + + @Suppress("USELESS_CAST")//Showing Error in Latest Android Studio Canary + TabRow( + selectedTabIndex = selectedIndex, + indicator = indicator as @Composable (List) -> Unit, + modifier = modifier, + ) { + categories.forEachIndexed { index, category -> + Tab( + selected = index == selectedIndex, + onClick = { selectCategory(category) }, + text = { + Text( + text = when (category) { + HomeCategory.About -> "About" + HomeCategory.History -> "History" + }, + style = MaterialTheme.typography.body2 + ) + }, + icon = { + when (category) { + HomeCategory.About -> Icon(Icons.Outlined.Info) + HomeCategory.History -> Icon(Icons.Outlined.DateRange) + } + } + ) + } + } +} + +@Composable +fun SearchPanel( + link:String, + updateLink:(String) -> Unit, + onSearch:(String) -> Unit, + modifier: Modifier = Modifier +){ + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = modifier.padding(top = 16.dp) + ){ + TextField( + leadingIcon = { + Icon(Icons.Rounded.Edit,tint = Color.LightGray) + }, + label = { Text(text = "Paste Link Here...",color = Color.LightGray) }, + value = link, + onValueChange = { updateLink(it) }, + singleLine = true, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Uri), + modifier = Modifier.padding(12.dp).fillMaxWidth() + .border( + BorderStroke(2.dp, Brush.horizontalGradient(listOf(colorPrimary, colorAccent))), + RoundedCornerShape(30.dp) + ), + backgroundColor = Color.Black, + textStyle = AmbientTextStyle.current.merge(TextStyle(fontSize = 18.sp,color = Color.White)), + shape = RoundedCornerShape(size = 30.dp), + activeColor = Color.Transparent, + inactiveColor = Color.Transparent + ) + OutlinedButton( + modifier = Modifier.padding(12.dp).wrapContentWidth(), + onClick = { + if(link.isBlank()) showPopUpMessage("Enter A Link!") + else{ + //TODO if(!isOnline(ctx)) showPopUpMessage("Check Your Internet Connection") else + onSearch(link) + } + }, + border = BorderStroke(1.dp, Brush.horizontalGradient(listOf(colorPrimary, colorAccent))) + ){ + Text(text = "Search",style = SpotiFlyerTypography.h6,modifier = Modifier.padding(4.dp)) + } + } +} + +@Composable +fun AboutColumn(modifier: Modifier = Modifier) { + ScrollableColumn(modifier.fillMaxSize(),contentPadding = PaddingValues(16.dp)) { + Card( + modifier = modifier.fillMaxWidth(), + border = BorderStroke(1.dp,Color.Gray) + ) { + Column(modifier.padding(12.dp)) { + Text( + text = "Supported Platforms", + style = SpotiFlyerTypography.body1, + color = colorAccent + ) + Spacer(modifier = Modifier.padding(top = 12.dp)) + Row(horizontalArrangement = Arrangement.Center,modifier = modifier.fillMaxWidth()) { + Icon( + imageVector = SpotifyLogo(), tint = Color.Unspecified, + modifier = Modifier.clickable( + onClick = { openPlatform("com.spotify.music","http://open.spotify.com") }) + ) + Spacer(modifier = modifier.padding(start = 16.dp)) + Icon(imageVector = GaanaLogo(),tint = Color.Unspecified, + modifier = Modifier.clickable( + onClick = { openPlatform("com.gaana","http://gaana.com") }) + ) + Spacer(modifier = modifier.padding(start = 16.dp)) + Icon(imageVector = YoutubeLogo(),tint = Color.Unspecified, + modifier = Modifier.clickable( + onClick = { openPlatform("com.google.android.youtube","http://m.youtube.com") }) + ) + Spacer(modifier = modifier.padding(start = 12.dp)) + Icon(imageVector = YoutubeMusicLogo(),tint = Color.Unspecified, + modifier = Modifier.clickable( + onClick = { openPlatform("com.google.android.apps.youtube.music","https://music.youtube.com/") }) + ) + } + } + } + Spacer(modifier = Modifier.padding(top = 8.dp)) + Card( + modifier = modifier.fillMaxWidth(), + border = BorderStroke(1.dp,Color.Gray) + ) { + Column(modifier.padding(12.dp)) { + Text( + text = "Support Development", + style = SpotiFlyerTypography.body1, + color = colorAccent + ) + Spacer(modifier = Modifier.padding(top = 6.dp)) + Row(verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth().clickable( + onClick = { openPlatform("","http://github.com/Shabinder/SpotiFlyer") }) + .padding(vertical = 6.dp) + ) { + Icon(imageVector = GithubLogo(),tint = Color.LightGray) + Spacer(modifier = Modifier.padding(start = 16.dp)) + Column { + Text( + text = "GitHub", + style = SpotiFlyerTypography.h6 + ) + Text( + text = "Star / Fork the project on Github.", + style = SpotiFlyerTypography.subtitle2 + ) + } + } + Row( + modifier = modifier.fillMaxWidth().padding(vertical = 6.dp) + .clickable(onClick = { openPlatform("","http://github.com/Shabinder/SpotiFlyer") }), + verticalAlignment = Alignment.CenterVertically + ) { + Icon(Icons.Rounded.Info.copy(defaultHeight = 32.dp,defaultWidth = 32.dp)) + Spacer(modifier = Modifier.padding(start = 16.dp)) + Column { + Text( + text = "Translate", + style = SpotiFlyerTypography.h6 + ) + Text( + text = "Help us translate this app in your local language.", + style = SpotiFlyerTypography.subtitle2 + ) + } + } + /*Row( + modifier = modifier.fillMaxWidth().padding(vertical = 6.dp) + .clickable(onClick = { startPayment(mainActivity) }), + verticalAlignment = Alignment.CenterVertically + ) { + Icon(Icons.Rounded.MailOutline.copy(defaultHeight = 32.dp,defaultWidth = 32.dp)) + Spacer(modifier = Modifier.padding(start = 16.dp)) + Column { + Text( + text = stringResource(R.string.donate), + style = SpotiFlyerTypography.h6 + ) + Text( + text = stringResource(R.string.donate_subtitle), + style = SpotiFlyerTypography.subtitle2 + ) + } + }*/ + /*Row( + modifier = modifier.fillMaxWidth().padding(vertical = 6.dp) + .clickable(onClick = { + val sendIntent: Intent = Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_TEXT, "Hey, checkout this excellent Music Downloader http://github.com/Shabinder/SpotiFlyer") + type = "text/plain" + } + + val shareIntent = Intent.createChooser(sendIntent, null) + ctx.startActivity(shareIntent) + }), + verticalAlignment = Alignment.CenterVertically + ) { + Icon(Icons.Rounded.Share.copy(defaultHeight = 32.dp,defaultWidth = 32.dp)) + Spacer(modifier = Modifier.padding(start = 16.dp)) + Column { + Text( + text = stringResource(R.string.share), + style = SpotiFlyerTypography.h6 + ) + Text( + text = stringResource(R.string.share_subtitle), + style = SpotiFlyerTypography.subtitle2 + ) + } + }*/ + } + } + } +} + +@Composable +fun HistoryColumn( + list: List, + loadImage:(String)->Picture?, + onItemClicked: (String) -> Unit +) { + LazyColumn( + verticalArrangement = Arrangement.spacedBy(8.dp), + content = { + items(list) { + DownloadRecordItem( + item = it, + loadImage, + onItemClicked + ) + } + }, + modifier = Modifier.padding(top = 8.dp).fillMaxSize() + ) +} + +@Composable +fun DownloadRecordItem( + item: DownloadRecord, + loadImage:(String)->Picture?, + onItemClicked:(String)->Unit +) { + Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(end = 8.dp)) { + val pic = loadImage(item.coverUrl) + ImageLoad( + pic, + 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 = Icons.Rounded.Share, + modifier = Modifier.clickable(onClick = { + //if(!isOnline(ctx)) showDialog("Check Your Internet Connection") else + onItemClicked(item.link) + }) + ) + } +} + + +@Composable +fun HomeCategoryTabIndicator( + modifier: Modifier = Modifier, + color: Color = MaterialTheme.colors.onSurface +) { + Spacer( + modifier.padding(horizontal = 24.dp) + .preferredHeight(4.dp) + .background(color, RoundedCornerShape(topLeftPercent = 100, topRightPercent = 100)) + ) } \ No newline at end of file diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/integration/SpotiFlyerMainImpl.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/integration/SpotiFlyerMainImpl.kt index 4d73340f..936c17a4 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/integration/SpotiFlyerMainImpl.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/integration/SpotiFlyerMainImpl.kt @@ -2,6 +2,7 @@ package com.shabinder.common.main.integration import com.arkivanov.decompose.ComponentContext import com.arkivanov.mvikotlin.extensions.coroutines.states +import com.shabinder.common.Picture import com.shabinder.common.main.SpotiFlyerMain import com.shabinder.common.main.SpotiFlyerMain.* import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent @@ -31,4 +32,10 @@ internal class SpotiFlyerMainImpl( override fun onInputLinkChanged(link: String) { store.accept(Intent.SetLink(link)) } + + override fun selectCategory(category: HomeCategory) { + store.accept(Intent.SelectCategory(category)) + } + + override fun loadImage(url: String): Picture? = dir.loadImage(url) } \ No newline at end of file diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/store/SpotiFlyerMainStore.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/store/SpotiFlyerMainStore.kt index 234083fb..71c257fb 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/store/SpotiFlyerMainStore.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/store/SpotiFlyerMainStore.kt @@ -9,6 +9,7 @@ internal interface SpotiFlyerMainStore: Store) : Result() + data class CategoryChanged(val category: SpotiFlyerMain.HomeCategory) : Result() data class LinkChanged(val link: String) : Result() } @@ -65,6 +67,7 @@ internal class SpotiFlyerMainStoreProvider( is Intent.GiveDonation -> giveDonation() is Intent.ShareApp -> shareApp() is Intent.SetLink -> dispatch(Result.LinkChanged(link = intent.link)) + is Intent.SelectCategory -> dispatch(Result.CategoryChanged(intent.category)) } } } @@ -74,6 +77,7 @@ internal class SpotiFlyerMainStoreProvider( when (result) { is Result.ItemsLoaded -> copy(records = result.items) is Result.LinkChanged -> copy(link = result.link) - } + is Result.CategoryChanged -> copy(selectedCategory = result.category) + } } } \ No newline at end of file diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/root/SpotiFlyerRootUi.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/root/SpotiFlyerRootUi.kt index c1b9c103..a58a65c0 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/root/SpotiFlyerRootUi.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/root/SpotiFlyerRootUi.kt @@ -1,20 +1,80 @@ package com.shabinder.common.root +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp import com.arkivanov.decompose.extensions.compose.jetbrains.Children +import com.shabinder.common.ui.utils.verticalGradientScrim import com.shabinder.common.list.SpotiFlyerListContent import com.shabinder.common.main.SpotiFlyerMainContent import com.shabinder.common.root.SpotiFlyerRoot.Child +import com.shabinder.common.ui.SpotiFlyerLogo +import com.shabinder.common.ui.appNameStyle @Composable fun SpotiFlyerRootContent(component: SpotiFlyerRoot) { - Children( - routerState = component.routerState, - //TODO animation = crossfade() - ) { child, _ -> - when (child) { - is Child.Main -> SpotiFlyerMainContent(component = child.component) - is Child.List -> SpotiFlyerListContent(component = child.component) + val appBarColor = MaterialTheme.colors.surface.copy(alpha = 0.65f) + Column( + modifier = Modifier.fillMaxSize().verticalGradientScrim( + //color = sharedViewModel.gradientColor.copy(alpha = 0.38f), + color = appBarColor.copy(alpha = 0.38f), + startYPercentage = 0.29f, + endYPercentage = 0f, + ) + ) { + AppBar( + backgroundColor = appBarColor, + modifier = Modifier.fillMaxWidth() + ) + Children( + routerState = component.routerState, + //TODO animation = crossfade() + ) { child, _ -> + when (child) { + is Child.Main -> SpotiFlyerMainContent(component = child.component) + is Child.List -> SpotiFlyerListContent(component = child.component) + } } } } + +@Composable +fun AppBar( + backgroundColor: Color, + modifier: Modifier = Modifier +) { + TopAppBar( + backgroundColor = backgroundColor, + title = { + Row(verticalAlignment = Alignment.CenterVertically) { + Image( + imageVector = SpotiFlyerLogo(), + Modifier.preferredSize(32.dp) + ) + Spacer(Modifier.padding(horizontal = 4.dp)) + Text( + text = "SpotiFlyer", + style = appNameStyle + ) + } + }, + /*actions = { + Providers(AmbientContentAlpha provides ContentAlpha.medium) { + IconButton( + onClick = { *//* TODO: Open Preferences*//* } + ) { + Icon(Icons.Filled.Settings, tint = Color.Gray) + } + } + },*/ + modifier = modifier, + elevation = 0.dp + ) +} diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/root/integration/SpotiFlyerRootImpl.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/root/integration/SpotiFlyerRootImpl.kt index 4855f46a..1bf45dba 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/root/integration/SpotiFlyerRootImpl.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/root/integration/SpotiFlyerRootImpl.kt @@ -41,6 +41,7 @@ internal class SpotiFlyerRootImpl( componentContext = componentContext, dependencies = object : SpotiFlyerMain.Dependencies, Dependencies by this { override fun mainOutput(searched: SpotiFlyerMain.Output): Consumer = Consumer(::onMainOutput) + override val dir: Dir = directories } ) diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Color.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Color.kt index c4c0149f..ef21d6ee 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Color.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Color.kt @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.ui +package com.shabinder.common.ui import androidx.compose.material.Colors import androidx.compose.material.darkColors diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Expect.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Expect.kt index 11db9122..8f0a75e9 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Expect.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Expect.kt @@ -4,6 +4,10 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.font.font +import androidx.compose.ui.text.font.fontFamily import androidx.compose.ui.unit.Dp import com.shabinder.common.Picture diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Images.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Images.kt index 18669e4d..30d78cc8 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Images.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Images.kt @@ -10,6 +10,27 @@ expect fun DownloadImageTick(modifier: Modifier = Modifier) @Composable expect fun DownloadAllImage():ImageVector +@Composable +expect fun SpotiFlyerLogo():ImageVector + +@Composable +expect fun SpotifyLogo():ImageVector + +@Composable +expect fun YoutubeLogo():ImageVector + +@Composable +expect fun GaanaLogo():ImageVector + +@Composable +expect fun YoutubeMusicLogo():ImageVector + +@Composable +expect fun GithubLogo():ImageVector + +@Composable +expect fun HeartIcon():ImageVector + @Composable expect fun DownloadImageError(modifier: Modifier = Modifier) diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Shape.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Shape.kt index 04bbfcf0..ca4454f9 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Shape.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Shape.kt @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.ui +package com.shabinder.common.ui import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Shapes diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/SpotiFlyerMain.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/SpotiFlyerMain.kt deleted file mode 100644 index 01e17d00..00000000 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/SpotiFlyerMain.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.shabinder.common.ui - -import androidx.compose.foundation.layout.Column -import androidx.compose.material.Text -import androidx.compose.runtime.Composable - -@Composable -fun SpotiFlyerMain(){ - Column { - Text("Hello World") - } -} \ No newline at end of file diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Theme.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Theme.kt index 6466bc67..51236207 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Theme.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Theme.kt @@ -14,13 +14,13 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.ui +package com.shabinder.common.ui import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable @Composable -fun ComposeLearnTheme(content: @Composable() () -> Unit) { +fun SpotiFlyerTheme(content: @Composable() () -> Unit) { MaterialTheme( colors = SpotiFlyerColors, typography = SpotiFlyerTypography, diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Type.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Type.kt index cd6042cd..52045321 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Type.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/Type.kt @@ -14,63 +14,57 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.ui +package com.shabinder.common.ui import androidx.compose.material.Typography import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.font.font -import androidx.compose.ui.text.font.fontFamily import androidx.compose.ui.unit.sp -import com.shabinder.spotiflyer.R -private val Montserrat = fontFamily( +/*private val Montserrat = fontFamily( font(R.font.montserrat_light, FontWeight.Light), font(R.font.montserrat_regular, FontWeight.Normal), font(R.font.montserrat_medium, FontWeight.Medium), font(R.font.montserrat_semibold, FontWeight.SemiBold), -) - -val pristineFont = fontFamily( - font(R.font.pristine_script, FontWeight.Bold) -) +)*/ val SpotiFlyerTypography = Typography( h1 = TextStyle( - fontFamily = Montserrat, + //fontFamily = Montserrat, fontSize = 96.sp, fontWeight = FontWeight.Light, lineHeight = 117.sp, letterSpacing = (-1.5).sp ), h2 = TextStyle( - fontFamily = Montserrat, + //fontFamily = Montserrat, fontSize = 60.sp, fontWeight = FontWeight.Light, lineHeight = 73.sp, letterSpacing = (-0.5).sp ), h3 = TextStyle( - fontFamily = Montserrat, + //fontFamily = Montserrat, fontSize = 48.sp, fontWeight = FontWeight.Normal, lineHeight = 59.sp ), h4 = TextStyle( - fontFamily = Montserrat, + //fontFamily = Montserrat, fontSize = 30.sp, fontWeight = FontWeight.SemiBold, lineHeight = 37.sp ), h5 = TextStyle( - fontFamily = Montserrat, + //fontFamily = Montserrat, fontSize = 24.sp, fontWeight = FontWeight.SemiBold, lineHeight = 29.sp ), h6 = TextStyle( - fontFamily = Montserrat, + //fontFamily = Montserrat, fontSize = 18.sp, fontWeight = FontWeight.Medium, lineHeight = 26.sp, @@ -78,49 +72,49 @@ val SpotiFlyerTypography = Typography( ), subtitle1 = TextStyle( - fontFamily = Montserrat, + //fontFamily = Montserrat, fontSize = 16.sp, fontWeight = FontWeight.SemiBold, lineHeight = 20.sp, letterSpacing = 0.5.sp ), subtitle2 = TextStyle( - fontFamily = Montserrat, + //fontFamily = Montserrat, fontSize = 14.sp, fontWeight = FontWeight.Medium, lineHeight = 17.sp, letterSpacing = 0.1.sp ), body1 = TextStyle( - fontFamily = Montserrat, + //fontFamily = Montserrat, fontSize = 16.sp, fontWeight = FontWeight.Medium, lineHeight = 20.sp, letterSpacing = 0.15.sp, ), body2 = TextStyle( - fontFamily = Montserrat, + //fontFamily = Montserrat, fontSize = 14.sp, fontWeight = FontWeight.SemiBold, lineHeight = 20.sp, letterSpacing = 0.25.sp ), button = TextStyle( - fontFamily = Montserrat, + //fontFamily = Montserrat, fontSize = 14.sp, fontWeight = FontWeight.SemiBold, lineHeight = 16.sp, letterSpacing = 1.25.sp ), caption = TextStyle( - fontFamily = Montserrat, + //fontFamily = Montserrat, fontSize = 12.sp, fontWeight = FontWeight.SemiBold, lineHeight = 16.sp, letterSpacing = 0.sp ), overline = TextStyle( - fontFamily = Montserrat, + //fontFamily = Montserrat, fontSize = 12.sp, fontWeight = FontWeight.SemiBold, lineHeight = 16.sp, @@ -129,7 +123,7 @@ val SpotiFlyerTypography = Typography( ) val appNameStyle = TextStyle( - fontFamily = pristineFont, + fontFamily = FontFamily.Cursive, fontSize = 40.sp, fontWeight = FontWeight.SemiBold, lineHeight = 42.sp, diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/home/Home.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/home/Home.kt deleted file mode 100644 index 6a2f7697..00000000 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/home/Home.kt +++ /dev/null @@ -1,436 +0,0 @@ -/* - * Copyright (c) 2021 Shabinder Singh - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.shabinder.common.ui.home - -import android.content.Intent -import androidx.compose.foundation.* -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.* -import androidx.compose.material.AmbientTextStyle -import androidx.compose.material.Icon -import androidx.compose.material.TabDefaults.tabIndicatorOffset -import androidx.compose.material.Text -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.History -import androidx.compose.material.icons.outlined.Info -import androidx.compose.material.icons.rounded.CardGiftcard -import androidx.compose.material.icons.rounded.Flag -import androidx.compose.material.icons.rounded.InsertLink -import androidx.compose.material.icons.rounded.Share -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.AmbientContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.text.TextStyle -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.sp -import androidx.compose.ui.viewinterop.viewModel -import androidx.core.net.toUri -import androidx.navigation.NavController -import com.razorpay.Checkout -import com.shabinder.spotiflyer.MainActivity -import com.shabinder.spotiflyer.R -import com.shabinder.spotiflyer.database.DownloadRecord -import com.shabinder.spotiflyer.navigation.navigateToTrackList -import com.shabinder.spotiflyer.ui.SpotiFlyerTypography -import com.shabinder.spotiflyer.ui.colorAccent -import com.shabinder.spotiflyer.ui.colorPrimary -import com.shabinder.spotiflyer.ui.home.HomeCategory -import com.shabinder.spotiflyer.ui.home.HomeViewModel -import com.shabinder.spotiflyer.utils.isOnline -import com.shabinder.spotiflyer.utils.openPlatform -import com.shabinder.spotiflyer.utils.sharedViewModel -import com.shabinder.spotiflyer.utils.showDialog -import dev.chrisbanes.accompanist.coil.CoilImage -import org.json.JSONObject - -@Composable -fun Home( - navController: NavController, - mainActivity: MainActivity, - modifier: Modifier = Modifier) { - val viewModel: HomeViewModel = viewModel() - - Column(modifier = modifier) { - - AuthenticationBanner(sharedViewModel.isAuthenticated,modifier) - - SearchPanel( - sharedViewModel.link, - sharedViewModel::updateLink, - navController, - modifier - ) - - HomeTabBar( - viewModel.selectedCategory, - HomeCategory.values(), - viewModel::selectCategory, - modifier - ) - - when(viewModel.selectedCategory){ - HomeCategory.About -> AboutColumn(mainActivity) - HomeCategory.History -> HistoryColumn(viewModel.downloadRecordList,navController) - } - } - //Update Download List - viewModel.getDownloadRecordList() - //reset Gradient - sharedViewModel.resetGradient() -} - - -@Composable -fun AboutColumn(mainActivity: MainActivity,modifier: Modifier = Modifier) { - val ctx = AmbientContext.current - ScrollableColumn(modifier.fillMaxSize(),contentPadding = PaddingValues(16.dp)) { - Card( - modifier = modifier.fillMaxWidth(), - border = BorderStroke(1.dp,Color.Gray) - ) { - Column(modifier.padding(12.dp)) { - Text( - text = stringResource(R.string.supported_platform), - style = SpotiFlyerTypography.body1, - color = colorAccent - ) - Spacer(modifier = Modifier.padding(top = 12.dp)) - Row(horizontalArrangement = Arrangement.Center,modifier = modifier.fillMaxWidth()) { - Icon( - imageVector = vectorResource(id = R.drawable.ic_spotify_logo), tint = Color.Unspecified, - modifier = Modifier.clickable( - onClick = { openPlatform("com.spotify.music","http://open.spotify.com",ctx) }) - ) - Spacer(modifier = modifier.padding(start = 16.dp)) - Icon(imageVector = vectorResource(id = R.drawable.ic_gaana ),tint = Color.Unspecified, - modifier = Modifier.clickable( - onClick = { openPlatform("com.gaana","http://gaana.com",ctx) }) - ) - Spacer(modifier = modifier.padding(start = 16.dp)) - Icon(imageVector = vectorResource(id = R.drawable.ic_youtube),tint = Color.Unspecified, - modifier = Modifier.clickable( - onClick = { openPlatform("com.google.android.youtube","http://m.youtube.com",ctx) }) - ) - Spacer(modifier = modifier.padding(start = 12.dp)) - Icon(imageVector = vectorResource(id = R.drawable.ic_youtube_music_logo),tint = Color.Unspecified, - modifier = Modifier.clickable( - onClick = { openPlatform("com.google.android.apps.youtube.music","https://music.youtube.com/",ctx) }) - ) - } - } - } - Spacer(modifier = Modifier.padding(top = 8.dp)) - Card( - modifier = modifier.fillMaxWidth(), - border = BorderStroke(1.dp,Color.Gray) - ) { - Column(modifier.padding(12.dp)) { - Text( - text = stringResource(R.string.support_development), - style = SpotiFlyerTypography.body1, - color = colorAccent - ) - Spacer(modifier = Modifier.padding(top = 6.dp)) - Row(verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth().clickable( - onClick = { openPlatform("http://github.com/Shabinder/SpotiFlyer",ctx) }) - .padding(vertical = 6.dp) - ) { - Icon(imageVector = vectorResource(id = R.drawable.ic_github ),tint = Color.LightGray) - Spacer(modifier = Modifier.padding(start = 16.dp)) - Column { - Text( - text = stringResource(R.string.github), - style = SpotiFlyerTypography.h6 - ) - Text( - text = stringResource(R.string.github_star), - style = SpotiFlyerTypography.subtitle2 - ) - } - } - Row( - modifier = modifier.fillMaxWidth().padding(vertical = 6.dp) - .clickable(onClick = { openPlatform("http://github.com/Shabinder/SpotiFlyer", ctx) }), - verticalAlignment = Alignment.CenterVertically - ) { - Icon(Icons.Rounded.Flag.copy(defaultHeight = 32.dp,defaultWidth = 32.dp)) - Spacer(modifier = Modifier.padding(start = 16.dp)) - Column { - Text( - text = stringResource(R.string.translate), - style = SpotiFlyerTypography.h6 - ) - Text( - text = stringResource(R.string.help_us_translate), - style = SpotiFlyerTypography.subtitle2 - ) - } - } - Row( - modifier = modifier.fillMaxWidth().padding(vertical = 6.dp) - .clickable(onClick = { startPayment(mainActivity) }), - verticalAlignment = Alignment.CenterVertically - ) { - Icon(Icons.Rounded.CardGiftcard.copy(defaultHeight = 32.dp,defaultWidth = 32.dp)) - Spacer(modifier = Modifier.padding(start = 16.dp)) - Column { - Text( - text = stringResource(R.string.donate), - style = SpotiFlyerTypography.h6 - ) - Text( - text = stringResource(R.string.donate_subtitle), - style = SpotiFlyerTypography.subtitle2 - ) - } - } - Row( - modifier = modifier.fillMaxWidth().padding(vertical = 6.dp) - .clickable(onClick = { - val sendIntent: Intent = Intent().apply { - action = Intent.ACTION_SEND - putExtra(Intent.EXTRA_TEXT, "Hey, checkout this excellent Music Downloader http://github.com/Shabinder/SpotiFlyer") - type = "text/plain" - } - - val shareIntent = Intent.createChooser(sendIntent, null) - ctx.startActivity(shareIntent) - }), - verticalAlignment = Alignment.CenterVertically - ) { - Icon(Icons.Rounded.Share.copy(defaultHeight = 32.dp,defaultWidth = 32.dp)) - Spacer(modifier = Modifier.padding(start = 16.dp)) - Column { - Text( - text = stringResource(R.string.share), - style = SpotiFlyerTypography.h6 - ) - Text( - text = stringResource(R.string.share_subtitle), - style = SpotiFlyerTypography.subtitle2 - ) - } - } - } - } - } -} - -@Composable -fun HistoryColumn( - list: List, - navController: NavController -) { - 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) { - val ctx = AmbientContext.current - Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(end = 8.dp)) { - val imgUri = item.coverUrl.toUri().buildUpon().scheme("https").build() - CoilImage( - data = imgUri, - //Loading Placeholder Makes Scrolling very stuttery -// loading = { Image(vectorResource(id = R.drawable.ic_song_placeholder)) }, - error = {Image(vectorResource(id = R.drawable.ic_musicplaceholder))}, - contentScale = ContentScale.Inside, -// fadeIn = true, - modifier = Modifier.preferredHeight(75.dp).preferredWidth(90.dp) - ) - Column(modifier = Modifier.padding(horizontal = 8.dp).preferredHeight(60.dp).weight(1f),verticalArrangement = Arrangement.SpaceEvenly) { - Text(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 = { - if(!isOnline(ctx)) showDialog("Check Your Internet Connection") - else navController.navigateToTrackList(item.link) - }) - ) - } -} - -private fun startPayment(mainActivity: MainActivity) { - /* - * You need to pass current activity in order to let Razorpay create CheckoutActivity - * */ - val co = Checkout().apply { - setKeyID("rzp_live_3ZQeoFYOxjmXye") - setImage(R.drawable.ic_launcher_foreground) - } - - try { - val preFill = JSONObject() - - val options = JSONObject().apply { - put("name","SpotiFlyer") - put("description","Thanks For the Donation!") - //You can omit the image option to fetch the image from dashboard - //put("image","https://github.com/Shabinder/SpotiFlyer/raw/master/app/SpotifyDownload.png") - put("currency","INR") - put("amount","4900") - put("prefill",preFill) - } - - co.open(mainActivity,options) - }catch (e: Exception){ - showDialog("Error in payment: "+ e.message) - e.printStackTrace() - } -} - - -@Composable -fun AuthenticationBanner(isAuthenticated: Boolean, modifier: Modifier) { - - if (!isAuthenticated) { - // TODO show a progress indicator or similar - } -} - -@Composable -fun HomeTabBar( - selectedCategory: HomeCategory, - categories: Array, - selectCategory: (HomeCategory) -> Unit, - modifier: Modifier = Modifier -) { - val selectedIndex =categories.indexOfFirst { it == selectedCategory } - val indicator = @Composable { tabPositions: List -> - HomeCategoryTabIndicator( - Modifier.tabIndicatorOffset(tabPositions[selectedIndex]) - ) - } - - TabRow( - selectedTabIndex = selectedIndex, - indicator = indicator, - modifier = modifier, - ) { - categories.forEachIndexed { index, category -> - Tab( - selected = index == selectedIndex, - onClick = { selectCategory(category) }, - text = { - Text( - text = when (category) { - HomeCategory.About -> stringResource(R.string.home_about) - HomeCategory.History -> stringResource(R.string.home_history) - }, - style = MaterialTheme.typography.body2 - ) - }, - icon = { - when (category) { - HomeCategory.About -> Icon(Icons.Outlined.Info) - HomeCategory.History -> Icon(Icons.Outlined.History) - } - } - ) - } - } -} - -@Composable -fun SearchPanel( - link:String, - updateLink:(s:String) -> Unit, - navController: NavController, - modifier: Modifier = Modifier -){ - val ctx = AmbientContext.current - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = modifier.padding(top = 16.dp) - ){ - TextField( - leadingIcon = { - Icon(Icons.Rounded.InsertLink,tint = Color.LightGray) - }, - label = {Text(text = "Paste Link Here...",color = Color.LightGray)}, - value = link, - onValueChange = { updateLink(it) }, - singleLine = true, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Uri), - modifier = Modifier.padding(12.dp).fillMaxWidth() - .border( - BorderStroke(2.dp, Brush.horizontalGradient(listOf(colorPrimary, colorAccent))), - RoundedCornerShape(30.dp) - ), - backgroundColor = Color.Black, - textStyle = AmbientTextStyle.current.merge(TextStyle(fontSize = 18.sp,color = Color.White)), - shape = RoundedCornerShape(size = 30.dp), - activeColor = Color.Transparent, - inactiveColor = Color.Transparent - ) - OutlinedButton( - modifier = Modifier.padding(12.dp).wrapContentWidth(), - onClick = { - if(link.isBlank()) showDialog("Enter A Link!") - else{ - if(!isOnline(ctx)) showDialog("Check Your Internet Connection") - else navController.navigateToTrackList(link) - } - }, - border = BorderStroke(1.dp, Brush.horizontalGradient(listOf(colorPrimary, colorAccent))) - ){ - Text(text = "Search",style = SpotiFlyerTypography.h6,modifier = Modifier.padding(4.dp)) - } - } -} - - -@Composable -fun HomeCategoryTabIndicator( - modifier: Modifier = Modifier, - color: Color = MaterialTheme.colors.onSurface -) { - Spacer( - modifier.padding(horizontal = 24.dp) - .preferredHeight(4.dp) - .background(color, RoundedCornerShape(topLeftPercent = 100, topRightPercent = 100)) - ) -} \ No newline at end of file diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/home/HomeViewModel.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/home/HomeViewModel.kt deleted file mode 100644 index ef2d20ab..00000000 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/home/HomeViewModel.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2021 Shabinder Singh - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.shabinder.spotiflyer.ui.home - -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.shabinder.spotiflyer.database.DownloadRecord -import com.shabinder.spotiflyer.utils.sharedViewModel -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -class HomeViewModel : ViewModel() { - - var selectedCategory by mutableStateOf(HomeCategory.About) - private set - - fun selectCategory(s:HomeCategory) { - selectedCategory = s - } - - var downloadRecordList by mutableStateOf>(listOf()) - - fun getDownloadRecordList() { - viewModelScope.launch { - withContext(Dispatchers.IO){ - delay(100) //TEMP - downloadRecordList = sharedViewModel.databaseDAO.getRecord() - } - } - } -} - -enum class HomeCategory { - About, History -} \ No newline at end of file diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/home/MainScreen.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/home/MainScreen.kt deleted file mode 100644 index 8ba35d86..00000000 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/home/MainScreen.kt +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2021 Shabinder Singh - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.shabinder.spotiflyer.ui.home - -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.material.TopAppBar -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import androidx.navigation.NavHostController -import com.example.jetcaster.util.verticalGradientScrim -import com.shabinder.spotiflyer.MainActivity -import com.shabinder.spotiflyer.R -import com.shabinder.spotiflyer.SharedViewModel -import com.shabinder.spotiflyer.navigation.ComposeNavigation -import com.shabinder.spotiflyer.ui.appNameStyle -import dev.chrisbanes.accompanist.insets.statusBarsHeight - -@Composable -fun MainScreen( - modifier: Modifier, - mainActivity: MainActivity, - sharedViewModel: SharedViewModel, - navController: NavHostController, - topPadding: Dp = 0.dp -){ - val appBarColor = MaterialTheme.colors.surface.copy(alpha = 0.65f) - - Column( - modifier = modifier.fillMaxSize().verticalGradientScrim( - color = sharedViewModel.gradientColor.copy(alpha = 0.38f), - startYPercentage = 0.29f, - endYPercentage = 0f, - ) - ) { - // Draw a scrim over the status bar which matches the app bar - Spacer( - Modifier.background(appBarColor).fillMaxWidth() - .statusBarsHeight() - ) - AppBar( - backgroundColor = appBarColor, - modifier = Modifier.fillMaxWidth() - ) - //Space for Animation - Spacer(Modifier.padding(top = topPadding)) - ComposeNavigation( - mainActivity, - navController, - sharedViewModel.spotifyProvider, - sharedViewModel.gaanaProvider, - sharedViewModel.youtubeProvider - ) - } -} - -@Composable -fun AppBar( - backgroundColor: Color, - modifier: Modifier = Modifier -) { - TopAppBar( - backgroundColor = backgroundColor, - title = { - Row(verticalAlignment = Alignment.CenterVertically) { - Image( - imageVector = vectorResource(R.drawable.ic_spotiflyer_logo), - Modifier.preferredSize(32.dp) - ) - Spacer(Modifier.padding(horizontal = 4.dp)) - Text( - text = "SpotiFlyer", - style = appNameStyle - ) - } - }, - /*actions = { - Providers(AmbientContentAlpha provides ContentAlpha.medium) { - IconButton( - onClick = { *//* TODO: Open Preferences*//* } - ) { - Icon(Icons.Filled.Settings, tint = Color.Gray) - } - } - },*/ - modifier = modifier, - elevation = 0.dp - ) -} diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/splash/Splash.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/splash/Splash.kt index 99d06f0b..92fdbf9f 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/splash/Splash.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/splash/Splash.kt @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.ui.splash +package com.shabinder.common.ui.splash import androidx.compose.foundation.Image import androidx.compose.foundation.layout.* @@ -27,14 +27,9 @@ import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.shabinder.spotiflyer.R -import com.shabinder.spotiflyer.ui.SpotiFlyerTypography -import com.shabinder.spotiflyer.ui.colorAccent -import com.shabinder.spotiflyer.ui.colorPrimary +import com.shabinder.common.ui.* import kotlinx.coroutines.delay private const val SplashWaitTime: Long = 1100 @@ -49,7 +44,7 @@ fun Splash(modifier: Modifier = Modifier, onTimeout: () -> Unit) { delay(SplashWaitTime) currentOnTimeout() } - Image(imageVector = vectorResource(id = R.drawable.ic_spotiflyer_logo)) + Image(imageVector = SpotiFlyerLogo()) MadeInIndia(Modifier.align(Alignment.BottomCenter)) } } @@ -67,15 +62,15 @@ fun MadeInIndia( verticalAlignment = Alignment.CenterVertically, ) { Text( - text = stringResource(id = R.string.made_with_love), + text = "Made with ", color = colorPrimary, fontSize = 22.sp ) Spacer(modifier = Modifier.padding(start = 4.dp)) - Icon(vectorResource(id = R.drawable.ic_heart),tint = Color.Unspecified) + Icon(HeartIcon(),tint = Color.Unspecified) Spacer(modifier = Modifier.padding(start = 4.dp)) Text( - text = stringResource(id = R.string.in_india), + text = " in India", color = colorPrimary, fontSize = 22.sp ) diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/utils/Colors.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/utils/Colors.kt index aefb163c..89bd90fa 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/utils/Colors.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/utils/Colors.kt @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.ui.utils +package com.shabinder.common.ui.utils import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.compositeOver diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/utils/DynamicTheming.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/utils/DynamicTheming.kt deleted file mode 100644 index 995c9cd3..00000000 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/utils/DynamicTheming.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2021 Shabinder Singh - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.shabinder.spotiflyer.ui.utils - -import android.content.Context -import androidx.compose.runtime.Immutable -import androidx.compose.ui.graphics.Color -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 kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext - -@Immutable -data class DominantColors(val color: Color, val onColor: Color) - - -suspend fun calculateDominantColor(url: String,ctx:Context): DominantColors? { - // we calculate the swatches in the image, and return the first valid color - return calculateSwatchesInImage(ctx, 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 { - 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() -} diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/utils/GradientScrim.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/utils/GradientScrim.kt index 1ebc2f7f..f4fa8993 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/utils/GradientScrim.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/utils/GradientScrim.kt @@ -14,9 +14,8 @@ * along with this program. If not, see . */ -package com.example.jetcaster.util +package com.shabinder.common.ui.utils -import androidx.annotation.FloatRange import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -41,8 +40,10 @@ import kotlin.math.pow */ 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, + //@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 diff --git a/common/compose-ui/src/desktopMain/kotlin/com/shabinder/common/ui/Images.kt b/common/compose-ui/src/desktopMain/kotlin/com/shabinder/common/ui/Images.kt index 91dad9cd..58b5f150 100644 --- a/common/compose-ui/src/desktopMain/kotlin/com/shabinder/common/ui/Images.kt +++ b/common/compose-ui/src/desktopMain/kotlin/com/shabinder/common/ui/Images.kt @@ -32,3 +32,24 @@ actual fun DownloadImageArrow(modifier: Modifier){ @Composable actual fun DownloadAllImage():ImageVector = vectorXmlResource("common/compose-ui/src/main/res/drawable/ic_download_arrow.xml") + +@Composable +actual fun SpotiFlyerLogo():ImageVector = vectorXmlResource("common/compose-ui/src/main/res/drawable/ic_spotiflyer_logo.xml") + +@Composable +actual fun HeartIcon():ImageVector = vectorXmlResource("common/compose-ui/src/main/res/drawable/ic_heart.xml") + +@Composable +actual fun SpotifyLogo():ImageVector = vectorXmlResource("common/compose-ui/src/main/res/drawable/ic_spotify_logo.xml") + +@Composable +actual fun YoutubeLogo():ImageVector = vectorXmlResource("common/compose-ui/src/main/res/drawable/ic_youtube.xml") + +@Composable +actual fun GaanaLogo():ImageVector = vectorXmlResource("common/compose-ui/src/main/res/drawable/ic_gaana.xml") + +@Composable +actual fun YoutubeMusicLogo():ImageVector = vectorXmlResource("common/compose-ui/src/main/res/drawable/ic_youtube_music_logo.xml") + +@Composable +actual fun GithubLogo():ImageVector = vectorXmlResource("common/compose-ui/src/main/res/drawable/ic_github.xml") diff --git a/common/compose-ui/src/main/res/drawable/ic_github.xml b/common/compose-ui/src/main/res/drawable/ic_github.xml new file mode 100644 index 00000000..0e14b28a --- /dev/null +++ b/common/compose-ui/src/main/res/drawable/ic_github.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/common/compose-ui/src/main/res/font/montserrat_light.ttf b/common/compose-ui/src/main/res/font/montserrat_light.ttf new file mode 100644 index 00000000..990857de Binary files /dev/null and b/common/compose-ui/src/main/res/font/montserrat_light.ttf differ diff --git a/common/compose-ui/src/main/res/font/montserrat_medium.ttf b/common/compose-ui/src/main/res/font/montserrat_medium.ttf new file mode 100644 index 00000000..6e079f69 Binary files /dev/null and b/common/compose-ui/src/main/res/font/montserrat_medium.ttf differ diff --git a/common/compose-ui/src/main/res/font/montserrat_regular.ttf b/common/compose-ui/src/main/res/font/montserrat_regular.ttf new file mode 100644 index 00000000..8d443d5d Binary files /dev/null and b/common/compose-ui/src/main/res/font/montserrat_regular.ttf differ diff --git a/common/compose-ui/src/main/res/font/montserrat_semibold.ttf b/common/compose-ui/src/main/res/font/montserrat_semibold.ttf new file mode 100644 index 00000000..f8a43f2b Binary files /dev/null and b/common/compose-ui/src/main/res/font/montserrat_semibold.ttf differ diff --git a/common/compose-ui/src/main/res/font/pristine_script.ttf b/common/compose-ui/src/main/res/font/pristine_script.ttf new file mode 100644 index 00000000..e8d3e494 Binary files /dev/null and b/common/compose-ui/src/main/res/font/pristine_script.ttf differ diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/Dir.kt b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/Dir.kt index d0f6a882..65bd506b 100644 --- a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/Dir.kt +++ b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/Dir.kt @@ -7,13 +7,16 @@ import android.os.Environment import co.touchlab.kermit.Kermit import com.mpatric.mp3agic.Mp3File import com.shabinder.common.database.appContext +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch import java.io.* import java.lang.Exception import java.net.HttpURLConnection import java.net.URL import java.nio.charset.StandardCharsets -actual open class Dir actual constructor( +actual class Dir actual constructor( private val logger: Kermit ) { @@ -53,7 +56,7 @@ actual open class Dir actual constructor( File(imageCacheDir()).deleteRecursively() } - actual fun cacheImage(picture: Picture) { + actual suspend fun cacheImage(picture: Picture) { try { val path = imageCacheDir() + picture.name FileOutputStream(path).use { out -> @@ -92,9 +95,10 @@ actual open class Dir actual constructor( .setId3v2TagsAndSaveFile(trackDetails,path) } - actual fun loadImage(url: String, cachePath: String):Picture? { + actual fun loadImage(url: String):Picture? { + val cachePath = imageCacheDir() + getNameURL(url) var picture: Picture? = loadCachedImage(cachePath) - if (picture == null) picture = freshImage(url,cachePath) + if (picture == null) picture = freshImage(url) return picture } @@ -129,7 +133,7 @@ actual open class Dir actual constructor( null } } - private fun freshImage(url:String,cachePath: String):Picture?{ + private fun freshImage(url:String):Picture?{ return try { val source = URL(url) val connection: HttpURLConnection = source.openConnection() as HttpURLConnection @@ -147,8 +151,9 @@ actual open class Dir actual constructor( result.width, result.height ) - - cacheImage(picture) + GlobalScope.launch(Dispatchers.IO) { + cacheImage(picture) + } picture } else null } catch (e: Exception) { diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/DI.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/DI.kt index b88993be..9676ea8f 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/DI.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/DI.kt @@ -32,6 +32,7 @@ fun commonModule(enableNetworkLogs: Boolean) = module { single { SpotifyProvider(get(),get(),get(),get()) } single { GaanaProvider(get(),get(),get(),get()) } single { YoutubeProvider(get(),get(),get(),get()) } + single { FetchPlatformQueryResult(get(),get(),get(),get()) } single { createHttpClient(enableNetworkLogs = enableNetworkLogs) } } diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/Dir.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/Dir.kt index 9c113a93..c09f07f4 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/Dir.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/Dir.kt @@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlin.math.roundToInt -expect open class Dir( +expect class Dir( logger: Kermit, ) { fun isPresent(path:String):Boolean @@ -18,7 +18,7 @@ expect open class Dir( fun imageCacheDir(): String fun createDirectory(dirPath:String) suspend fun cacheImage(picture: Picture) - fun loadImage(url:String, cachePath:String = imageCacheDir() + getNameURL(url)):Picture? + fun loadImage(url:String):Picture? suspend fun clearCache() suspend fun saveFileWithMetadata(mp3ByteArray: ByteArray, path: String, trackDetails: TrackDetails) } diff --git a/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/Dir.kt b/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/Dir.kt index 8d4e8775..5bc65f61 100644 --- a/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/Dir.kt +++ b/common/dependency-injection/src/desktopMain/kotlin/com/shabinder/common/Dir.kt @@ -13,7 +13,7 @@ import java.net.URL import java.nio.charset.StandardCharsets import javax.imageio.ImageIO -actual open class Dir actual constructor(private val logger: Kermit) { +actual class Dir actual constructor(private val logger: Kermit) { actual fun fileSeparator(): String = File.separator @@ -85,7 +85,8 @@ actual open class Dir actual constructor(private val logger: Kermit) { .setId3v2TagsAndSaveFile(trackDetails,path) } - actual fun loadImage(url: String, cachePath: String):Picture? { + actual fun loadImage(url: String):Picture? { + val cachePath = imageCacheDir() + getNameURL(url) var picture: Picture? = loadCachedImage(cachePath) if (picture == null) picture = freshImage(url,cachePath) return picture