From c489c8c84abd3fcad8d686cad31bc1cbfce15d69 Mon Sep 17 00:00:00 2001 From: Shabinder Date: Mon, 9 Nov 2020 01:47:26 +0530 Subject: [PATCH] Network Connection Change Handling Improved,i.e,Less Crashes and Gaana Implementation Also Started --- .idea/dictionaries/shabinder.xml | 7 + app/build.gradle | 5 +- app/proguard-rules.pro | 18 ++ .../com/shabinder/spotiflyer/MainActivity.kt | 59 ++--- .../shabinder/spotiflyer/SharedViewModel.kt | 29 +-- ...ifyDownloadHelper.kt => DownloadHelper.kt} | 56 +++-- .../downloadHelper/YTDownloadHelper.kt | 6 + .../downloadHelper/YoutubeProvider.kt | 2 +- .../spotiflyer/models/DownloadObject.kt | 5 +- .../models/{YTTrack.kt => Optional.kt} | 14 +- .../spotiflyer/models/gaana/Artist.kt | 24 ++ .../spotiflyer/models/gaana/CustomArtworks.kt | 28 +++ .../spotiflyer/models/gaana/GaanaAlbum.kt | 26 +++ .../models/gaana/GaanaArtistDetails.kt | 23 ++ .../models/gaana/GaanaArtistTracks.kt | 23 ++ .../spotiflyer/models/gaana/GaanaPlaylist.kt | 28 +++ .../spotiflyer/models/gaana/GaanaSong.kt | 22 ++ .../spotiflyer/models/gaana/Genre.kt | 23 ++ .../shabinder/spotiflyer/models/gaana/Tags.kt | 23 ++ .../spotiflyer/models/gaana/Tracks.kt | 38 ++++ .../spotiflyer/models/{ => spotify}/Album.kt | 2 +- .../spotiflyer/models/{ => spotify}/Artist.kt | 2 +- .../models/{ => spotify}/Copyright.kt | 2 +- .../models/{ => spotify}/Episodes.kt | 2 +- .../models/{ => spotify}/Followers.kt | 2 +- .../spotiflyer/models/{ => spotify}/Image.kt | 2 +- .../models/{ => spotify}/LinkedTrack.kt | 2 +- .../PagingObjectPlaylistTrack.kt | 2 +- .../models/{ => spotify}/PagingObjectTrack.kt | 2 +- .../models/{ => spotify}/Playlist.kt | 2 +- .../models/{ => spotify}/PlaylistTrack.kt | 2 +- .../spotiflyer/models/spotify/Source.kt | 23 ++ .../spotiflyer/models/{ => spotify}/Token.kt | 2 +- .../spotiflyer/models/{ => spotify}/Track.kt | 6 +- .../models/{ => spotify}/UserPrivate.kt | 2 +- .../models/{ => spotify}/UserPublic.kt | 2 +- .../spotiflyer/networking/GaanaInterface.kt | 101 +++++++++ .../{utils => networking}/SpotifyInterface.kt | 37 +-- .../{utils => networking}/YoutubeMusicApi.kt | 15 +- .../recyclerView/SpotifyTrackListAdapter.kt | 50 +++-- .../recyclerView/YoutubeTrackListAdapter.kt | 13 +- .../spotiflyer/samples/response examples.txt | 2 +- .../spotiflyer/ui/gaana/GaanaFragment.kt | 54 +++++ .../spotiflyer/ui/gaana/GaanaViewModel.kt | 24 ++ .../ui/mainfragment/MainFragment.kt | 27 ++- .../spotiflyer/ui/spotify/SpotifyFragment.kt | 211 +++++++++--------- .../spotiflyer/ui/spotify/SpotifyViewModel.kt | 13 +- .../spotiflyer/ui/youtube/YoutubeFragment.kt | 82 +++---- .../spotiflyer/ui/youtube/YoutubeViewModel.kt | 19 +- .../spotiflyer/utils/NetworkInterceptor.kt | 66 ++++++ .../shabinder/spotiflyer/utils/Provider.kt | 52 +++-- .../com/shabinder/spotiflyer/utils/Utils.kt | 62 ++++- .../spotiflyer/worker/ForegroundService.kt | 4 +- app/src/main/res/layout/main_activity.xml | 18 +- app/src/main/res/layout/main_fragment.xml | 1 + ...y_fragment.xml => track_list_fragment.xml} | 3 +- app/src/main/res/layout/track_list_item.xml | 2 +- app/src/main/res/layout/youtube_fragment.xml | 152 ------------- app/src/main/res/navigation/navigation.xml | 4 +- app/src/main/res/values/styles.xml | 11 +- build.gradle | 2 +- 61 files changed, 977 insertions(+), 564 deletions(-) rename app/src/main/java/com/shabinder/spotiflyer/downloadHelper/{SpotifyDownloadHelper.kt => DownloadHelper.kt} (79%) rename app/src/main/java/com/shabinder/spotiflyer/models/{YTTrack.kt => Optional.kt} (73%) mode change 100755 => 100644 create mode 100644 app/src/main/java/com/shabinder/spotiflyer/models/gaana/Artist.kt create mode 100644 app/src/main/java/com/shabinder/spotiflyer/models/gaana/CustomArtworks.kt create mode 100644 app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaAlbum.kt create mode 100644 app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaArtistDetails.kt create mode 100644 app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaArtistTracks.kt create mode 100644 app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaPlaylist.kt create mode 100644 app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaSong.kt create mode 100644 app/src/main/java/com/shabinder/spotiflyer/models/gaana/Genre.kt create mode 100644 app/src/main/java/com/shabinder/spotiflyer/models/gaana/Tags.kt create mode 100644 app/src/main/java/com/shabinder/spotiflyer/models/gaana/Tracks.kt rename app/src/main/java/com/shabinder/spotiflyer/models/{ => spotify}/Album.kt (96%) rename app/src/main/java/com/shabinder/spotiflyer/models/{ => spotify}/Artist.kt (95%) rename app/src/main/java/com/shabinder/spotiflyer/models/{ => spotify}/Copyright.kt (94%) rename app/src/main/java/com/shabinder/spotiflyer/models/{ => spotify}/Episodes.kt (96%) rename app/src/main/java/com/shabinder/spotiflyer/models/{ => spotify}/Followers.kt (94%) rename app/src/main/java/com/shabinder/spotiflyer/models/{ => spotify}/Image.kt (94%) rename app/src/main/java/com/shabinder/spotiflyer/models/{ => spotify}/LinkedTrack.kt (95%) rename app/src/main/java/com/shabinder/spotiflyer/models/{ => spotify}/PagingObjectPlaylistTrack.kt (95%) rename app/src/main/java/com/shabinder/spotiflyer/models/{ => spotify}/PagingObjectTrack.kt (95%) rename app/src/main/java/com/shabinder/spotiflyer/models/{ => spotify}/Playlist.kt (96%) rename app/src/main/java/com/shabinder/spotiflyer/models/{ => spotify}/PlaylistTrack.kt (95%) create mode 100644 app/src/main/java/com/shabinder/spotiflyer/models/spotify/Source.kt rename app/src/main/java/com/shabinder/spotiflyer/models/{ => spotify}/Token.kt (94%) rename app/src/main/java/com/shabinder/spotiflyer/models/{ => spotify}/Track.kt (88%) rename app/src/main/java/com/shabinder/spotiflyer/models/{ => spotify}/UserPrivate.kt (96%) rename app/src/main/java/com/shabinder/spotiflyer/models/{ => spotify}/UserPublic.kt (95%) create mode 100644 app/src/main/java/com/shabinder/spotiflyer/networking/GaanaInterface.kt rename app/src/main/java/com/shabinder/spotiflyer/{utils => networking}/SpotifyInterface.kt (54%) rename app/src/main/java/com/shabinder/spotiflyer/{utils => networking}/YoutubeMusicApi.kt (83%) create mode 100644 app/src/main/java/com/shabinder/spotiflyer/ui/gaana/GaanaFragment.kt create mode 100644 app/src/main/java/com/shabinder/spotiflyer/ui/gaana/GaanaViewModel.kt create mode 100644 app/src/main/java/com/shabinder/spotiflyer/utils/NetworkInterceptor.kt rename app/src/main/res/layout/{spotify_fragment.xml => track_list_fragment.xml} (98%) delete mode 100755 app/src/main/res/layout/youtube_fragment.xml diff --git a/.idea/dictionaries/shabinder.xml b/.idea/dictionaries/shabinder.xml index 739b47bf..b91cbc33 100755 --- a/.idea/dictionaries/shabinder.xml +++ b/.idea/dictionaries/shabinder.xml @@ -1,11 +1,16 @@ + albumseokey + amita + cardview cherrypick downloadrecord emoji ffmpeg flyer + gaana + gener hqdefault insta instagram @@ -19,8 +24,10 @@ musicplaceholder raleway semibold + seokey shabinder singh + snackbar spoti spotiflyer spotify diff --git a/app/build.gradle b/app/build.gradle index 236c0f2b..3382dc5f 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 481bb434..503cf7ce 100755 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -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 diff --git a/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt b/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt index 3b7745d6..bf6e941f 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt @@ -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!!) - } + authenticateSpotify() 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 } } \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/SharedViewModel.kt b/app/src/main/java/com/shabinder/spotiflyer/SharedViewModel.kt index 3705a905..1f9e0a45 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/SharedViewModel.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/SharedViewModel.kt @@ -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().apply { value = "" } var spotifyService = MutableLiveData() - var accessToken = MutableLiveData().apply { value = "" } - var isConnected = MutableLiveData().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 - } } \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/SpotifyDownloadHelper.kt b/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/DownloadHelper.kt similarity index 79% rename from app/src/main/java/com/shabinder/spotiflyer/downloadHelper/SpotifyDownloadHelper.kt rename to app/src/main/java/com/shabinder/spotiflyer/downloadHelper/DownloadHelper.kt index 8d8a903d..106750de 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/SpotifyDownloadHelper.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/DownloadHelper.kt @@ -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,10 +43,10 @@ import retrofit2.Callback import retrofit2.Response import java.io.File -object SpotifyDownloadHelper { +object DownloadHelper { var statusBar:TextView? = null - var youtubeMusicApi:YoutubeMusicApi? = null + var youtubeMusicApi: YoutubeMusicApi? = null var sharedViewModel: SharedViewModel? = null private var total = 0 @@ -55,12 +59,16 @@ object SpotifyDownloadHelper { suspend fun downloadAllTracks( type:String, subFolder: String?, - trackList: List) { + trackList: List) { + resetStatusBar()// For New Download Request's Status val downloadList = ArrayList() - 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() - 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{ override fun onResponse(call: Call, response: Response) { 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 diff --git a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YTDownloadHelper.kt b/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YTDownloadHelper.kt index 523447d4..453903ba 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YTDownloadHelper.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YTDownloadHelper.kt @@ -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() tracks.forEach { + if(!isOnline()){ + showNoConnectionAlert() + return + } val outputFile: String = Environment.getExternalStorageDirectory().toString() + File.separator + defaultDir + diff --git a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YoutubeProvider.kt b/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YoutubeProvider.kt index 1ca9a5b5..743f2dd0 100644 --- a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YoutubeProvider.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YoutubeProvider.kt @@ -85,7 +85,7 @@ fun getYTTracks(response: String):List{ ! 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) ! diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/DownloadObject.kt b/app/src/main/java/com/shabinder/spotiflyer/models/DownloadObject.kt index 033a6944..4fc8016f 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/models/DownloadObject.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/DownloadObject.kt @@ -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 @@ -39,8 +40,8 @@ data class TrackDetails( var lyrics:String?=null, var trackUrl:String?=null, var albumArt: File, - var source:Source, - var downloaded:DownloadStatus = DownloadStatus.NotDownloaded + var source: Source, + var downloaded: DownloadStatus = DownloadStatus.NotDownloaded ):Parcelable enum class DownloadStatus{ diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/YTTrack.kt b/app/src/main/java/com/shabinder/spotiflyer/models/Optional.kt old mode 100755 new mode 100644 similarity index 73% rename from app/src/main/java/com/shabinder/spotiflyer/models/YTTrack.kt rename to app/src/main/java/com/shabinder/spotiflyer/models/Optional.kt index 703a708b..2257df5d --- a/app/src/main/java/com/shabinder/spotiflyer/models/YTTrack.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/Optional.kt @@ -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? -):Parcelable \ No newline at end of file +@Serializable +data class Optional(val value: T?) \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/Artist.kt b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/Artist.kt new file mode 100644 index 00000000..15b28e16 --- /dev/null +++ b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/Artist.kt @@ -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 . + */ + +package com.shabinder.spotiflyer.models.gaana + +data class Artist ( + val popularity : Int, + val seokey : String, + val name : String, +) \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/CustomArtworks.kt b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/CustomArtworks.kt new file mode 100644 index 00000000..f73b5510 --- /dev/null +++ b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/CustomArtworks.kt @@ -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 . + */ + +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, +) \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaAlbum.kt b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaAlbum.kt new file mode 100644 index 00000000..06cecd0f --- /dev/null +++ b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaAlbum.kt @@ -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 . + */ + +package com.shabinder.spotiflyer.models.gaana + +data class GaanaAlbum ( + val tracks : List, + val count : Int, + val custom_artworks : CustomArtworks, + val release_year : Int, + val favorite_count : Int, +) \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaArtistDetails.kt b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaArtistDetails.kt new file mode 100644 index 00000000..17d57ac3 --- /dev/null +++ b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaArtistDetails.kt @@ -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 . + */ + +package com.shabinder.spotiflyer.models.gaana + +data class GaanaArtistDetails( + val artist : List, + val count : Int, +) \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaArtistTracks.kt b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaArtistTracks.kt new file mode 100644 index 00000000..c9fa3050 --- /dev/null +++ b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaArtistTracks.kt @@ -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 . + */ + +package com.shabinder.spotiflyer.models.gaana + +data class GaanaArtistTracks( + val count : Int, + val tracks : List +) \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaPlaylist.kt b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaPlaylist.kt new file mode 100644 index 00000000..268121c5 --- /dev/null +++ b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaPlaylist.kt @@ -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 . + */ + +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, +) \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaSong.kt b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaSong.kt new file mode 100644 index 00000000..0d7a65df --- /dev/null +++ b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaSong.kt @@ -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 . + */ + +package com.shabinder.spotiflyer.models.gaana + +data class GaanaSong( + val tracks : List +) \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/Genre.kt b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/Genre.kt new file mode 100644 index 00000000..0f4fcd21 --- /dev/null +++ b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/Genre.kt @@ -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 . + */ + +package com.shabinder.spotiflyer.models.gaana + +data class Genre ( + val genre_id : Int, + val name : String +) \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/Tags.kt b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/Tags.kt new file mode 100644 index 00000000..c348a321 --- /dev/null +++ b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/Tags.kt @@ -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 . + */ + +package com.shabinder.spotiflyer.models.gaana + +data class Tags ( + val tag_id : Int, + val tag_name : String +) \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/Tracks.kt b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/Tracks.kt new file mode 100644 index 00000000..031e6687 --- /dev/null +++ b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/Tracks.kt @@ -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 . + */ + +package com.shabinder.spotiflyer.models.gaana + +import com.squareup.moshi.Json + +data class Tracks ( + val tags : List, + 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, + @Json(name = "gener") val genre : List, + 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, +) \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/Album.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Album.kt similarity index 96% rename from app/src/main/java/com/shabinder/spotiflyer/models/Album.kt rename to app/src/main/java/com/shabinder/spotiflyer/models/spotify/Album.kt index 025a1e71..3c06e6ad 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/models/Album.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Album.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.models +package com.shabinder.spotiflyer.models.spotify import android.os.Parcelable import kotlinx.android.parcel.Parcelize diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/Artist.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Artist.kt similarity index 95% rename from app/src/main/java/com/shabinder/spotiflyer/models/Artist.kt rename to app/src/main/java/com/shabinder/spotiflyer/models/spotify/Artist.kt index 59a9ac54..23b12247 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/models/Artist.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Artist.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.models +package com.shabinder.spotiflyer.models.spotify import android.os.Parcelable import kotlinx.android.parcel.Parcelize diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/Copyright.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Copyright.kt similarity index 94% rename from app/src/main/java/com/shabinder/spotiflyer/models/Copyright.kt rename to app/src/main/java/com/shabinder/spotiflyer/models/spotify/Copyright.kt index d16ba35d..19761164 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/models/Copyright.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Copyright.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.models +package com.shabinder.spotiflyer.models.spotify import android.os.Parcelable import kotlinx.android.parcel.Parcelize diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/Episodes.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Episodes.kt similarity index 96% rename from app/src/main/java/com/shabinder/spotiflyer/models/Episodes.kt rename to app/src/main/java/com/shabinder/spotiflyer/models/spotify/Episodes.kt index 804d438d..8fbfc49c 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/models/Episodes.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Episodes.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.models +package com.shabinder.spotiflyer.models.spotify import android.os.Parcelable import kotlinx.android.parcel.Parcelize diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/Followers.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Followers.kt similarity index 94% rename from app/src/main/java/com/shabinder/spotiflyer/models/Followers.kt rename to app/src/main/java/com/shabinder/spotiflyer/models/spotify/Followers.kt index 8a198e09..423f21d8 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/models/Followers.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Followers.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.models +package com.shabinder.spotiflyer.models.spotify import android.os.Parcelable import kotlinx.android.parcel.Parcelize diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/Image.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Image.kt similarity index 94% rename from app/src/main/java/com/shabinder/spotiflyer/models/Image.kt rename to app/src/main/java/com/shabinder/spotiflyer/models/spotify/Image.kt index 764f59ad..11bf5242 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/models/Image.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Image.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.models +package com.shabinder.spotiflyer.models.spotify import android.os.Parcelable import kotlinx.android.parcel.Parcelize diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/LinkedTrack.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/LinkedTrack.kt similarity index 95% rename from app/src/main/java/com/shabinder/spotiflyer/models/LinkedTrack.kt rename to app/src/main/java/com/shabinder/spotiflyer/models/spotify/LinkedTrack.kt index 361378d4..ac00564d 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/models/LinkedTrack.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/LinkedTrack.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.models +package com.shabinder.spotiflyer.models.spotify import android.os.Parcelable import kotlinx.android.parcel.Parcelize diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/PagingObjectPlaylistTrack.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/PagingObjectPlaylistTrack.kt similarity index 95% rename from app/src/main/java/com/shabinder/spotiflyer/models/PagingObjectPlaylistTrack.kt rename to app/src/main/java/com/shabinder/spotiflyer/models/spotify/PagingObjectPlaylistTrack.kt index 3f298934..caca876d 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/models/PagingObjectPlaylistTrack.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/PagingObjectPlaylistTrack.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.models +package com.shabinder.spotiflyer.models.spotify import android.os.Parcelable import kotlinx.android.parcel.Parcelize diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/PagingObjectTrack.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/PagingObjectTrack.kt similarity index 95% rename from app/src/main/java/com/shabinder/spotiflyer/models/PagingObjectTrack.kt rename to app/src/main/java/com/shabinder/spotiflyer/models/spotify/PagingObjectTrack.kt index 004a79ec..98567afd 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/models/PagingObjectTrack.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/PagingObjectTrack.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.models +package com.shabinder.spotiflyer.models.spotify import android.os.Parcelable import kotlinx.android.parcel.Parcelize diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/Playlist.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Playlist.kt similarity index 96% rename from app/src/main/java/com/shabinder/spotiflyer/models/Playlist.kt rename to app/src/main/java/com/shabinder/spotiflyer/models/spotify/Playlist.kt index 4842ae91..1d44e64e 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/models/Playlist.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Playlist.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.models +package com.shabinder.spotiflyer.models.spotify import android.os.Parcelable import com.squareup.moshi.Json diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/PlaylistTrack.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/PlaylistTrack.kt similarity index 95% rename from app/src/main/java/com/shabinder/spotiflyer/models/PlaylistTrack.kt rename to app/src/main/java/com/shabinder/spotiflyer/models/spotify/PlaylistTrack.kt index 56a5d103..f5c5cac1 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/models/PlaylistTrack.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/PlaylistTrack.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.models +package com.shabinder.spotiflyer.models.spotify import android.os.Parcelable import kotlinx.android.parcel.Parcelize diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Source.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Source.kt new file mode 100644 index 00000000..ca609c28 --- /dev/null +++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Source.kt @@ -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 . + */ + +package com.shabinder.spotiflyer.models.spotify + +enum class Source { + Spotify, + YouTube, +} \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/Token.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Token.kt similarity index 94% rename from app/src/main/java/com/shabinder/spotiflyer/models/Token.kt rename to app/src/main/java/com/shabinder/spotiflyer/models/spotify/Token.kt index c2fdb85b..10d35b3a 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/models/Token.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Token.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.models +package com.shabinder.spotiflyer.models.spotify import android.os.Parcelable import kotlinx.android.parcel.Parcelize diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/Track.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Track.kt similarity index 88% rename from app/src/main/java/com/shabinder/spotiflyer/models/Track.kt rename to app/src/main/java/com/shabinder/spotiflyer/models/spotify/Track.kt index 01f8e984..0be692b4 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/models/Track.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Track.kt @@ -15,9 +15,10 @@ * along with this program. If not, see . */ -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? = null, var popularity: Int? = null, - var downloaded:DownloadStatus? = DownloadStatus.NotDownloaded):Parcelable + var downloaded: DownloadStatus? = DownloadStatus.NotDownloaded +):Parcelable diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/UserPrivate.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/UserPrivate.kt similarity index 96% rename from app/src/main/java/com/shabinder/spotiflyer/models/UserPrivate.kt rename to app/src/main/java/com/shabinder/spotiflyer/models/spotify/UserPrivate.kt index 178798dd..fa090fec 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/models/UserPrivate.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/UserPrivate.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.models +package com.shabinder.spotiflyer.models.spotify import android.os.Parcelable import kotlinx.android.parcel.Parcelize diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/UserPublic.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/UserPublic.kt similarity index 95% rename from app/src/main/java/com/shabinder/spotiflyer/models/UserPublic.kt rename to app/src/main/java/com/shabinder/spotiflyer/models/spotify/UserPublic.kt index 06df67cd..5732dc12 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/models/UserPublic.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/UserPublic.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.shabinder.spotiflyer.models +package com.shabinder.spotiflyer.models.spotify import android.os.Parcelable import kotlinx.android.parcel.Parcelize diff --git a/app/src/main/java/com/shabinder/spotiflyer/networking/GaanaInterface.kt b/app/src/main/java/com/shabinder/spotiflyer/networking/GaanaInterface.kt new file mode 100644 index 00000000..34da4ad2 --- /dev/null +++ b/app/src/main/java/com/shabinder/spotiflyer/networking/GaanaInterface.kt @@ -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 . + */ + +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 + + /* + * 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 + + /* + * 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 + + /* + * 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 + /* + * 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 + +} \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/utils/SpotifyInterface.kt b/app/src/main/java/com/shabinder/spotiflyer/networking/SpotifyInterface.kt similarity index 54% rename from app/src/main/java/com/shabinder/spotiflyer/utils/SpotifyInterface.kt rename to app/src/main/java/com/shabinder/spotiflyer/networking/SpotifyInterface.kt index 4758f80c..fcf469b5 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/utils/SpotifyInterface.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/networking/SpotifyInterface.kt @@ -15,58 +15,41 @@ * along with this program. If not, see . */ -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 . -*/ - - interface SpotifyService { @GET("playlists/{playlist_id}") - suspend fun getPlaylist(@Path("playlist_id") playlistId: String?): Playlist + suspend fun getPlaylist(@Path("playlist_id") playlistId: String?): Optional @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 @GET("tracks/{id}") - suspend fun getTrack(@Path("id") trackId: String?): Track + suspend fun getTrack(@Path("id") trackId: String?): Optional @GET("episodes/{id}") - suspend fun getEpisode(@Path("id") episodeId: String?): Track + suspend fun getEpisode(@Path("id") episodeId: String?): Optional @GET("shows/{id}") - suspend fun getShow(@Path("id") showId: String?): Track + suspend fun getShow(@Path("id") showId: String?): Optional @GET("albums/{id}") - suspend fun getAlbum(@Path("id") albumId: String?): Album + suspend fun getAlbum(@Path("id") albumId: String?): Optional } 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 } diff --git a/app/src/main/java/com/shabinder/spotiflyer/utils/YoutubeMusicApi.kt b/app/src/main/java/com/shabinder/spotiflyer/networking/YoutubeMusicApi.kt similarity index 83% rename from app/src/main/java/com/shabinder/spotiflyer/utils/YoutubeMusicApi.kt rename to app/src/main/java/com/shabinder/spotiflyer/networking/YoutubeMusicApi.kt index ccd72c67..e321d19a 100644 --- a/app/src/main/java/com/shabinder/spotiflyer/utils/YoutubeMusicApi.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/networking/YoutubeMusicApi.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -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 - + fun getYoutubeMusicResponse(@Body text: String): Call } fun makeJsonBody(query: String):JsonObject{ diff --git a/app/src/main/java/com/shabinder/spotiflyer/recyclerView/SpotifyTrackListAdapter.kt b/app/src/main/java/com/shabinder/spotiflyer/recyclerView/SpotifyTrackListAdapter.kt index 277ee1f4..c469c589 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/recyclerView/SpotifyTrackListAdapter.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/recyclerView/SpotifyTrackListAdapter.kt @@ -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(SpotifyTrackDiffCallback()) { -class SpotifyTrackListAdapter: ListAdapter(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,9 +54,9 @@ class SpotifyTrackListAdapter: ListAdapter() - itemList.add(item) - downloadAllTracks(spotifyViewModel!!.folderType,spotifyViewModel!!.subFolder,itemList) + spotifyViewModel.uiScope.launch { + val itemList = mutableListOf() + itemList.add(item.let { track -> + val artistsList = mutableListOf() + 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! } diff --git a/app/src/main/java/com/shabinder/spotiflyer/recyclerView/YoutubeTrackListAdapter.kt b/app/src/main/java/com/shabinder/spotiflyer/recyclerView/YoutubeTrackListAdapter.kt index 0a98d88c..fc51cf8c 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/recyclerView/YoutubeTrackListAdapter.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/recyclerView/YoutubeTrackListAdapter.kt @@ -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(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 diff --git a/app/src/main/java/com/shabinder/spotiflyer/samples/response examples.txt b/app/src/main/java/com/shabinder/spotiflyer/samples/response examples.txt index b181b4b1..750587d1 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/samples/response examples.txt +++ b/app/src/main/java/com/shabinder/spotiflyer/samples/response examples.txt @@ -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 diff --git a/app/src/main/java/com/shabinder/spotiflyer/ui/gaana/GaanaFragment.kt b/app/src/main/java/com/shabinder/spotiflyer/ui/gaana/GaanaFragment.kt new file mode 100644 index 00000000..7f80f9ca --- /dev/null +++ b/app/src/main/java/com/shabinder/spotiflyer/ui/gaana/GaanaFragment.kt @@ -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 . + */ + +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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/ui/gaana/GaanaViewModel.kt b/app/src/main/java/com/shabinder/spotiflyer/ui/gaana/GaanaViewModel.kt new file mode 100644 index 00000000..e133edb7 --- /dev/null +++ b/app/src/main/java/com/shabinder/spotiflyer/ui/gaana/GaanaViewModel.kt @@ -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 . + */ + +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() \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/ui/mainfragment/MainFragment.kt b/app/src/main/java/com/shabinder/spotiflyer/ui/mainfragment/MainFragment.kt index b2accaf3..37779f6c 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/ui/mainfragment/MainFragment.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/ui/mainfragment/MainFragment.kt @@ -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 == "") { - //Waiting for Authentication to Finish - Thread.sleep(1000) + 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() diff --git a/app/src/main/java/com/shabinder/spotiflyer/ui/spotify/SpotifyFragment.kt b/app/src/main/java/com/shabinder/spotiflyer/ui/spotify/SpotifyFragment.kt index 24c7d99c..3c3d9052 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/ui/spotify/SpotifyFragment.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/ui/spotify/SpotifyFragment.kt @@ -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() - 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() + 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("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, { - if(it!="Loading") bindImage(binding.coverImage,it,Source.Spotify) + 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("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){ - 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 - } - } \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/ui/spotify/SpotifyViewModel.kt b/app/src/main/java/com/shabinder/spotiflyer/ui/spotify/SpotifyViewModel.kt index 2ab721a6..f93ce2b9 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/ui/spotify/SpotifyViewModel.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/ui/spotify/SpotifyViewModel.kt @@ -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() { diff --git a/app/src/main/java/com/shabinder/spotiflyer/ui/youtube/YoutubeFragment.kt b/app/src/main/java/com/shabinder/spotiflyer/ui/youtube/YoutubeFragment.kt index 7f382b40..dcf3737c 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/ui/youtube/YoutubeFragment.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/ui/youtube/YoutubeFragment.kt @@ -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() - 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("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, { - if(it!="Loading") bindImage(binding.coverImage,it,Source.YouTube) + 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){ - adapter.submitList(list) - } - - /** - * Util. Function to create toasts! - **/ - private fun showToast(message:String){ - Toast.makeText(context,message, Toast.LENGTH_SHORT).show() - } - } diff --git a/app/src/main/java/com/shabinder/spotiflyer/ui/youtube/YoutubeViewModel.kt b/app/src/main/java/com/shabinder/spotiflyer/ui/youtube/YoutubeViewModel.kt index dedfb6e0..35f6b11d 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/ui/youtube/YoutubeViewModel.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/ui/youtube/YoutubeViewModel.kt @@ -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>() - val format = MutableLiveData() private val loading = "Loading" var title = MutableLiveData().apply { value = "\"Loading!\"" } var coverUrl = MutableLiveData().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!") } } } diff --git a/app/src/main/java/com/shabinder/spotiflyer/utils/NetworkInterceptor.kt b/app/src/main/java/com/shabinder/spotiflyer/utils/NetworkInterceptor.kt new file mode 100644 index 00000000..b137c203 --- /dev/null +++ b/app/src/main/java/com/shabinder/spotiflyer/utils/NetworkInterceptor.kt @@ -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 . + */ + +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() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/utils/Provider.kt b/app/src/main/java/com/shabinder/spotiflyer/utils/Provider.kt index 84863e5f..aad87022 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/utils/Provider.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/utils/Provider.kt @@ -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() - } } \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/utils/Utils.kt b/app/src/main/java/com/shabinder/spotiflyer/utils/Utils.kt index 77c84308..e4e62be6 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/utils/Utils.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/utils/Utils.kt @@ -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() diff --git a/app/src/main/java/com/shabinder/spotiflyer/worker/ForegroundService.kt b/app/src/main/java/com/shabinder/spotiflyer/worker/ForegroundService.kt index 9dcf145e..4cddc100 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/worker/ForegroundService.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/worker/ForegroundService.kt @@ -71,7 +71,7 @@ class ForegroundService : Service(){ private lateinit var downloadManager : DownloadManager private var serviceJob = Job() private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob) - private val requestMap = mutableMapOf() + private val requestMap = mutableMapOf() private var speed :Long = 0 private var defaultDir = Environment.DIRECTORY_MUSIC + File.separator + "SpotiFlyer" + File.separator private val parentDirectory = File(Environment.getExternalStorageDirectory(), @@ -458,7 +458,7 @@ class ForegroundService : Service(){ } /** - *Modifying Mp3 Tags with MetaData! + *Modifying Mp3 com.shabinder.spotiflyer.models.gaana.Tags with MetaData! **/ private fun setId3v1Tags(mp3File: Mp3File, track: TrackDetails): Mp3File { val id3v1Tag = ID3v1Tag().apply { diff --git a/app/src/main/res/layout/main_activity.xml b/app/src/main/res/layout/main_activity.xml index c90179a2..0120a3f3 100755 --- a/app/src/main/res/layout/main_activity.xml +++ b/app/src/main/res/layout/main_activity.xml @@ -23,19 +23,15 @@ - - + type="com.shabinder.spotiflyer.models.spotify.Track" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/navigation/navigation.xml b/app/src/main/res/navigation/navigation.xml index a333ae27..6c8b8bb3 100755 --- a/app/src/main/res/navigation/navigation.xml +++ b/app/src/main/res/navigation/navigation.xml @@ -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" > @@ -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"> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index f54381ef..1801bfbb 100755 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -21,7 +21,6 @@ #000000 #FC5C7D - #000000 #FFFFFF #6A82FB #A9B200FF @@ -29,7 +28,7 @@ @style/TextAppearance.AppTheme.Headline4 - + @style/TextAppearance.MaterialComponents.Body2 @@ -38,18 +37,22 @@ @style/CutShapeAppearance @style/Alert.Button.Positive @style/Alert.Button.Neutral + 22sp + @font/amita diff --git a/build.gradle b/build.gradle index d5e5db36..ea0f8eaa 100755 --- a/build.gradle +++ b/build.gradle @@ -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 }