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">
|
||||
<dictionary name="shabinder">
|
||||
<words>
|
||||
<w>albumseokey</w>
|
||||
<w>amita</w>
|
||||
<w>cardview</w>
|
||||
<w>cherrypick</w>
|
||||
<w>downloadrecord</w>
|
||||
<w>emoji</w>
|
||||
<w>ffmpeg</w>
|
||||
<w>flyer</w>
|
||||
<w>gaana</w>
|
||||
<w>gener</w>
|
||||
<w>hqdefault</w>
|
||||
<w>insta</w>
|
||||
<w>instagram</w>
|
||||
@ -19,8 +24,10 @@
|
||||
<w>musicplaceholder</w>
|
||||
<w>raleway</w>
|
||||
<w>semibold</w>
|
||||
<w>seokey</w>
|
||||
<w>shabinder</w>
|
||||
<w>singh</w>
|
||||
<w>snackbar</w>
|
||||
<w>spoti</w>
|
||||
<w>spotiflyer</w>
|
||||
<w>spotify</w>
|
||||
|
@ -21,7 +21,7 @@ apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: "androidx.navigation.safeargs.kotlin"
|
||||
apply plugin: 'dagger.hilt.android.plugin'
|
||||
//apply plugin: 'kotlinx-serialization'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
@ -90,8 +90,10 @@ dependencies {
|
||||
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-android:1.4.0'
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1"
|
||||
|
||||
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"
|
||||
implementation "androidx.room:room-ktx:2.2.5"
|
||||
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.retrofit2:converter-moshi: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 '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 {
|
||||
# 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
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
@ -28,6 +28,7 @@ import android.os.Bundle
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.databinding.DataBindingUtil
|
||||
@ -35,10 +36,12 @@ import androidx.lifecycle.ViewModelProvider
|
||||
import com.github.javiersantos.appupdater.AppUpdater
|
||||
import com.github.javiersantos.appupdater.enums.UpdateFrom
|
||||
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.SpotifyService
|
||||
import com.shabinder.spotiflyer.utils.SpotifyServiceTokenRequest
|
||||
import com.shabinder.spotiflyer.utils.createDirectories
|
||||
import com.shabinder.spotiflyer.utils.isOnline
|
||||
import com.shabinder.spotiflyer.utils.startService
|
||||
import com.squareup.moshi.Moshi
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@ -54,35 +57,29 @@ import javax.inject.Inject
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : AppCompatActivity(){
|
||||
private var spotifyService : SpotifyService? = null
|
||||
private var isConnected: Boolean = false
|
||||
private var sharedPref :SharedPreferences? = null
|
||||
private var token :String =""
|
||||
private lateinit var binding: MainActivityBinding
|
||||
lateinit var snackBarAnchor: View
|
||||
private lateinit var sharedViewModel: SharedViewModel
|
||||
@Inject lateinit var spotifyServiceTokenRequest: SpotifyServiceTokenRequest
|
||||
@Inject lateinit var moshi: Moshi
|
||||
@Inject lateinit var spotifyServiceTokenRequest: SpotifyServiceTokenRequest
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = DataBindingUtil.setContentView(this, R.layout.main_activity)
|
||||
snackBarAnchor = binding.snackBarPosition
|
||||
sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)
|
||||
//Enabling Dark Mode
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
|
||||
sharedPref = this.getPreferences(Context.MODE_PRIVATE)
|
||||
|
||||
if(sharedViewModel.spotifyService.value == null){
|
||||
authenticateSpotify()
|
||||
}else{
|
||||
implementSpotifyService(sharedViewModel.accessToken.value!!)
|
||||
}
|
||||
|
||||
requestPermission()
|
||||
disableDozeMode()
|
||||
checkIfLatestVersion()
|
||||
createDirectories()
|
||||
isConnected = sharedViewModel.isOnline(this)
|
||||
sharedViewModel.isConnected.value = isConnected
|
||||
Log.i("Connection Status", isConnected.toString())
|
||||
Log.i("Connection Status", isOnline().toString())
|
||||
|
||||
//starting Notification and Downloader Service!
|
||||
startService(this)
|
||||
@ -140,7 +137,7 @@ class MainActivity : AppCompatActivity(){
|
||||
"Bearer $token"
|
||||
).build()
|
||||
chain.proceed(request)
|
||||
})
|
||||
}).addInterceptor(NetworkInterceptor())
|
||||
|
||||
val retrofit = Retrofit.Builder()
|
||||
.baseUrl("https://api.spotify.com/v1/")
|
||||
@ -155,16 +152,12 @@ class MainActivity : AppCompatActivity(){
|
||||
|
||||
fun authenticateSpotify() {
|
||||
sharedViewModel.uiScope.launch {
|
||||
if (isConnected) {
|
||||
Log.i("Post Request", "Made")
|
||||
token = spotifyServiceTokenRequest.getToken()!!.access_token
|
||||
implementSpotifyService(token)
|
||||
Log.i("Post Request", token)
|
||||
sharedViewModel.accessToken.value = token
|
||||
}else{
|
||||
Log.i("network", "unavailable")
|
||||
// sharedViewModel.showAlertDialog(resources,this@MainActivity)
|
||||
Log.i("Spotify Authentication","Started")
|
||||
val token = spotifyServiceTokenRequest.getToken()
|
||||
token.value?.let {
|
||||
implementSpotifyService(it.access_token)
|
||||
}
|
||||
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() {
|
||||
val appUpdater = AppUpdater(this)
|
||||
.showAppUpdated(false)//true:Show App is Update Dialog
|
||||
@ -220,14 +200,7 @@ class MainActivity : AppCompatActivity(){
|
||||
appUpdater.start()
|
||||
}
|
||||
|
||||
companion object{
|
||||
private var instance = MainActivity()
|
||||
fun getInstance():MainActivity{
|
||||
return instance
|
||||
}
|
||||
}
|
||||
init {
|
||||
instance = this
|
||||
activity = this
|
||||
}
|
||||
}
|
@ -17,49 +17,22 @@
|
||||
|
||||
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.ViewModel
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.shabinder.spotiflyer.utils.SpotifyService
|
||||
import com.shabinder.spotiflyer.networking.SpotifyService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import java.io.File
|
||||
|
||||
class SharedViewModel : ViewModel() {
|
||||
var intentString = MutableLiveData<String>().apply { value = "" }
|
||||
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()
|
||||
|
||||
val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
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.Toast
|
||||
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.Provider.activity
|
||||
import com.shabinder.spotiflyer.utils.Provider.defaultDir
|
||||
@ -39,7 +43,7 @@ import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.io.File
|
||||
|
||||
object SpotifyDownloadHelper {
|
||||
object DownloadHelper {
|
||||
|
||||
var statusBar:TextView? = null
|
||||
var youtubeMusicApi: YoutubeMusicApi? = null
|
||||
@ -55,12 +59,16 @@ object SpotifyDownloadHelper {
|
||||
suspend fun downloadAllTracks(
|
||||
type:String,
|
||||
subFolder: String?,
|
||||
trackList: List<Track>) {
|
||||
trackList: List<TrackDetails>) {
|
||||
resetStatusBar()// For New Download Request's Status
|
||||
val downloadList = ArrayList<DownloadObject>()
|
||||
|
||||
withContext(Dispatchers.Main){
|
||||
total += trackList.size // Adding New Download List Count to StatusBar
|
||||
trackList.forEachIndexed { index, it ->
|
||||
if(!isOnline()){
|
||||
showNoConnectionAlert()
|
||||
return@withContext
|
||||
}
|
||||
if(it.downloaded == DownloadStatus.Downloaded){//Download Already Present!!
|
||||
processed++
|
||||
if(index == (trackList.size-1)){//LastElement
|
||||
@ -74,49 +82,32 @@ object SpotifyDownloadHelper {
|
||||
},5000)
|
||||
}
|
||||
}else{
|
||||
val artistsList = mutableListOf<String>()
|
||||
it.artists?.forEach { artist -> artistsList.add(artist!!.name!!) }
|
||||
val searchQuery = "${it.name} - ${artistsList.joinToString(",")}"
|
||||
|
||||
val jsonBody = makeJsonBody(searchQuery.trim())
|
||||
val searchQuery = "${it.title} - ${it.artists.joinToString(",")}"
|
||||
val jsonBody = makeJsonBody(searchQuery.trim()).toJsonString()
|
||||
youtubeMusicApi?.getYoutubeMusicResponse(jsonBody)?.enqueue(
|
||||
object : Callback<String>{
|
||||
override fun onResponse(call: Call<String>, response: Response<String>) {
|
||||
sharedViewModel?.uiScope?.launch {
|
||||
val videoId = sortByBestMatch(
|
||||
getYTTracks(response.body().toString()),
|
||||
trackName = it.name.toString(),
|
||||
trackArtists = artistsList,
|
||||
trackDurationSec = (it.duration_ms/1000).toInt()
|
||||
trackName = it.title,
|
||||
trackArtists = it.artists,
|
||||
trackDurationSec = it.durationSec
|
||||
).keys.firstOrNull()
|
||||
Log.i("Spotify Helper Video ID",videoId ?: "Not Found")
|
||||
|
||||
if(videoId.isNullOrBlank()) {notFound++ ; updateStatusBar()}
|
||||
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 =
|
||||
Environment.getExternalStorageDirectory().toString() + File.separator +
|
||||
defaultDir +
|
||||
removeIllegalChars(type) + File.separator +
|
||||
(if (subFolder == null) { "" }
|
||||
else { removeIllegalChars(subFolder) + File.separator }
|
||||
+ removeIllegalChars(it.name!!) + ".m4a")
|
||||
+ removeIllegalChars(it.title) + ".m4a")
|
||||
|
||||
val downloadObject = DownloadObject(
|
||||
trackDetails = trackDetails,
|
||||
trackDetails = it,
|
||||
ytVideoId = videoId,
|
||||
outputFile = outputFile
|
||||
)
|
||||
@ -150,6 +141,13 @@ object SpotifyDownloadHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetStatusBar() {
|
||||
total = 0
|
||||
processed = 0
|
||||
notFound = 0
|
||||
updateStatusBar()
|
||||
}
|
||||
|
||||
private fun animateStatusBar() {
|
||||
val anim: Animation = AlphaAnimation(0.3f, 0.9f)
|
||||
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.utils.Provider.activity
|
||||
import com.shabinder.spotiflyer.utils.Provider.defaultDir
|
||||
import com.shabinder.spotiflyer.utils.isOnline
|
||||
import com.shabinder.spotiflyer.utils.removeIllegalChars
|
||||
import com.shabinder.spotiflyer.utils.showNoConnectionAlert
|
||||
import com.shabinder.spotiflyer.utils.startService
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
@ -38,6 +40,10 @@ object YTDownloadHelper {
|
||||
){
|
||||
val downloadList = ArrayList<DownloadObject>()
|
||||
tracks.forEach {
|
||||
if(!isOnline()){
|
||||
showNoConnectionAlert()
|
||||
return
|
||||
}
|
||||
val outputFile: String =
|
||||
Environment.getExternalStorageDirectory().toString() + File.separator +
|
||||
defaultDir +
|
||||
|
@ -85,7 +85,7 @@ fun getYTTracks(response: String):List<YoutubeTrack>{
|
||||
! Songs details are ALWAYS in the following order:
|
||||
! 0 - Name
|
||||
! 1 - Type (Song)
|
||||
! 2 - Artist
|
||||
! 2 - com.shabinder.spotiflyer.models.gaana.Artist
|
||||
! 3 - Album
|
||||
! 4 - Duration (mm:ss)
|
||||
!
|
||||
|
@ -18,6 +18,7 @@
|
||||
package com.shabinder.spotiflyer.models
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.shabinder.spotiflyer.models.spotify.Source
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import java.io.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
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
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Parcelize
|
||||
data class YTTrack(
|
||||
var id:String?,
|
||||
var title:String?,
|
||||
var duration:Int?,
|
||||
var author:String?,
|
||||
var viewCount:Long?,
|
||||
var thumbnails:List<String?>?
|
||||
):Parcelable
|
||||
@Serializable
|
||||
data class Optional<T>(val value: T?)
|
@ -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/>.
|
||||
*/
|
||||
|
||||
package com.shabinder.spotiflyer.models
|
||||
package com.shabinder.spotiflyer.models.spotify
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,7 @@
|
||||
* 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 kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,7 @@
|
||||
* 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 kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,7 @@
|
||||
* 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 kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,7 @@
|
||||
* 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 kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,7 @@
|
||||
* 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 kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,7 @@
|
||||
* 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 kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,7 @@
|
||||
* 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 kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,7 @@
|
||||
* 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 kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,7 @@
|
||||
* 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 com.squareup.moshi.Json
|
@ -15,7 +15,7 @@
|
||||
* 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 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/>.
|
||||
*/
|
||||
|
||||
package com.shabinder.spotiflyer.models
|
||||
package com.shabinder.spotiflyer.models.spotify
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.android.parcel.Parcelize
|
@ -15,9 +15,10 @@
|
||||
* 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 com.shabinder.spotiflyer.models.DownloadStatus
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
@ -39,5 +40,6 @@ data class Track(
|
||||
var album: Album? = null,
|
||||
var external_ids: Map<String?, String?>? = 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/>.
|
||||
*/
|
||||
|
||||
package com.shabinder.spotiflyer.models
|
||||
package com.shabinder.spotiflyer.models.spotify
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.android.parcel.Parcelize
|
@ -15,7 +15,7 @@
|
||||
* 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 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/>.
|
||||
*/
|
||||
|
||||
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.*
|
||||
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
@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")
|
||||
suspend fun getPlaylistTracks(
|
||||
@Path("playlist_id") playlistId: String?,
|
||||
@Query("offset") offset: Int = 0,
|
||||
@Query("limit") limit: Int = 100
|
||||
): PagingObjectPlaylistTrack
|
||||
): Optional<PagingObjectPlaylistTrack>
|
||||
|
||||
@GET("tracks/{id}")
|
||||
suspend fun getTrack(@Path("id") trackId: String?): Track
|
||||
suspend fun getTrack(@Path("id") trackId: String?): Optional<Track>
|
||||
|
||||
@GET("episodes/{id}")
|
||||
suspend fun getEpisode(@Path("id") episodeId: String?): Track
|
||||
suspend fun getEpisode(@Path("id") episodeId: String?): Optional<Track>
|
||||
|
||||
@GET("shows/{id}")
|
||||
suspend fun getShow(@Path("id") showId: String?): Track
|
||||
suspend fun getShow(@Path("id") showId: String?): Optional<Track>
|
||||
|
||||
@GET("albums/{id}")
|
||||
suspend fun getAlbum(@Path("id") albumId: String?): Album
|
||||
suspend fun getAlbum(@Path("id") albumId: String?): Optional<Album>
|
||||
}
|
||||
|
||||
interface SpotifyServiceTokenRequest{
|
||||
|
||||
@POST("api/token")
|
||||
@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/>.
|
||||
*/
|
||||
|
||||
package com.shabinder.spotiflyer.utils
|
||||
package com.shabinder.spotiflyer.networking
|
||||
|
||||
import com.beust.klaxon.JsonObject
|
||||
import retrofit2.Call
|
||||
@ -25,21 +25,12 @@ import retrofit2.http.POST
|
||||
|
||||
|
||||
const val apiKey = "AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30"
|
||||
/*val body = """{
|
||||
"context": {
|
||||
"client": {
|
||||
"clientName": "WEB_REMIX",
|
||||
"clientVersion": "0.1"
|
||||
}
|
||||
},
|
||||
"query": "songSearchQuery"
|
||||
}"""*/
|
||||
|
||||
interface YoutubeMusicApi {
|
||||
|
||||
@Headers("Content-Type: application/json", "Referer: https://music.youtube.com/search")
|
||||
@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{
|
@ -18,6 +18,7 @@
|
||||
package com.shabinder.spotiflyer.recyclerView
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Environment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@ -27,23 +28,21 @@ import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.shabinder.spotiflyer.R
|
||||
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.Source
|
||||
import com.shabinder.spotiflyer.models.Track
|
||||
import com.shabinder.spotiflyer.models.TrackDetails
|
||||
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.utils.*
|
||||
import com.shabinder.spotiflyer.utils.Provider.activity
|
||||
import com.shabinder.spotiflyer.utils.bindImage
|
||||
import com.shabinder.spotiflyer.utils.rotateAnim
|
||||
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
|
||||
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val binding = TrackListItemBinding.inflate(layoutInflater,parent,false)
|
||||
@ -55,7 +54,7 @@ class SpotifyTrackListAdapter: ListAdapter<Track,SpotifyTrackListAdapter.ViewHol
|
||||
val item = getItem(position)
|
||||
if(itemCount ==1 || isAlbum){
|
||||
holder.binding.imageUrl.visibility = View.GONE}else{
|
||||
spotifyViewModel!!.uiScope.launch {
|
||||
spotifyViewModel.uiScope.launch {
|
||||
//Placeholder Set
|
||||
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.clearAnimation()
|
||||
holder.binding.btnDownload.setOnClickListener{
|
||||
if(!isOnline()){
|
||||
showNoConnectionAlert()
|
||||
return@setOnClickListener
|
||||
}
|
||||
Toast.makeText(activity,"Processing!",Toast.LENGTH_SHORT).show()
|
||||
holder.binding.btnDownload.setImageResource(R.drawable.ic_refresh)
|
||||
rotateAnim(it)
|
||||
item.downloaded = DownloadStatus.Downloading
|
||||
spotifyViewModel!!.uiScope.launch {
|
||||
val itemList = mutableListOf<Track>()
|
||||
itemList.add(item)
|
||||
downloadAllTracks(spotifyViewModel!!.folderType,spotifyViewModel!!.subFolder,itemList)
|
||||
spotifyViewModel.uiScope.launch {
|
||||
val itemList = mutableListOf<TrackDetails>()
|
||||
itemList.add(item.let { track ->
|
||||
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!
|
||||
}
|
||||
|
@ -20,19 +20,16 @@ package com.shabinder.spotiflyer.recyclerView
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import com.shabinder.spotiflyer.R
|
||||
import com.shabinder.spotiflyer.databinding.TrackListItemBinding
|
||||
import com.shabinder.spotiflyer.downloadHelper.YTDownloadHelper
|
||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||
import com.shabinder.spotiflyer.models.Source
|
||||
import com.shabinder.spotiflyer.models.TrackDetails
|
||||
import com.shabinder.spotiflyer.models.spotify.Source
|
||||
import com.shabinder.spotiflyer.ui.youtube.YoutubeViewModel
|
||||
import com.shabinder.spotiflyer.utils.Provider
|
||||
import com.shabinder.spotiflyer.utils.bindImage
|
||||
import com.shabinder.spotiflyer.utils.rotateAnim
|
||||
import com.shabinder.spotiflyer.utils.*
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
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.clearAnimation()
|
||||
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)
|
||||
rotateAnim(it)
|
||||
item.downloaded = DownloadStatus.Downloading
|
||||
|
@ -1,5 +1,5 @@
|
||||
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
|
||||
|
@ -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.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.shabinder.spotiflyer.MainActivity
|
||||
import com.shabinder.spotiflyer.R
|
||||
import com.shabinder.spotiflyer.SharedViewModel
|
||||
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 dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@ -55,14 +59,20 @@ class MainFragment : Fragment() {
|
||||
): View? {
|
||||
binding = DataBindingUtil.inflate(inflater,R.layout.main_fragment,container,false)
|
||||
initializeAll()
|
||||
|
||||
binding.btnSearch.setOnClickListener {
|
||||
if(!isOnline()){
|
||||
showNoConnectionAlert()
|
||||
return@setOnClickListener
|
||||
}
|
||||
val link = binding.linkSearch.text.toString()
|
||||
if (link.contains("spotify",true)){
|
||||
if(sharedViewModel.spotifyService.value == null){//Authentication pending!!
|
||||
(activity as MainActivity).authenticateSpotify()
|
||||
}
|
||||
findNavController().navigate(MainFragmentDirections.actionMainFragmentToSpotifyFragment(link))
|
||||
}else if(link.contains("youtube.com",true) || link.contains("youtu.be",true) ){
|
||||
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()
|
||||
return binding.root
|
||||
@ -97,10 +107,15 @@ class MainFragment : Fragment() {
|
||||
sharedViewModel.intentString.observe(viewLifecycleOwner,{
|
||||
if(it != ""){
|
||||
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
|
||||
Thread.sleep(1000)
|
||||
}
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main){
|
||||
binding.linkSearch.setText(sharedViewModel.intentString.value)
|
||||
binding.btnSearch.performClick()
|
||||
|
@ -22,46 +22,43 @@ import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.ConnectivityManager
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import com.shabinder.spotiflyer.MainActivity
|
||||
import com.shabinder.spotiflyer.R
|
||||
import com.shabinder.spotiflyer.SharedViewModel
|
||||
import com.shabinder.spotiflyer.databinding.SpotifyFragmentBinding
|
||||
import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper
|
||||
import com.shabinder.spotiflyer.databinding.TrackListFragmentBinding
|
||||
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper
|
||||
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.spotify.Source
|
||||
import com.shabinder.spotiflyer.networking.YoutubeMusicApi
|
||||
import com.shabinder.spotiflyer.recyclerView.SpotifyTrackListAdapter
|
||||
import com.shabinder.spotiflyer.utils.YoutubeMusicApi
|
||||
import com.shabinder.spotiflyer.utils.bindImage
|
||||
import com.shabinder.spotiflyer.utils.loadAllImages
|
||||
import com.shabinder.spotiflyer.utils.rotateAnim
|
||||
import com.shabinder.spotiflyer.utils.*
|
||||
import com.shabinder.spotiflyer.utils.Provider.defaultDir
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SpotifyFragment : Fragment() {
|
||||
private lateinit var binding:SpotifyFragmentBinding
|
||||
private lateinit var spotifyViewModel: SpotifyViewModel
|
||||
private lateinit var binding:TrackListFragmentBinding
|
||||
private lateinit var sharedViewModel: SharedViewModel
|
||||
private lateinit var adapterSpotify:SpotifyTrackListAdapter
|
||||
@Inject lateinit var youtubeMusicApi: YoutubeMusicApi
|
||||
private lateinit var viewModel: SpotifyViewModel
|
||||
private lateinit var adapter:SpotifyTrackListAdapter
|
||||
private var intentFilter:IntentFilter? = null
|
||||
private var updateUIReceiver: BroadcastReceiver? = null
|
||||
|
||||
@ -71,8 +68,7 @@ class SpotifyFragment : Fragment() {
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = DataBindingUtil.inflate(inflater,R.layout.spotify_fragment,container,false)
|
||||
adapterSpotify = SpotifyTrackListAdapter()
|
||||
binding = DataBindingUtil.inflate(inflater,R.layout.track_list_fragment,container,false)
|
||||
initializeAll()
|
||||
initializeLiveDataObservers()
|
||||
initializeBroadcast()
|
||||
@ -88,34 +84,35 @@ class SpotifyFragment : Fragment() {
|
||||
if(sharedViewModel.spotifyService.value == null){//Authentication pending!!
|
||||
(activity as MainActivity).authenticateSpotify()
|
||||
}
|
||||
if(!isOnline()){//Device Offline
|
||||
sharedViewModel.showAlertDialog(resources,requireContext())
|
||||
}else if (type == "Error" || link == "Error") {//Incorrect Link
|
||||
showToast("Please Check Your Link!")
|
||||
if (type == "Error" || link == "Error") {//Incorrect Link
|
||||
showMessage("Please Check Your Link!")
|
||||
}else if(spotifyLink.contains("open.spotify",true)){//Link Validation!!
|
||||
if(type == "episode" || type == "show"){//TODO Implementation
|
||||
showToast("Implementing Soon, Stay Tuned!")
|
||||
showMessage("Implementing Soon, Stay Tuned!")
|
||||
}
|
||||
else{
|
||||
spotifyViewModel.spotifySearch(type,link)
|
||||
if(type=="album")adapterSpotify.isAlbum = true
|
||||
viewModel.spotifySearch(type,link)
|
||||
if(type=="album")adapter.isAlbum = true
|
||||
|
||||
binding.btnDownloadAll.setOnClickListener {
|
||||
|
||||
if(!isOnline()){
|
||||
showNoConnectionAlert()
|
||||
return@setOnClickListener
|
||||
}
|
||||
binding.btnDownloadAll.visibility = View.GONE
|
||||
binding.downloadingFab.visibility = View.VISIBLE
|
||||
|
||||
rotateAnim(binding.downloadingFab)
|
||||
for (track in spotifyViewModel.trackList.value!!){
|
||||
for (track in viewModel.trackList.value!!){
|
||||
if(track.downloaded != DownloadStatus.Downloaded){
|
||||
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){
|
||||
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
|
||||
urlList.add("spotify")
|
||||
loadAllImages(
|
||||
@ -123,11 +120,29 @@ class SpotifyFragment : Fragment() {
|
||||
urlList
|
||||
)
|
||||
}
|
||||
spotifyViewModel.uiScope.launch {
|
||||
SpotifyDownloadHelper.downloadAllTracks(
|
||||
spotifyViewModel.folderType,
|
||||
spotifyViewModel.subFolder,
|
||||
spotifyViewModel.trackList.value!!,
|
||||
viewModel.uiScope.launch {
|
||||
val finalList = viewModel.trackList.value?.map{
|
||||
val artistsList = mutableListOf<String>()
|
||||
it.artists?.forEach { artist -> artistsList.add(artist!!.name!!) }
|
||||
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
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
initializeBroadcast()
|
||||
/**
|
||||
* Basic Initialization
|
||||
**/
|
||||
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
|
||||
@ -181,17 +176,17 @@ class SpotifyFragment : Fragment() {
|
||||
/**
|
||||
* CoverUrl Binding Observer!
|
||||
**/
|
||||
spotifyViewModel.coverUrl.observe(viewLifecycleOwner, {
|
||||
viewModel.coverUrl.observe(viewLifecycleOwner, {
|
||||
if(it!="Loading") bindImage(binding.coverImage,it, Source.Spotify)
|
||||
})
|
||||
|
||||
/**
|
||||
* TrackList Binding Observer!
|
||||
**/
|
||||
spotifyViewModel.trackList.observe(viewLifecycleOwner, {
|
||||
viewModel.trackList.observe(viewLifecycleOwner, {
|
||||
if (it.isNotEmpty()){
|
||||
Log.i("SpotifyFragment","TrackList Updated")
|
||||
adapterConfig(it)
|
||||
adapter.submitList(it)
|
||||
checkIfAllDownloaded()
|
||||
}
|
||||
})
|
||||
@ -199,7 +194,7 @@ class SpotifyFragment : Fragment() {
|
||||
/**
|
||||
* Title Binding Observer!
|
||||
**/
|
||||
spotifyViewModel.title.observe(viewLifecycleOwner, {
|
||||
viewModel.title.observe(viewLifecycleOwner, {
|
||||
binding.titleView.text = it
|
||||
})
|
||||
|
||||
@ -213,7 +208,7 @@ class SpotifyFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun checkIfAllDownloaded() {
|
||||
if(!spotifyViewModel.trackList.value!!.any { it.downloaded != DownloadStatus.Downloaded }){
|
||||
if(!viewModel.trackList.value!!.any { it.downloaded != DownloadStatus.Downloaded }){
|
||||
//All Tracks Downloaded
|
||||
binding.btnDownloadAll.visibility = View.GONE
|
||||
binding.downloadingFab.apply{
|
||||
@ -224,47 +219,41 @@ class SpotifyFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun initializeBroadcast() {
|
||||
intentFilter = IntentFilter()
|
||||
intentFilter?.addAction("track_download_completed")
|
||||
|
||||
/**
|
||||
* Basic Initialization
|
||||
**/
|
||||
private fun initializeAll() {
|
||||
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
|
||||
spotifyViewModel = ViewModelProvider(this).get(SpotifyViewModel::class.java)
|
||||
sharedViewModel.spotifyService.observe(viewLifecycleOwner, Observer {
|
||||
spotifyViewModel.spotifyService = it
|
||||
})
|
||||
SpotifyDownloadHelper.youtubeMusicApi = youtubeMusicApi
|
||||
SpotifyDownloadHelper.sharedViewModel = sharedViewModel
|
||||
SpotifyDownloadHelper.statusBar = binding.statusBar
|
||||
binding.trackList.adapter = adapterSpotify
|
||||
(binding.trackList.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
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 = viewModel.trackList.value?.map { it.name }?.indexOf(trackDetails.title) ?: -1
|
||||
Log.i("Track","Download Completed Intent :$position")
|
||||
if(position != -1) {
|
||||
val track = viewModel.trackList.value?.get(position)
|
||||
track?.let{
|
||||
it.downloaded = DownloadStatus.Downloaded
|
||||
viewModel.trackList.value?.set(position, it)
|
||||
adapter.notifyItemChanged(position)
|
||||
checkIfAllDownloaded()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
requireActivity().registerReceiver(updateUIReceiver, intentFilter)
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure Recycler View Adapter
|
||||
**/
|
||||
private fun adapterConfig(trackList: List<Track>){
|
||||
adapterSpotify.spotifyViewModel = spotifyViewModel
|
||||
adapterSpotify.submitList(trackList)
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
initializeBroadcast()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Util. Function to create toasts!
|
||||
**/
|
||||
private fun showToast(message:String){
|
||||
Toast.makeText(context,message,Toast.LENGTH_SHORT).show()
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
requireActivity().unregisterReceiver(updateUIReceiver)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 com.shabinder.spotiflyer.database.DatabaseDAO
|
||||
import com.shabinder.spotiflyer.database.DownloadRecord
|
||||
import com.shabinder.spotiflyer.models.*
|
||||
import com.shabinder.spotiflyer.utils.SpotifyService
|
||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||
import com.shabinder.spotiflyer.models.spotify.*
|
||||
import com.shabinder.spotiflyer.networking.SpotifyService
|
||||
import com.shabinder.spotiflyer.utils.finalOutputDir
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.File
|
||||
@ -153,19 +154,19 @@ class SpotifyViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO
|
||||
|
||||
private suspend fun getTrackDetails(trackLink:String): Track?{
|
||||
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?{
|
||||
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?{
|
||||
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?{
|
||||
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() {
|
||||
|
@ -26,22 +26,19 @@ import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.github.kiulian.downloader.YoutubeDownloader
|
||||
import com.shabinder.spotiflyer.R
|
||||
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.models.DownloadStatus
|
||||
import com.shabinder.spotiflyer.models.Source
|
||||
import com.shabinder.spotiflyer.models.TrackDetails
|
||||
import com.shabinder.spotiflyer.models.spotify.Source
|
||||
import com.shabinder.spotiflyer.recyclerView.YoutubeTrackListAdapter
|
||||
import com.shabinder.spotiflyer.utils.bindImage
|
||||
import com.shabinder.spotiflyer.utils.loadAllImages
|
||||
import com.shabinder.spotiflyer.utils.rotateAnim
|
||||
import com.shabinder.spotiflyer.utils.*
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@ -50,24 +47,24 @@ import javax.inject.Inject
|
||||
@AndroidEntryPoint
|
||||
class YoutubeFragment : Fragment() {
|
||||
|
||||
private lateinit var binding:YoutubeFragmentBinding
|
||||
private lateinit var youtubeViewModel: YoutubeViewModel
|
||||
private lateinit var binding: TrackListFragmentBinding
|
||||
private lateinit var viewModel: YoutubeViewModel
|
||||
private lateinit var sharedViewModel: SharedViewModel
|
||||
@Inject lateinit var ytDownloader: YoutubeDownloader
|
||||
private lateinit var adapter : YoutubeTrackListAdapter
|
||||
private val sampleDomain1 = "youtube.com"
|
||||
private val sampleDomain2 = "youtu.be"
|
||||
private var intentFilter: IntentFilter? = null
|
||||
private var updateUIReceiver: BroadcastReceiver? = null
|
||||
private val sampleDomain2 = "youtu.be"
|
||||
private val sampleDomain1 = "youtube.com"
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = DataBindingUtil.inflate(inflater,R.layout.youtube_fragment,container,false)
|
||||
youtubeViewModel = ViewModelProvider(this).get(YoutubeViewModel::class.java)
|
||||
binding = DataBindingUtil.inflate(inflater,R.layout.track_list_fragment,container,false)
|
||||
viewModel = ViewModelProvider(this).get(YoutubeViewModel::class.java)
|
||||
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
|
||||
adapter = YoutubeTrackListAdapter(youtubeViewModel)
|
||||
adapter = YoutubeTrackListAdapter(viewModel)
|
||||
binding.trackList.adapter = adapter
|
||||
|
||||
initializeLiveDataObservers()
|
||||
@ -84,7 +81,7 @@ class YoutubeFragment : Fragment() {
|
||||
if(link.contains("playlist",true) || link.contains("list",true)){
|
||||
// Given Link is of a Playlist
|
||||
val playlistId = link.substringAfter("?list=").substringAfter("&list=").substringBefore("&")
|
||||
youtubeViewModel.getYTPlaylist(playlistId,ytDownloader)
|
||||
viewModel.getYTPlaylist(playlistId,ytDownloader)
|
||||
}else{//Given Link is of a Video
|
||||
var searchId = "error"
|
||||
if(link.contains(sampleDomain1,true) ){
|
||||
@ -94,29 +91,33 @@ class YoutubeFragment : Fragment() {
|
||||
searchId = link.substringAfterLast("/","error")
|
||||
}
|
||||
if(searchId != "error") {
|
||||
youtubeViewModel.getYTTrack(searchId,ytDownloader)
|
||||
}else{showToast("Your Youtube Link is not of a Video!!")}
|
||||
viewModel.getYTTrack(searchId,ytDownloader)
|
||||
}else{showMessage("Your Youtube Link is not of a Video!!")}
|
||||
}
|
||||
|
||||
/*
|
||||
* Download All Tracks
|
||||
* */
|
||||
binding.btnDownloadAll.setOnClickListener {
|
||||
if(!isOnline()){
|
||||
showNoConnectionAlert()
|
||||
return@setOnClickListener
|
||||
}
|
||||
binding.btnDownloadAll.visibility = View.GONE
|
||||
binding.downloadingFab.visibility = View.VISIBLE
|
||||
|
||||
rotateAnim(binding.downloadingFab)
|
||||
|
||||
for (track in youtubeViewModel.ytTrackList.value?: listOf()){
|
||||
for (track in viewModel.ytTrackList.value?: listOf()){
|
||||
if(track.downloaded != DownloadStatus.Downloaded){
|
||||
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){
|
||||
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")}
|
||||
//Appending Source
|
||||
urlList.add("youtube")
|
||||
@ -125,11 +126,11 @@ class YoutubeFragment : Fragment() {
|
||||
urlList
|
||||
)
|
||||
}
|
||||
youtubeViewModel.uiScope.launch {
|
||||
viewModel.uiScope.launch {
|
||||
YTDownloadHelper.downloadYTTracks(
|
||||
type = youtubeViewModel.folderType,
|
||||
subFolder = youtubeViewModel.subFolder,
|
||||
tracks = youtubeViewModel.ytTrackList.value ?: listOf()
|
||||
type = viewModel.folderType,
|
||||
subFolder = viewModel.subFolder,
|
||||
tracks = viewModel.ytTrackList.value ?: listOf()
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -149,13 +150,13 @@ class YoutubeFragment : Fragment() {
|
||||
if (intent != null){
|
||||
val trackDetails = intent.getParcelableExtra<TrackDetails?>("track")
|
||||
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")
|
||||
if(position != -1) {
|
||||
val track = youtubeViewModel.ytTrackList.value?.get(position)
|
||||
val track = viewModel.ytTrackList.value?.get(position)
|
||||
track?.let{
|
||||
it.downloaded = DownloadStatus.Downloaded
|
||||
youtubeViewModel.ytTrackList.value?.set(position, it)
|
||||
viewModel.ytTrackList.value?.set(position, it)
|
||||
adapter.notifyItemChanged(position)
|
||||
checkIfAllDownloaded()
|
||||
}
|
||||
@ -173,7 +174,7 @@ class YoutubeFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun checkIfAllDownloaded() {
|
||||
if(!youtubeViewModel.ytTrackList.value!!.any { it.downloaded != DownloadStatus.Downloaded }){
|
||||
if(!viewModel.ytTrackList.value!!.any { it.downloaded != DownloadStatus.Downloaded }){
|
||||
//All Tracks Downloaded
|
||||
binding.btnDownloadAll.visibility = View.GONE
|
||||
binding.downloadingFab.apply{
|
||||
@ -188,38 +189,23 @@ class YoutubeFragment : Fragment() {
|
||||
/**
|
||||
* CoverUrl Binding Observer!
|
||||
**/
|
||||
youtubeViewModel.coverUrl.observe(viewLifecycleOwner, {
|
||||
viewModel.coverUrl.observe(viewLifecycleOwner, {
|
||||
if(it!="Loading") bindImage(binding.coverImage,it, Source.YouTube)
|
||||
})
|
||||
|
||||
/**
|
||||
* TrackList Binding Observer!
|
||||
**/
|
||||
youtubeViewModel.ytTrackList.observe(viewLifecycleOwner, {
|
||||
adapterConfig(it)
|
||||
viewModel.ytTrackList.observe(viewLifecycleOwner, {
|
||||
adapter.submitList(it)
|
||||
})
|
||||
|
||||
/**
|
||||
* Title Binding Observer!
|
||||
**/
|
||||
youtubeViewModel.title.observe(viewLifecycleOwner, {
|
||||
viewModel.title.observe(viewLifecycleOwner, {
|
||||
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.ViewModel
|
||||
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.DownloadRecord
|
||||
import com.shabinder.spotiflyer.models.DownloadStatus
|
||||
import com.shabinder.spotiflyer.models.Source
|
||||
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.showToast
|
||||
import com.shabinder.spotiflyer.utils.finalOutputDir
|
||||
import com.shabinder.spotiflyer.utils.removeIllegalChars
|
||||
import com.shabinder.spotiflyer.utils.showMessage
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.File
|
||||
|
||||
@ -47,7 +45,6 @@ class YoutubeViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO
|
||||
* */
|
||||
|
||||
val ytTrackList = MutableLiveData<MutableList<TrackDetails>>()
|
||||
val format = MutableLiveData<Format>()
|
||||
private val loading = "Loading"
|
||||
var title = 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){
|
||||
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() ?: ""
|
||||
Log.i("YT View Model",detail.toString())
|
||||
ytTrackList.postValue(
|
||||
listOf(TrackDetails(
|
||||
listOf(
|
||||
TrackDetails(
|
||||
title = name,
|
||||
artists = listOf(detail?.author().toString()),
|
||||
durationSec = detail?.lengthSeconds()?:0,
|
||||
albumArt = File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
Provider.defaultDir +".Images/" + searchId + ".jpeg"
|
||||
defaultDir +".Images/" + searchId + ".jpeg"
|
||||
),
|
||||
source = Source.YouTube
|
||||
)).toMutableList()
|
||||
)
|
||||
).toMutableList()
|
||||
)
|
||||
title.postValue(
|
||||
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){
|
||||
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.os.Environment
|
||||
import android.widget.Toast
|
||||
import com.github.kiulian.downloader.YoutubeDownloader
|
||||
import com.shabinder.spotiflyer.App
|
||||
import com.shabinder.spotiflyer.MainActivity
|
||||
import com.shabinder.spotiflyer.database.DatabaseDAO
|
||||
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.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
@ -37,12 +39,12 @@ import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
import retrofit2.converter.scalars.ScalarsConverterFactory
|
||||
import java.io.File
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
||||
@InstallIn(ApplicationComponent::class)
|
||||
@Module
|
||||
object Provider {
|
||||
@ -66,7 +68,7 @@ object Provider {
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideUpi():EasyUpiPayment {
|
||||
return EasyUpiPayment.Builder(MainActivity.getInstance())
|
||||
return EasyUpiPayment.Builder(activity)
|
||||
.setPayeeVpa("technoshab@paytm")
|
||||
.setPayeeName("Shabinder Singh")
|
||||
.setTransactionId("UNIQUE_TRANSACTION_ID")
|
||||
@ -86,39 +88,59 @@ object Provider {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun getSpotifyTokenInterface():SpotifyServiceTokenRequest{
|
||||
fun getSpotifyTokenInterface(moshi: Moshi): SpotifyServiceTokenRequest {
|
||||
val httpClient2: OkHttpClient.Builder = OkHttpClient.Builder()
|
||||
httpClient2.addInterceptor(Interceptor { chain ->
|
||||
.addInterceptor(Interceptor { chain ->
|
||||
val request: Request =
|
||||
chain.request().newBuilder().addHeader(
|
||||
chain.request().newBuilder()
|
||||
.addHeader(
|
||||
"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()
|
||||
chain.proceed(request)
|
||||
})
|
||||
}).addInterceptor(NetworkInterceptor())
|
||||
|
||||
val retrofit = Retrofit.Builder()
|
||||
.baseUrl("https://accounts.spotify.com/")
|
||||
.client(httpClient2.build())
|
||||
.addConverterFactory(MoshiConverterFactory.create(getMoshi()))
|
||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||
.build()
|
||||
return retrofit.create(SpotifyServiceTokenRequest::class.java)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@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()
|
||||
.baseUrl("https://music.youtube.com/youtubei/v1/")
|
||||
.addConverterFactory(ScalarsConverterFactory.create())
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||
.build()
|
||||
|
||||
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.Intent
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
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.request.RequestListener
|
||||
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.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.worker.ForegroundService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@ -64,6 +70,48 @@ fun finalOutputDir(itemName:String? = null,type:String, subFolder:String?=null,e
|
||||
+ 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){
|
||||
val rotate = RotateAnimation(
|
||||
0F, 360F,
|
||||
@ -76,6 +124,18 @@ fun rotateAnim(view: View){
|
||||
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?) {
|
||||
imgUrl?.let {
|
||||
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
|
||||
|
@ -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 {
|
||||
val id3v1Tag = ID3v1Tag().apply {
|
||||
|
@ -23,19 +23,15 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/mainActivity"
|
||||
android:layout_width="match_parent"
|
||||
android:background="@color/black"
|
||||
android:layout_height="match_parent">
|
||||
<TextView
|
||||
android:id="@+id/message"
|
||||
<View
|
||||
android:id="@+id/snackBarPosition"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:background="@drawable/text_background_accented"
|
||||
android:padding="5dp"
|
||||
android:visibility="gone"
|
||||
android:paddingTop="6dp"
|
||||
android:text="Authentication Needed"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="10dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="@drawable/transparent"
|
||||
android:visibility="invisible"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@ -47,7 +43,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:defaultNavHost="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/message"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
|
@ -21,6 +21,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
android:background="@color/black"
|
||||
>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
|
@ -22,6 +22,7 @@
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:background="@color/black"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="25dp"
|
||||
android:fitsSystemWindows="true"
|
||||
@ -74,10 +75,10 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/topLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:background="@color/black"
|
||||
android:foreground="@drawable/gradient"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/cover_image"
|
||||
android:layout_width="0dp"
|
@ -23,7 +23,7 @@
|
||||
<data>
|
||||
<variable
|
||||
name="track"
|
||||
type="com.shabinder.spotiflyer.models.Track" />
|
||||
type="com.shabinder.spotiflyer.models.spotify.Track" />
|
||||
</data>
|
||||
|
||||
<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:name="com.shabinder.spotiflyer.ui.spotify.SpotifyFragment"
|
||||
android:label="main_fragment"
|
||||
tools:layout="@layout/spotify_fragment" >
|
||||
tools:layout="@layout/track_list_fragment" >
|
||||
<argument
|
||||
android:name="link"
|
||||
app:argType="string" />
|
||||
@ -56,7 +56,7 @@
|
||||
android:id="@+id/youtubeFragment"
|
||||
android:name="com.shabinder.spotiflyer.ui.youtube.YoutubeFragment"
|
||||
android:label="YoutubeFragment"
|
||||
tools:layout="@layout/youtube_fragment">
|
||||
tools:layout="@layout/track_list_fragment">
|
||||
<argument
|
||||
android:name="link"
|
||||
app:argType="string" />
|
||||
|
@ -21,7 +21,6 @@
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimaryDark">#000000</item>
|
||||
<item name="colorPrimary">#FC5C7D</item>
|
||||
<item name="android:background">#000000</item>
|
||||
<item name="android:textColor">#FFFFFF</item>
|
||||
<item name="colorAccent">#6A82FB</item>
|
||||
<item name="android:outlineAmbientShadowColor" tools:targetApi="p">#A9B200FF</item>
|
||||
@ -29,7 +28,7 @@
|
||||
<!-- Text Appearances !-->
|
||||
<!-- use our brand's custom TextAppearance4 !-->
|
||||
<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>
|
||||
</style>
|
||||
|
||||
@ -38,18 +37,22 @@
|
||||
<item name="shapeAppearanceMediumComponent">@style/CutShapeAppearance</item>
|
||||
<item name="buttonBarPositiveButtonStyle">@style/Alert.Button.Positive</item>
|
||||
<item name="buttonBarNeutralButtonStyle">@style/Alert.Button.Neutral</item>
|
||||
<item name="android:textSize">22sp</item>
|
||||
<item name="fontFamily">@font/amita</item>
|
||||
</style>
|
||||
<style name="CutShapeAppearance" parent="ShapeAppearance.MaterialComponents.MediumComponent">
|
||||
<item name="background">@color/white</item>
|
||||
<item name="cornerFamily">rounded</item>
|
||||
<item name="cornerSize">20dp</item>
|
||||
<item name="cornerSize">8dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Alert.Button.Positive" parent="Widget.MaterialComponents.Button.TextButton">
|
||||
<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:textSize">14sp</item>
|
||||
<item name="android:layout_marginEnd">4dp</item>
|
||||
<item name="android:layout_marginBottom">2dp</item>
|
||||
<item name="android:textAllCaps">false</item>
|
||||
</style>
|
||||
|
||||
|
@ -33,7 +33,7 @@ buildscript {
|
||||
//safe-Args
|
||||
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
|
||||
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
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user