mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-22 17:14:32 +01:00
Refactor and Icons Added
This commit is contained in:
parent
475e1834bf
commit
7d7e82b3b7
@ -91,6 +91,13 @@ dependencies {
|
|||||||
implementation(fetch)
|
implementation(fetch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
implementation(MVIKotlin.mvikotlin)
|
||||||
|
implementation(MVIKotlin.mvikotlinMain)
|
||||||
|
implementation(MVIKotlin.mvikotlinLogging)
|
||||||
|
implementation(MVIKotlin.mvikotlinTimeTravel)
|
||||||
|
implementation(Decompose.decompose)
|
||||||
|
implementation(Decompose.extensionsCompose)
|
||||||
|
|
||||||
//Test
|
//Test
|
||||||
testImplementation("junit:junit:4.13.1")
|
testImplementation("junit:junit:4.13.1")
|
||||||
androidTestImplementation(Androidx.junit)
|
androidTestImplementation(Androidx.junit)
|
||||||
|
@ -1,16 +1,59 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.shabinder.android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
package="com.shabinder.android">
|
||||||
|
|
||||||
|
<queries>
|
||||||
|
<package android:name="com.gaana" />
|
||||||
|
<package android:name="com.spotify.music" />
|
||||||
|
<package android:name="com.google.android.youtube" />
|
||||||
|
<package android:name="com.google.android.apps.youtube.music" />
|
||||||
|
</queries>
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
|
tools:ignore="ScopedStorage" />
|
||||||
|
<uses-permission android:name="android.permission.READ_STORAGE_PERMISSION" />
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="false"
|
android:name=".App"
|
||||||
android:supportsRtl="true"
|
android:allowBackup="false"
|
||||||
android:usesCleartextTraffic="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
|
android:usesCleartextTraffic="true"
|
||||||
<activity android:name=".MainActivity">
|
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">
|
||||||
|
<activity android:name=".MainActivity"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:theme="@style/Theme.SpotiFlyer"
|
||||||
|
>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
<data android:mimeType="text/plain" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="com.razorpay.ApiKey"
|
||||||
|
android:value="rzp_live_3ZQeoFYOxjmXye"
|
||||||
|
/>
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
@ -2,21 +2,43 @@ package com.shabinder.android
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.ui.platform.setContent
|
import androidx.compose.ui.platform.setContent
|
||||||
import com.shabinder.android.di.appModule
|
import com.arkivanov.decompose.ComponentContext
|
||||||
import com.shabinder.common.database.appContext
|
import com.arkivanov.decompose.extensions.compose.jetbrains.rootComponent
|
||||||
import com.shabinder.common.initKoin
|
import com.arkivanov.mvikotlin.logging.store.LoggingStoreFactory
|
||||||
import com.shabinder.common.ui.SpotiFlyerMain
|
import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
|
||||||
import org.koin.android.ext.koin.androidLogger
|
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() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
val database: Database by inject()
|
||||||
|
val fetcher: FetchPlatformQueryResult by inject()
|
||||||
|
val dir: Dir by inject()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContent {
|
setContent {
|
||||||
val scope = rememberCoroutineScope()
|
SpotiFlyerTheme {
|
||||||
SpotiFlyerMain()
|
SpotiFlyerRootContent(rootComponent(::spotiFlyerRoot))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<TrackDetails>()
|
|
||||||
|
|
||||||
fun updateTrackList(list:List<TrackDetails>){
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +1,7 @@
|
|||||||
package com.shabinder.android.di
|
package com.shabinder.android.di
|
||||||
|
|
||||||
import com.shabinder.android.SharedViewModel
|
|
||||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
|
||||||
val appModule = module {
|
val appModule = module {
|
||||||
viewModel { SharedViewModel(get(),get(),get(),get(),get()) }
|
|
||||||
}
|
}
|
||||||
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
75
android/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
75
android/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<!--
|
||||||
|
~ 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 <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<group android:scaleX="0.10546875"
|
||||||
|
android:scaleY="0.10546875"
|
||||||
|
android:translateX="27"
|
||||||
|
android:translateY="27">
|
||||||
|
<path
|
||||||
|
android:pathData="M256,256m-256,0a256,256 0,1 1,512 0a256,256 0,1 1,-512 0">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="437.0193"
|
||||||
|
android:startX="74.9807"
|
||||||
|
android:endY="74.9807"
|
||||||
|
android:endX="437.0193"
|
||||||
|
android:type="linear">
|
||||||
|
<item android:offset="0" android:color="#FF736BFD"/>
|
||||||
|
<item android:offset="1" android:color="#FFF54187"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M377,356.7c-68.9,-45.4 -155.6,-56.4 -257.6,-32.7c-20.5,4.8 -13.6,35.8 7.3,31.2C290.7,317 351.6,386 368.2,386C384,386 390.2,365.4 377,356.7z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M112.1,275.1C203.9,253.4 308.1,266 384,308c18.5,10.2 34,-17.8 15.5,-28c-82.7,-45.7 -195.6,-59.5 -294.7,-36C84.2,248.8 91.5,280 112.1,275.1L112.1,275.1z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M100,191.9c96.6,-29.6 232.2,-13.4 308.7,36.9c17.6,11.5 35.3,-15.1 17.6,-26.7c-84.9,-55.8 -229.2,-73.3 -335.6,-40.8C70.4,167.5 79.9,198.1 100,191.9L100,191.9z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M507.8,438.2c-1.6,97.2 -141.9,97.1 -143.5,0C365.9,341 506.2,341 507.8,438.2z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="386.3741"
|
||||||
|
android:startX="487.8323"
|
||||||
|
android:endY="490.009"
|
||||||
|
android:endX="384.1974"
|
||||||
|
android:type="linear">
|
||||||
|
<item android:offset="0" android:color="#FF736BFD"/>
|
||||||
|
<item android:offset="1" android:color="#FFF54187"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M486.8,456.8c-0.6,-2.4 -6.9,-1 -8.5,-1.4c11.5,-82 -82.4,-86.7 -87.1,-22.2c0.3,1.8 -1,6.7 2.2,6.6c0,0 8.6,0 8.6,0c3.1,0.1 2,-4.7 2.2,-6.6c0.1,-23.3 35,-23.3 35.2,0c0,0 0,6.9 0,6.9c-0.1,2.8 4.4,2.8 4.3,0c5,-35.2 -43.8,-40.1 -43.8,-4.7h-4.3c-1.6,-53.7 77.2,-55.9 78.4,-2.2c0,0 0,24.4 0,24.4c-0.1,2.9 3.8,2.1 5.6,2.2l-20.7,21l-20.7,-21c1.8,-0.1 5.6,0.7 5.6,-2.2c0,0 0,-8.8 0,-8.8c0,-2.8 -4.4,-2.8 -4.3,0c0,0 0,6.6 0,6.6c-2.2,0.2 -11.3,-1.3 -8,3.7c0,0 25.9,26.3 25.9,26.3c0.8,0.9 2.2,0.9 3.1,0C460.6,484.4 489.4,458.3 486.8,456.8z"
|
||||||
|
android:strokeWidth="0.75"
|
||||||
|
android:strokeColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M510,437.5c-1.7,96.2 -142.1,96.2 -143.8,0C367.9,341.3 508.4,341.3 510,437.5z"
|
||||||
|
android:strokeWidth="6"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000000"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
21
android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
21
android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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 <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
21
android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
21
android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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 <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
29
android/src/main/res/values/colors.xml
Normal file
29
android/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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 <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<color name="colorPrimary">#FC5C7D</color>
|
||||||
|
<color name="colorPrimaryDark">#CE1CFF</color>
|
||||||
|
<color name="colorAccent">#9AB3FF</color>
|
||||||
|
<color name="white">#FFFFFF</color>
|
||||||
|
<color name="grey">#99FFFFFF</color>
|
||||||
|
<color name="darkGrey">#E6333333</color>
|
||||||
|
<color name="black">#000000</color>
|
||||||
|
<color name="dark">#121212</color>
|
||||||
|
<color name="successGreen">#59C351</color>
|
||||||
|
<color name="errorRed">#FF9494</color>
|
||||||
|
</resources>
|
20
android/src/main/res/values/ic_launcher_background.xml
Normal file
20
android/src/main/res/values/ic_launcher_background.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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 <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<color name="ic_launcher_background">#000000</color>
|
||||||
|
</resources>
|
33
android/src/main/res/values/strings.xml
Normal file
33
android/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<!--
|
||||||
|
~ 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 <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">SpotiFlyer</string>
|
||||||
|
<string name="home_about">About</string>
|
||||||
|
<string name="home_history">History</string>
|
||||||
|
<string name="supported_platform">Supported Platforms</string>
|
||||||
|
<string name="support_development">Support Development</string>
|
||||||
|
<string name="github_star">Star / Fork the project on Github.</string>
|
||||||
|
<string name="github">GitHub</string>
|
||||||
|
<string name="translate">Translate</string>
|
||||||
|
<string name="help_us_translate">Help us translate this app in your local language.</string>
|
||||||
|
<string name="donate">Donate</string>
|
||||||
|
<string name="donate_subtitle">If you think I deserve to get paid for my work, you can leave me some money here.</string>
|
||||||
|
<string name="share">Share</string>
|
||||||
|
<string name="share_subtitle">Share this app with your friends and family.</string>
|
||||||
|
<string name="made_with_love">Made with</string>
|
||||||
|
<string name="in_india">in India</string>
|
||||||
|
</resources>
|
29
android/src/main/res/values/themes.xml
Normal file
29
android/src/main/res/values/themes.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<!--
|
||||||
|
~ 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 <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="Theme.SpotiFlyer" parent="Theme.AppCompat.NoActionBar">
|
||||||
|
<!-- Primary brand color. -->
|
||||||
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
|
<item name="android:textColor">@color/white</item>
|
||||||
|
<item name="android:background">@android:color/black</item>
|
||||||
|
<item name="android:backgroundTint">@android:color/black</item>
|
||||||
|
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||||
|
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
@ -34,3 +34,23 @@ actual fun DownloadImageArrow(modifier: Modifier){
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
actual fun DownloadAllImage() = vectorResource(R.drawable.ic_download_arrow)
|
actual fun DownloadAllImage() = vectorResource(R.drawable.ic_download_arrow)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
actual fun 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)
|
@ -20,8 +20,8 @@ import com.shabinder.common.DownloadStatus
|
|||||||
import com.shabinder.common.Picture
|
import com.shabinder.common.Picture
|
||||||
import com.shabinder.common.TrackDetails
|
import com.shabinder.common.TrackDetails
|
||||||
import com.shabinder.common.ui.*
|
import com.shabinder.common.ui.*
|
||||||
import com.shabinder.spotiflyer.ui.SpotiFlyerTypography
|
import com.shabinder.common.ui.SpotiFlyerTypography
|
||||||
import com.shabinder.spotiflyer.ui.colorAccent
|
import com.shabinder.common.ui.colorAccent
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -2,7 +2,9 @@ package com.shabinder.common.main
|
|||||||
|
|
||||||
import com.arkivanov.decompose.ComponentContext
|
import com.arkivanov.decompose.ComponentContext
|
||||||
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
import com.arkivanov.mvikotlin.core.store.StoreFactory
|
||||||
|
import com.shabinder.common.Dir
|
||||||
import com.shabinder.common.DownloadRecord
|
import com.shabinder.common.DownloadRecord
|
||||||
|
import com.shabinder.common.Picture
|
||||||
import com.shabinder.common.main.integration.SpotiFlyerMainImpl
|
import com.shabinder.common.main.integration.SpotiFlyerMainImpl
|
||||||
import com.shabinder.common.utils.Consumer
|
import com.shabinder.common.utils.Consumer
|
||||||
import com.shabinder.database.Database
|
import com.shabinder.database.Database
|
||||||
@ -23,10 +25,21 @@ interface SpotiFlyerMain {
|
|||||||
* */
|
* */
|
||||||
fun onInputLinkChanged(link: String)
|
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 {
|
interface Dependencies {
|
||||||
fun mainOutput(searched: Output): Consumer<Output>
|
fun mainOutput(searched: Output): Consumer<Output>
|
||||||
val storeFactory: StoreFactory
|
val storeFactory: StoreFactory
|
||||||
val database: Database
|
val database: Database
|
||||||
|
val dir: Dir
|
||||||
}
|
}
|
||||||
sealed class Output {
|
sealed class Output {
|
||||||
data class Search(val link: String) : Output()
|
data class Search(val link: String) : Output()
|
||||||
@ -34,8 +47,12 @@ interface SpotiFlyerMain {
|
|||||||
|
|
||||||
data class State(
|
data class State(
|
||||||
val records: List<DownloadRecord> = emptyList(),
|
val records: List<DownloadRecord> = emptyList(),
|
||||||
val link: String = ""
|
val link: String = "",
|
||||||
|
val selectedCategory: HomeCategory = HomeCategory.About
|
||||||
)
|
)
|
||||||
|
enum class HomeCategory {
|
||||||
|
About, History
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("FunctionName") // Factory function
|
@Suppress("FunctionName") // Factory function
|
||||||
|
@ -1,10 +1,359 @@
|
|||||||
package com.shabinder.common.main
|
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.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
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
|
@Composable
|
||||||
fun SpotiFlyerMainContent(component: SpotiFlyerMain){
|
fun SpotiFlyerMainContent(component: SpotiFlyerMain){
|
||||||
val model by component.models.collectAsState(SpotiFlyerMain.State())
|
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<HomeCategory>,
|
||||||
|
selectCategory: (HomeCategory) -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
val selectedIndex =categories.indexOfFirst { it == selectedCategory }
|
||||||
|
val indicator = @Composable { tabPositions: List<TabPosition> ->
|
||||||
|
HomeCategoryTabIndicator(
|
||||||
|
Modifier.tabIndicatorOffset(tabPositions[selectedIndex])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("USELESS_CAST")//Showing Error in Latest Android Studio Canary
|
||||||
|
TabRow(
|
||||||
|
selectedTabIndex = selectedIndex,
|
||||||
|
indicator = indicator as @Composable (List<TabPosition>) -> 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<DownloadRecord>,
|
||||||
|
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))
|
||||||
|
)
|
||||||
}
|
}
|
@ -2,6 +2,7 @@ package com.shabinder.common.main.integration
|
|||||||
|
|
||||||
import com.arkivanov.decompose.ComponentContext
|
import com.arkivanov.decompose.ComponentContext
|
||||||
import com.arkivanov.mvikotlin.extensions.coroutines.states
|
import com.arkivanov.mvikotlin.extensions.coroutines.states
|
||||||
|
import com.shabinder.common.Picture
|
||||||
import com.shabinder.common.main.SpotiFlyerMain
|
import com.shabinder.common.main.SpotiFlyerMain
|
||||||
import com.shabinder.common.main.SpotiFlyerMain.*
|
import com.shabinder.common.main.SpotiFlyerMain.*
|
||||||
import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
|
import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
|
||||||
@ -31,4 +32,10 @@ internal class SpotiFlyerMainImpl(
|
|||||||
override fun onInputLinkChanged(link: String) {
|
override fun onInputLinkChanged(link: String) {
|
||||||
store.accept(Intent.SetLink(link))
|
store.accept(Intent.SetLink(link))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun selectCategory(category: HomeCategory) {
|
||||||
|
store.accept(Intent.SelectCategory(category))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadImage(url: String): Picture? = dir.loadImage(url)
|
||||||
}
|
}
|
@ -9,6 +9,7 @@ internal interface SpotiFlyerMainStore: Store<Intent, SpotiFlyerMain.State, Noth
|
|||||||
sealed class Intent {
|
sealed class Intent {
|
||||||
data class OpenPlatform(val platformID:String,val platformLink:String):Intent()
|
data class OpenPlatform(val platformID:String,val platformLink:String):Intent()
|
||||||
data class SetLink(val link:String):Intent()
|
data class SetLink(val link:String):Intent()
|
||||||
|
data class SelectCategory(val category: SpotiFlyerMain.HomeCategory):Intent()
|
||||||
object GiveDonation : Intent()
|
object GiveDonation : Intent()
|
||||||
object ShareApp: Intent()
|
object ShareApp: Intent()
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import com.arkivanov.mvikotlin.core.store.StoreFactory
|
|||||||
import com.arkivanov.mvikotlin.extensions.coroutines.SuspendExecutor
|
import com.arkivanov.mvikotlin.extensions.coroutines.SuspendExecutor
|
||||||
import com.shabinder.common.DownloadRecord
|
import com.shabinder.common.DownloadRecord
|
||||||
import com.shabinder.common.giveDonation
|
import com.shabinder.common.giveDonation
|
||||||
|
import com.shabinder.common.main.SpotiFlyerMain
|
||||||
import com.shabinder.common.main.SpotiFlyerMain.State
|
import com.shabinder.common.main.SpotiFlyerMain.State
|
||||||
import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
|
import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
|
||||||
import com.shabinder.common.openPlatform
|
import com.shabinder.common.openPlatform
|
||||||
@ -49,6 +50,7 @@ internal class SpotiFlyerMainStoreProvider(
|
|||||||
|
|
||||||
private sealed class Result {
|
private sealed class Result {
|
||||||
data class ItemsLoaded(val items: List<DownloadRecord>) : Result()
|
data class ItemsLoaded(val items: List<DownloadRecord>) : Result()
|
||||||
|
data class CategoryChanged(val category: SpotiFlyerMain.HomeCategory) : Result()
|
||||||
data class LinkChanged(val link: String) : Result()
|
data class LinkChanged(val link: String) : Result()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +67,7 @@ internal class SpotiFlyerMainStoreProvider(
|
|||||||
is Intent.GiveDonation -> giveDonation()
|
is Intent.GiveDonation -> giveDonation()
|
||||||
is Intent.ShareApp -> shareApp()
|
is Intent.ShareApp -> shareApp()
|
||||||
is Intent.SetLink -> dispatch(Result.LinkChanged(link = intent.link))
|
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) {
|
when (result) {
|
||||||
is Result.ItemsLoaded -> copy(records = result.items)
|
is Result.ItemsLoaded -> copy(records = result.items)
|
||||||
is Result.LinkChanged -> copy(link = result.link)
|
is Result.LinkChanged -> copy(link = result.link)
|
||||||
}
|
is Result.CategoryChanged -> copy(selectedCategory = result.category)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,20 +1,80 @@
|
|||||||
package com.shabinder.common.root
|
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.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.arkivanov.decompose.extensions.compose.jetbrains.Children
|
||||||
|
import com.shabinder.common.ui.utils.verticalGradientScrim
|
||||||
import com.shabinder.common.list.SpotiFlyerListContent
|
import com.shabinder.common.list.SpotiFlyerListContent
|
||||||
import com.shabinder.common.main.SpotiFlyerMainContent
|
import com.shabinder.common.main.SpotiFlyerMainContent
|
||||||
import com.shabinder.common.root.SpotiFlyerRoot.Child
|
import com.shabinder.common.root.SpotiFlyerRoot.Child
|
||||||
|
import com.shabinder.common.ui.SpotiFlyerLogo
|
||||||
|
import com.shabinder.common.ui.appNameStyle
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SpotiFlyerRootContent(component: SpotiFlyerRoot) {
|
fun SpotiFlyerRootContent(component: SpotiFlyerRoot) {
|
||||||
Children(
|
val appBarColor = MaterialTheme.colors.surface.copy(alpha = 0.65f)
|
||||||
routerState = component.routerState,
|
Column(
|
||||||
//TODO animation = crossfade()
|
modifier = Modifier.fillMaxSize().verticalGradientScrim(
|
||||||
) { child, _ ->
|
//color = sharedViewModel.gradientColor.copy(alpha = 0.38f),
|
||||||
when (child) {
|
color = appBarColor.copy(alpha = 0.38f),
|
||||||
is Child.Main -> SpotiFlyerMainContent(component = child.component)
|
startYPercentage = 0.29f,
|
||||||
is Child.List -> SpotiFlyerListContent(component = child.component)
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -41,6 +41,7 @@ internal class SpotiFlyerRootImpl(
|
|||||||
componentContext = componentContext,
|
componentContext = componentContext,
|
||||||
dependencies = object : SpotiFlyerMain.Dependencies, Dependencies by this {
|
dependencies = object : SpotiFlyerMain.Dependencies, Dependencies by this {
|
||||||
override fun mainOutput(searched: SpotiFlyerMain.Output): Consumer<SpotiFlyerMain.Output> = Consumer(::onMainOutput)
|
override fun mainOutput(searched: SpotiFlyerMain.Output): Consumer<SpotiFlyerMain.Output> = Consumer(::onMainOutput)
|
||||||
|
override val dir: Dir = directories
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.shabinder.spotiflyer.ui
|
package com.shabinder.common.ui
|
||||||
|
|
||||||
import androidx.compose.material.Colors
|
import androidx.compose.material.Colors
|
||||||
import androidx.compose.material.darkColors
|
import androidx.compose.material.darkColors
|
||||||
|
@ -4,6 +4,10 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
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 androidx.compose.ui.unit.Dp
|
||||||
import com.shabinder.common.Picture
|
import com.shabinder.common.Picture
|
||||||
|
|
||||||
|
@ -10,6 +10,27 @@ expect fun DownloadImageTick(modifier: Modifier = Modifier)
|
|||||||
@Composable
|
@Composable
|
||||||
expect fun DownloadAllImage():ImageVector
|
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
|
@Composable
|
||||||
expect fun DownloadImageError(modifier: Modifier = Modifier)
|
expect fun DownloadImageError(modifier: Modifier = Modifier)
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.shabinder.spotiflyer.ui
|
package com.shabinder.common.ui
|
||||||
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.Shapes
|
import androidx.compose.material.Shapes
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,13 +14,13 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.shabinder.spotiflyer.ui
|
package com.shabinder.common.ui
|
||||||
|
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ComposeLearnTheme(content: @Composable() () -> Unit) {
|
fun SpotiFlyerTheme(content: @Composable() () -> Unit) {
|
||||||
MaterialTheme(
|
MaterialTheme(
|
||||||
colors = SpotiFlyerColors,
|
colors = SpotiFlyerColors,
|
||||||
typography = SpotiFlyerTypography,
|
typography = SpotiFlyerTypography,
|
||||||
|
@ -14,63 +14,57 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.shabinder.spotiflyer.ui
|
package com.shabinder.common.ui
|
||||||
|
|
||||||
import androidx.compose.material.Typography
|
import androidx.compose.material.Typography
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.text.TextStyle
|
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.FontWeight
|
||||||
import androidx.compose.ui.text.font.font
|
|
||||||
import androidx.compose.ui.text.font.fontFamily
|
|
||||||
import androidx.compose.ui.unit.sp
|
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_light, FontWeight.Light),
|
||||||
font(R.font.montserrat_regular, FontWeight.Normal),
|
font(R.font.montserrat_regular, FontWeight.Normal),
|
||||||
font(R.font.montserrat_medium, FontWeight.Medium),
|
font(R.font.montserrat_medium, FontWeight.Medium),
|
||||||
font(R.font.montserrat_semibold, FontWeight.SemiBold),
|
font(R.font.montserrat_semibold, FontWeight.SemiBold),
|
||||||
)
|
)*/
|
||||||
|
|
||||||
val pristineFont = fontFamily(
|
|
||||||
font(R.font.pristine_script, FontWeight.Bold)
|
|
||||||
)
|
|
||||||
|
|
||||||
val SpotiFlyerTypography = Typography(
|
val SpotiFlyerTypography = Typography(
|
||||||
h1 = TextStyle(
|
h1 = TextStyle(
|
||||||
fontFamily = Montserrat,
|
//fontFamily = Montserrat,
|
||||||
fontSize = 96.sp,
|
fontSize = 96.sp,
|
||||||
fontWeight = FontWeight.Light,
|
fontWeight = FontWeight.Light,
|
||||||
lineHeight = 117.sp,
|
lineHeight = 117.sp,
|
||||||
letterSpacing = (-1.5).sp
|
letterSpacing = (-1.5).sp
|
||||||
),
|
),
|
||||||
h2 = TextStyle(
|
h2 = TextStyle(
|
||||||
fontFamily = Montserrat,
|
//fontFamily = Montserrat,
|
||||||
fontSize = 60.sp,
|
fontSize = 60.sp,
|
||||||
fontWeight = FontWeight.Light,
|
fontWeight = FontWeight.Light,
|
||||||
lineHeight = 73.sp,
|
lineHeight = 73.sp,
|
||||||
letterSpacing = (-0.5).sp
|
letterSpacing = (-0.5).sp
|
||||||
),
|
),
|
||||||
h3 = TextStyle(
|
h3 = TextStyle(
|
||||||
fontFamily = Montserrat,
|
//fontFamily = Montserrat,
|
||||||
fontSize = 48.sp,
|
fontSize = 48.sp,
|
||||||
fontWeight = FontWeight.Normal,
|
fontWeight = FontWeight.Normal,
|
||||||
lineHeight = 59.sp
|
lineHeight = 59.sp
|
||||||
),
|
),
|
||||||
h4 = TextStyle(
|
h4 = TextStyle(
|
||||||
fontFamily = Montserrat,
|
//fontFamily = Montserrat,
|
||||||
fontSize = 30.sp,
|
fontSize = 30.sp,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
lineHeight = 37.sp
|
lineHeight = 37.sp
|
||||||
),
|
),
|
||||||
h5 = TextStyle(
|
h5 = TextStyle(
|
||||||
fontFamily = Montserrat,
|
//fontFamily = Montserrat,
|
||||||
fontSize = 24.sp,
|
fontSize = 24.sp,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
lineHeight = 29.sp
|
lineHeight = 29.sp
|
||||||
),
|
),
|
||||||
h6 = TextStyle(
|
h6 = TextStyle(
|
||||||
fontFamily = Montserrat,
|
//fontFamily = Montserrat,
|
||||||
fontSize = 18.sp,
|
fontSize = 18.sp,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
lineHeight = 26.sp,
|
lineHeight = 26.sp,
|
||||||
@ -78,49 +72,49 @@ val SpotiFlyerTypography = Typography(
|
|||||||
|
|
||||||
),
|
),
|
||||||
subtitle1 = TextStyle(
|
subtitle1 = TextStyle(
|
||||||
fontFamily = Montserrat,
|
//fontFamily = Montserrat,
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
lineHeight = 20.sp,
|
lineHeight = 20.sp,
|
||||||
letterSpacing = 0.5.sp
|
letterSpacing = 0.5.sp
|
||||||
),
|
),
|
||||||
subtitle2 = TextStyle(
|
subtitle2 = TextStyle(
|
||||||
fontFamily = Montserrat,
|
//fontFamily = Montserrat,
|
||||||
fontSize = 14.sp,
|
fontSize = 14.sp,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
lineHeight = 17.sp,
|
lineHeight = 17.sp,
|
||||||
letterSpacing = 0.1.sp
|
letterSpacing = 0.1.sp
|
||||||
),
|
),
|
||||||
body1 = TextStyle(
|
body1 = TextStyle(
|
||||||
fontFamily = Montserrat,
|
//fontFamily = Montserrat,
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
lineHeight = 20.sp,
|
lineHeight = 20.sp,
|
||||||
letterSpacing = 0.15.sp,
|
letterSpacing = 0.15.sp,
|
||||||
),
|
),
|
||||||
body2 = TextStyle(
|
body2 = TextStyle(
|
||||||
fontFamily = Montserrat,
|
//fontFamily = Montserrat,
|
||||||
fontSize = 14.sp,
|
fontSize = 14.sp,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
lineHeight = 20.sp,
|
lineHeight = 20.sp,
|
||||||
letterSpacing = 0.25.sp
|
letterSpacing = 0.25.sp
|
||||||
),
|
),
|
||||||
button = TextStyle(
|
button = TextStyle(
|
||||||
fontFamily = Montserrat,
|
//fontFamily = Montserrat,
|
||||||
fontSize = 14.sp,
|
fontSize = 14.sp,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
lineHeight = 16.sp,
|
lineHeight = 16.sp,
|
||||||
letterSpacing = 1.25.sp
|
letterSpacing = 1.25.sp
|
||||||
),
|
),
|
||||||
caption = TextStyle(
|
caption = TextStyle(
|
||||||
fontFamily = Montserrat,
|
//fontFamily = Montserrat,
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
lineHeight = 16.sp,
|
lineHeight = 16.sp,
|
||||||
letterSpacing = 0.sp
|
letterSpacing = 0.sp
|
||||||
),
|
),
|
||||||
overline = TextStyle(
|
overline = TextStyle(
|
||||||
fontFamily = Montserrat,
|
//fontFamily = Montserrat,
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
lineHeight = 16.sp,
|
lineHeight = 16.sp,
|
||||||
@ -129,7 +123,7 @@ val SpotiFlyerTypography = Typography(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val appNameStyle = TextStyle(
|
val appNameStyle = TextStyle(
|
||||||
fontFamily = pristineFont,
|
fontFamily = FontFamily.Cursive,
|
||||||
fontSize = 40.sp,
|
fontSize = 40.sp,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
lineHeight = 42.sp,
|
lineHeight = 42.sp,
|
||||||
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<DownloadRecord>,
|
|
||||||
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<HomeCategory>,
|
|
||||||
selectCategory: (HomeCategory) -> Unit,
|
|
||||||
modifier: Modifier = Modifier
|
|
||||||
) {
|
|
||||||
val selectedIndex =categories.indexOfFirst { it == selectedCategory }
|
|
||||||
val indicator = @Composable { tabPositions: List<TabPosition> ->
|
|
||||||
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))
|
|
||||||
)
|
|
||||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<List<DownloadRecord>>(listOf())
|
|
||||||
|
|
||||||
fun getDownloadRecordList() {
|
|
||||||
viewModelScope.launch {
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
delay(100) //TEMP
|
|
||||||
downloadRecordList = sharedViewModel.databaseDAO.getRecord()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class HomeCategory {
|
|
||||||
About, History
|
|
||||||
}
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
@ -14,7 +14,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.shabinder.spotiflyer.ui.splash
|
package com.shabinder.common.ui.splash
|
||||||
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
@ -27,14 +27,9 @@ import androidx.compose.runtime.rememberUpdatedState
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.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.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import com.shabinder.spotiflyer.R
|
import com.shabinder.common.ui.*
|
||||||
import com.shabinder.spotiflyer.ui.SpotiFlyerTypography
|
|
||||||
import com.shabinder.spotiflyer.ui.colorAccent
|
|
||||||
import com.shabinder.spotiflyer.ui.colorPrimary
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
private const val SplashWaitTime: Long = 1100
|
private const val SplashWaitTime: Long = 1100
|
||||||
@ -49,7 +44,7 @@ fun Splash(modifier: Modifier = Modifier, onTimeout: () -> Unit) {
|
|||||||
delay(SplashWaitTime)
|
delay(SplashWaitTime)
|
||||||
currentOnTimeout()
|
currentOnTimeout()
|
||||||
}
|
}
|
||||||
Image(imageVector = vectorResource(id = R.drawable.ic_spotiflyer_logo))
|
Image(imageVector = SpotiFlyerLogo())
|
||||||
MadeInIndia(Modifier.align(Alignment.BottomCenter))
|
MadeInIndia(Modifier.align(Alignment.BottomCenter))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,15 +62,15 @@ fun MadeInIndia(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.made_with_love),
|
text = "Made with ",
|
||||||
color = colorPrimary,
|
color = colorPrimary,
|
||||||
fontSize = 22.sp
|
fontSize = 22.sp
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.padding(start = 4.dp))
|
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))
|
Spacer(modifier = Modifier.padding(start = 4.dp))
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.in_india),
|
text = " in India",
|
||||||
color = colorPrimary,
|
color = colorPrimary,
|
||||||
fontSize = 22.sp
|
fontSize = 22.sp
|
||||||
)
|
)
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.shabinder.spotiflyer.ui.utils
|
package com.shabinder.common.ui.utils
|
||||||
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.compositeOver
|
import androidx.compose.ui.graphics.compositeOver
|
||||||
|
@ -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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<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()
|
|
||||||
}
|
|
@ -14,9 +14,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/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.example.jetcaster.util
|
package com.shabinder.common.ui.utils
|
||||||
|
|
||||||
import androidx.annotation.FloatRange
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
@ -41,8 +40,10 @@ import kotlin.math.pow
|
|||||||
*/
|
*/
|
||||||
fun Modifier.verticalGradientScrim(
|
fun Modifier.verticalGradientScrim(
|
||||||
color: Color,
|
color: Color,
|
||||||
@FloatRange(from = 0.0, to = 1.0) startYPercentage: Float = 0f,
|
//@FloatRange(from = 0.0, to = 1.0)
|
||||||
@FloatRange(from = 0.0, to = 1.0) endYPercentage: Float = 1f,
|
startYPercentage: Float = 0f,
|
||||||
|
//@FloatRange(from = 0.0, to = 1.0)
|
||||||
|
endYPercentage: Float = 1f,
|
||||||
decay: Float = 1.0f,
|
decay: Float = 1.0f,
|
||||||
numStops: Int = 16,
|
numStops: Int = 16,
|
||||||
fixedHeight: Float? = null
|
fixedHeight: Float? = null
|
||||||
|
@ -32,3 +32,24 @@ actual fun DownloadImageArrow(modifier: Modifier){
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
actual fun DownloadAllImage():ImageVector = vectorXmlResource("common/compose-ui/src/main/res/drawable/ic_download_arrow.xml")
|
actual fun DownloadAllImage():ImageVector = vectorXmlResource("common/compose-ui/src/main/res/drawable/ic_download_arrow.xml")
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
actual fun 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")
|
||||||
|
21
common/compose-ui/src/main/res/drawable/ic_github.xml
Normal file
21
common/compose-ui/src/main/res/drawable/ic_github.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<!--
|
||||||
|
~ 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 <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="38dp"
|
||||||
|
android:height="38dp" android:viewportWidth="512" android:viewportHeight="512">
|
||||||
|
<path android:fillColor="#4EA4FF" android:pathData="M255.968,5.329C114.624,5.329 0,120.401 0,262.353c0,113.536 73.344,209.856 175.104,243.872c12.8,2.368 17.472,-5.568 17.472,-12.384c0,-6.112 -0.224,-22.272 -0.352,-43.712c-71.2,15.52 -86.24,-34.464 -86.24,-34.464c-11.616,-29.696 -28.416,-37.6 -28.416,-37.6c-23.264,-15.936 1.728,-15.616 1.728,-15.616c25.696,1.824 39.2,26.496 39.2,26.496c22.848,39.264 59.936,27.936 74.528,21.344c2.304,-16.608 8.928,-27.936 16.256,-34.368c-56.832,-6.496 -116.608,-28.544 -116.608,-127.008c0,-28.064 9.984,-51.008 26.368,-68.992c-2.656,-6.496 -11.424,-32.64 2.496,-68c0,0 21.504,-6.912 70.4,26.336c20.416,-5.696 42.304,-8.544 64.096,-8.64c21.728,0.128 43.648,2.944 64.096,8.672c48.864,-33.248 70.336,-26.336 70.336,-26.336c13.952,35.392 5.184,61.504 2.56,68c16.416,17.984 26.304,40.928 26.304,68.992c0,98.72 -59.84,120.448 -116.864,126.816c9.184,7.936 17.376,23.616 17.376,47.584c0,34.368 -0.32,62.08 -0.32,70.496c0,6.88 4.608,14.88 17.6,12.352C438.72,472.145 512,375.857 512,262.353C512,120.401 397.376,5.329 255.968,5.329z"/>
|
||||||
|
</vector>
|
||||||
|
|
BIN
common/compose-ui/src/main/res/font/montserrat_light.ttf
Normal file
BIN
common/compose-ui/src/main/res/font/montserrat_light.ttf
Normal file
Binary file not shown.
BIN
common/compose-ui/src/main/res/font/montserrat_medium.ttf
Normal file
BIN
common/compose-ui/src/main/res/font/montserrat_medium.ttf
Normal file
Binary file not shown.
BIN
common/compose-ui/src/main/res/font/montserrat_regular.ttf
Normal file
BIN
common/compose-ui/src/main/res/font/montserrat_regular.ttf
Normal file
Binary file not shown.
BIN
common/compose-ui/src/main/res/font/montserrat_semibold.ttf
Normal file
BIN
common/compose-ui/src/main/res/font/montserrat_semibold.ttf
Normal file
Binary file not shown.
BIN
common/compose-ui/src/main/res/font/pristine_script.ttf
Normal file
BIN
common/compose-ui/src/main/res/font/pristine_script.ttf
Normal file
Binary file not shown.
@ -7,13 +7,16 @@ import android.os.Environment
|
|||||||
import co.touchlab.kermit.Kermit
|
import co.touchlab.kermit.Kermit
|
||||||
import com.mpatric.mp3agic.Mp3File
|
import com.mpatric.mp3agic.Mp3File
|
||||||
import com.shabinder.common.database.appContext
|
import com.shabinder.common.database.appContext
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.lang.Exception
|
import java.lang.Exception
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
actual open class Dir actual constructor(
|
actual class Dir actual constructor(
|
||||||
private val logger: Kermit
|
private val logger: Kermit
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -53,7 +56,7 @@ actual open class Dir actual constructor(
|
|||||||
File(imageCacheDir()).deleteRecursively()
|
File(imageCacheDir()).deleteRecursively()
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun cacheImage(picture: Picture) {
|
actual suspend fun cacheImage(picture: Picture) {
|
||||||
try {
|
try {
|
||||||
val path = imageCacheDir() + picture.name
|
val path = imageCacheDir() + picture.name
|
||||||
FileOutputStream(path).use { out ->
|
FileOutputStream(path).use { out ->
|
||||||
@ -92,9 +95,10 @@ actual open class Dir actual constructor(
|
|||||||
.setId3v2TagsAndSaveFile(trackDetails,path)
|
.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)
|
var picture: Picture? = loadCachedImage(cachePath)
|
||||||
if (picture == null) picture = freshImage(url,cachePath)
|
if (picture == null) picture = freshImage(url)
|
||||||
return picture
|
return picture
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +133,7 @@ actual open class Dir actual constructor(
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private fun freshImage(url:String,cachePath: String):Picture?{
|
private fun freshImage(url:String):Picture?{
|
||||||
return try {
|
return try {
|
||||||
val source = URL(url)
|
val source = URL(url)
|
||||||
val connection: HttpURLConnection = source.openConnection() as HttpURLConnection
|
val connection: HttpURLConnection = source.openConnection() as HttpURLConnection
|
||||||
@ -147,8 +151,9 @@ actual open class Dir actual constructor(
|
|||||||
result.width,
|
result.width,
|
||||||
result.height
|
result.height
|
||||||
)
|
)
|
||||||
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
cacheImage(picture)
|
cacheImage(picture)
|
||||||
|
}
|
||||||
picture
|
picture
|
||||||
} else null
|
} else null
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -32,6 +32,7 @@ fun commonModule(enableNetworkLogs: Boolean) = module {
|
|||||||
single { SpotifyProvider(get(),get(),get(),get()) }
|
single { SpotifyProvider(get(),get(),get(),get()) }
|
||||||
single { GaanaProvider(get(),get(),get(),get()) }
|
single { GaanaProvider(get(),get(),get(),get()) }
|
||||||
single { YoutubeProvider(get(),get(),get(),get()) }
|
single { YoutubeProvider(get(),get(),get(),get()) }
|
||||||
|
single { FetchPlatformQueryResult(get(),get(),get(),get()) }
|
||||||
single { createHttpClient(enableNetworkLogs = enableNetworkLogs) }
|
single { createHttpClient(enableNetworkLogs = enableNetworkLogs) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
expect open class Dir(
|
expect class Dir(
|
||||||
logger: Kermit,
|
logger: Kermit,
|
||||||
) {
|
) {
|
||||||
fun isPresent(path:String):Boolean
|
fun isPresent(path:String):Boolean
|
||||||
@ -18,7 +18,7 @@ expect open class Dir(
|
|||||||
fun imageCacheDir(): String
|
fun imageCacheDir(): String
|
||||||
fun createDirectory(dirPath:String)
|
fun createDirectory(dirPath:String)
|
||||||
suspend fun cacheImage(picture: Picture)
|
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 clearCache()
|
||||||
suspend fun saveFileWithMetadata(mp3ByteArray: ByteArray, path: String, trackDetails: TrackDetails)
|
suspend fun saveFileWithMetadata(mp3ByteArray: ByteArray, path: String, trackDetails: TrackDetails)
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import java.net.URL
|
|||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import javax.imageio.ImageIO
|
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
|
actual fun fileSeparator(): String = File.separator
|
||||||
|
|
||||||
@ -85,7 +85,8 @@ actual open class Dir actual constructor(private val logger: Kermit) {
|
|||||||
.setId3v2TagsAndSaveFile(trackDetails,path)
|
.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)
|
var picture: Picture? = loadCachedImage(cachePath)
|
||||||
if (picture == null) picture = freshImage(url,cachePath)
|
if (picture == null) picture = freshImage(url,cachePath)
|
||||||
return picture
|
return picture
|
||||||
|
Loading…
Reference in New Issue
Block a user