mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-25 02:14:32 +01:00
Network Connection Change Handling Improved,i.e,Less Crashes and Gaana Implementation Also Started
This commit is contained in:
parent
a5793cc72c
commit
c489c8c84a
@ -1,11 +1,16 @@
|
|||||||
<component name="ProjectDictionaryState">
|
<component name="ProjectDictionaryState">
|
||||||
<dictionary name="shabinder">
|
<dictionary name="shabinder">
|
||||||
<words>
|
<words>
|
||||||
|
<w>albumseokey</w>
|
||||||
|
<w>amita</w>
|
||||||
|
<w>cardview</w>
|
||||||
<w>cherrypick</w>
|
<w>cherrypick</w>
|
||||||
<w>downloadrecord</w>
|
<w>downloadrecord</w>
|
||||||
<w>emoji</w>
|
<w>emoji</w>
|
||||||
<w>ffmpeg</w>
|
<w>ffmpeg</w>
|
||||||
<w>flyer</w>
|
<w>flyer</w>
|
||||||
|
<w>gaana</w>
|
||||||
|
<w>gener</w>
|
||||||
<w>hqdefault</w>
|
<w>hqdefault</w>
|
||||||
<w>insta</w>
|
<w>insta</w>
|
||||||
<w>instagram</w>
|
<w>instagram</w>
|
||||||
@ -19,8 +24,10 @@
|
|||||||
<w>musicplaceholder</w>
|
<w>musicplaceholder</w>
|
||||||
<w>raleway</w>
|
<w>raleway</w>
|
||||||
<w>semibold</w>
|
<w>semibold</w>
|
||||||
|
<w>seokey</w>
|
||||||
<w>shabinder</w>
|
<w>shabinder</w>
|
||||||
<w>singh</w>
|
<w>singh</w>
|
||||||
|
<w>snackbar</w>
|
||||||
<w>spoti</w>
|
<w>spoti</w>
|
||||||
<w>spotiflyer</w>
|
<w>spotiflyer</w>
|
||||||
<w>spotify</w>
|
<w>spotify</w>
|
||||||
|
@ -21,7 +21,7 @@ apply plugin: 'kotlin-android-extensions'
|
|||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
apply plugin: "androidx.navigation.safeargs.kotlin"
|
apply plugin: "androidx.navigation.safeargs.kotlin"
|
||||||
apply plugin: 'dagger.hilt.android.plugin'
|
apply plugin: 'dagger.hilt.android.plugin'
|
||||||
//apply plugin: 'kotlinx-serialization'
|
apply plugin: 'kotlinx-serialization'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion 29
|
||||||
@ -90,8 +90,10 @@ dependencies {
|
|||||||
implementation 'com.google.android.material:material:1.2.1'
|
implementation 'com.google.android.material:material:1.2.1'
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.0'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.0'
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.0'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.0'
|
||||||
|
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1"
|
||||||
|
|
||||||
implementation "androidx.room:room-runtime:2.2.5"
|
implementation "androidx.room:room-runtime:2.2.5"
|
||||||
|
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
|
||||||
kapt "androidx.room:room-compiler:2.2.5"
|
kapt "androidx.room:room-compiler:2.2.5"
|
||||||
implementation "androidx.room:room-ktx:2.2.5"
|
implementation "androidx.room:room-ktx:2.2.5"
|
||||||
implementation "com.google.dagger:hilt-android:$hilt_version"
|
implementation "com.google.dagger:hilt-android:$hilt_version"
|
||||||
@ -116,7 +118,6 @@ dependencies {
|
|||||||
implementation 'com.squareup.moshi:moshi-kotlin:1.11.0'
|
implementation 'com.squareup.moshi:moshi-kotlin:1.11.0'
|
||||||
implementation "com.squareup.retrofit2:converter-moshi:2.9.0"
|
implementation "com.squareup.retrofit2:converter-moshi:2.9.0"
|
||||||
implementation "com.squareup.retrofit2:converter-scalars:2.9.0"
|
implementation "com.squareup.retrofit2:converter-scalars:2.9.0"
|
||||||
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
|
||||||
implementation 'com.beust:klaxon:5.4'
|
implementation 'com.beust:klaxon:5.4'
|
||||||
implementation 'me.xdrop:fuzzywuzzy:1.3.1'
|
implementation 'me.xdrop:fuzzywuzzy:1.3.1'
|
||||||
|
|
||||||
|
18
app/proguard-rules.pro
vendored
18
app/proguard-rules.pro
vendored
@ -11,7 +11,25 @@
|
|||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
# public *;
|
# public *;
|
||||||
#}
|
#}
|
||||||
|
-keepattributes *Annotation*, InnerClasses
|
||||||
|
-dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations
|
||||||
|
|
||||||
|
# kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer
|
||||||
|
-keepclassmembers class kotlinx.serialization.json.* {
|
||||||
|
*** Companion;
|
||||||
|
}
|
||||||
|
-keepclasseswithmembers class kotlinx.serialization.json.* {
|
||||||
|
kotlinx.serialization.KSerializer serializer(...);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Change here com.yourcompany.yourpackage
|
||||||
|
-keep,includedescriptorclasses class com.shabinder.spotiflyer.**$$serializer { *; } # <-- change package name to your app's
|
||||||
|
-keepclassmembers class com.shabinder.spotiflyer* { # <-- change package name to your app's
|
||||||
|
*** Companion;
|
||||||
|
}
|
||||||
|
-keepclasseswithmembers class com.shabinder.spotiflyer.* { # <-- change package name to your app's
|
||||||
|
kotlinx.serialization.KSerializer serializer(...);
|
||||||
|
}
|
||||||
# Uncomment this to preserve the line number information for
|
# Uncomment this to preserve the line number information for
|
||||||
# debugging stack traces.
|
# debugging stack traces.
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
@ -28,6 +28,7 @@ import android.os.Bundle
|
|||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.databinding.DataBindingUtil
|
import androidx.databinding.DataBindingUtil
|
||||||
@ -35,10 +36,12 @@ import androidx.lifecycle.ViewModelProvider
|
|||||||
import com.github.javiersantos.appupdater.AppUpdater
|
import com.github.javiersantos.appupdater.AppUpdater
|
||||||
import com.github.javiersantos.appupdater.enums.UpdateFrom
|
import com.github.javiersantos.appupdater.enums.UpdateFrom
|
||||||
import com.shabinder.spotiflyer.databinding.MainActivityBinding
|
import com.shabinder.spotiflyer.databinding.MainActivityBinding
|
||||||
|
import com.shabinder.spotiflyer.networking.SpotifyService
|
||||||
|
import com.shabinder.spotiflyer.networking.SpotifyServiceTokenRequest
|
||||||
|
import com.shabinder.spotiflyer.utils.NetworkInterceptor
|
||||||
import com.shabinder.spotiflyer.utils.Provider.activity
|
import com.shabinder.spotiflyer.utils.Provider.activity
|
||||||
import com.shabinder.spotiflyer.utils.SpotifyService
|
|
||||||
import com.shabinder.spotiflyer.utils.SpotifyServiceTokenRequest
|
|
||||||
import com.shabinder.spotiflyer.utils.createDirectories
|
import com.shabinder.spotiflyer.utils.createDirectories
|
||||||
|
import com.shabinder.spotiflyer.utils.isOnline
|
||||||
import com.shabinder.spotiflyer.utils.startService
|
import com.shabinder.spotiflyer.utils.startService
|
||||||
import com.squareup.moshi.Moshi
|
import com.squareup.moshi.Moshi
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
@ -54,35 +57,29 @@ import javax.inject.Inject
|
|||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : AppCompatActivity(){
|
class MainActivity : AppCompatActivity(){
|
||||||
private var spotifyService : SpotifyService? = null
|
private var spotifyService : SpotifyService? = null
|
||||||
private var isConnected: Boolean = false
|
|
||||||
private var sharedPref :SharedPreferences? = null
|
private var sharedPref :SharedPreferences? = null
|
||||||
private var token :String =""
|
|
||||||
private lateinit var binding: MainActivityBinding
|
private lateinit var binding: MainActivityBinding
|
||||||
|
lateinit var snackBarAnchor: View
|
||||||
private lateinit var sharedViewModel: SharedViewModel
|
private lateinit var sharedViewModel: SharedViewModel
|
||||||
@Inject lateinit var spotifyServiceTokenRequest: SpotifyServiceTokenRequest
|
|
||||||
@Inject lateinit var moshi: Moshi
|
@Inject lateinit var moshi: Moshi
|
||||||
|
@Inject lateinit var spotifyServiceTokenRequest: SpotifyServiceTokenRequest
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = DataBindingUtil.setContentView(this, R.layout.main_activity)
|
binding = DataBindingUtil.setContentView(this, R.layout.main_activity)
|
||||||
|
snackBarAnchor = binding.snackBarPosition
|
||||||
sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)
|
sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)
|
||||||
//Enabling Dark Mode
|
//Enabling Dark Mode
|
||||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
|
||||||
sharedPref = this.getPreferences(Context.MODE_PRIVATE)
|
sharedPref = this.getPreferences(Context.MODE_PRIVATE)
|
||||||
|
|
||||||
if(sharedViewModel.spotifyService.value == null){
|
|
||||||
authenticateSpotify()
|
authenticateSpotify()
|
||||||
}else{
|
|
||||||
implementSpotifyService(sharedViewModel.accessToken.value!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
requestPermission()
|
requestPermission()
|
||||||
disableDozeMode()
|
disableDozeMode()
|
||||||
checkIfLatestVersion()
|
checkIfLatestVersion()
|
||||||
createDirectories()
|
createDirectories()
|
||||||
isConnected = sharedViewModel.isOnline(this)
|
Log.i("Connection Status", isOnline().toString())
|
||||||
sharedViewModel.isConnected.value = isConnected
|
|
||||||
Log.i("Connection Status", isConnected.toString())
|
|
||||||
|
|
||||||
//starting Notification and Downloader Service!
|
//starting Notification and Downloader Service!
|
||||||
startService(this)
|
startService(this)
|
||||||
@ -140,7 +137,7 @@ class MainActivity : AppCompatActivity(){
|
|||||||
"Bearer $token"
|
"Bearer $token"
|
||||||
).build()
|
).build()
|
||||||
chain.proceed(request)
|
chain.proceed(request)
|
||||||
})
|
}).addInterceptor(NetworkInterceptor())
|
||||||
|
|
||||||
val retrofit = Retrofit.Builder()
|
val retrofit = Retrofit.Builder()
|
||||||
.baseUrl("https://api.spotify.com/v1/")
|
.baseUrl("https://api.spotify.com/v1/")
|
||||||
@ -155,16 +152,12 @@ class MainActivity : AppCompatActivity(){
|
|||||||
|
|
||||||
fun authenticateSpotify() {
|
fun authenticateSpotify() {
|
||||||
sharedViewModel.uiScope.launch {
|
sharedViewModel.uiScope.launch {
|
||||||
if (isConnected) {
|
Log.i("Spotify Authentication","Started")
|
||||||
Log.i("Post Request", "Made")
|
val token = spotifyServiceTokenRequest.getToken()
|
||||||
token = spotifyServiceTokenRequest.getToken()!!.access_token
|
token.value?.let {
|
||||||
implementSpotifyService(token)
|
implementSpotifyService(it.access_token)
|
||||||
Log.i("Post Request", token)
|
|
||||||
sharedViewModel.accessToken.value = token
|
|
||||||
}else{
|
|
||||||
Log.i("network", "unavailable")
|
|
||||||
// sharedViewModel.showAlertDialog(resources,this@MainActivity)
|
|
||||||
}
|
}
|
||||||
|
Log.i("Spotify Token", token.value.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,19 +182,6 @@ class MainActivity : AppCompatActivity(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(savedInstanceState: Bundle) {
|
|
||||||
savedInstanceState.putString("token", token)
|
|
||||||
super.onSaveInstanceState(savedInstanceState)
|
|
||||||
}
|
|
||||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
|
||||||
if (savedInstanceState.getString("token") ==""){
|
|
||||||
super.onRestoreInstanceState(savedInstanceState)
|
|
||||||
}else{
|
|
||||||
implementSpotifyService(savedInstanceState.getString("token")!!)
|
|
||||||
super.onRestoreInstanceState(savedInstanceState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkIfLatestVersion() {
|
private fun checkIfLatestVersion() {
|
||||||
val appUpdater = AppUpdater(this)
|
val appUpdater = AppUpdater(this)
|
||||||
.showAppUpdated(false)//true:Show App is Update Dialog
|
.showAppUpdated(false)//true:Show App is Update Dialog
|
||||||
@ -220,14 +200,7 @@ class MainActivity : AppCompatActivity(){
|
|||||||
appUpdater.start()
|
appUpdater.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object{
|
|
||||||
private var instance = MainActivity()
|
|
||||||
fun getInstance():MainActivity{
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
}
|
|
||||||
init {
|
init {
|
||||||
instance = this
|
|
||||||
activity = this
|
activity = this
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,49 +17,22 @@
|
|||||||
|
|
||||||
package com.shabinder.spotiflyer
|
package com.shabinder.spotiflyer
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.res.Resources
|
|
||||||
import android.net.ConnectivityManager
|
|
||||||
import android.os.Environment
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.shabinder.spotiflyer.networking.SpotifyService
|
||||||
import com.shabinder.spotiflyer.utils.SpotifyService
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class SharedViewModel : ViewModel() {
|
class SharedViewModel : ViewModel() {
|
||||||
var intentString = MutableLiveData<String>().apply { value = "" }
|
var intentString = MutableLiveData<String>().apply { value = "" }
|
||||||
var spotifyService = MutableLiveData<SpotifyService>()
|
var spotifyService = MutableLiveData<SpotifyService>()
|
||||||
var accessToken = MutableLiveData<String>().apply { value = "" }
|
|
||||||
var isConnected = MutableLiveData<Boolean>().apply { value = false }
|
|
||||||
val defaultDir = Environment.DIRECTORY_MUSIC + File.separator + "SpotiFlyer" + File.separator + ".Images" + File.separator
|
|
||||||
|
|
||||||
|
|
||||||
private var viewModelJob = Job()
|
private var viewModelJob = Job()
|
||||||
|
|
||||||
val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
|
val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
viewModelJob.cancel()
|
viewModelJob.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showAlertDialog(resources:Resources,context: Context){
|
|
||||||
MaterialAlertDialogBuilder(context,R.style.AlertDialogTheme)
|
|
||||||
.setTitle(resources.getString(R.string.title))
|
|
||||||
.setMessage(resources.getString(R.string.supporting_text))
|
|
||||||
.setPositiveButton(resources.getString(R.string.cancel)) { _, _ ->
|
|
||||||
// Respond to neutral button press
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
fun isOnline(context: Context): Boolean {
|
|
||||||
val cm =
|
|
||||||
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
|
||||||
val netInfo = cm.activeNetworkInfo
|
|
||||||
return netInfo != null && netInfo.isConnectedOrConnecting
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -27,7 +27,11 @@ import android.view.animation.Animation
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.shabinder.spotiflyer.SharedViewModel
|
import com.shabinder.spotiflyer.SharedViewModel
|
||||||
import com.shabinder.spotiflyer.models.*
|
import com.shabinder.spotiflyer.models.DownloadObject
|
||||||
|
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||||
|
import com.shabinder.spotiflyer.models.TrackDetails
|
||||||
|
import com.shabinder.spotiflyer.networking.YoutubeMusicApi
|
||||||
|
import com.shabinder.spotiflyer.networking.makeJsonBody
|
||||||
import com.shabinder.spotiflyer.utils.*
|
import com.shabinder.spotiflyer.utils.*
|
||||||
import com.shabinder.spotiflyer.utils.Provider.activity
|
import com.shabinder.spotiflyer.utils.Provider.activity
|
||||||
import com.shabinder.spotiflyer.utils.Provider.defaultDir
|
import com.shabinder.spotiflyer.utils.Provider.defaultDir
|
||||||
@ -39,10 +43,10 @@ import retrofit2.Callback
|
|||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
object SpotifyDownloadHelper {
|
object DownloadHelper {
|
||||||
|
|
||||||
var statusBar:TextView? = null
|
var statusBar:TextView? = null
|
||||||
var youtubeMusicApi:YoutubeMusicApi? = null
|
var youtubeMusicApi: YoutubeMusicApi? = null
|
||||||
var sharedViewModel: SharedViewModel? = null
|
var sharedViewModel: SharedViewModel? = null
|
||||||
|
|
||||||
private var total = 0
|
private var total = 0
|
||||||
@ -55,12 +59,16 @@ object SpotifyDownloadHelper {
|
|||||||
suspend fun downloadAllTracks(
|
suspend fun downloadAllTracks(
|
||||||
type:String,
|
type:String,
|
||||||
subFolder: String?,
|
subFolder: String?,
|
||||||
trackList: List<Track>) {
|
trackList: List<TrackDetails>) {
|
||||||
|
resetStatusBar()// For New Download Request's Status
|
||||||
val downloadList = ArrayList<DownloadObject>()
|
val downloadList = ArrayList<DownloadObject>()
|
||||||
|
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
total += trackList.size // Adding New Download List Count to StatusBar
|
total += trackList.size // Adding New Download List Count to StatusBar
|
||||||
trackList.forEachIndexed { index, it ->
|
trackList.forEachIndexed { index, it ->
|
||||||
|
if(!isOnline()){
|
||||||
|
showNoConnectionAlert()
|
||||||
|
return@withContext
|
||||||
|
}
|
||||||
if(it.downloaded == DownloadStatus.Downloaded){//Download Already Present!!
|
if(it.downloaded == DownloadStatus.Downloaded){//Download Already Present!!
|
||||||
processed++
|
processed++
|
||||||
if(index == (trackList.size-1)){//LastElement
|
if(index == (trackList.size-1)){//LastElement
|
||||||
@ -74,49 +82,32 @@ object SpotifyDownloadHelper {
|
|||||||
},5000)
|
},5000)
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
val artistsList = mutableListOf<String>()
|
val searchQuery = "${it.title} - ${it.artists.joinToString(",")}"
|
||||||
it.artists?.forEach { artist -> artistsList.add(artist!!.name!!) }
|
val jsonBody = makeJsonBody(searchQuery.trim()).toJsonString()
|
||||||
val searchQuery = "${it.name} - ${artistsList.joinToString(",")}"
|
|
||||||
|
|
||||||
val jsonBody = makeJsonBody(searchQuery.trim())
|
|
||||||
youtubeMusicApi?.getYoutubeMusicResponse(jsonBody)?.enqueue(
|
youtubeMusicApi?.getYoutubeMusicResponse(jsonBody)?.enqueue(
|
||||||
object : Callback<String>{
|
object : Callback<String>{
|
||||||
override fun onResponse(call: Call<String>, response: Response<String>) {
|
override fun onResponse(call: Call<String>, response: Response<String>) {
|
||||||
sharedViewModel?.uiScope?.launch {
|
sharedViewModel?.uiScope?.launch {
|
||||||
val videoId = sortByBestMatch(
|
val videoId = sortByBestMatch(
|
||||||
getYTTracks(response.body().toString()),
|
getYTTracks(response.body().toString()),
|
||||||
trackName = it.name.toString(),
|
trackName = it.title,
|
||||||
trackArtists = artistsList,
|
trackArtists = it.artists,
|
||||||
trackDurationSec = (it.duration_ms/1000).toInt()
|
trackDurationSec = it.durationSec
|
||||||
).keys.firstOrNull()
|
).keys.firstOrNull()
|
||||||
Log.i("Spotify Helper Video ID",videoId ?: "Not Found")
|
Log.i("Spotify Helper Video ID",videoId ?: "Not Found")
|
||||||
|
|
||||||
if(videoId.isNullOrBlank()) {notFound++ ; updateStatusBar()}
|
if(videoId.isNullOrBlank()) {notFound++ ; updateStatusBar()}
|
||||||
else {//Found Youtube Video ID
|
else {//Found Youtube Video ID
|
||||||
val trackDetails = TrackDetails(
|
|
||||||
title = it.name.toString(),
|
|
||||||
artists = artistsList,
|
|
||||||
durationSec = (it.duration_ms/1000).toInt(),
|
|
||||||
albumArt = File(
|
|
||||||
Environment.getExternalStorageDirectory(),
|
|
||||||
defaultDir +".Images/" + (it.album?.images?.get(0)?.url.toString()).substringAfterLast('/') + ".jpeg"),
|
|
||||||
albumName = it.album?.name,
|
|
||||||
year = it.album?.release_date,
|
|
||||||
comment = "Genres:${it.album?.genres?.joinToString()}",
|
|
||||||
trackUrl = it.href,
|
|
||||||
source = Source.Spotify
|
|
||||||
)
|
|
||||||
|
|
||||||
val outputFile: String =
|
val outputFile: String =
|
||||||
Environment.getExternalStorageDirectory().toString() + File.separator +
|
Environment.getExternalStorageDirectory().toString() + File.separator +
|
||||||
defaultDir +
|
defaultDir +
|
||||||
removeIllegalChars(type) + File.separator +
|
removeIllegalChars(type) + File.separator +
|
||||||
(if (subFolder == null) { "" }
|
(if (subFolder == null) { "" }
|
||||||
else { removeIllegalChars(subFolder) + File.separator }
|
else { removeIllegalChars(subFolder) + File.separator }
|
||||||
+ removeIllegalChars(it.name!!) + ".m4a")
|
+ removeIllegalChars(it.title) + ".m4a")
|
||||||
|
|
||||||
val downloadObject = DownloadObject(
|
val downloadObject = DownloadObject(
|
||||||
trackDetails = trackDetails,
|
trackDetails = it,
|
||||||
ytVideoId = videoId,
|
ytVideoId = videoId,
|
||||||
outputFile = outputFile
|
outputFile = outputFile
|
||||||
)
|
)
|
||||||
@ -150,6 +141,13 @@ object SpotifyDownloadHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun resetStatusBar() {
|
||||||
|
total = 0
|
||||||
|
processed = 0
|
||||||
|
notFound = 0
|
||||||
|
updateStatusBar()
|
||||||
|
}
|
||||||
|
|
||||||
private fun animateStatusBar() {
|
private fun animateStatusBar() {
|
||||||
val anim: Animation = AlphaAnimation(0.3f, 0.9f)
|
val anim: Animation = AlphaAnimation(0.3f, 0.9f)
|
||||||
anim.duration = 1500 //You can manage the blinking time with this parameter
|
anim.duration = 1500 //You can manage the blinking time with this parameter
|
@ -24,7 +24,9 @@ import com.shabinder.spotiflyer.models.DownloadObject
|
|||||||
import com.shabinder.spotiflyer.models.TrackDetails
|
import com.shabinder.spotiflyer.models.TrackDetails
|
||||||
import com.shabinder.spotiflyer.utils.Provider.activity
|
import com.shabinder.spotiflyer.utils.Provider.activity
|
||||||
import com.shabinder.spotiflyer.utils.Provider.defaultDir
|
import com.shabinder.spotiflyer.utils.Provider.defaultDir
|
||||||
|
import com.shabinder.spotiflyer.utils.isOnline
|
||||||
import com.shabinder.spotiflyer.utils.removeIllegalChars
|
import com.shabinder.spotiflyer.utils.removeIllegalChars
|
||||||
|
import com.shabinder.spotiflyer.utils.showNoConnectionAlert
|
||||||
import com.shabinder.spotiflyer.utils.startService
|
import com.shabinder.spotiflyer.utils.startService
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -38,6 +40,10 @@ object YTDownloadHelper {
|
|||||||
){
|
){
|
||||||
val downloadList = ArrayList<DownloadObject>()
|
val downloadList = ArrayList<DownloadObject>()
|
||||||
tracks.forEach {
|
tracks.forEach {
|
||||||
|
if(!isOnline()){
|
||||||
|
showNoConnectionAlert()
|
||||||
|
return
|
||||||
|
}
|
||||||
val outputFile: String =
|
val outputFile: String =
|
||||||
Environment.getExternalStorageDirectory().toString() + File.separator +
|
Environment.getExternalStorageDirectory().toString() + File.separator +
|
||||||
defaultDir +
|
defaultDir +
|
||||||
|
@ -85,7 +85,7 @@ fun getYTTracks(response: String):List<YoutubeTrack>{
|
|||||||
! Songs details are ALWAYS in the following order:
|
! Songs details are ALWAYS in the following order:
|
||||||
! 0 - Name
|
! 0 - Name
|
||||||
! 1 - Type (Song)
|
! 1 - Type (Song)
|
||||||
! 2 - Artist
|
! 2 - com.shabinder.spotiflyer.models.gaana.Artist
|
||||||
! 3 - Album
|
! 3 - Album
|
||||||
! 4 - Duration (mm:ss)
|
! 4 - Duration (mm:ss)
|
||||||
!
|
!
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
package com.shabinder.spotiflyer.models
|
package com.shabinder.spotiflyer.models
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import com.shabinder.spotiflyer.models.spotify.Source
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@ -39,8 +40,8 @@ data class TrackDetails(
|
|||||||
var lyrics:String?=null,
|
var lyrics:String?=null,
|
||||||
var trackUrl:String?=null,
|
var trackUrl:String?=null,
|
||||||
var albumArt: File,
|
var albumArt: File,
|
||||||
var source:Source,
|
var source: Source,
|
||||||
var downloaded:DownloadStatus = DownloadStatus.NotDownloaded
|
var downloaded: DownloadStatus = DownloadStatus.NotDownloaded
|
||||||
):Parcelable
|
):Parcelable
|
||||||
|
|
||||||
enum class DownloadStatus{
|
enum class DownloadStatus{
|
||||||
|
14
app/src/main/java/com/shabinder/spotiflyer/models/YTTrack.kt → app/src/main/java/com/shabinder/spotiflyer/models/Optional.kt
Executable file → Normal file
14
app/src/main/java/com/shabinder/spotiflyer/models/YTTrack.kt → app/src/main/java/com/shabinder/spotiflyer/models/Optional.kt
Executable file → Normal file
@ -17,15 +17,7 @@
|
|||||||
|
|
||||||
package com.shabinder.spotiflyer.models
|
package com.shabinder.spotiflyer.models
|
||||||
|
|
||||||
import android.os.Parcelable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.android.parcel.Parcelize
|
|
||||||
|
|
||||||
@Parcelize
|
@Serializable
|
||||||
data class YTTrack(
|
data class Optional<T>(val value: T?)
|
||||||
var id:String?,
|
|
||||||
var title:String?,
|
|
||||||
var duration:Int?,
|
|
||||||
var author:String?,
|
|
||||||
var viewCount:Long?,
|
|
||||||
var thumbnails:List<String?>?
|
|
||||||
):Parcelable
|
|
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Shabinder Singh
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.shabinder.spotiflyer.models.gaana
|
||||||
|
|
||||||
|
data class Artist (
|
||||||
|
val popularity : Int,
|
||||||
|
val seokey : String,
|
||||||
|
val name : String,
|
||||||
|
)
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Shabinder Singh
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.shabinder.spotiflyer.models.gaana
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
|
||||||
|
data class CustomArtworks (
|
||||||
|
@Json(name = "40x40") val size_40p : String,
|
||||||
|
@Json(name = "80x80") val size_80p : String,
|
||||||
|
@Json(name = "110x110")val size_110p : String,
|
||||||
|
@Json(name = "175x175")val size_175p : String,
|
||||||
|
@Json(name = "480x480")val size_480p : String,
|
||||||
|
)
|
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Shabinder Singh
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.shabinder.spotiflyer.models.gaana
|
||||||
|
|
||||||
|
data class GaanaAlbum (
|
||||||
|
val tracks : List<Tracks>,
|
||||||
|
val count : Int,
|
||||||
|
val custom_artworks : CustomArtworks,
|
||||||
|
val release_year : Int,
|
||||||
|
val favorite_count : Int,
|
||||||
|
)
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Shabinder Singh
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.shabinder.spotiflyer.models.gaana
|
||||||
|
|
||||||
|
data class GaanaArtistDetails(
|
||||||
|
val artist : List<Artist>,
|
||||||
|
val count : Int,
|
||||||
|
)
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Shabinder Singh
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.shabinder.spotiflyer.models.gaana
|
||||||
|
|
||||||
|
data class GaanaArtistTracks(
|
||||||
|
val count : Int,
|
||||||
|
val tracks : List<Tracks>
|
||||||
|
)
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Shabinder Singh
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.shabinder.spotiflyer.models.gaana
|
||||||
|
|
||||||
|
data class GaanaPlaylist (
|
||||||
|
val tags : String,
|
||||||
|
val fromCache : Int,
|
||||||
|
val modified_on : String,
|
||||||
|
val count : Int,
|
||||||
|
val created_on : String,
|
||||||
|
val favorite_count : Int,
|
||||||
|
val tracks : List<Tracks>,
|
||||||
|
)
|
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Shabinder Singh
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.shabinder.spotiflyer.models.gaana
|
||||||
|
|
||||||
|
data class GaanaSong(
|
||||||
|
val tracks : List<Tracks>
|
||||||
|
)
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Shabinder Singh
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.shabinder.spotiflyer.models.gaana
|
||||||
|
|
||||||
|
data class Genre (
|
||||||
|
val genre_id : Int,
|
||||||
|
val name : String
|
||||||
|
)
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Shabinder Singh
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.shabinder.spotiflyer.models.gaana
|
||||||
|
|
||||||
|
data class Tags (
|
||||||
|
val tag_id : Int,
|
||||||
|
val tag_name : String
|
||||||
|
)
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Shabinder Singh
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.shabinder.spotiflyer.models.gaana
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
|
||||||
|
data class Tracks (
|
||||||
|
val tags : List<Tags>,
|
||||||
|
val seokey : String,
|
||||||
|
val albumseokey : String,
|
||||||
|
val track_title : String,
|
||||||
|
val album_title : String,
|
||||||
|
val language : String,
|
||||||
|
@Json(name = "artwork_large") val artworkLink : String,
|
||||||
|
val artist : List<Artist>,
|
||||||
|
@Json(name = "gener") val genre : List<Genre>,
|
||||||
|
val lyrics_url : String,
|
||||||
|
val youtube_id : String,
|
||||||
|
val total_favourite_count : Int,
|
||||||
|
val release_date : String,
|
||||||
|
val play_ct : String,
|
||||||
|
val secondary_language : String,
|
||||||
|
)
|
@ -15,7 +15,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.models
|
package com.shabinder.spotiflyer.models.spotify
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,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.models
|
package com.shabinder.spotiflyer.models.spotify
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,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.models
|
package com.shabinder.spotiflyer.models.spotify
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,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.models
|
package com.shabinder.spotiflyer.models.spotify
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,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.models
|
package com.shabinder.spotiflyer.models.spotify
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,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.models
|
package com.shabinder.spotiflyer.models.spotify
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,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.models
|
package com.shabinder.spotiflyer.models.spotify
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,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.models
|
package com.shabinder.spotiflyer.models.spotify
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,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.models
|
package com.shabinder.spotiflyer.models.spotify
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,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.models
|
package com.shabinder.spotiflyer.models.spotify
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
@ -15,7 +15,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.models
|
package com.shabinder.spotiflyer.models.spotify
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Shabinder Singh
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.shabinder.spotiflyer.models.spotify
|
||||||
|
|
||||||
|
enum class Source {
|
||||||
|
Spotify,
|
||||||
|
YouTube,
|
||||||
|
}
|
@ -15,7 +15,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.models
|
package com.shabinder.spotiflyer.models.spotify
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
@ -15,9 +15,10 @@
|
|||||||
* 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.models
|
package com.shabinder.spotiflyer.models.spotify
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
@ -39,5 +40,6 @@ data class Track(
|
|||||||
var album: Album? = null,
|
var album: Album? = null,
|
||||||
var external_ids: Map<String?, String?>? = null,
|
var external_ids: Map<String?, String?>? = null,
|
||||||
var popularity: Int? = null,
|
var popularity: Int? = null,
|
||||||
var downloaded:DownloadStatus? = DownloadStatus.NotDownloaded):Parcelable
|
var downloaded: DownloadStatus? = DownloadStatus.NotDownloaded
|
||||||
|
):Parcelable
|
||||||
|
|
@ -15,7 +15,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.models
|
package com.shabinder.spotiflyer.models.spotify
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,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.models
|
package com.shabinder.spotiflyer.models.spotify
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Shabinder Singh
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.shabinder.spotiflyer.networking
|
||||||
|
|
||||||
|
import com.shabinder.spotiflyer.models.Optional
|
||||||
|
import com.shabinder.spotiflyer.models.gaana.*
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.Query
|
||||||
|
|
||||||
|
const val gaana_token = "b2e6d7fbc136547a940516e9b77e5990"
|
||||||
|
|
||||||
|
interface GaanaInterface {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Api Request: http://api.gaana.com/?type=playlist&subtype=playlist_detail&seokey=gaana-dj-hindi-top-50-1&token=b2e6d7fbc136547a940516e9b77e5990&format=JSON
|
||||||
|
*
|
||||||
|
* subtype : ["most_popular_playlist" , "playlist_home_featured" ,"playlist_detail" ,"user_playlist" ,"topCharts"]
|
||||||
|
**/
|
||||||
|
@GET
|
||||||
|
suspend fun getGaanaPlaylist(
|
||||||
|
@Query("type") type: String = "playlist",
|
||||||
|
@Query("subtype") subtype: String = "playlist_detail",
|
||||||
|
@Query("seokey") seokey: String,
|
||||||
|
@Query("token") token: String = gaana_token,
|
||||||
|
@Query("format") format: String = "JSON",
|
||||||
|
@Query("limit") limit: Int = 2000
|
||||||
|
): Optional<GaanaPlaylist>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Api Request: http://api.gaana.com/?type=album&subtype=album_detail&seokey=kabir-singh&token=b2e6d7fbc136547a940516e9b77e5990&format=JSON
|
||||||
|
*
|
||||||
|
* subtype : ["most_popular" , "new_release" ,"featured_album" ,"similar_album" ,"all_albums", "album" ,"album_detail" ,"album_detail_info"]
|
||||||
|
**/
|
||||||
|
@GET
|
||||||
|
suspend fun getGaanaAlbum(
|
||||||
|
@Query("type") type: String = "album",
|
||||||
|
@Query("subtype") subtype: String = "album_detail",
|
||||||
|
@Query("seokey") seokey: String,
|
||||||
|
@Query("token") token: String = gaana_token,
|
||||||
|
@Query("format") format: String = "JSON",
|
||||||
|
@Query("limit") limit: Int = 2000
|
||||||
|
): Optional<GaanaAlbum>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Api Request: http://api.gaana.com/?type=song&subtype=song_detail&seokey=pachtaoge&token=b2e6d7fbc136547a940516e9b77e5990&format=JSON
|
||||||
|
*
|
||||||
|
* subtype : ["most_popular" , "hot_songs" ,"recommendation" ,"song_detail"]
|
||||||
|
**/
|
||||||
|
@GET
|
||||||
|
suspend fun getGaanaSong(
|
||||||
|
@Query("type") type: String = "song",
|
||||||
|
@Query("subtype") subtype: String = "song_detail",
|
||||||
|
@Query("seokey") seokey: String,
|
||||||
|
@Query("token") token: String = gaana_token,
|
||||||
|
@Query("format") format: String = "JSON",
|
||||||
|
): Optional<GaanaSong>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Api Request: https://api.gaana.com/?type=artist&subtype=artist_details_info&seokey=neha-kakkar&token=b2e6d7fbc136547a940516e9b77e5990&format=JSON
|
||||||
|
*
|
||||||
|
* subtype : ["most_popular" , "artist_list" ,"artist_track_listing" ,"artist_album" ,"similar_artist","artist_details" ,"artist_details_info"]
|
||||||
|
**/
|
||||||
|
@GET
|
||||||
|
suspend fun getGaanaArtistDetails(
|
||||||
|
@Query("type") type: String = "artist",
|
||||||
|
@Query("subtype") subtype: String = "artist_details_info",
|
||||||
|
@Query("seokey") seokey: String,
|
||||||
|
@Query("token") token: String = gaana_token,
|
||||||
|
@Query("format") format: String = "JSON",
|
||||||
|
): Optional<GaanaArtistDetails>
|
||||||
|
/*
|
||||||
|
* Api Request: http://api.gaana.com/?type=artist&subtype=artist_track_listing&seokey=neha-kakkar&limit=50&token=b2e6d7fbc136547a940516e9b77e5990&format=JSON
|
||||||
|
*
|
||||||
|
* subtype : ["most_popular" , "artist_list" ,"artist_track_listing" ,"artist_album" ,"similar_artist","artist_details" ,"artist_details_info"]
|
||||||
|
**/
|
||||||
|
@GET
|
||||||
|
suspend fun getGaanaArtistTracks(
|
||||||
|
@Query("type") type: String = "artist",
|
||||||
|
@Query("subtype") subtype: String = "artist_track_listing",
|
||||||
|
@Query("seokey") seokey: String,
|
||||||
|
@Query("token") token: String = gaana_token,
|
||||||
|
@Query("format") format: String = "JSON",
|
||||||
|
@Query("limit") limit: Int = 50
|
||||||
|
): Optional<GaanaArtistTracks>
|
||||||
|
|
||||||
|
}
|
@ -15,58 +15,41 @@
|
|||||||
* 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.utils
|
package com.shabinder.spotiflyer.networking
|
||||||
|
|
||||||
import com.shabinder.spotiflyer.models.*
|
import com.shabinder.spotiflyer.models.Optional
|
||||||
|
import com.shabinder.spotiflyer.models.spotify.*
|
||||||
import retrofit2.http.*
|
import retrofit2.http.*
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2020 Shabinder Singh
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
interface SpotifyService {
|
interface SpotifyService {
|
||||||
|
|
||||||
@GET("playlists/{playlist_id}")
|
@GET("playlists/{playlist_id}")
|
||||||
suspend fun getPlaylist(@Path("playlist_id") playlistId: String?): Playlist
|
suspend fun getPlaylist(@Path("playlist_id") playlistId: String?): Optional<Playlist>
|
||||||
|
|
||||||
@GET("playlists/{playlist_id}/tracks")
|
@GET("playlists/{playlist_id}/tracks")
|
||||||
suspend fun getPlaylistTracks(
|
suspend fun getPlaylistTracks(
|
||||||
@Path("playlist_id") playlistId: String?,
|
@Path("playlist_id") playlistId: String?,
|
||||||
@Query("offset") offset: Int = 0,
|
@Query("offset") offset: Int = 0,
|
||||||
@Query("limit") limit: Int = 100
|
@Query("limit") limit: Int = 100
|
||||||
): PagingObjectPlaylistTrack
|
): Optional<PagingObjectPlaylistTrack>
|
||||||
|
|
||||||
@GET("tracks/{id}")
|
@GET("tracks/{id}")
|
||||||
suspend fun getTrack(@Path("id") trackId: String?): Track
|
suspend fun getTrack(@Path("id") trackId: String?): Optional<Track>
|
||||||
|
|
||||||
@GET("episodes/{id}")
|
@GET("episodes/{id}")
|
||||||
suspend fun getEpisode(@Path("id") episodeId: String?): Track
|
suspend fun getEpisode(@Path("id") episodeId: String?): Optional<Track>
|
||||||
|
|
||||||
@GET("shows/{id}")
|
@GET("shows/{id}")
|
||||||
suspend fun getShow(@Path("id") showId: String?): Track
|
suspend fun getShow(@Path("id") showId: String?): Optional<Track>
|
||||||
|
|
||||||
@GET("albums/{id}")
|
@GET("albums/{id}")
|
||||||
suspend fun getAlbum(@Path("id") albumId: String?): Album
|
suspend fun getAlbum(@Path("id") albumId: String?): Optional<Album>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SpotifyServiceTokenRequest{
|
interface SpotifyServiceTokenRequest{
|
||||||
|
|
||||||
@POST("api/token")
|
@POST("api/token")
|
||||||
@FormUrlEncoded
|
@FormUrlEncoded
|
||||||
suspend fun getToken(@Field("grant_type") grant_type:String = "client_credentials"):Token?
|
suspend fun getToken(@Field("grant_type") grant_type:String = "client_credentials"): Optional<Token>
|
||||||
|
|
||||||
}
|
}
|
@ -15,7 +15,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.utils
|
package com.shabinder.spotiflyer.networking
|
||||||
|
|
||||||
import com.beust.klaxon.JsonObject
|
import com.beust.klaxon.JsonObject
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
@ -25,21 +25,12 @@ import retrofit2.http.POST
|
|||||||
|
|
||||||
|
|
||||||
const val apiKey = "AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30"
|
const val apiKey = "AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30"
|
||||||
/*val body = """{
|
|
||||||
"context": {
|
|
||||||
"client": {
|
|
||||||
"clientName": "WEB_REMIX",
|
|
||||||
"clientVersion": "0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"query": "songSearchQuery"
|
|
||||||
}"""*/
|
|
||||||
interface YoutubeMusicApi {
|
interface YoutubeMusicApi {
|
||||||
|
|
||||||
@Headers("Content-Type: application/json", "Referer: https://music.youtube.com/search")
|
@Headers("Content-Type: application/json", "Referer: https://music.youtube.com/search")
|
||||||
@POST("search?alt=json&key=$apiKey")
|
@POST("search?alt=json&key=$apiKey")
|
||||||
fun getYoutubeMusicResponse(@Body text: JsonObject): Call<String>
|
fun getYoutubeMusicResponse(@Body text: String): Call<String>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun makeJsonBody(query: String):JsonObject{
|
fun makeJsonBody(query: String):JsonObject{
|
@ -18,6 +18,7 @@
|
|||||||
package com.shabinder.spotiflyer.recyclerView
|
package com.shabinder.spotiflyer.recyclerView
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.os.Environment
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@ -27,23 +28,21 @@ import androidx.recyclerview.widget.ListAdapter
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.shabinder.spotiflyer.R
|
import com.shabinder.spotiflyer.R
|
||||||
import com.shabinder.spotiflyer.databinding.TrackListItemBinding
|
import com.shabinder.spotiflyer.databinding.TrackListItemBinding
|
||||||
import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper.downloadAllTracks
|
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper.downloadAllTracks
|
||||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||||
import com.shabinder.spotiflyer.models.Source
|
import com.shabinder.spotiflyer.models.TrackDetails
|
||||||
import com.shabinder.spotiflyer.models.Track
|
import com.shabinder.spotiflyer.models.spotify.Source
|
||||||
|
import com.shabinder.spotiflyer.models.spotify.Track
|
||||||
import com.shabinder.spotiflyer.ui.spotify.SpotifyViewModel
|
import com.shabinder.spotiflyer.ui.spotify.SpotifyViewModel
|
||||||
|
import com.shabinder.spotiflyer.utils.*
|
||||||
import com.shabinder.spotiflyer.utils.Provider.activity
|
import com.shabinder.spotiflyer.utils.Provider.activity
|
||||||
import com.shabinder.spotiflyer.utils.bindImage
|
|
||||||
import com.shabinder.spotiflyer.utils.rotateAnim
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class SpotifyTrackListAdapter(private val spotifyViewModel : SpotifyViewModel): ListAdapter<Track,SpotifyTrackListAdapter.ViewHolder>(SpotifyTrackDiffCallback()) {
|
||||||
|
|
||||||
class SpotifyTrackListAdapter: ListAdapter<Track,SpotifyTrackListAdapter.ViewHolder>(SpotifyTrackDiffCallback()) {
|
|
||||||
|
|
||||||
var spotifyViewModel : SpotifyViewModel? = null
|
|
||||||
var isAlbum:Boolean = false
|
var isAlbum:Boolean = false
|
||||||
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
val layoutInflater = LayoutInflater.from(parent.context)
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
val binding = TrackListItemBinding.inflate(layoutInflater,parent,false)
|
val binding = TrackListItemBinding.inflate(layoutInflater,parent,false)
|
||||||
@ -55,9 +54,9 @@ class SpotifyTrackListAdapter: ListAdapter<Track,SpotifyTrackListAdapter.ViewHol
|
|||||||
val item = getItem(position)
|
val item = getItem(position)
|
||||||
if(itemCount ==1 || isAlbum){
|
if(itemCount ==1 || isAlbum){
|
||||||
holder.binding.imageUrl.visibility = View.GONE}else{
|
holder.binding.imageUrl.visibility = View.GONE}else{
|
||||||
spotifyViewModel!!.uiScope.launch {
|
spotifyViewModel.uiScope.launch {
|
||||||
//Placeholder Set
|
//Placeholder Set
|
||||||
bindImage(holder.binding.imageUrl, item.album?.images?.get(0)?.url,Source.Spotify)
|
bindImage(holder.binding.imageUrl, item.album?.images?.get(0)?.url, Source.Spotify)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,14 +76,35 @@ class SpotifyTrackListAdapter: ListAdapter<Track,SpotifyTrackListAdapter.ViewHol
|
|||||||
holder.binding.btnDownload.setImageResource(R.drawable.ic_arrow)
|
holder.binding.btnDownload.setImageResource(R.drawable.ic_arrow)
|
||||||
holder.binding.btnDownload.clearAnimation()
|
holder.binding.btnDownload.clearAnimation()
|
||||||
holder.binding.btnDownload.setOnClickListener{
|
holder.binding.btnDownload.setOnClickListener{
|
||||||
|
if(!isOnline()){
|
||||||
|
showNoConnectionAlert()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
Toast.makeText(activity,"Processing!",Toast.LENGTH_SHORT).show()
|
Toast.makeText(activity,"Processing!",Toast.LENGTH_SHORT).show()
|
||||||
holder.binding.btnDownload.setImageResource(R.drawable.ic_refresh)
|
holder.binding.btnDownload.setImageResource(R.drawable.ic_refresh)
|
||||||
rotateAnim(it)
|
rotateAnim(it)
|
||||||
item.downloaded = DownloadStatus.Downloading
|
item.downloaded = DownloadStatus.Downloading
|
||||||
spotifyViewModel!!.uiScope.launch {
|
spotifyViewModel.uiScope.launch {
|
||||||
val itemList = mutableListOf<Track>()
|
val itemList = mutableListOf<TrackDetails>()
|
||||||
itemList.add(item)
|
itemList.add(item.let { track ->
|
||||||
downloadAllTracks(spotifyViewModel!!.folderType,spotifyViewModel!!.subFolder,itemList)
|
val artistsList = mutableListOf<String>()
|
||||||
|
track.artists?.forEach { artist -> artistsList.add(artist!!.name!!) }
|
||||||
|
TrackDetails(
|
||||||
|
title = track.name.toString(),
|
||||||
|
artists = artistsList,
|
||||||
|
durationSec = (track.duration_ms/1000).toInt(),
|
||||||
|
albumArt = File(
|
||||||
|
Environment.getExternalStorageDirectory(),
|
||||||
|
Provider.defaultDir +".Images/" + (track.album?.images?.get(0)?.url.toString()).substringAfterLast('/') + ".jpeg"),
|
||||||
|
albumName = track.album?.name,
|
||||||
|
year = track.album?.release_date,
|
||||||
|
comment = "Genres:${track.album?.genres?.joinToString()}",
|
||||||
|
trackUrl = track.href,
|
||||||
|
source = Source.Spotify
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
downloadAllTracks(spotifyViewModel.folderType,spotifyViewModel.subFolder,itemList)
|
||||||
}
|
}
|
||||||
notifyItemChanged(position)//start showing anim!
|
notifyItemChanged(position)//start showing anim!
|
||||||
}
|
}
|
||||||
|
@ -20,19 +20,16 @@ package com.shabinder.spotiflyer.recyclerView
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import com.shabinder.spotiflyer.R
|
import com.shabinder.spotiflyer.R
|
||||||
import com.shabinder.spotiflyer.databinding.TrackListItemBinding
|
import com.shabinder.spotiflyer.databinding.TrackListItemBinding
|
||||||
import com.shabinder.spotiflyer.downloadHelper.YTDownloadHelper
|
import com.shabinder.spotiflyer.downloadHelper.YTDownloadHelper
|
||||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||||
import com.shabinder.spotiflyer.models.Source
|
|
||||||
import com.shabinder.spotiflyer.models.TrackDetails
|
import com.shabinder.spotiflyer.models.TrackDetails
|
||||||
|
import com.shabinder.spotiflyer.models.spotify.Source
|
||||||
import com.shabinder.spotiflyer.ui.youtube.YoutubeViewModel
|
import com.shabinder.spotiflyer.ui.youtube.YoutubeViewModel
|
||||||
import com.shabinder.spotiflyer.utils.Provider
|
import com.shabinder.spotiflyer.utils.*
|
||||||
import com.shabinder.spotiflyer.utils.bindImage
|
|
||||||
import com.shabinder.spotiflyer.utils.rotateAnim
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class YoutubeTrackListAdapter(private val youtubeViewModel :YoutubeViewModel): ListAdapter<TrackDetails,SpotifyTrackListAdapter.ViewHolder>(YouTubeTrackDiffCallback()) {
|
class YoutubeTrackListAdapter(private val youtubeViewModel :YoutubeViewModel): ListAdapter<TrackDetails,SpotifyTrackListAdapter.ViewHolder>(YouTubeTrackDiffCallback()) {
|
||||||
@ -74,7 +71,11 @@ class YoutubeTrackListAdapter(private val youtubeViewModel :YoutubeViewModel): L
|
|||||||
holder.binding.btnDownload.setImageResource(R.drawable.ic_arrow)
|
holder.binding.btnDownload.setImageResource(R.drawable.ic_arrow)
|
||||||
holder.binding.btnDownload.clearAnimation()
|
holder.binding.btnDownload.clearAnimation()
|
||||||
holder.binding.btnDownload.setOnClickListener{
|
holder.binding.btnDownload.setOnClickListener{
|
||||||
Toast.makeText(Provider.activity,"Processing!", Toast.LENGTH_SHORT).show()
|
if(!isOnline()){
|
||||||
|
showNoConnectionAlert()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
showMessage("Processing!")
|
||||||
holder.binding.btnDownload.setImageResource(R.drawable.ic_refresh)
|
holder.binding.btnDownload.setImageResource(R.drawable.ic_refresh)
|
||||||
rotateAnim(it)
|
rotateAnim(it)
|
||||||
item.downloaded = DownloadStatus.Downloading
|
item.downloaded = DownloadStatus.Downloading
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
D/Retrofit: <--- HTTP 200 https://api.spotify.com/v1/me/top/artists (7170ms)
|
D/Retrofit: <--- HTTP 200 https://api.spotify.com/v1/me/top/artists (7170ms)
|
||||||
2020-07-17 18:24:00.718 25414-25414/com.shabinder.musicforeveryone I/Network: [kaaes.spotify.webapi.android.models.Artist@4fae9ec, kaaes.spotify.webapi.android.models.Artist@aa3b1b5, kaaes.spotify.webapi.android.models.Artist@ed6004a, kaaes.spotify.webapi.android.models.Artist@870dbbb, kaaes.spotify.webapi.android.models.Artist@8a2b8d8, kaaes.spotify.webapi.android.models.Artist@aab431, kaaes.spotify.webapi.android.models.Artist@a7bd716, kaaes.spotify.webapi.android.models.Artist@3477897, kaaes.spotify.webapi.android.models.Artist@7f68a84]
|
2020-07-17 18:24:00.718 25414-25414/com.shabinder.musicforeveryone I/Network: [kaaes.spotify.webapi.android.models.com.shabinder.spotiflyer.models.gaana.Artist@4fae9ec, kaaes.spotify.webapi.android.models.com.shabinder.spotiflyer.models.gaana.Artist@aa3b1b5, kaaes.spotify.webapi.android.models.com.shabinder.spotiflyer.models.gaana.Artist@ed6004a, kaaes.spotify.webapi.android.models.com.shabinder.spotiflyer.models.gaana.Artist@870dbbb, kaaes.spotify.webapi.android.models.com.shabinder.spotiflyer.models.gaana.Artist@8a2b8d8, kaaes.spotify.webapi.android.models.com.shabinder.spotiflyer.models.gaana.Artist@aab431, kaaes.spotify.webapi.android.models.com.shabinder.spotiflyer.models.gaana.Artist@a7bd716, kaaes.spotify.webapi.android.models.com.shabinder.spotiflyer.models.gaana.Artist@3477897, kaaes.spotify.webapi.android.models.com.shabinder.spotiflyer.models.gaana.Artist@7f68a84]
|
||||||
|
|
||||||
|
|
||||||
I/Network: https://api.spotify.com/v1/artists/7vk5e3vY1uw9plTHJAMwjN
|
I/Network: https://api.spotify.com/v1/artists/7vk5e3vY1uw9plTHJAMwjN
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Shabinder Singh
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.shabinder.spotiflyer.ui.gaana
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.databinding.DataBindingUtil
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.shabinder.spotiflyer.R
|
||||||
|
import com.shabinder.spotiflyer.SharedViewModel
|
||||||
|
import com.shabinder.spotiflyer.databinding.TrackListFragmentBinding
|
||||||
|
import com.shabinder.spotiflyer.networking.GaanaInterface
|
||||||
|
import com.shabinder.spotiflyer.networking.YoutubeMusicApi
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class GaanaFragment : Fragment() {
|
||||||
|
|
||||||
|
private lateinit var binding: TrackListFragmentBinding
|
||||||
|
private lateinit var sharedViewModel: SharedViewModel
|
||||||
|
@Inject lateinit var youtubeMusicApi: YoutubeMusicApi
|
||||||
|
private lateinit var viewModel: GaanaViewModel
|
||||||
|
@Inject lateinit var gaanaInterface: GaanaInterface
|
||||||
|
private var intentFilter: IntentFilter? = null
|
||||||
|
private var updateUIReceiver: BroadcastReceiver? = null
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
binding = DataBindingUtil.inflate(inflater,R.layout.track_list_fragment, container, false)
|
||||||
|
viewModel = ViewModelProvider(this).get(GaanaViewModel::class.java)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Shabinder Singh
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.shabinder.spotiflyer.ui.gaana
|
||||||
|
|
||||||
|
import androidx.hilt.lifecycle.ViewModelInject
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.shabinder.spotiflyer.database.DatabaseDAO
|
||||||
|
|
||||||
|
class GaanaViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) : ViewModel()
|
@ -25,14 +25,18 @@ import android.text.SpannableStringBuilder
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.databinding.DataBindingUtil
|
import androidx.databinding.DataBindingUtil
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import com.shabinder.spotiflyer.MainActivity
|
||||||
import com.shabinder.spotiflyer.R
|
import com.shabinder.spotiflyer.R
|
||||||
import com.shabinder.spotiflyer.SharedViewModel
|
import com.shabinder.spotiflyer.SharedViewModel
|
||||||
import com.shabinder.spotiflyer.databinding.MainFragmentBinding
|
import com.shabinder.spotiflyer.databinding.MainFragmentBinding
|
||||||
|
import com.shabinder.spotiflyer.utils.Provider
|
||||||
|
import com.shabinder.spotiflyer.utils.isOnline
|
||||||
|
import com.shabinder.spotiflyer.utils.showMessage
|
||||||
|
import com.shabinder.spotiflyer.utils.showNoConnectionAlert
|
||||||
import com.shreyaspatil.easyupipayment.EasyUpiPayment
|
import com.shreyaspatil.easyupipayment.EasyUpiPayment
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -55,14 +59,20 @@ class MainFragment : Fragment() {
|
|||||||
): View? {
|
): View? {
|
||||||
binding = DataBindingUtil.inflate(inflater,R.layout.main_fragment,container,false)
|
binding = DataBindingUtil.inflate(inflater,R.layout.main_fragment,container,false)
|
||||||
initializeAll()
|
initializeAll()
|
||||||
|
|
||||||
binding.btnSearch.setOnClickListener {
|
binding.btnSearch.setOnClickListener {
|
||||||
|
if(!isOnline()){
|
||||||
|
showNoConnectionAlert()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
val link = binding.linkSearch.text.toString()
|
val link = binding.linkSearch.text.toString()
|
||||||
if (link.contains("spotify",true)){
|
if (link.contains("spotify",true)){
|
||||||
|
if(sharedViewModel.spotifyService.value == null){//Authentication pending!!
|
||||||
|
(activity as MainActivity).authenticateSpotify()
|
||||||
|
}
|
||||||
findNavController().navigate(MainFragmentDirections.actionMainFragmentToSpotifyFragment(link))
|
findNavController().navigate(MainFragmentDirections.actionMainFragmentToSpotifyFragment(link))
|
||||||
}else if(link.contains("youtube.com",true) || link.contains("youtu.be",true) ){
|
}else if(link.contains("youtube.com",true) || link.contains("youtu.be",true) ){
|
||||||
findNavController().navigate(MainFragmentDirections.actionMainFragmentToYoutubeFragment(link))
|
findNavController().navigate(MainFragmentDirections.actionMainFragmentToYoutubeFragment(link))
|
||||||
}else{Toast.makeText(context,"Link is Not Valid",Toast.LENGTH_SHORT).show()}
|
}else showMessage("Link is Not Valid",true)
|
||||||
}
|
}
|
||||||
handleIntent()
|
handleIntent()
|
||||||
return binding.root
|
return binding.root
|
||||||
@ -97,10 +107,15 @@ class MainFragment : Fragment() {
|
|||||||
sharedViewModel.intentString.observe(viewLifecycleOwner,{
|
sharedViewModel.intentString.observe(viewLifecycleOwner,{
|
||||||
if(it != ""){
|
if(it != ""){
|
||||||
sharedViewModel.uiScope.launch(Dispatchers.IO) {
|
sharedViewModel.uiScope.launch(Dispatchers.IO) {
|
||||||
while (sharedViewModel.accessToken.value == "") {
|
if(sharedViewModel.spotifyService.value == null){
|
||||||
|
//Not Authenticated Yet
|
||||||
|
Provider.activity.authenticateSpotify()
|
||||||
|
while (sharedViewModel.spotifyService.value == null) {
|
||||||
//Waiting for Authentication to Finish
|
//Waiting for Authentication to Finish
|
||||||
Thread.sleep(1000)
|
Thread.sleep(1000)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
binding.linkSearch.setText(sharedViewModel.intentString.value)
|
binding.linkSearch.setText(sharedViewModel.intentString.value)
|
||||||
binding.btnSearch.performClick()
|
binding.btnSearch.performClick()
|
||||||
|
@ -22,46 +22,43 @@ import android.content.BroadcastReceiver
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import android.net.ConnectivityManager
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.Environment
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.databinding.DataBindingUtil
|
import androidx.databinding.DataBindingUtil
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||||
import com.shabinder.spotiflyer.MainActivity
|
import com.shabinder.spotiflyer.MainActivity
|
||||||
import com.shabinder.spotiflyer.R
|
import com.shabinder.spotiflyer.R
|
||||||
import com.shabinder.spotiflyer.SharedViewModel
|
import com.shabinder.spotiflyer.SharedViewModel
|
||||||
import com.shabinder.spotiflyer.databinding.SpotifyFragmentBinding
|
import com.shabinder.spotiflyer.databinding.TrackListFragmentBinding
|
||||||
import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper
|
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper
|
||||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||||
import com.shabinder.spotiflyer.models.Source
|
|
||||||
import com.shabinder.spotiflyer.models.Track
|
|
||||||
import com.shabinder.spotiflyer.models.TrackDetails
|
import com.shabinder.spotiflyer.models.TrackDetails
|
||||||
|
import com.shabinder.spotiflyer.models.spotify.Source
|
||||||
|
import com.shabinder.spotiflyer.networking.YoutubeMusicApi
|
||||||
import com.shabinder.spotiflyer.recyclerView.SpotifyTrackListAdapter
|
import com.shabinder.spotiflyer.recyclerView.SpotifyTrackListAdapter
|
||||||
import com.shabinder.spotiflyer.utils.YoutubeMusicApi
|
import com.shabinder.spotiflyer.utils.*
|
||||||
import com.shabinder.spotiflyer.utils.bindImage
|
import com.shabinder.spotiflyer.utils.Provider.defaultDir
|
||||||
import com.shabinder.spotiflyer.utils.loadAllImages
|
|
||||||
import com.shabinder.spotiflyer.utils.rotateAnim
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.File
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class SpotifyFragment : Fragment() {
|
class SpotifyFragment : Fragment() {
|
||||||
private lateinit var binding:SpotifyFragmentBinding
|
private lateinit var binding:TrackListFragmentBinding
|
||||||
private lateinit var spotifyViewModel: SpotifyViewModel
|
|
||||||
private lateinit var sharedViewModel: SharedViewModel
|
private lateinit var sharedViewModel: SharedViewModel
|
||||||
private lateinit var adapterSpotify:SpotifyTrackListAdapter
|
|
||||||
@Inject lateinit var youtubeMusicApi: YoutubeMusicApi
|
@Inject lateinit var youtubeMusicApi: YoutubeMusicApi
|
||||||
|
private lateinit var viewModel: SpotifyViewModel
|
||||||
|
private lateinit var adapter:SpotifyTrackListAdapter
|
||||||
private var intentFilter:IntentFilter? = null
|
private var intentFilter:IntentFilter? = null
|
||||||
private var updateUIReceiver: BroadcastReceiver? = null
|
private var updateUIReceiver: BroadcastReceiver? = null
|
||||||
|
|
||||||
@ -71,8 +68,7 @@ class SpotifyFragment : Fragment() {
|
|||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = DataBindingUtil.inflate(inflater,R.layout.spotify_fragment,container,false)
|
binding = DataBindingUtil.inflate(inflater,R.layout.track_list_fragment,container,false)
|
||||||
adapterSpotify = SpotifyTrackListAdapter()
|
|
||||||
initializeAll()
|
initializeAll()
|
||||||
initializeLiveDataObservers()
|
initializeLiveDataObservers()
|
||||||
initializeBroadcast()
|
initializeBroadcast()
|
||||||
@ -88,34 +84,35 @@ class SpotifyFragment : Fragment() {
|
|||||||
if(sharedViewModel.spotifyService.value == null){//Authentication pending!!
|
if(sharedViewModel.spotifyService.value == null){//Authentication pending!!
|
||||||
(activity as MainActivity).authenticateSpotify()
|
(activity as MainActivity).authenticateSpotify()
|
||||||
}
|
}
|
||||||
if(!isOnline()){//Device Offline
|
if (type == "Error" || link == "Error") {//Incorrect Link
|
||||||
sharedViewModel.showAlertDialog(resources,requireContext())
|
showMessage("Please Check Your Link!")
|
||||||
}else if (type == "Error" || link == "Error") {//Incorrect Link
|
|
||||||
showToast("Please Check Your Link!")
|
|
||||||
}else if(spotifyLink.contains("open.spotify",true)){//Link Validation!!
|
}else if(spotifyLink.contains("open.spotify",true)){//Link Validation!!
|
||||||
if(type == "episode" || type == "show"){//TODO Implementation
|
if(type == "episode" || type == "show"){//TODO Implementation
|
||||||
showToast("Implementing Soon, Stay Tuned!")
|
showMessage("Implementing Soon, Stay Tuned!")
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
spotifyViewModel.spotifySearch(type,link)
|
viewModel.spotifySearch(type,link)
|
||||||
if(type=="album")adapterSpotify.isAlbum = true
|
if(type=="album")adapter.isAlbum = true
|
||||||
|
|
||||||
binding.btnDownloadAll.setOnClickListener {
|
binding.btnDownloadAll.setOnClickListener {
|
||||||
|
if(!isOnline()){
|
||||||
|
showNoConnectionAlert()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
binding.btnDownloadAll.visibility = View.GONE
|
binding.btnDownloadAll.visibility = View.GONE
|
||||||
binding.downloadingFab.visibility = View.VISIBLE
|
binding.downloadingFab.visibility = View.VISIBLE
|
||||||
|
|
||||||
rotateAnim(binding.downloadingFab)
|
rotateAnim(binding.downloadingFab)
|
||||||
for (track in spotifyViewModel.trackList.value!!){
|
for (track in viewModel.trackList.value!!){
|
||||||
if(track.downloaded != DownloadStatus.Downloaded){
|
if(track.downloaded != DownloadStatus.Downloaded){
|
||||||
track.downloaded = DownloadStatus.Downloading
|
track.downloaded = DownloadStatus.Downloading
|
||||||
adapterSpotify.notifyItemChanged(spotifyViewModel.trackList.value!!.indexOf(track))
|
adapter.notifyItemChanged(viewModel.trackList.value!!.indexOf(track))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
showToast("Processing!")
|
showMessage("Processing!")
|
||||||
sharedViewModel.uiScope.launch(Dispatchers.Default){
|
sharedViewModel.uiScope.launch(Dispatchers.Default){
|
||||||
val urlList = arrayListOf<String>()
|
val urlList = arrayListOf<String>()
|
||||||
spotifyViewModel.trackList.value?.forEach { urlList.add(it.album?.images?.get(0)?.url.toString()) }
|
viewModel.trackList.value?.forEach { urlList.add(it.album?.images?.get(0)?.url.toString()) }
|
||||||
//Appending Source
|
//Appending Source
|
||||||
urlList.add("spotify")
|
urlList.add("spotify")
|
||||||
loadAllImages(
|
loadAllImages(
|
||||||
@ -123,11 +120,29 @@ class SpotifyFragment : Fragment() {
|
|||||||
urlList
|
urlList
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
spotifyViewModel.uiScope.launch {
|
viewModel.uiScope.launch {
|
||||||
SpotifyDownloadHelper.downloadAllTracks(
|
val finalList = viewModel.trackList.value?.map{
|
||||||
spotifyViewModel.folderType,
|
val artistsList = mutableListOf<String>()
|
||||||
spotifyViewModel.subFolder,
|
it.artists?.forEach { artist -> artistsList.add(artist!!.name!!) }
|
||||||
spotifyViewModel.trackList.value!!,
|
TrackDetails(
|
||||||
|
title = it.name.toString(),
|
||||||
|
artists = artistsList,
|
||||||
|
durationSec = (it.duration_ms/1000).toInt(),
|
||||||
|
albumArt = File(
|
||||||
|
Environment.getExternalStorageDirectory(),
|
||||||
|
defaultDir +".Images/" + (it.album?.images?.get(0)?.url.toString()).substringAfterLast('/') + ".jpeg"),
|
||||||
|
albumName = it.album?.name,
|
||||||
|
year = it.album?.release_date,
|
||||||
|
comment = "Genres:${it.album?.genres?.joinToString()}",
|
||||||
|
trackUrl = it.href,
|
||||||
|
source = Source.Spotify
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if(finalList.isNullOrEmpty())showMessage("Not Downloading Any Song")
|
||||||
|
DownloadHelper.downloadAllTracks(
|
||||||
|
viewModel.folderType,
|
||||||
|
viewModel.subFolder,
|
||||||
|
finalList ?: listOf(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,43 +151,23 @@ class SpotifyFragment : Fragment() {
|
|||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
/**
|
||||||
super.onResume()
|
* Basic Initialization
|
||||||
initializeBroadcast()
|
**/
|
||||||
|
private fun initializeAll() {
|
||||||
|
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
|
||||||
|
viewModel = ViewModelProvider(this).get(SpotifyViewModel::class.java)
|
||||||
|
sharedViewModel.spotifyService.observe(viewLifecycleOwner, {
|
||||||
|
viewModel.spotifyService = it
|
||||||
|
})
|
||||||
|
adapter = SpotifyTrackListAdapter(viewModel)
|
||||||
|
DownloadHelper.youtubeMusicApi = youtubeMusicApi
|
||||||
|
DownloadHelper.sharedViewModel = sharedViewModel
|
||||||
|
DownloadHelper.statusBar = binding.statusBar
|
||||||
|
binding.trackList.adapter = adapter
|
||||||
|
(binding.trackList.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initializeBroadcast() {
|
|
||||||
intentFilter = IntentFilter()
|
|
||||||
intentFilter?.addAction("track_download_completed")
|
|
||||||
|
|
||||||
updateUIReceiver = object : BroadcastReceiver() {
|
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
|
||||||
//UI update here
|
|
||||||
if (intent != null){
|
|
||||||
val trackDetails = intent.getParcelableExtra<TrackDetails?>("track")
|
|
||||||
trackDetails?.let {
|
|
||||||
val position: Int = spotifyViewModel.trackList.value?.map { it.name }?.indexOf(trackDetails.title) ?: -1
|
|
||||||
Log.i("Track","Download Completed Intent :$position")
|
|
||||||
if(position != -1) {
|
|
||||||
val track = spotifyViewModel.trackList.value?.get(position)
|
|
||||||
track?.let{
|
|
||||||
it.downloaded = DownloadStatus.Downloaded
|
|
||||||
spotifyViewModel.trackList.value?.set(position, it)
|
|
||||||
adapterSpotify.notifyItemChanged(position)
|
|
||||||
checkIfAllDownloaded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
requireActivity().registerReceiver(updateUIReceiver, intentFilter)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
super.onPause()
|
|
||||||
requireActivity().unregisterReceiver(updateUIReceiver)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*Live Data Observers
|
*Live Data Observers
|
||||||
@ -181,17 +176,17 @@ class SpotifyFragment : Fragment() {
|
|||||||
/**
|
/**
|
||||||
* CoverUrl Binding Observer!
|
* CoverUrl Binding Observer!
|
||||||
**/
|
**/
|
||||||
spotifyViewModel.coverUrl.observe(viewLifecycleOwner, {
|
viewModel.coverUrl.observe(viewLifecycleOwner, {
|
||||||
if(it!="Loading") bindImage(binding.coverImage,it,Source.Spotify)
|
if(it!="Loading") bindImage(binding.coverImage,it, Source.Spotify)
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TrackList Binding Observer!
|
* TrackList Binding Observer!
|
||||||
**/
|
**/
|
||||||
spotifyViewModel.trackList.observe(viewLifecycleOwner, {
|
viewModel.trackList.observe(viewLifecycleOwner, {
|
||||||
if (it.isNotEmpty()){
|
if (it.isNotEmpty()){
|
||||||
Log.i("SpotifyFragment","TrackList Updated")
|
Log.i("SpotifyFragment","TrackList Updated")
|
||||||
adapterConfig(it)
|
adapter.submitList(it)
|
||||||
checkIfAllDownloaded()
|
checkIfAllDownloaded()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -199,7 +194,7 @@ class SpotifyFragment : Fragment() {
|
|||||||
/**
|
/**
|
||||||
* Title Binding Observer!
|
* Title Binding Observer!
|
||||||
**/
|
**/
|
||||||
spotifyViewModel.title.observe(viewLifecycleOwner, {
|
viewModel.title.observe(viewLifecycleOwner, {
|
||||||
binding.titleView.text = it
|
binding.titleView.text = it
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -213,7 +208,7 @@ class SpotifyFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun checkIfAllDownloaded() {
|
private fun checkIfAllDownloaded() {
|
||||||
if(!spotifyViewModel.trackList.value!!.any { it.downloaded != DownloadStatus.Downloaded }){
|
if(!viewModel.trackList.value!!.any { it.downloaded != DownloadStatus.Downloaded }){
|
||||||
//All Tracks Downloaded
|
//All Tracks Downloaded
|
||||||
binding.btnDownloadAll.visibility = View.GONE
|
binding.btnDownloadAll.visibility = View.GONE
|
||||||
binding.downloadingFab.apply{
|
binding.downloadingFab.apply{
|
||||||
@ -224,47 +219,41 @@ class SpotifyFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private fun initializeBroadcast() {
|
||||||
|
intentFilter = IntentFilter()
|
||||||
|
intentFilter?.addAction("track_download_completed")
|
||||||
|
|
||||||
/**
|
updateUIReceiver = object : BroadcastReceiver() {
|
||||||
* Basic Initialization
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
**/
|
//UI update here
|
||||||
private fun initializeAll() {
|
if (intent != null){
|
||||||
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
|
val trackDetails = intent.getParcelableExtra<TrackDetails?>("track")
|
||||||
spotifyViewModel = ViewModelProvider(this).get(SpotifyViewModel::class.java)
|
trackDetails?.let {
|
||||||
sharedViewModel.spotifyService.observe(viewLifecycleOwner, Observer {
|
val position: Int = viewModel.trackList.value?.map { it.name }?.indexOf(trackDetails.title) ?: -1
|
||||||
spotifyViewModel.spotifyService = it
|
Log.i("Track","Download Completed Intent :$position")
|
||||||
})
|
if(position != -1) {
|
||||||
SpotifyDownloadHelper.youtubeMusicApi = youtubeMusicApi
|
val track = viewModel.trackList.value?.get(position)
|
||||||
SpotifyDownloadHelper.sharedViewModel = sharedViewModel
|
track?.let{
|
||||||
SpotifyDownloadHelper.statusBar = binding.statusBar
|
it.downloaded = DownloadStatus.Downloaded
|
||||||
binding.trackList.adapter = adapterSpotify
|
viewModel.trackList.value?.set(position, it)
|
||||||
(binding.trackList.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
adapter.notifyItemChanged(position)
|
||||||
|
checkIfAllDownloaded()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requireActivity().registerReceiver(updateUIReceiver, intentFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override fun onResume() {
|
||||||
* Configure Recycler View Adapter
|
super.onResume()
|
||||||
**/
|
initializeBroadcast()
|
||||||
private fun adapterConfig(trackList: List<Track>){
|
|
||||||
adapterSpotify.spotifyViewModel = spotifyViewModel
|
|
||||||
adapterSpotify.submitList(trackList)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
/**
|
super.onPause()
|
||||||
* Util. Function to create toasts!
|
requireActivity().unregisterReceiver(updateUIReceiver)
|
||||||
**/
|
|
||||||
private fun showToast(message:String){
|
|
||||||
Toast.makeText(context,message,Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Util. Function To Check Connection Status
|
|
||||||
**/
|
|
||||||
private fun isOnline(): Boolean {
|
|
||||||
val cm =
|
|
||||||
requireActivity().getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
|
||||||
val netInfo = cm.activeNetworkInfo
|
|
||||||
return netInfo != null && netInfo.isConnectedOrConnecting
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -23,8 +23,9 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.shabinder.spotiflyer.database.DatabaseDAO
|
import com.shabinder.spotiflyer.database.DatabaseDAO
|
||||||
import com.shabinder.spotiflyer.database.DownloadRecord
|
import com.shabinder.spotiflyer.database.DownloadRecord
|
||||||
import com.shabinder.spotiflyer.models.*
|
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||||
import com.shabinder.spotiflyer.utils.SpotifyService
|
import com.shabinder.spotiflyer.models.spotify.*
|
||||||
|
import com.shabinder.spotiflyer.networking.SpotifyService
|
||||||
import com.shabinder.spotiflyer.utils.finalOutputDir
|
import com.shabinder.spotiflyer.utils.finalOutputDir
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -153,19 +154,19 @@ class SpotifyViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO
|
|||||||
|
|
||||||
private suspend fun getTrackDetails(trackLink:String): Track?{
|
private suspend fun getTrackDetails(trackLink:String): Track?{
|
||||||
Log.i("Requesting","https://api.spotify.com/v1/tracks/$trackLink")
|
Log.i("Requesting","https://api.spotify.com/v1/tracks/$trackLink")
|
||||||
return spotifyService?.getTrack(trackLink)
|
return spotifyService?.getTrack(trackLink)?.value
|
||||||
}
|
}
|
||||||
private suspend fun getAlbumDetails(albumLink:String): Album?{
|
private suspend fun getAlbumDetails(albumLink:String): Album?{
|
||||||
Log.i("Requesting","https://api.spotify.com/v1/albums/$albumLink")
|
Log.i("Requesting","https://api.spotify.com/v1/albums/$albumLink")
|
||||||
return spotifyService?.getAlbum(albumLink)
|
return spotifyService?.getAlbum(albumLink)?.value
|
||||||
}
|
}
|
||||||
private suspend fun getPlaylistDetails(link:String): Playlist?{
|
private suspend fun getPlaylistDetails(link:String): Playlist?{
|
||||||
Log.i("Requesting","https://api.spotify.com/v1/playlists/$link")
|
Log.i("Requesting","https://api.spotify.com/v1/playlists/$link")
|
||||||
return spotifyService?.getPlaylist(link)
|
return spotifyService?.getPlaylist(link)?.value
|
||||||
}
|
}
|
||||||
private suspend fun getPlaylistTrackDetails(link:String,offset:Int = 0,limit:Int = 100): PagingObjectPlaylistTrack?{
|
private suspend fun getPlaylistTrackDetails(link:String,offset:Int = 0,limit:Int = 100): PagingObjectPlaylistTrack?{
|
||||||
Log.i("Requesting","https://api.spotify.com/v1/playlists/$link/tracks?offset=$offset&limit=$limit")
|
Log.i("Requesting","https://api.spotify.com/v1/playlists/$link/tracks?offset=$offset&limit=$limit")
|
||||||
return spotifyService?.getPlaylistTracks(link, offset, limit)
|
return spotifyService?.getPlaylistTracks(link, offset, limit)?.value
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
|
@ -26,22 +26,19 @@ import android.util.Log
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.databinding.DataBindingUtil
|
import androidx.databinding.DataBindingUtil
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import com.github.kiulian.downloader.YoutubeDownloader
|
import com.github.kiulian.downloader.YoutubeDownloader
|
||||||
import com.shabinder.spotiflyer.R
|
import com.shabinder.spotiflyer.R
|
||||||
import com.shabinder.spotiflyer.SharedViewModel
|
import com.shabinder.spotiflyer.SharedViewModel
|
||||||
import com.shabinder.spotiflyer.databinding.YoutubeFragmentBinding
|
import com.shabinder.spotiflyer.databinding.TrackListFragmentBinding
|
||||||
import com.shabinder.spotiflyer.downloadHelper.YTDownloadHelper
|
import com.shabinder.spotiflyer.downloadHelper.YTDownloadHelper
|
||||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||||
import com.shabinder.spotiflyer.models.Source
|
|
||||||
import com.shabinder.spotiflyer.models.TrackDetails
|
import com.shabinder.spotiflyer.models.TrackDetails
|
||||||
|
import com.shabinder.spotiflyer.models.spotify.Source
|
||||||
import com.shabinder.spotiflyer.recyclerView.YoutubeTrackListAdapter
|
import com.shabinder.spotiflyer.recyclerView.YoutubeTrackListAdapter
|
||||||
import com.shabinder.spotiflyer.utils.bindImage
|
import com.shabinder.spotiflyer.utils.*
|
||||||
import com.shabinder.spotiflyer.utils.loadAllImages
|
|
||||||
import com.shabinder.spotiflyer.utils.rotateAnim
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -50,24 +47,24 @@ import javax.inject.Inject
|
|||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class YoutubeFragment : Fragment() {
|
class YoutubeFragment : Fragment() {
|
||||||
|
|
||||||
private lateinit var binding:YoutubeFragmentBinding
|
private lateinit var binding: TrackListFragmentBinding
|
||||||
private lateinit var youtubeViewModel: YoutubeViewModel
|
private lateinit var viewModel: YoutubeViewModel
|
||||||
private lateinit var sharedViewModel: SharedViewModel
|
private lateinit var sharedViewModel: SharedViewModel
|
||||||
@Inject lateinit var ytDownloader: YoutubeDownloader
|
@Inject lateinit var ytDownloader: YoutubeDownloader
|
||||||
private lateinit var adapter : YoutubeTrackListAdapter
|
private lateinit var adapter : YoutubeTrackListAdapter
|
||||||
private val sampleDomain1 = "youtube.com"
|
|
||||||
private val sampleDomain2 = "youtu.be"
|
|
||||||
private var intentFilter: IntentFilter? = null
|
private var intentFilter: IntentFilter? = null
|
||||||
private var updateUIReceiver: BroadcastReceiver? = null
|
private var updateUIReceiver: BroadcastReceiver? = null
|
||||||
|
private val sampleDomain2 = "youtu.be"
|
||||||
|
private val sampleDomain1 = "youtube.com"
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
binding = DataBindingUtil.inflate(inflater,R.layout.youtube_fragment,container,false)
|
binding = DataBindingUtil.inflate(inflater,R.layout.track_list_fragment,container,false)
|
||||||
youtubeViewModel = ViewModelProvider(this).get(YoutubeViewModel::class.java)
|
viewModel = ViewModelProvider(this).get(YoutubeViewModel::class.java)
|
||||||
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
|
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
|
||||||
adapter = YoutubeTrackListAdapter(youtubeViewModel)
|
adapter = YoutubeTrackListAdapter(viewModel)
|
||||||
binding.trackList.adapter = adapter
|
binding.trackList.adapter = adapter
|
||||||
|
|
||||||
initializeLiveDataObservers()
|
initializeLiveDataObservers()
|
||||||
@ -84,7 +81,7 @@ class YoutubeFragment : Fragment() {
|
|||||||
if(link.contains("playlist",true) || link.contains("list",true)){
|
if(link.contains("playlist",true) || link.contains("list",true)){
|
||||||
// Given Link is of a Playlist
|
// Given Link is of a Playlist
|
||||||
val playlistId = link.substringAfter("?list=").substringAfter("&list=").substringBefore("&")
|
val playlistId = link.substringAfter("?list=").substringAfter("&list=").substringBefore("&")
|
||||||
youtubeViewModel.getYTPlaylist(playlistId,ytDownloader)
|
viewModel.getYTPlaylist(playlistId,ytDownloader)
|
||||||
}else{//Given Link is of a Video
|
}else{//Given Link is of a Video
|
||||||
var searchId = "error"
|
var searchId = "error"
|
||||||
if(link.contains(sampleDomain1,true) ){
|
if(link.contains(sampleDomain1,true) ){
|
||||||
@ -94,29 +91,33 @@ class YoutubeFragment : Fragment() {
|
|||||||
searchId = link.substringAfterLast("/","error")
|
searchId = link.substringAfterLast("/","error")
|
||||||
}
|
}
|
||||||
if(searchId != "error") {
|
if(searchId != "error") {
|
||||||
youtubeViewModel.getYTTrack(searchId,ytDownloader)
|
viewModel.getYTTrack(searchId,ytDownloader)
|
||||||
}else{showToast("Your Youtube Link is not of a Video!!")}
|
}else{showMessage("Your Youtube Link is not of a Video!!")}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Download All Tracks
|
* Download All Tracks
|
||||||
* */
|
* */
|
||||||
binding.btnDownloadAll.setOnClickListener {
|
binding.btnDownloadAll.setOnClickListener {
|
||||||
|
if(!isOnline()){
|
||||||
|
showNoConnectionAlert()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
binding.btnDownloadAll.visibility = View.GONE
|
binding.btnDownloadAll.visibility = View.GONE
|
||||||
binding.downloadingFab.visibility = View.VISIBLE
|
binding.downloadingFab.visibility = View.VISIBLE
|
||||||
|
|
||||||
rotateAnim(binding.downloadingFab)
|
rotateAnim(binding.downloadingFab)
|
||||||
|
|
||||||
for (track in youtubeViewModel.ytTrackList.value?: listOf()){
|
for (track in viewModel.ytTrackList.value?: listOf()){
|
||||||
if(track.downloaded != DownloadStatus.Downloaded){
|
if(track.downloaded != DownloadStatus.Downloaded){
|
||||||
track.downloaded = DownloadStatus.Downloading
|
track.downloaded = DownloadStatus.Downloading
|
||||||
adapter.notifyItemChanged(youtubeViewModel.ytTrackList.value!!.indexOf(track))
|
adapter.notifyItemChanged(viewModel.ytTrackList.value!!.indexOf(track))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
showToast("Processing!")
|
showMessage("Processing!")
|
||||||
sharedViewModel.uiScope.launch(Dispatchers.Default){
|
sharedViewModel.uiScope.launch(Dispatchers.Default){
|
||||||
val urlList = arrayListOf<String>()
|
val urlList = arrayListOf<String>()
|
||||||
youtubeViewModel.ytTrackList.value?.forEach { urlList.add("https://i.ytimg.com/vi/${it.albumArt.absolutePath.substringAfterLast("/")
|
viewModel.ytTrackList.value?.forEach { urlList.add("https://i.ytimg.com/vi/${it.albumArt.absolutePath.substringAfterLast("/")
|
||||||
.substringBeforeLast(".")}/hqdefault.jpg")}
|
.substringBeforeLast(".")}/hqdefault.jpg")}
|
||||||
//Appending Source
|
//Appending Source
|
||||||
urlList.add("youtube")
|
urlList.add("youtube")
|
||||||
@ -125,11 +126,11 @@ class YoutubeFragment : Fragment() {
|
|||||||
urlList
|
urlList
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
youtubeViewModel.uiScope.launch {
|
viewModel.uiScope.launch {
|
||||||
YTDownloadHelper.downloadYTTracks(
|
YTDownloadHelper.downloadYTTracks(
|
||||||
type = youtubeViewModel.folderType,
|
type = viewModel.folderType,
|
||||||
subFolder = youtubeViewModel.subFolder,
|
subFolder = viewModel.subFolder,
|
||||||
tracks = youtubeViewModel.ytTrackList.value ?: listOf()
|
tracks = viewModel.ytTrackList.value ?: listOf()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,13 +150,13 @@ class YoutubeFragment : Fragment() {
|
|||||||
if (intent != null){
|
if (intent != null){
|
||||||
val trackDetails = intent.getParcelableExtra<TrackDetails?>("track")
|
val trackDetails = intent.getParcelableExtra<TrackDetails?>("track")
|
||||||
trackDetails?.let {
|
trackDetails?.let {
|
||||||
val position: Int = youtubeViewModel.ytTrackList.value?.map { it.title }?.indexOf(trackDetails.title) ?: -1
|
val position: Int = viewModel.ytTrackList.value?.map { it.title }?.indexOf(trackDetails.title) ?: -1
|
||||||
Log.i("Track","Download Completed Intent :$position")
|
Log.i("Track","Download Completed Intent :$position")
|
||||||
if(position != -1) {
|
if(position != -1) {
|
||||||
val track = youtubeViewModel.ytTrackList.value?.get(position)
|
val track = viewModel.ytTrackList.value?.get(position)
|
||||||
track?.let{
|
track?.let{
|
||||||
it.downloaded = DownloadStatus.Downloaded
|
it.downloaded = DownloadStatus.Downloaded
|
||||||
youtubeViewModel.ytTrackList.value?.set(position, it)
|
viewModel.ytTrackList.value?.set(position, it)
|
||||||
adapter.notifyItemChanged(position)
|
adapter.notifyItemChanged(position)
|
||||||
checkIfAllDownloaded()
|
checkIfAllDownloaded()
|
||||||
}
|
}
|
||||||
@ -173,7 +174,7 @@ class YoutubeFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun checkIfAllDownloaded() {
|
private fun checkIfAllDownloaded() {
|
||||||
if(!youtubeViewModel.ytTrackList.value!!.any { it.downloaded != DownloadStatus.Downloaded }){
|
if(!viewModel.ytTrackList.value!!.any { it.downloaded != DownloadStatus.Downloaded }){
|
||||||
//All Tracks Downloaded
|
//All Tracks Downloaded
|
||||||
binding.btnDownloadAll.visibility = View.GONE
|
binding.btnDownloadAll.visibility = View.GONE
|
||||||
binding.downloadingFab.apply{
|
binding.downloadingFab.apply{
|
||||||
@ -188,38 +189,23 @@ class YoutubeFragment : Fragment() {
|
|||||||
/**
|
/**
|
||||||
* CoverUrl Binding Observer!
|
* CoverUrl Binding Observer!
|
||||||
**/
|
**/
|
||||||
youtubeViewModel.coverUrl.observe(viewLifecycleOwner, {
|
viewModel.coverUrl.observe(viewLifecycleOwner, {
|
||||||
if(it!="Loading") bindImage(binding.coverImage,it,Source.YouTube)
|
if(it!="Loading") bindImage(binding.coverImage,it, Source.YouTube)
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TrackList Binding Observer!
|
* TrackList Binding Observer!
|
||||||
**/
|
**/
|
||||||
youtubeViewModel.ytTrackList.observe(viewLifecycleOwner, {
|
viewModel.ytTrackList.observe(viewLifecycleOwner, {
|
||||||
adapterConfig(it)
|
adapter.submitList(it)
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Title Binding Observer!
|
* Title Binding Observer!
|
||||||
**/
|
**/
|
||||||
youtubeViewModel.title.observe(viewLifecycleOwner, {
|
viewModel.title.observe(viewLifecycleOwner, {
|
||||||
binding.titleView.text = it
|
binding.titleView.text = it
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Configure Recycler View Adapter
|
|
||||||
**/
|
|
||||||
private fun adapterConfig(list:List<TrackDetails>){
|
|
||||||
adapter.submitList(list)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Util. Function to create toasts!
|
|
||||||
**/
|
|
||||||
private fun showToast(message:String){
|
|
||||||
Toast.makeText(context,message, Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,17 +24,15 @@ import androidx.hilt.lifecycle.ViewModelInject
|
|||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.github.kiulian.downloader.YoutubeDownloader
|
import com.github.kiulian.downloader.YoutubeDownloader
|
||||||
import com.github.kiulian.downloader.model.formats.Format
|
|
||||||
import com.shabinder.spotiflyer.database.DatabaseDAO
|
import com.shabinder.spotiflyer.database.DatabaseDAO
|
||||||
import com.shabinder.spotiflyer.database.DownloadRecord
|
import com.shabinder.spotiflyer.database.DownloadRecord
|
||||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||||
import com.shabinder.spotiflyer.models.Source
|
|
||||||
import com.shabinder.spotiflyer.models.TrackDetails
|
import com.shabinder.spotiflyer.models.TrackDetails
|
||||||
import com.shabinder.spotiflyer.utils.Provider
|
import com.shabinder.spotiflyer.models.spotify.Source
|
||||||
import com.shabinder.spotiflyer.utils.Provider.defaultDir
|
import com.shabinder.spotiflyer.utils.Provider.defaultDir
|
||||||
import com.shabinder.spotiflyer.utils.Provider.showToast
|
|
||||||
import com.shabinder.spotiflyer.utils.finalOutputDir
|
import com.shabinder.spotiflyer.utils.finalOutputDir
|
||||||
import com.shabinder.spotiflyer.utils.removeIllegalChars
|
import com.shabinder.spotiflyer.utils.removeIllegalChars
|
||||||
|
import com.shabinder.spotiflyer.utils.showMessage
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@ -47,7 +45,6 @@ class YoutubeViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO
|
|||||||
* */
|
* */
|
||||||
|
|
||||||
val ytTrackList = MutableLiveData<MutableList<TrackDetails>>()
|
val ytTrackList = MutableLiveData<MutableList<TrackDetails>>()
|
||||||
val format = MutableLiveData<Format>()
|
|
||||||
private val loading = "Loading"
|
private val loading = "Loading"
|
||||||
var title = MutableLiveData<String>().apply { value = "\"Loading!\"" }
|
var title = MutableLiveData<String>().apply { value = "\"Loading!\"" }
|
||||||
var coverUrl = MutableLiveData<String>().apply { value = loading }
|
var coverUrl = MutableLiveData<String>().apply { value = loading }
|
||||||
@ -108,7 +105,7 @@ class YoutubeViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}catch (e:com.github.kiulian.downloader.YoutubeException.BadPageException){
|
}catch (e:com.github.kiulian.downloader.YoutubeException.BadPageException){
|
||||||
showToast("An Error Occurred While Processing!")
|
showMessage("An Error Occurred While Processing!")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -124,16 +121,18 @@ class YoutubeViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO
|
|||||||
val name = detail?.title()?.replace(detail.author()!!.toUpperCase(),"",true) ?: detail?.title() ?: ""
|
val name = detail?.title()?.replace(detail.author()!!.toUpperCase(),"",true) ?: detail?.title() ?: ""
|
||||||
Log.i("YT View Model",detail.toString())
|
Log.i("YT View Model",detail.toString())
|
||||||
ytTrackList.postValue(
|
ytTrackList.postValue(
|
||||||
listOf(TrackDetails(
|
listOf(
|
||||||
|
TrackDetails(
|
||||||
title = name,
|
title = name,
|
||||||
artists = listOf(detail?.author().toString()),
|
artists = listOf(detail?.author().toString()),
|
||||||
durationSec = detail?.lengthSeconds()?:0,
|
durationSec = detail?.lengthSeconds()?:0,
|
||||||
albumArt = File(
|
albumArt = File(
|
||||||
Environment.getExternalStorageDirectory(),
|
Environment.getExternalStorageDirectory(),
|
||||||
Provider.defaultDir +".Images/" + searchId + ".jpeg"
|
defaultDir +".Images/" + searchId + ".jpeg"
|
||||||
),
|
),
|
||||||
source = Source.YouTube
|
source = Source.YouTube
|
||||||
)).toMutableList()
|
)
|
||||||
|
).toMutableList()
|
||||||
)
|
)
|
||||||
title.postValue(
|
title.postValue(
|
||||||
if(name.length > 17){"${name.subSequence(0,16)}..."}else{name}
|
if(name.length > 17){"${name.subSequence(0,16)}..."}else{name}
|
||||||
@ -152,7 +151,7 @@ class YoutubeViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e:com.github.kiulian.downloader.YoutubeException){
|
} catch (e:com.github.kiulian.downloader.YoutubeException){
|
||||||
showToast("An Error Occurred While Processing!")
|
showMessage("An Error Occurred While Processing!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Shabinder Singh
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.shabinder.spotiflyer.utils
|
||||||
|
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.Protocol
|
||||||
|
import okhttp3.RequestBody
|
||||||
|
import okhttp3.Response
|
||||||
|
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||||
|
|
||||||
|
const val NoInternetErrorCode = 222
|
||||||
|
|
||||||
|
class NetworkInterceptor: Interceptor {
|
||||||
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
|
return if (!isOnline()){
|
||||||
|
//No Internet Connection
|
||||||
|
showNoConnectionAlert()
|
||||||
|
//Lets Stop the Incoming Request
|
||||||
|
Response.Builder()
|
||||||
|
.code(NoInternetErrorCode) // code(200.300) = successful else = unsuccessful
|
||||||
|
.body("{}".toResponseBody(null)) // Whatever body
|
||||||
|
.protocol(Protocol.HTTP_2)
|
||||||
|
.message("No Internet Connection")
|
||||||
|
.request(chain.request())
|
||||||
|
.build()
|
||||||
|
}else {
|
||||||
|
val response = chain.proceed(chain.request())
|
||||||
|
val responseBody = response.body
|
||||||
|
val bodyString = responseBody?.string()
|
||||||
|
//Log.i("Network Request",bodyString)
|
||||||
|
//chain.proceed(chain.request())
|
||||||
|
//Log.i("Network Request","{\"unchecked\":${bodyString}}")
|
||||||
|
Response.Builder()
|
||||||
|
.code(response.code) // code(200.300) = successful else = unsuccessful
|
||||||
|
.body("{\"value\":${bodyString}}".toResponseBody(responseBody?.contentType())) // Whatever body
|
||||||
|
.protocol(response.protocol)
|
||||||
|
.message(response.message)
|
||||||
|
.request(chain.request())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Converts REQUEST's Body to String
|
||||||
|
* */
|
||||||
|
private fun RequestBody?.bodyToString(): String {
|
||||||
|
if (this == null) return ""
|
||||||
|
val buffer = okio.Buffer()
|
||||||
|
writeTo(buffer)
|
||||||
|
return buffer.readUtf8()
|
||||||
|
}
|
||||||
|
}
|
@ -19,12 +19,14 @@ package com.shabinder.spotiflyer.utils
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.widget.Toast
|
|
||||||
import com.github.kiulian.downloader.YoutubeDownloader
|
import com.github.kiulian.downloader.YoutubeDownloader
|
||||||
import com.shabinder.spotiflyer.App
|
import com.shabinder.spotiflyer.App
|
||||||
import com.shabinder.spotiflyer.MainActivity
|
import com.shabinder.spotiflyer.MainActivity
|
||||||
import com.shabinder.spotiflyer.database.DatabaseDAO
|
import com.shabinder.spotiflyer.database.DatabaseDAO
|
||||||
import com.shabinder.spotiflyer.database.DownloadRecordDatabase
|
import com.shabinder.spotiflyer.database.DownloadRecordDatabase
|
||||||
|
import com.shabinder.spotiflyer.networking.GaanaInterface
|
||||||
|
import com.shabinder.spotiflyer.networking.SpotifyServiceTokenRequest
|
||||||
|
import com.shabinder.spotiflyer.networking.YoutubeMusicApi
|
||||||
import com.shreyaspatil.easyupipayment.EasyUpiPayment
|
import com.shreyaspatil.easyupipayment.EasyUpiPayment
|
||||||
import com.squareup.moshi.Moshi
|
import com.squareup.moshi.Moshi
|
||||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||||
@ -37,12 +39,12 @@ import okhttp3.Interceptor
|
|||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.converter.gson.GsonConverterFactory
|
|
||||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||||
import retrofit2.converter.scalars.ScalarsConverterFactory
|
import retrofit2.converter.scalars.ScalarsConverterFactory
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
|
||||||
@InstallIn(ApplicationComponent::class)
|
@InstallIn(ApplicationComponent::class)
|
||||||
@Module
|
@Module
|
||||||
object Provider {
|
object Provider {
|
||||||
@ -66,7 +68,7 @@ object Provider {
|
|||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideUpi():EasyUpiPayment {
|
fun provideUpi():EasyUpiPayment {
|
||||||
return EasyUpiPayment.Builder(MainActivity.getInstance())
|
return EasyUpiPayment.Builder(activity)
|
||||||
.setPayeeVpa("technoshab@paytm")
|
.setPayeeVpa("technoshab@paytm")
|
||||||
.setPayeeName("Shabinder Singh")
|
.setPayeeName("Shabinder Singh")
|
||||||
.setTransactionId("UNIQUE_TRANSACTION_ID")
|
.setTransactionId("UNIQUE_TRANSACTION_ID")
|
||||||
@ -86,39 +88,59 @@ object Provider {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun getSpotifyTokenInterface():SpotifyServiceTokenRequest{
|
fun getSpotifyTokenInterface(moshi: Moshi): SpotifyServiceTokenRequest {
|
||||||
val httpClient2: OkHttpClient.Builder = OkHttpClient.Builder()
|
val httpClient2: OkHttpClient.Builder = OkHttpClient.Builder()
|
||||||
httpClient2.addInterceptor(Interceptor { chain ->
|
.addInterceptor(Interceptor { chain ->
|
||||||
val request: Request =
|
val request: Request =
|
||||||
chain.request().newBuilder().addHeader(
|
chain.request().newBuilder()
|
||||||
|
.addHeader(
|
||||||
"Authorization",
|
"Authorization",
|
||||||
"Basic ${android.util.Base64.encodeToString("${App.clientId}:${App.clientSecret}".toByteArray(),android.util.Base64.NO_WRAP)}"
|
"Basic ${
|
||||||
|
android.util.Base64.encodeToString(
|
||||||
|
"${App.clientId}:${App.clientSecret}".toByteArray(),
|
||||||
|
android.util.Base64.NO_WRAP
|
||||||
|
)
|
||||||
|
}"
|
||||||
).build()
|
).build()
|
||||||
chain.proceed(request)
|
chain.proceed(request)
|
||||||
})
|
}).addInterceptor(NetworkInterceptor())
|
||||||
|
|
||||||
val retrofit = Retrofit.Builder()
|
val retrofit = Retrofit.Builder()
|
||||||
.baseUrl("https://accounts.spotify.com/")
|
.baseUrl("https://accounts.spotify.com/")
|
||||||
.client(httpClient2.build())
|
.client(httpClient2.build())
|
||||||
.addConverterFactory(MoshiConverterFactory.create(getMoshi()))
|
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||||
.build()
|
.build()
|
||||||
return retrofit.create(SpotifyServiceTokenRequest::class.java)
|
return retrofit.create(SpotifyServiceTokenRequest::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun getYoutubeMusicApi():YoutubeMusicApi{
|
fun okHttpClient():OkHttpClient{
|
||||||
|
return OkHttpClient.Builder()
|
||||||
|
.addInterceptor(NetworkInterceptor())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun getGaanaInterface(moshi: Moshi,okHttpClient: OkHttpClient):GaanaInterface{
|
||||||
|
val retrofit = Retrofit.Builder()
|
||||||
|
.baseUrl("http://api.gaana.com/")
|
||||||
|
.client(okHttpClient)
|
||||||
|
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||||
|
.build()
|
||||||
|
return retrofit.create(GaanaInterface::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun getYoutubeMusicApi(moshi: Moshi): YoutubeMusicApi {
|
||||||
val retrofit = Retrofit.Builder()
|
val retrofit = Retrofit.Builder()
|
||||||
.baseUrl("https://music.youtube.com/youtubei/v1/")
|
.baseUrl("https://music.youtube.com/youtubei/v1/")
|
||||||
.addConverterFactory(ScalarsConverterFactory.create())
|
.addConverterFactory(ScalarsConverterFactory.create())
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
return retrofit.create(YoutubeMusicApi::class.java)
|
return retrofit.create(YoutubeMusicApi::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showToast(string: String,long:Boolean=false){
|
|
||||||
Toast.makeText(activity,string,if(long)Toast.LENGTH_LONG else Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -19,6 +19,9 @@ package com.shabinder.spotiflyer.utils
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.net.NetworkCapabilities
|
||||||
|
import android.os.Build
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -33,9 +36,12 @@ import com.bumptech.glide.load.DataSource
|
|||||||
import com.bumptech.glide.load.engine.GlideException
|
import com.bumptech.glide.load.engine.GlideException
|
||||||
import com.bumptech.glide.request.RequestListener
|
import com.bumptech.glide.request.RequestListener
|
||||||
import com.bumptech.glide.request.target.Target
|
import com.bumptech.glide.request.target.Target
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.shabinder.spotiflyer.R
|
import com.shabinder.spotiflyer.R
|
||||||
import com.shabinder.spotiflyer.models.DownloadObject
|
import com.shabinder.spotiflyer.models.DownloadObject
|
||||||
import com.shabinder.spotiflyer.models.Source
|
import com.shabinder.spotiflyer.models.spotify.Source
|
||||||
|
import com.shabinder.spotiflyer.utils.Provider.activity
|
||||||
import com.shabinder.spotiflyer.utils.Provider.defaultDir
|
import com.shabinder.spotiflyer.utils.Provider.defaultDir
|
||||||
import com.shabinder.spotiflyer.worker.ForegroundService
|
import com.shabinder.spotiflyer.worker.ForegroundService
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@ -64,6 +70,48 @@ fun finalOutputDir(itemName:String? = null,type:String, subFolder:String?=null,e
|
|||||||
+ itemName?.let { removeIllegalChars(it) + extension})
|
+ itemName?.let { removeIllegalChars(it) + extension})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Util. Function To Check Connection Status
|
||||||
|
**/
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
fun isOnline(): Boolean {
|
||||||
|
var result = false
|
||||||
|
val connectivityManager =
|
||||||
|
activity.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
|
||||||
|
connectivityManager?.let {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
it.getNetworkCapabilities(connectivityManager.activeNetwork)?.apply {
|
||||||
|
result = when {
|
||||||
|
hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
|
||||||
|
hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
|
||||||
|
hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val netInfo =
|
||||||
|
(activity.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).activeNetworkInfo
|
||||||
|
result = netInfo != null && netInfo.isConnected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showMessage(message: String, long: Boolean = false){
|
||||||
|
CoroutineScope(Dispatchers.Main).launch{
|
||||||
|
Snackbar.make(
|
||||||
|
activity.snackBarAnchor,
|
||||||
|
message,
|
||||||
|
if (long) Snackbar.LENGTH_LONG else Snackbar.LENGTH_SHORT
|
||||||
|
).also { snackbar ->
|
||||||
|
snackbar.setAction("Ok") {
|
||||||
|
snackbar.dismiss()
|
||||||
|
}
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun rotateAnim(view: View){
|
fun rotateAnim(view: View){
|
||||||
val rotate = RotateAnimation(
|
val rotate = RotateAnimation(
|
||||||
0F, 360F,
|
0F, 360F,
|
||||||
@ -76,6 +124,18 @@ fun rotateAnim(view: View){
|
|||||||
view.animation = rotate
|
view.animation = rotate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showNoConnectionAlert(){
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
activity.apply {
|
||||||
|
MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)
|
||||||
|
.setTitle(resources.getString(R.string.title))
|
||||||
|
.setMessage(resources.getString(R.string.supporting_text))
|
||||||
|
.setPositiveButton(resources.getString(R.string.cancel)) { _, _ ->
|
||||||
|
// Respond to neutral button press
|
||||||
|
}.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
fun bindImage(imgView: ImageView, imgUrl: String?,source: Source?) {
|
fun bindImage(imgView: ImageView, imgUrl: String?,source: Source?) {
|
||||||
imgUrl?.let {
|
imgUrl?.let {
|
||||||
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
|
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
|
||||||
|
@ -71,7 +71,7 @@ class ForegroundService : Service(){
|
|||||||
private lateinit var downloadManager : DownloadManager
|
private lateinit var downloadManager : DownloadManager
|
||||||
private var serviceJob = Job()
|
private var serviceJob = Job()
|
||||||
private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)
|
private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)
|
||||||
private val requestMap = mutableMapOf<Request,TrackDetails>()
|
private val requestMap = mutableMapOf<Request, TrackDetails>()
|
||||||
private var speed :Long = 0
|
private var speed :Long = 0
|
||||||
private var defaultDir = Environment.DIRECTORY_MUSIC + File.separator + "SpotiFlyer" + File.separator
|
private var defaultDir = Environment.DIRECTORY_MUSIC + File.separator + "SpotiFlyer" + File.separator
|
||||||
private val parentDirectory = File(Environment.getExternalStorageDirectory(),
|
private val parentDirectory = File(Environment.getExternalStorageDirectory(),
|
||||||
@ -458,7 +458,7 @@ class ForegroundService : Service(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*Modifying Mp3 Tags with MetaData!
|
*Modifying Mp3 com.shabinder.spotiflyer.models.gaana.Tags with MetaData!
|
||||||
**/
|
**/
|
||||||
private fun setId3v1Tags(mp3File: Mp3File, track: TrackDetails): Mp3File {
|
private fun setId3v1Tags(mp3File: Mp3File, track: TrackDetails): Mp3File {
|
||||||
val id3v1Tag = ID3v1Tag().apply {
|
val id3v1Tag = ID3v1Tag().apply {
|
||||||
|
@ -23,19 +23,15 @@
|
|||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/mainActivity"
|
android:id="@+id/mainActivity"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
android:background="@color/black"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
<TextView
|
<View
|
||||||
android:id="@+id/message"
|
android:id="@+id/snackBarPosition"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="2dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:background="@drawable/text_background_accented"
|
android:background="@drawable/transparent"
|
||||||
android:padding="5dp"
|
android:visibility="invisible"
|
||||||
android:visibility="gone"
|
|
||||||
android:paddingTop="6dp"
|
|
||||||
android:text="Authentication Needed"
|
|
||||||
android:textColor="@color/colorPrimary"
|
|
||||||
android:textSize="10dp"
|
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
@ -47,7 +43,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
app:defaultNavHost="true"
|
app:defaultNavHost="true"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/message"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:fillViewport="true"
|
android:fillViewport="true"
|
||||||
|
android:background="@color/black"
|
||||||
>
|
>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:id="@+id/main"
|
android:id="@+id/main"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
android:background="@color/black"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginTop="25dp"
|
android:layout_marginTop="25dp"
|
||||||
android:fitsSystemWindows="true"
|
android:fitsSystemWindows="true"
|
||||||
@ -74,10 +75,10 @@
|
|||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/topLayout"
|
android:id="@+id/topLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
android:background="@color/black"
|
||||||
android:foreground="@drawable/gradient"
|
android:foreground="@drawable/gradient"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/cover_image"
|
android:id="@+id/cover_image"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
@ -23,7 +23,7 @@
|
|||||||
<data>
|
<data>
|
||||||
<variable
|
<variable
|
||||||
name="track"
|
name="track"
|
||||||
type="com.shabinder.spotiflyer.models.Track" />
|
type="com.shabinder.spotiflyer.models.spotify.Track" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
@ -1,152 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
~ Copyright (C) 2020 Shabinder Singh
|
|
||||||
~
|
|
||||||
~ This program is free software: you can redistribute it and/or modify
|
|
||||||
~ it under the terms of the GNU General Public License as published by
|
|
||||||
~ the Free Software Foundation, either version 3 of the License, or
|
|
||||||
~ (at your option) any later version.
|
|
||||||
~
|
|
||||||
~ This program is distributed in the hope that it will be useful,
|
|
||||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
~ GNU General Public License for more details.
|
|
||||||
~
|
|
||||||
~ You should have received a copy of the GNU General Public License
|
|
||||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
android:id="@+id/main"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_marginTop="25dp"
|
|
||||||
android:fitsSystemWindows="true">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatButton
|
|
||||||
android:id="@+id/btn_download_all"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="44dp"
|
|
||||||
android:background="@drawable/btn_design"
|
|
||||||
android:drawableEnd="@drawable/ic_arrow_slim"
|
|
||||||
android:drawablePadding="4dp"
|
|
||||||
android:drawableTint="@color/black"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:text="Download All |"
|
|
||||||
android:textColor="@color/black"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:visibility="visible"
|
|
||||||
app:layout_anchor="@+id/appbar"
|
|
||||||
app:layout_anchorGravity="bottom|center" />
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
android:id="@+id/downloading_fab"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:backgroundTint="@color/black"
|
|
||||||
android:scaleType="fitCenter"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:borderWidth="0dp"
|
|
||||||
app:layout_anchor="@+id/appbar"
|
|
||||||
app:layout_anchorGravity="bottom|center"
|
|
||||||
app:maxImageSize="38dp"
|
|
||||||
app:rippleColor="@color/colorPrimaryDark"
|
|
||||||
app:srcCompat="@drawable/ic_refresh"
|
|
||||||
app:tint="@null" />
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
|
||||||
android:id="@+id/appbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="230dp">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:contentScrim="#F2C102B7"
|
|
||||||
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
|
|
||||||
app:layout_scrollInterpolator="@android:anim/decelerate_interpolator"
|
|
||||||
app:toolbarId="@+id/toolbar">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:id="@+id/topLayout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:foreground="@drawable/gradient"
|
|
||||||
>
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/cover_image"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginTop="23dp"
|
|
||||||
android:layout_marginBottom="3dp"
|
|
||||||
android:contentDescription="Album Cover"
|
|
||||||
android:padding="15dp"
|
|
||||||
android:src="@drawable/spotify_download"
|
|
||||||
android:visibility="visible"
|
|
||||||
app:layout_collapseMode="parallax"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/title_view"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/statusBar"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="2dp"
|
|
||||||
android:background="@drawable/text_background_accented"
|
|
||||||
android:fontFamily="@font/raleway_semibold"
|
|
||||||
android:foreground="@drawable/rounded_gradient"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingLeft="12dp"
|
|
||||||
android:paddingTop="1dp"
|
|
||||||
android:paddingRight="12dp"
|
|
||||||
android:paddingBottom="1dp"
|
|
||||||
android:text="Total: 100 Processed: 50"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textColor="@color/grey"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/cover_image"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/title_view"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="2dp"
|
|
||||||
android:layout_marginBottom="20dp"
|
|
||||||
android:background="#00000000"
|
|
||||||
android:fontFamily="@font/raleway_semibold"
|
|
||||||
android:gravity="end"
|
|
||||||
android:text='"SpotiFlyer"'
|
|
||||||
android:textAlignment="viewEnd"
|
|
||||||
android:textColor="#9AB3FF"
|
|
||||||
android:textSize="28sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:visibility="visible"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/track_list"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:paddingTop="26dp"
|
|
||||||
android:visibility="visible"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/appbar" />
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
</layout>
|
|
@ -26,7 +26,7 @@
|
|||||||
android:id="@+id/spotifyFragment"
|
android:id="@+id/spotifyFragment"
|
||||||
android:name="com.shabinder.spotiflyer.ui.spotify.SpotifyFragment"
|
android:name="com.shabinder.spotiflyer.ui.spotify.SpotifyFragment"
|
||||||
android:label="main_fragment"
|
android:label="main_fragment"
|
||||||
tools:layout="@layout/spotify_fragment" >
|
tools:layout="@layout/track_list_fragment" >
|
||||||
<argument
|
<argument
|
||||||
android:name="link"
|
android:name="link"
|
||||||
app:argType="string" />
|
app:argType="string" />
|
||||||
@ -56,7 +56,7 @@
|
|||||||
android:id="@+id/youtubeFragment"
|
android:id="@+id/youtubeFragment"
|
||||||
android:name="com.shabinder.spotiflyer.ui.youtube.YoutubeFragment"
|
android:name="com.shabinder.spotiflyer.ui.youtube.YoutubeFragment"
|
||||||
android:label="YoutubeFragment"
|
android:label="YoutubeFragment"
|
||||||
tools:layout="@layout/youtube_fragment">
|
tools:layout="@layout/track_list_fragment">
|
||||||
<argument
|
<argument
|
||||||
android:name="link"
|
android:name="link"
|
||||||
app:argType="string" />
|
app:argType="string" />
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
<!-- Customize your theme here. -->
|
<!-- Customize your theme here. -->
|
||||||
<item name="colorPrimaryDark">#000000</item>
|
<item name="colorPrimaryDark">#000000</item>
|
||||||
<item name="colorPrimary">#FC5C7D</item>
|
<item name="colorPrimary">#FC5C7D</item>
|
||||||
<item name="android:background">#000000</item>
|
|
||||||
<item name="android:textColor">#FFFFFF</item>
|
<item name="android:textColor">#FFFFFF</item>
|
||||||
<item name="colorAccent">#6A82FB</item>
|
<item name="colorAccent">#6A82FB</item>
|
||||||
<item name="android:outlineAmbientShadowColor" tools:targetApi="p">#A9B200FF</item>
|
<item name="android:outlineAmbientShadowColor" tools:targetApi="p">#A9B200FF</item>
|
||||||
@ -29,7 +28,7 @@
|
|||||||
<!-- Text Appearances !-->
|
<!-- Text Appearances !-->
|
||||||
<!-- use our brand's custom TextAppearance4 !-->
|
<!-- use our brand's custom TextAppearance4 !-->
|
||||||
<item name="textAppearanceHeadline4">@style/TextAppearance.AppTheme.Headline4</item>
|
<item name="textAppearanceHeadline4">@style/TextAppearance.AppTheme.Headline4</item>
|
||||||
<!-- use default Body2 text apperance !-->
|
<!-- use default Body2 text appearance !-->
|
||||||
<item name="textAppearanceBody2">@style/TextAppearance.MaterialComponents.Body2</item>
|
<item name="textAppearanceBody2">@style/TextAppearance.MaterialComponents.Body2</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@ -38,18 +37,22 @@
|
|||||||
<item name="shapeAppearanceMediumComponent">@style/CutShapeAppearance</item>
|
<item name="shapeAppearanceMediumComponent">@style/CutShapeAppearance</item>
|
||||||
<item name="buttonBarPositiveButtonStyle">@style/Alert.Button.Positive</item>
|
<item name="buttonBarPositiveButtonStyle">@style/Alert.Button.Positive</item>
|
||||||
<item name="buttonBarNeutralButtonStyle">@style/Alert.Button.Neutral</item>
|
<item name="buttonBarNeutralButtonStyle">@style/Alert.Button.Neutral</item>
|
||||||
|
<item name="android:textSize">22sp</item>
|
||||||
|
<item name="fontFamily">@font/amita</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="CutShapeAppearance" parent="ShapeAppearance.MaterialComponents.MediumComponent">
|
<style name="CutShapeAppearance" parent="ShapeAppearance.MaterialComponents.MediumComponent">
|
||||||
<item name="background">@color/white</item>
|
<item name="background">@color/white</item>
|
||||||
<item name="cornerFamily">rounded</item>
|
<item name="cornerFamily">rounded</item>
|
||||||
<item name="cornerSize">20dp</item>
|
<item name="cornerSize">8dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Alert.Button.Positive" parent="Widget.MaterialComponents.Button.TextButton">
|
<style name="Alert.Button.Positive" parent="Widget.MaterialComponents.Button.TextButton">
|
||||||
<item name="backgroundTint">@color/colorPrimary</item>
|
<item name="backgroundTint">@color/colorPrimary</item>
|
||||||
<item name="rippleColor">@color/colorPrimaryDark</item>
|
<item name="rippleColor">@color/cardview_dark_background</item>
|
||||||
<item name="android:textColor">@android:color/black</item>
|
<item name="android:textColor">@android:color/black</item>
|
||||||
<item name="android:textSize">14sp</item>
|
<item name="android:textSize">14sp</item>
|
||||||
|
<item name="android:layout_marginEnd">4dp</item>
|
||||||
|
<item name="android:layout_marginBottom">2dp</item>
|
||||||
<item name="android:textAllCaps">false</item>
|
<item name="android:textAllCaps">false</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ buildscript {
|
|||||||
//safe-Args
|
//safe-Args
|
||||||
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
|
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
|
||||||
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
|
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
|
||||||
// classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user