diff --git a/.idea/dictionaries/shabinder.xml b/.idea/dictionaries/shabinder.xml
index 82062cc1..33624671 100755
--- a/.idea/dictionaries/shabinder.xml
+++ b/.idea/dictionaries/shabinder.xml
@@ -5,10 +5,12 @@
amita
cardview
cherrypick
+ crashlytics
downloadrecord
emoji
ffmpeg
flyer
+ fmpeg
gaana
gener
hqdefault
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 37d461f3..23a89bbb 100755
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -12,7 +12,6 @@
-
diff --git a/app/build.gradle b/app/build.gradle
index 86a31347..2844c795 100755
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -15,42 +15,35 @@
* along with this program. If not, see .
*/
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-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'
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+ id 'kotlin-kapt'
+ id 'kotlin-parcelize'
+ id 'androidx.navigation.safeargs.kotlin'
+ id 'dagger.hilt.android.plugin'
+ id 'kotlinx-serialization'
+ id 'com.google.gms.google-services'
+ id 'com.google.firebase.crashlytics'
+}
android {
- compileSdkVersion 29
+ compileSdkVersion 30
buildToolsVersion "30.0.2"
buildFeatures{
- //dataBinding = true
viewBinding = true
}
defaultConfig {
applicationId 'com.shabinder.spotiflyer'
minSdkVersion 22
- targetSdkVersion 29
- versionCode 8
- versionName "1.6"
+ targetSdkVersion 30
+ versionCode 9
+ versionName "1.7"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
- packagingOptions {
- exclude 'META-INF/DEPENDENCIES'
- exclude 'META-INF/LICENSE'
- exclude 'META-INF/LICENSE.txt'
- exclude 'META-INF/license.txt'
- exclude 'META-INF/NOTICE'
- exclude 'META-INF/NOTICE.txt'
- exclude 'META-INF/notice.txt'
- exclude 'META-INF/ASL2.0'
- exclude("META-INF/*.kotlin_module")
- }
+
buildTypes {
release {
minifyEnabled false
@@ -62,73 +55,101 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
}
+
kotlinOptions {
jvmTarget = "1.8"
}
+
lintOptions {
abortOnError false
}
+
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
+
+ packagingOptions {
+ exclude 'META-INF/DEPENDENCIES'
+ exclude 'META-INF/LICENSE'
+ exclude 'META-INF/LICENSE.txt'
+ exclude 'META-INF/license.txt'
+ exclude 'META-INF/NOTICE'
+ exclude 'META-INF/NOTICE.txt'
+ exclude 'META-INF/notice.txt'
+ exclude 'META-INF/ASL2.0'
+ exclude("META-INF/*.kotlin_module")
+ }
+ ndkVersion '21.3.6528147'
}
dependencies {
- implementation fileTree(dir: 'libs', include:['*.jar'])
- implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.10"
+ //Android
+ //noinspection DifferentStdlibGradleVersion
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.browser:browser:1.2.0'
implementation 'androidx.webkit:webkit:1.3.0'
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ implementation "androidx.fragment:fragment-ktx:1.2.5"
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
+ implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
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-coroutines-core:1.4.2-native-mt'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1"
+ //FFmpeg
+ implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
+
+ //Room: Local SQL-lite Database
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"
+
+ //Hilt: Dependency Injection
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
- implementation project(path: ':mobile-ffmpeg')
- implementation 'androidx.legacy:legacy-support-v4:1.0.0'
- implementation ("com.github.bumptech.glide:recyclerview-integration:4.11.0") {
- transitive = true
- }
- kapt ("com.github.bumptech.glide:recyclerview-integration:4.11.0") {
- transitive = true
- }
+ //Glide-Image Loading
+ implementation ("com.github.bumptech.glide:recyclerview-integration:4.11.0") {transitive = true}
+ kapt ("com.github.bumptech.glide:recyclerview-integration:4.11.0") {transitive = true}
+ //HTTP
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
-
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+
+ //Json
implementation 'com.squareup.moshi:moshi:1.11.0'
implementation 'com.squareup.moshi:moshi-kotlin:1.11.0'
implementation "com.squareup.retrofit2:converter-moshi:2.9.0"
implementation "com.squareup.retrofit2:converter-scalars:2.9.0"
implementation 'com.beust:klaxon:5.4'
- implementation 'me.xdrop:fuzzywuzzy:1.3.1'
+ //Crashlytics & Analytics
+ implementation platform('com.google.firebase:firebase-bom:26.1.0')
+ implementation 'com.google.firebase:firebase-crashlytics-ktx'
+ implementation 'com.google.firebase:firebase-analytics-ktx'
+
+ //Extras
+ implementation 'me.xdrop:fuzzywuzzy:1.3.1'
implementation 'com.mpatric:mp3agic:0.9.1'
implementation 'com.shreyaspatil:EasyUpiPayment:3.0.0'
- implementation 'com.github.sealedtx:java-youtube-downloader:2.4.4'
- implementation "androidx.tonyodev.fetch2:xfetch2:3.1.5"
implementation 'com.github.javiersantos:AppUpdater:2.7'
+ implementation 'com.github.lzyzsd:circleprogress:1.2.1'
+ implementation "androidx.tonyodev.fetch2:xfetch2:3.1.5"
+ implementation 'com.github.sealedtx:java-youtube-downloader:2.4.4'
- implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
diff --git a/mobile-ffmpeg/mobile-ffmpeg.aar b/app/libs/mobile-ffmpeg.aar
similarity index 100%
rename from mobile-ffmpeg/mobile-ffmpeg.aar
rename to app/libs/mobile-ffmpeg.aar
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a0b844e7..cdaef4ca 100755
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -20,12 +20,22 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.shabinder.spotiflyer">
+
+
+
+
+
+
-
+
+
+
@@ -38,9 +48,11 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:forceDarkAllowed="true"
- android:requestLegacyExternalStorage="true"
android:theme="@style/AppTheme"
+ android:requestLegacyExternalStorage="true"
+ tools:ignore="AllowBackup"
tools:targetApi="q">
+
diff --git a/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt b/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt
index 74c0f886..b0496bc3 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt
@@ -31,6 +31,7 @@ import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.viewModelScope
import androidx.navigation.NavController
import androidx.navigation.findNavController
import com.github.javiersantos.appupdater.AppUpdater
@@ -38,9 +39,12 @@ 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.*
+import com.shabinder.spotiflyer.utils.NetworkInterceptor
+import com.shabinder.spotiflyer.utils.createDirectories
+import com.shabinder.spotiflyer.utils.showMessage
import com.squareup.moshi.Moshi
import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import okhttp3.Interceptor
import okhttp3.OkHttpClient
@@ -52,14 +56,15 @@ import javax.inject.Inject
/*
* This is App's God Activity
* */
-@Suppress("DEPRECATION")
@AndroidEntryPoint
class MainActivity : AppCompatActivity(){
private var spotifyService : SpotifyService? = null
+ val viewModelScope : CoroutineScope
+ get() = sharedViewModel.viewModelScope
private lateinit var binding: MainActivityBinding
- lateinit var snackBarAnchor: View
private lateinit var sharedViewModel: SharedViewModel
- private lateinit var navController: NavController
+ lateinit var snackBarAnchor: View
+ lateinit var navController: NavController
@Inject lateinit var moshi: Moshi
@Inject lateinit var spotifyServiceTokenRequest: SpotifyServiceTokenRequest
@@ -72,18 +77,15 @@ class MainActivity : AppCompatActivity(){
sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)
navController = findNavController(R.id.navHostFragment)
snackBarAnchor = binding.snackBarPosition
-
authenticateSpotify()
+ }
+ override fun onStart() {
+ super.onStart()
requestPermission()
disableDozeMode()
checkIfLatestVersion()
createDirectories()
- Log.i("Connection Status", isOnline().toString())
-
- //starting Notification and Downloader Service!
- startService(this)
-
handleIntentFromExternalActivity()
}
@@ -151,9 +153,8 @@ class MainActivity : AppCompatActivity(){
sharedViewModel.spotifyService.value = spotifyService
}
-
fun authenticateSpotify() {
- sharedViewModel.uiScope.launch {
+ sharedViewModel.viewModelScope.launch {
Log.i("Spotify Authentication","Started")
val token = spotifyServiceTokenRequest.getToken()
token.value?.let {
@@ -207,7 +208,7 @@ class MainActivity : AppCompatActivity(){
companion object{
private lateinit var instance: MainActivity
- fun getInstance():MainActivity = instance
+ fun getInstance():MainActivity = this.instance
}
init {
diff --git a/app/src/main/java/com/shabinder/spotiflyer/SharedViewModel.kt b/app/src/main/java/com/shabinder/spotiflyer/SharedViewModel.kt
index 7d8eb67f..9686d8e2 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/SharedViewModel.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/SharedViewModel.kt
@@ -17,22 +17,15 @@
package com.shabinder.spotiflyer
+import androidx.hilt.lifecycle.ViewModelInject
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.shabinder.spotiflyer.networking.SpotifyService
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Job
+import com.shabinder.spotiflyer.networking.YoutubeMusicApi
-class SharedViewModel : ViewModel() {
+class SharedViewModel @ViewModelInject constructor(
+ val youtubeMusicApi: YoutubeMusicApi
+) : ViewModel() {
var intentString = MutableLiveData()
var spotifyService = MutableLiveData()
-
- private var viewModelJob = Job()
- val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
-
- override fun onCleared() {
- super.onCleared()
- viewModelJob.cancel()
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/shabinder/spotiflyer/database/DownloadRecord.kt b/app/src/main/java/com/shabinder/spotiflyer/database/DownloadRecord.kt
index e5df89b5..0a734771 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/database/DownloadRecord.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/database/DownloadRecord.kt
@@ -22,7 +22,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
@Parcelize
@Entity(
@@ -48,10 +48,4 @@ data class DownloadRecord(
@ColumnInfo(name = "totalFiles")
var totalFiles:Int = 1,
-
- @ColumnInfo(name = "downloaded")
- var downloaded:Boolean=false,
-
- @ColumnInfo(name = "directory")
- var directory:String?=null
):Parcelable
\ No newline at end of file
diff --git a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/DownloadHelper.kt b/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/DownloadHelper.kt
deleted file mode 100755
index 33c8973f..00000000
--- a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/DownloadHelper.kt
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * 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.downloadHelper
-
-import android.annotation.SuppressLint
-import android.os.Environment
-import android.os.Handler
-import android.util.Log
-import android.view.View
-import android.view.animation.AlphaAnimation
-import android.view.animation.Animation
-import android.widget.TextView
-import android.widget.Toast
-import com.shabinder.spotiflyer.SharedViewModel
-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.defaultDir
-import com.shabinder.spotiflyer.utils.Provider.mainActivity
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import retrofit2.Call
-import retrofit2.Callback
-import retrofit2.Response
-import java.io.File
-
-object DownloadHelper {
-
- var statusBar:TextView? = null
- var youtubeMusicApi: YoutubeMusicApi? = null
- var sharedViewModel: SharedViewModel? = null
-
- private var total = 0
- private var processed = 0
- var notFound = 0
-
- /**
- * Function To Download All Tracks Available in a List
- **/
- suspend fun downloadAllTracks(
- type:String,
- subFolder: String?,
- 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
- Handler().postDelayed({
- //Delay is Added ,if a request is in processing it may finish
- Log.i("Spotify Helper","Download Request Sent")
- sharedViewModel?.uiScope?.launch (Dispatchers.Main){
- showMessage("Download Started, Now You can leave the App!")
- }
- startService(mainActivity,downloadList)
- },3000)
- }
- }else{
- 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.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 outputFile: String =
- Environment.getExternalStorageDirectory().toString() + File.separator +
- defaultDir +
- removeIllegalChars(type) + File.separator +
- (if (subFolder == null) { "" }
- else { removeIllegalChars(subFolder) + File.separator }
- + removeIllegalChars(it.title) + ".m4a")
-
- val downloadObject = DownloadObject(
- trackDetails = it,
- ytVideoId = videoId,
- outputFile = outputFile
- )
- processed++
- sharedViewModel?.uiScope?.launch(Dispatchers.Main) {
- updateStatusBar()
- }
- downloadList.add(downloadObject)
- if(index == (trackList.size-1)){//LastElement
- Handler().postDelayed({
- //Delay is Added ,if a request is in processing it may finish
- Log.i("Spotify Helper","Download Request Sent")
- sharedViewModel?.uiScope?.launch (Dispatchers.Main){
- Toast.makeText(mainActivity,"Download Started, Now You can leave the App!", Toast.LENGTH_SHORT).show()
- }
- startService(mainActivity,downloadList)
- },5000)
- }
- }
- }
- }
- override fun onFailure(call: Call, t: Throwable) {
- if(t.message.toString().contains("Failed to connect")) showMessage("Failed, Check Your Internet Connection!")
- Log.i("YT API Req. Fail",t.message.toString())
- }
- }
- )
- }
- updateStatusBar()
- }
- animateStatusBar()
- }
- }
-
- 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
- anim.startOffset = 20
- anim.repeatMode = Animation.REVERSE
- anim.repeatCount = Animation.INFINITE
- statusBar?.animation = anim
- }
-
- @SuppressLint("SetTextI18n")
- fun updateStatusBar() {
- statusBar!!.visibility = View.VISIBLE
- statusBar?.text = "Total: $total ${getEmojiByUnicode(0x2705)}: $processed ${getEmojiByUnicode(0x274C)}: $notFound"
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YTDownloadHelper.kt b/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YTDownloadHelper.kt
deleted file mode 100755
index 2e6637fa..00000000
--- a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YTDownloadHelper.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.downloadHelper
-
-import android.os.Environment
-import android.util.Log
-import android.widget.Toast
-import com.shabinder.spotiflyer.models.DownloadObject
-import com.shabinder.spotiflyer.models.TrackDetails
-import com.shabinder.spotiflyer.utils.Provider.defaultDir
-import com.shabinder.spotiflyer.utils.Provider.mainActivity
-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
-import java.io.File
-
-object YTDownloadHelper {
- suspend fun downloadYTTracks(
- type:String,
- subFolder: String?,
- tracks:List,
- ){
- val downloadList = ArrayList()
- tracks.forEach {
- if(!isOnline()){
- showNoConnectionAlert()
- return
- }
- val outputFile: String =
- Environment.getExternalStorageDirectory().toString() + File.separator +
- defaultDir +
- removeIllegalChars(type) + File.separator +
- (if (subFolder == null) { "" }
- else { removeIllegalChars(subFolder) + File.separator }
- + removeIllegalChars(it.title) + ".m4a")
-
- val downloadObject = DownloadObject(
- trackDetails = it,
- ytVideoId = it.albumArt.absolutePath.substringAfterLast("/")
- .substringBeforeLast("."),
- outputFile = outputFile
- )
-
- downloadList.add(downloadObject)
- }
- Log.i("YT Downloader Helper","Download Request Sent")
- withContext(Dispatchers.Main){
- Toast.makeText(mainActivity,"Download Started, Now You can leave the App!", Toast.LENGTH_SHORT).show()
- startService(mainActivity,downloadList)
- }
- }
-}
\ No newline at end of file
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 f2448cbf..14001ee9 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/models/DownloadObject.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/DownloadObject.kt
@@ -19,16 +19,9 @@ package com.shabinder.spotiflyer.models
import android.os.Parcelable
import com.shabinder.spotiflyer.models.spotify.Source
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
import java.io.File
-@Parcelize
-data class DownloadObject(
- var trackDetails: TrackDetails,
- var ytVideoId:String,
- var outputFile:String
-):Parcelable
-
@Parcelize
data class TrackDetails(
var title:String,
@@ -42,11 +35,17 @@ data class TrackDetails(
var albumArt: File,
var albumArtURL: String,
var source: Source,
- var downloaded: DownloadStatus = DownloadStatus.NotDownloaded
+ var downloaded: DownloadStatus = DownloadStatus.NotDownloaded,
+ var progress: Int = 0,
+ var outputFile: String,
+ var videoID:String? = null
):Parcelable
enum class DownloadStatus{
Downloaded,
Downloading,
- NotDownloaded
+ Queued,
+ NotDownloaded,
+ Converting,
+ Failed
}
\ No newline at end of file
diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/YoutubeTrack.kt b/app/src/main/java/com/shabinder/spotiflyer/models/YoutubeTrack.kt
index 03b588ba..d82ab05c 100644
--- a/app/src/main/java/com/shabinder/spotiflyer/models/YoutubeTrack.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/YoutubeTrack.kt
@@ -18,7 +18,7 @@
package com.shabinder.spotiflyer.models
import android.os.Parcelable
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
@Parcelize
data class YoutubeTrack(
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
index 38dc43ec..b64ca02e 100644
--- a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaPlaylist.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaPlaylist.kt
@@ -18,7 +18,6 @@
package com.shabinder.spotiflyer.models.gaana
data class GaanaPlaylist (
- val tags : String?,
val modified_on : String,
val count : Int,
val created_on : String,
diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Album.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Album.kt
index 3c06e6ad..b8eec7b9 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Album.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Album.kt
@@ -18,7 +18,7 @@
package com.shabinder.spotiflyer.models.spotify
import android.os.Parcelable
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
@Parcelize
data class Album(
diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Artist.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Artist.kt
index 23b12247..6b7d2050 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Artist.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Artist.kt
@@ -18,7 +18,7 @@
package com.shabinder.spotiflyer.models.spotify
import android.os.Parcelable
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
@Parcelize
data class Artist(
diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Copyright.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Copyright.kt
index 19761164..ace8f1cc 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Copyright.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Copyright.kt
@@ -18,7 +18,7 @@
package com.shabinder.spotiflyer.models.spotify
import android.os.Parcelable
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
@Parcelize
data class Copyright(
diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Episodes.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Episodes.kt
index 8fbfc49c..cb502c13 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Episodes.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Episodes.kt
@@ -18,7 +18,7 @@
package com.shabinder.spotiflyer.models.spotify
import android.os.Parcelable
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
@Parcelize
data class Episodes(
diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Followers.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Followers.kt
index 423f21d8..8a3ad1a0 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Followers.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Followers.kt
@@ -18,7 +18,7 @@
package com.shabinder.spotiflyer.models.spotify
import android.os.Parcelable
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
@Parcelize
data class Followers(
diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Image.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Image.kt
index 11bf5242..f2b1e355 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Image.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Image.kt
@@ -18,7 +18,7 @@
package com.shabinder.spotiflyer.models.spotify
import android.os.Parcelable
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
@Parcelize
data class Image(
diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/LinkedTrack.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/LinkedTrack.kt
index ac00564d..ce1745d3 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/LinkedTrack.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/LinkedTrack.kt
@@ -18,7 +18,7 @@
package com.shabinder.spotiflyer.models.spotify
import android.os.Parcelable
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
@Parcelize
data class LinkedTrack(
diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/PagingObjectPlaylistTrack.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/PagingObjectPlaylistTrack.kt
index caca876d..55c7b000 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/PagingObjectPlaylistTrack.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/PagingObjectPlaylistTrack.kt
@@ -18,7 +18,7 @@
package com.shabinder.spotiflyer.models.spotify
import android.os.Parcelable
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
@Parcelize
data class PagingObjectPlaylistTrack(
diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/PagingObjectTrack.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/PagingObjectTrack.kt
index 98567afd..617c9ead 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/PagingObjectTrack.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/PagingObjectTrack.kt
@@ -18,7 +18,7 @@
package com.shabinder.spotiflyer.models.spotify
import android.os.Parcelable
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
@Parcelize
data class PagingObjectTrack(
diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Playlist.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Playlist.kt
index 1d44e64e..e60154e5 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Playlist.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Playlist.kt
@@ -19,7 +19,7 @@ package com.shabinder.spotiflyer.models.spotify
import android.os.Parcelable
import com.squareup.moshi.Json
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
@Parcelize
data class Playlist(
diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/PlaylistTrack.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/PlaylistTrack.kt
index f5c5cac1..56ebc380 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/PlaylistTrack.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/PlaylistTrack.kt
@@ -18,7 +18,7 @@
package com.shabinder.spotiflyer.models.spotify
import android.os.Parcelable
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
@Parcelize
data class PlaylistTrack(
diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Token.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Token.kt
index 10d35b3a..0d8bdaf8 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Token.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Token.kt
@@ -18,7 +18,7 @@
package com.shabinder.spotiflyer.models.spotify
import android.os.Parcelable
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
@Parcelize
data class Token(
diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Track.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Track.kt
index ecc0809e..33700996 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Track.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/Track.kt
@@ -19,7 +19,7 @@ package com.shabinder.spotiflyer.models.spotify
import android.os.Parcelable
import com.shabinder.spotiflyer.models.DownloadStatus
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
@Parcelize
data class Track(
diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/UserPrivate.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/UserPrivate.kt
index fa090fec..66b00f13 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/UserPrivate.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/UserPrivate.kt
@@ -18,7 +18,7 @@
package com.shabinder.spotiflyer.models.spotify
import android.os.Parcelable
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
@Parcelize
data class UserPrivate(
diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/UserPublic.kt b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/UserPublic.kt
index 5732dc12..7d504c8b 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/models/spotify/UserPublic.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/models/spotify/UserPublic.kt
@@ -18,7 +18,7 @@
package com.shabinder.spotiflyer.models.spotify
import android.os.Parcelable
-import kotlinx.android.parcel.Parcelize
+import kotlinx.parcelize.Parcelize
@Parcelize
data class UserPublic(
diff --git a/app/src/main/java/com/shabinder/spotiflyer/networking/GaanaInterface.kt b/app/src/main/java/com/shabinder/spotiflyer/networking/GaanaInterface.kt
index cc69047b..93d5c714 100644
--- a/app/src/main/java/com/shabinder/spotiflyer/networking/GaanaInterface.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/networking/GaanaInterface.kt
@@ -19,8 +19,11 @@ package com.shabinder.spotiflyer.networking
import com.shabinder.spotiflyer.models.Optional
import com.shabinder.spotiflyer.models.gaana.*
+import okhttp3.ResponseBody
+import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Query
+import retrofit2.http.Url
const val gaana_token = "b2e6d7fbc136547a940516e9b77e5990"
@@ -98,4 +101,9 @@ interface GaanaInterface {
@Query("limit") limit: Int = 50
): Optional
+ /*
+ * Dynamic Url Requests
+ * */
+ @GET
+ fun getResponse(@Url url:String): Call
}
\ No newline at end of file
diff --git a/app/src/main/java/com/shabinder/spotiflyer/recyclerView/TrackListAdapter.kt b/app/src/main/java/com/shabinder/spotiflyer/recyclerView/TrackListAdapter.kt
index a2f4905f..f45ef001 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/recyclerView/TrackListAdapter.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/recyclerView/TrackListAdapter.kt
@@ -17,23 +17,24 @@
package com.shabinder.spotiflyer.recyclerView
+import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.lifecycle.viewModelScope
import androidx.recyclerview.widget.DiffUtil
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.DownloadHelper
-import com.shabinder.spotiflyer.downloadHelper.YTDownloadHelper
import com.shabinder.spotiflyer.models.DownloadStatus
import com.shabinder.spotiflyer.models.TrackDetails
import com.shabinder.spotiflyer.models.spotify.Source
+import com.shabinder.spotiflyer.ui.base.tracklistbase.TrackListViewModel
import com.shabinder.spotiflyer.utils.*
import kotlinx.coroutines.launch
-class TrackListAdapter(private val viewModel :TrackListViewModel): ListAdapter(TrackDiffCallback()) {
+class TrackListAdapter(private val viewModel : TrackListViewModel): ListAdapter(TrackDiffCallback()){
var source:Source =Source.Spotify
@@ -46,62 +47,89 @@ class TrackListAdapter(private val viewModel :TrackListViewModel): ListAdapter {
- holder.binding.btnDownload.setImageResource(R.drawable.ic_tick)
- holder.binding.btnDownload.clearAnimation()
+ holder.binding.btnDownloadProgress.invisible()
+ holder.binding.btnDownload.apply{
+ setImageResource(R.drawable.ic_tick)
+ clearAnimation()
+ visible()
+ }
+ }
+ DownloadStatus.Queued -> {
+ holder.binding.btnDownloadProgress.invisible()
+ holder.binding.btnDownload.apply{
+ setImageResource(R.drawable.ic_refresh)
+ rotate()
+ visible()
+ }
+ }
+ DownloadStatus.Failed -> {
+ holder.binding.btnDownloadProgress.invisible()
+ holder.binding.btnDownload.apply{
+ setImageResource(R.drawable.ic_error)
+ clearAnimation()
+ visible()
+ }
}
DownloadStatus.Downloading -> {
- holder.binding.btnDownload.setImageResource(R.drawable.ic_refresh)
- rotateAnim(holder.binding.btnDownload)
+ holder.binding.btnDownload.invisible()
+ holder.binding.btnDownloadProgress.apply {
+ progress = item.progress
+ bottomText = "Downloading"
+ visible()
+ }
+ }
+ DownloadStatus.Converting -> {
+ holder.binding.btnDownload.invisible()
+ holder.binding.btnDownloadProgress.apply {
+ visible()
+ progress = 100
+ bottomText = "Converting"
+ }
}
DownloadStatus.NotDownloaded -> {
- holder.binding.btnDownload.setImageResource(R.drawable.ic_arrow)
- holder.binding.btnDownload.clearAnimation()
- holder.binding.btnDownload.setOnClickListener{
- if(!isOnline()){
- showNoConnectionAlert()
- return@setOnClickListener
- }
- showMessage("Processing!")
- holder.binding.btnDownload.setImageResource(R.drawable.ic_refresh)
- rotateAnim(it)
- item.downloaded = DownloadStatus.Downloading
- when(source){
- Source.YouTube -> {
- viewModel.uiScope.launch {
- YTDownloadHelper.downloadYTTracks(
- viewModel.folderType,
- viewModel.subFolder,
- listOf(item)
- )
- }
- }
- else -> {
- viewModel.uiScope.launch {
- DownloadHelper.downloadAllTracks(
- viewModel.folderType,
- viewModel.subFolder,
- listOf(item)
- )
+ holder.binding.btnDownloadProgress.invisible()
+ holder.binding.btnDownload.apply{
+ setImageResource(R.drawable.ic_arrow)
+ clearAnimation()
+ visible()
+ setOnClickListener{
+ if(!isOnline()){
+ showNoConnectionAlert()
+ return@setOnClickListener
+ }
+ showMessage("Processing!")
+ item.downloaded = DownloadStatus.Queued
+ when(source){
+ Source.YouTube -> {
+ viewModel.viewModelScope.launch {
+ downloadTracks(arrayListOf(item))
+ }
+ }
+ else -> {
+ viewModel.viewModelScope.launch {
+ downloadTracks(arrayListOf(item))
+ }
}
}
+ notifyItemChanged(position)//start showing anim!
}
- notifyItemChanged(position)//start showing anim!
}
}
}
- holder.binding.trackName.text = "${if(item.title.length > 17){"${item.title.subSequence(0,16)}..."}else{item.title}}"
- holder.binding.artist.text = "${item.artists.get(0)}..."
+ holder.binding.trackName.text = if(item.title.length > 20){"${item.title.subSequence(0,18)}..."}else{item.title}
+ holder.binding.artist.text = "${item.artists.firstOrNull()}..."
holder.binding.duration.text = "${item.durationSec/60} minutes, ${item.durationSec%60} sec"
}
diff --git a/app/src/main/java/com/shabinder/spotiflyer/splash/SplashScreen.kt b/app/src/main/java/com/shabinder/spotiflyer/splash/SplashScreen.kt
index 2ba1e77a..ae01668c 100755
--- a/app/src/main/java/com/shabinder/spotiflyer/splash/SplashScreen.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/splash/SplashScreen.kt
@@ -20,6 +20,7 @@ package com.shabinder.spotiflyer.splash
import android.content.Intent
import android.os.Bundle
import android.os.Handler
+import android.os.Looper
import androidx.appcompat.app.AppCompatActivity
import com.shabinder.spotiflyer.MainActivity
import com.shabinder.spotiflyer.R
@@ -33,7 +34,7 @@ class SplashScreen : AppCompatActivity(){
val splashTimeout = 400
val homeIntent = Intent(this@SplashScreen, MainActivity::class.java)
- Handler().postDelayed({
+ Handler(Looper.myLooper()!!).postDelayed({
//TODO:Bring Initial Setup here
startActivity(homeIntent)
finish()
diff --git a/app/src/main/java/com/shabinder/spotiflyer/ui/base/BaseFragment.kt b/app/src/main/java/com/shabinder/spotiflyer/ui/base/BaseFragment.kt
new file mode 100644
index 00000000..d3ae1050
--- /dev/null
+++ b/app/src/main/java/com/shabinder/spotiflyer/ui/base/BaseFragment.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.base
+
+import android.content.Context
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import androidx.viewbinding.ViewBinding
+import com.shabinder.spotiflyer.SharedViewModel
+
+abstract class BaseFragment : Fragment() {
+
+ protected val sharedViewModel: SharedViewModel by activityViewModels()
+ protected abstract val binding: VB
+ protected abstract val viewModel: VM
+ protected val viewModelScope by lazy{viewModel.viewModelScope}
+
+ protected val applicationContext: Context
+ get() = requireActivity().applicationContext
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/shabinder/spotiflyer/ui/base/tracklistbase/TrackListFragment.kt b/app/src/main/java/com/shabinder/spotiflyer/ui/base/tracklistbase/TrackListFragment.kt
new file mode 100644
index 00000000..cb316551
--- /dev/null
+++ b/app/src/main/java/com/shabinder/spotiflyer/ui/base/tracklistbase/TrackListFragment.kt
@@ -0,0 +1,207 @@
+/*
+ * 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.base.tracklistbase
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.navigation.NavArgs
+import com.shabinder.spotiflyer.R
+import com.shabinder.spotiflyer.databinding.TrackListFragmentBinding
+import com.shabinder.spotiflyer.models.DownloadStatus
+import com.shabinder.spotiflyer.models.TrackDetails
+import com.shabinder.spotiflyer.models.spotify.Source
+import com.shabinder.spotiflyer.recyclerView.TrackListAdapter
+import com.shabinder.spotiflyer.ui.base.BaseFragment
+import com.shabinder.spotiflyer.utils.*
+import com.shabinder.spotiflyer.utils.Provider.mainActivity
+import com.tonyodev.fetch2.Status
+
+abstract class TrackListFragment : BaseFragment() {
+
+ override lateinit var binding: TrackListFragmentBinding
+ protected abstract var adapter: TrackListAdapter
+ protected abstract var source: Source
+ private var intentFilter: IntentFilter? = null
+ private var updateUIReceiver: BroadcastReceiver? = null
+ private var queryReceiver: BroadcastReceiver? = null
+ protected abstract val args:NavArgs
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ if(!isOnline()){
+ showNoConnectionAlert()
+ mainActivity.navController.popBackStack()
+ }
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ binding = TrackListFragmentBinding.inflate(inflater,container,false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ binding.trackList.adapter = adapter
+ initializeLiveDataObservers()
+ }
+
+ /**
+ *Live Data Observers
+ **/
+ private fun initializeLiveDataObservers() {
+ viewModel.trackList.observe(viewLifecycleOwner, {
+ if (!it.isNullOrEmpty()){
+ Log.i("TrackListFragment","TrackList Updated")
+ adapter.submitList(it, source)
+ updateTracksStatus()
+ }
+ })
+
+ viewModel.coverUrl.observe(viewLifecycleOwner, {
+ it?.let{ bindImage(binding.coverImage,it, source) }
+ })
+
+ viewModel.title.observe(viewLifecycleOwner, {
+ binding.titleView.text = it
+ })
+ }
+
+ private fun initializeBroadcast() {
+ intentFilter = IntentFilter().apply {
+ addAction(Status.QUEUED.name)
+ addAction(Status.FAILED.name)
+ addAction(Status.DOWNLOADING.name)
+ addAction("Progress")
+ addAction("Converting")
+ 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 = viewModel.trackList.value?.map { it.title }?.indexOf(trackDetails.title) ?: -1
+ Log.i("BroadCast Received","$position, ${intent.action} , ${trackDetails.title}")
+ if(position != -1) {
+ val track = viewModel.trackList.value?.get(position)
+ track?.let{
+ when(intent.action){
+ Status.QUEUED.name -> {
+ it.downloaded = DownloadStatus.Queued
+ }
+ Status.FAILED.name -> {
+ it.downloaded = DownloadStatus.Failed
+ }
+ Status.DOWNLOADING.name -> {
+ it.downloaded = DownloadStatus.Downloading
+ }
+ "Progress" -> {
+ //Progress Update
+ it.progress = intent.getIntExtra("progress",0)
+ it.downloaded = DownloadStatus.Downloading
+ }
+ "Converting" -> {
+ //Progress Update
+ it.downloaded = DownloadStatus.Converting
+ }
+ "track_download_completed" -> {
+ it.downloaded = DownloadStatus.Downloaded
+ }
+ }
+ viewModel.trackList.value?.set(position, it)
+ adapter.notifyItemChanged(position)
+ updateTracksStatus()
+ }
+ }
+ }
+ }
+ }
+ }
+ val queryFilter = IntentFilter().apply { addAction("query_result") }
+ queryReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ //UI update here
+ if (intent != null){
+ val trackList = intent.getParcelableArrayListExtra("tracks") ?: listOf()
+ Log.i("Service Response", "${trackList.size} Tracks Active")
+ for (trackDetails in trackList) {
+ trackDetails?.let { it ->
+ val position: Int = viewModel.trackList.value?.map { it.title }?.indexOf(trackDetails.title) ?: -1
+ Log.i("BroadCast Received","$position, ${it.downloaded} , ${trackDetails.title}")
+ if(position != -1) {
+ viewModel.trackList.value?.set(position,it)
+ adapter.notifyItemChanged(position)
+ updateTracksStatus()
+ }
+ }
+ }
+ }
+ }
+ }
+ requireActivity().registerReceiver(updateUIReceiver, intentFilter)
+ requireActivity().registerReceiver(queryReceiver, queryFilter)
+ }
+
+ override fun onResume() {
+ super.onResume()
+ initializeBroadcast()
+ }
+
+ override fun onPause() {
+ super.onPause()
+ requireActivity().unregisterReceiver(updateUIReceiver)
+ requireActivity().unregisterReceiver(queryReceiver)
+ }
+
+ private fun updateTracksStatus() {
+ var allDownloaded = true
+ var allProcessing = true
+ for (track in viewModel.trackList.value!!){
+ if(track.downloaded != DownloadStatus.Downloaded)allDownloaded = false
+ if(track.downloaded == DownloadStatus.NotDownloaded)allProcessing = false
+ }
+ if(allProcessing){
+ binding.btnDownloadAll.visibility = View.GONE
+ binding.downloadingFab.apply{
+ setImageResource(R.drawable.ic_refresh)
+ visible()
+ rotate()
+ }
+ }
+ if(allDownloaded){
+ binding.btnDownloadAll.visibility = View.GONE
+ binding.downloadingFab.apply{
+ setImageResource(R.drawable.ic_tick)
+ visibility = View.VISIBLE
+ clearAnimation()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/shabinder/spotiflyer/utils/TrackListViewModel.kt b/app/src/main/java/com/shabinder/spotiflyer/ui/base/tracklistbase/TrackListViewModel.kt
similarity index 74%
rename from app/src/main/java/com/shabinder/spotiflyer/utils/TrackListViewModel.kt
rename to app/src/main/java/com/shabinder/spotiflyer/ui/base/tracklistbase/TrackListViewModel.kt
index 4bc69340..47d2da89 100644
--- a/app/src/main/java/com/shabinder/spotiflyer/utils/TrackListViewModel.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/ui/base/tracklistbase/TrackListViewModel.kt
@@ -15,30 +15,19 @@
* along with this program. If not, see .
*/
-package com.shabinder.spotiflyer.utils
+package com.shabinder.spotiflyer.ui.base.tracklistbase
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.shabinder.spotiflyer.models.TrackDetails
-import kotlinx.coroutines.CompletableJob
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Job
abstract class TrackListViewModel:ViewModel() {
abstract var folderType:String
abstract var subFolder:String
open val trackList = MutableLiveData>()
- private val viewModelJob:CompletableJob = Job()
- open val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
-
private val loading = "Loading!"
open var title = MutableLiveData().apply { value = loading }
open var coverUrl = MutableLiveData()
- override fun onCleared() {
- super.onCleared()
- viewModelJob.cancel()
- }
}
\ No newline at end of file
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
index 330e741a..238ae993 100644
--- a/app/src/main/java/com/shabinder/spotiflyer/ui/gaana/GaanaFragment.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/ui/gaana/GaanaFragment.kt
@@ -22,28 +22,23 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.lifecycle.ViewModelProvider
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.viewModelScope
import androidx.navigation.fragment.navArgs
-import androidx.recyclerview.widget.SimpleItemAnimator
-import com.shabinder.spotiflyer.SharedViewModel
-import com.shabinder.spotiflyer.downloadHelper.DownloadHelper
import com.shabinder.spotiflyer.models.DownloadStatus
+import com.shabinder.spotiflyer.models.TrackDetails
import com.shabinder.spotiflyer.models.spotify.Source
-import com.shabinder.spotiflyer.networking.GaanaInterface
-import com.shabinder.spotiflyer.networking.YoutubeMusicApi
import com.shabinder.spotiflyer.recyclerView.TrackListAdapter
+import com.shabinder.spotiflyer.ui.base.tracklistbase.TrackListFragment
import com.shabinder.spotiflyer.utils.*
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
-import javax.inject.Inject
@AndroidEntryPoint
-class GaanaFragment : TrackListFragment() {
+class GaanaFragment : TrackListFragment() {
- @Inject lateinit var youtubeMusicApi: YoutubeMusicApi
- @Inject lateinit var gaanaInterface: GaanaInterface
- override lateinit var viewModel: GaanaViewModel
+ override val viewModel: GaanaViewModel by viewModels()
override lateinit var adapter: TrackListAdapter
override var source: Source = Source.Gaana
override val args: GaanaFragmentArgs by navArgs()
@@ -51,10 +46,9 @@ class GaanaFragment : TrackListFragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
- ): View? {
+ ): View {
super.onCreateView(inflater, container, savedInstanceState)
-
- initializeAll()
+ adapter = TrackListAdapter(viewModel)
val gaanaLink = GaanaFragmentArgs.fromBundle(requireArguments()).link.substringAfter("gaana.com/")
//Link Schema: https://gaana.com/type/link
@@ -77,56 +71,30 @@ class GaanaFragment : TrackListFragment() {
showNoConnectionAlert()
return@setOnClickListener
}
- binding.btnDownloadAll.visibility = View.GONE
- binding.downloadingFab.visibility = View.VISIBLE
-
- rotateAnim(binding.downloadingFab)
- for (track in viewModel.trackList.value!!){
- if(track.downloaded != DownloadStatus.Downloaded){
- track.downloaded = DownloadStatus.Downloading
- adapter.notifyItemChanged(viewModel.trackList.value!!.indexOf(track))
- }
+ binding.btnDownloadAll.gone()
+ binding.downloadingFab.apply{
+ visible()
+ rotate()
}
showMessage("Processing!")
- sharedViewModel.uiScope.launch(Dispatchers.Default){
- val urlList = arrayListOf()
- viewModel.trackList.value?.forEach { urlList.add(it.albumArtURL) }
- //Appending Source
- urlList.add("gaana")
- loadAllImages(
- requireActivity(),
- urlList
- )
+ sharedViewModel.viewModelScope.launch(Dispatchers.Default){
+ loadAllImages(requireActivity(), viewModel.trackList.value?.map{it.albumArtURL}, Source.Gaana)
}
- viewModel.uiScope.launch {
- val finalList = viewModel.trackList.value
+ viewModel.viewModelScope.launch {
+ val finalList = viewModel.trackList.value?.filter{it.downloaded == DownloadStatus.NotDownloaded}
if(finalList.isNullOrEmpty())showMessage("Not Downloading Any Song")
- DownloadHelper.downloadAllTracks(
- viewModel.folderType,
- viewModel.subFolder,
- finalList ?: listOf(),
- )
+ finalList?.let { it1 -> downloadTracks(it1 as ArrayList) }
+ for (track in viewModel.trackList.value!!){
+ if(track.downloaded == DownloadStatus.NotDownloaded){
+ track.downloaded = DownloadStatus.Queued
+ //adapter.notifyItemChanged(viewModel.trackList.value!!.indexOf(track))
+ }
+ }
+ adapter.notifyDataSetChanged()
}
}
}
}
return binding.root
}
-
- /**
- * Basic Initialization
- **/
- private fun initializeAll() {
- sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
- viewModel = ViewModelProvider(this).get(GaanaViewModel::class.java)
- viewModel.gaanaInterface = gaanaInterface
- adapter = TrackListAdapter(viewModel)
- DownloadHelper.youtubeMusicApi = youtubeMusicApi
- DownloadHelper.sharedViewModel = sharedViewModel
- DownloadHelper.statusBar = binding.statusBar
- binding.trackList.adapter = adapter
- (binding.trackList.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
- }
-
-
}
\ 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
index 15f83620..e06f84d1 100644
--- a/app/src/main/java/com/shabinder/spotiflyer/ui/gaana/GaanaViewModel.kt
+++ b/app/src/main/java/com/shabinder/spotiflyer/ui/gaana/GaanaViewModel.kt
@@ -17,183 +17,188 @@
package com.shabinder.spotiflyer.ui.gaana
-import android.os.Environment
-import android.util.Log
import androidx.hilt.lifecycle.ViewModelInject
+import androidx.lifecycle.viewModelScope
import com.shabinder.spotiflyer.database.DatabaseDAO
import com.shabinder.spotiflyer.database.DownloadRecord
import com.shabinder.spotiflyer.models.DownloadStatus
import com.shabinder.spotiflyer.models.TrackDetails
-import com.shabinder.spotiflyer.models.gaana.*
+import com.shabinder.spotiflyer.models.gaana.GaanaTrack
import com.shabinder.spotiflyer.models.spotify.Source
import com.shabinder.spotiflyer.networking.GaanaInterface
+import com.shabinder.spotiflyer.ui.base.tracklistbase.TrackListViewModel
import com.shabinder.spotiflyer.utils.Provider
-import com.shabinder.spotiflyer.utils.TrackListViewModel
import com.shabinder.spotiflyer.utils.finalOutputDir
+import com.shabinder.spotiflyer.utils.queryActiveTracks
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
-class GaanaViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) : TrackListViewModel(){
+class GaanaViewModel @ViewModelInject constructor(
+ val databaseDAO: DatabaseDAO,
+ private val gaanaInterface : GaanaInterface
+) : TrackListViewModel(){
override var folderType:String = ""
override var subFolder:String = ""
- var gaanaInterface : GaanaInterface? = null
- val gaanaPlaceholderImageUrl = "https://a10.gaanacdn.com/images/social/gaana_social.jpg"
+
+ private val gaanaPlaceholderImageUrl = "https://a10.gaanacdn.com/images/social/gaana_social.jpg"
fun gaanaSearch(type:String,link:String){
- when(type){
- "song" -> {
- uiScope.launch {
- getGaanaSong(link)?.tracks?.firstOrNull()?.also {
+ viewModelScope.launch {
+ when (type) {
+ "song" -> {
+ gaanaInterface.getGaanaSong(seokey = link).value?.tracks?.firstOrNull()?.also {
folderType = "Tracks"
- if(File(finalOutputDir(it.track_title,folderType,subFolder)).exists()){//Download Already Present!!
+ subFolder = ""
+ if (File(
+ finalOutputDir(
+ it.track_title,
+ folderType,
+ subFolder
+ )
+ ).exists()
+ ) {//Download Already Present!!
it.downloaded = DownloadStatus.Downloaded
}
- trackList.value = listOf(it).toTrackDetailsList()
+ trackList.value = listOf(it).toTrackDetailsList(folderType, subFolder)
title.value = it.track_title
coverUrl.value = it.artworkLink
- withContext(Dispatchers.IO){
+ withContext(Dispatchers.IO) {
databaseDAO.insert(
DownloadRecord(
- type = "Track",
- name = title.value!!,
- link = "https://gaana.com/$type/$link",
- coverUrl = coverUrl.value!!,
- totalFiles = 1,
- downloaded = it.downloaded == DownloadStatus.Downloaded,
- directory = finalOutputDir(it.track_title,folderType,subFolder)
- )
+ type = "Track",
+ name = title.value!!,
+ link = "https://gaana.com/$type/$link",
+ coverUrl = coverUrl.value!!,
+ totalFiles = 1,
+ )
)
}
}
}
- }
- "album" -> {
- uiScope.launch {
- getGaanaAlbum(link)?.also {
+ "album" -> {
+ gaanaInterface.getGaanaAlbum(seokey = link).value?.also {
folderType = "Albums"
subFolder = link
it.tracks.forEach { track ->
- if(File(finalOutputDir(track.track_title,folderType,subFolder)).exists()){//Download Already Present!!
+ if (File(
+ finalOutputDir(
+ track.track_title,
+ folderType,
+ subFolder
+ )
+ ).exists()
+ ) {//Download Already Present!!
track.downloaded = DownloadStatus.Downloaded
}
}
- trackList.value = it.tracks.toTrackDetailsList()
+ trackList.value = it.tracks.toTrackDetailsList(folderType, subFolder)
title.value = link
coverUrl.value = it.custom_artworks.size_480p
- withContext(Dispatchers.IO){
- databaseDAO.insert(DownloadRecord(
- type = "Album",
- name = title.value!!,
- link = "https://gaana.com/$type/$link",
- coverUrl = coverUrl.value.toString(),
- totalFiles = trackList.value?.size ?: 0,
- downloaded = File(finalOutputDir(type = folderType,subFolder = subFolder)).listFiles()?.size == trackList.value?.size,
- directory = finalOutputDir(type = folderType,subFolder = subFolder)
- ))
+ withContext(Dispatchers.IO) {
+ databaseDAO.insert(
+ DownloadRecord(
+ type = "Album",
+ name = title.value!!,
+ link = "https://gaana.com/$type/$link",
+ coverUrl = coverUrl.value.toString(),
+ totalFiles = trackList.value?.size ?: 0,
+ )
+ )
}
}
}
- }
- "playlist" -> {
- uiScope.launch {
- getGaanaPlaylist(link)?.also {
+ "playlist" -> {
+ gaanaInterface.getGaanaPlaylist(seokey = link).value?.also {
folderType = "Playlists"
subFolder = link
- it.tracks.forEach {track ->
- if(File(finalOutputDir(track.track_title,folderType,subFolder)).exists()){//Download Already Present!!
+ it.tracks.forEach { track ->
+ if (File(
+ finalOutputDir(
+ track.track_title,
+ folderType,
+ subFolder
+ )
+ ).exists()
+ ) {//Download Already Present!!
track.downloaded = DownloadStatus.Downloaded
}
}
- trackList.value = it.tracks.toTrackDetailsList()
+ trackList.value = it.tracks.toTrackDetailsList(folderType, subFolder)
title.value = link
//coverUrl.value = "TODO"
coverUrl.value = gaanaPlaceholderImageUrl
- withContext(Dispatchers.IO){
- databaseDAO.insert(DownloadRecord(
- type = "Playlist",
- name = title.value.toString(),
- link = "https://gaana.com/$type/$link",
- coverUrl = coverUrl.value.toString(),
- totalFiles = it.tracks.size,
- downloaded = File(finalOutputDir(type = folderType,subFolder = subFolder)).listFiles()?.size == trackList.value?.size,
- directory = finalOutputDir(type = folderType,subFolder = subFolder)
- ))
+ withContext(Dispatchers.IO) {
+ databaseDAO.insert(
+ DownloadRecord(
+ type = "Playlist",
+ name = title.value.toString(),
+ link = "https://gaana.com/$type/$link",
+ coverUrl = coverUrl.value.toString(),
+ totalFiles = it.tracks.size,
+ )
+ )
}
}
}
- }
- "artist" -> {
- uiScope.launch {
+ "artist" -> {
folderType = "Artist"
subFolder = link
- val artistDetails = getGaanaArtistDetails(link)?.artist?.firstOrNull()?.also {
- title.value = it.name
- coverUrl.value = it.artworkLink
- }
- getGaanaArtistTracks(link)?.also {
- it.tracks.forEach {track ->
- if(File(finalOutputDir(track.track_title,folderType,subFolder)).exists()){//Download Already Present!!
+ val artistDetails =
+ gaanaInterface.getGaanaArtistDetails(seokey = link).value?.artist?.firstOrNull()
+ ?.also {
+ title.value = it.name
+ coverUrl.value = it.artworkLink
+ }
+ gaanaInterface.getGaanaArtistTracks(seokey = link).value?.also {
+ it.tracks.forEach { track ->
+ if (File(
+ finalOutputDir(
+ track.track_title,
+ folderType,
+ subFolder
+ )
+ ).exists()
+ ) {//Download Already Present!!
track.downloaded = DownloadStatus.Downloaded
}
}
- trackList.value = it.tracks.toTrackDetailsList()
- withContext(Dispatchers.IO){
- databaseDAO.insert(DownloadRecord(
- type = "Artist",
- name = artistDetails?.name ?: link,
- link = "https://gaana.com/$type/$link",
- coverUrl = coverUrl.value.toString(),
- totalFiles = trackList.value?.size ?: 0,
- downloaded = File(finalOutputDir(type = folderType,subFolder = subFolder)).listFiles()?.size == trackList.value?.size,
- directory = finalOutputDir(type = folderType,subFolder = subFolder)
- ))
+ trackList.value = it.tracks.toTrackDetailsList(folderType, subFolder)
+ withContext(Dispatchers.IO) {
+ databaseDAO.insert(
+ DownloadRecord(
+ type = "Artist",
+ name = artistDetails?.name ?: link,
+ link = "https://gaana.com/$type/$link",
+ coverUrl = coverUrl.value.toString(),
+ totalFiles = trackList.value?.size ?: 0,
+ )
+ )
}
}
}
}
+ queryActiveTracks()
}
}
-
- private fun List.toTrackDetailsList() = this.map {
+ private fun List.toTrackDetailsList(type:String , subFolder:String) = this.map {
TrackDetails(
title = it.track_title,
artists = it.artist.map { artist -> artist?.name.toString() },
durationSec = it.duration,
albumArt = File(
- Environment.getExternalStorageDirectory(),
- Provider.defaultDir +".Images/" + (it.artworkLink.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg"),
+ Provider.imageDir + (it.artworkLink.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg"),
albumName = it.album_title,
year = it.release_date,
comment = "Genres:${it.genre?.map { genre -> genre?.name }?.reduceOrNull { acc, s -> acc + s }}",
trackUrl = it.lyrics_url,
downloaded = it.downloaded ?: DownloadStatus.NotDownloaded,
source = Source.Gaana,
- albumArtURL = it.artworkLink
+ albumArtURL = it.artworkLink,
+ outputFile = finalOutputDir(it.track_title,type, subFolder,".m4a")
)
}.toMutableList()
-
- private suspend fun getGaanaSong(songLink:String): GaanaSong?{
- Log.i("Requesting","https://gaana.com/song/$songLink")
- return gaanaInterface?.getGaanaSong(seokey = songLink)?.value
- }
- private suspend fun getGaanaAlbum(albumLink:String): GaanaAlbum?{
- Log.i("Requesting","https://gaana.com/album/$albumLink")
- return gaanaInterface?.getGaanaAlbum(seokey = albumLink)?.value
- }
- private suspend fun getGaanaPlaylist(link:String): GaanaPlaylist?{
- Log.i("Requesting","https://gaana.com/playlist/$link")
- return gaanaInterface?.getGaanaPlaylist(seokey = link)?.value
- }
- private suspend fun getGaanaArtistDetails(link:String): GaanaArtistDetails?{
- Log.i("Requesting","https://gaana.com/artist/$link")
- return gaanaInterface?.getGaanaArtistDetails(seokey = link)?.value
- }
- private suspend fun getGaanaArtistTracks(link:String,limit:Int = 50): GaanaArtistTracks?{
- Log.i("Requesting","Tracks of: https://gaana.com/artist/$link")
- return gaanaInterface?.getGaanaArtistTracks(seokey = link,limit = limit)?.value
- }
}
\ 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 7a0033e8..96a8f587 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
@@ -23,7 +23,9 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
-import androidx.lifecycle.ViewModelProvider
+import androidx.fragment.app.activityViewModels
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.viewModelScope
import androidx.navigation.fragment.findNavController
import com.shabinder.spotiflyer.MainActivity
import com.shabinder.spotiflyer.R
@@ -33,6 +35,7 @@ import com.shabinder.spotiflyer.utils.*
import com.shreyaspatil.easyupipayment.EasyUpiPayment
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject
@@ -40,16 +43,15 @@ import javax.inject.Inject
@AndroidEntryPoint
class MainFragment : Fragment() {
- private lateinit var mainViewModel: MainViewModel
- private lateinit var sharedViewModel: SharedViewModel
+ private val mainViewModel: MainViewModel by viewModels()
+ private val sharedViewModel: SharedViewModel by activityViewModels()
private lateinit var binding: MainFragmentBinding
@Inject lateinit var easyUpiPayment: EasyUpiPayment
-
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
- ): View? {
+ ): View {
binding = MainFragmentBinding.inflate(inflater,container,false)
initializeAll()
binding.btnSearch.setOnClickListener {
@@ -89,16 +91,16 @@ class MainFragment : Fragment() {
**/
private fun handleIntent() {
sharedViewModel.intentString.observe(viewLifecycleOwner,{ it?.let {
- sharedViewModel.uiScope.launch(Dispatchers.IO) {
+ sharedViewModel.viewModelScope.launch(Dispatchers.IO) {
//Wait for any Authentication to Finish ,
// this Wait prevents from multiple Authentication Requests
- Thread.sleep(1000)
+ delay(1500)
if(sharedViewModel.spotifyService.value == null){
//Not Authenticated Yet
Provider.mainActivity.authenticateSpotify()
while (sharedViewModel.spotifyService.value == null) {
//Waiting for Authentication to Finish
- Thread.sleep(1000)
+ delay(1000)
}
}
@@ -114,8 +116,6 @@ class MainFragment : Fragment() {
}
private fun initializeAll() {
- mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
- sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
binding.apply {
btnGaana.openPlatformOnClick("com.gaana","http://gaana.com")
btnSpotify.openPlatformOnClick("com.spotify.music","http://open.spotify.com")
@@ -139,4 +139,5 @@ class MainFragment : Fragment() {
.append(getText(R.string.d_three)).append("\n")
.append(getText(R.string.d_four)).append("\n")
}
+
}
\ No newline at end of file
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 465d7eb6..c7e0d46e 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
@@ -23,29 +23,32 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.lifecycle.ViewModelProvider
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.viewModelScope
import androidx.navigation.fragment.navArgs
-import androidx.recyclerview.widget.SimpleItemAnimator
-import com.shabinder.spotiflyer.downloadHelper.DownloadHelper
import com.shabinder.spotiflyer.models.DownloadStatus
+import com.shabinder.spotiflyer.models.TrackDetails
import com.shabinder.spotiflyer.models.spotify.Source
-import com.shabinder.spotiflyer.networking.YoutubeMusicApi
+import com.shabinder.spotiflyer.networking.SpotifyService
import com.shabinder.spotiflyer.recyclerView.TrackListAdapter
+import com.shabinder.spotiflyer.ui.base.tracklistbase.TrackListFragment
import com.shabinder.spotiflyer.utils.*
import com.shabinder.spotiflyer.utils.Provider.mainActivity
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
-import javax.inject.Inject
@AndroidEntryPoint
-class SpotifyFragment : TrackListFragment() {
+class SpotifyFragment : TrackListFragment() {
- @Inject lateinit var youtubeMusicApi: YoutubeMusicApi
- override lateinit var viewModel: SpotifyViewModel
+ override val viewModel: SpotifyViewModel by viewModels()
+ override val args: SpotifyFragmentArgs by navArgs()
override lateinit var adapter: TrackListAdapter
override var source: Source = Source.Spotify
- override val args: SpotifyFragmentArgs by navArgs()
+ private val spotifyService:SpotifyService?
+ get() = sharedViewModel.spotifyService.value
+ lateinit var link:String
+ lateinit var type:String
@SuppressLint("SetJavaScriptEnabled")
override fun onCreateView(
@@ -55,71 +58,76 @@ class SpotifyFragment : TrackListFragment(
super.onCreateView(inflater, container, savedInstanceState)
initializeAll()
- val spotifyLink = args.link.substringAfter("open.spotify.com/")
+ var spotifyLink = "https://" + args.link.substringAfterLast("https://").substringBefore(" ").trim()
+ Log.i("Spotify Fragment Link", spotifyLink)
+ viewModelScope.launch(Dispatchers.IO) {
- val link = spotifyLink.substringAfterLast('/', "Error").substringBefore('?')
- val type = spotifyLink.substringBeforeLast('/', "Error").substringAfterLast('/')
-
- Log.i("Spotify Fragment", "$type : $link")
-
-
- if(sharedViewModel.spotifyService.value == null){//Authentication pending!!
- if(isOnline()) mainActivity.authenticateSpotify()
- }
-
- when{
- type == "Error" || link == "Error" -> {
- showMessage("Please Check Your Link!")
- mainActivity.onBackPressed()
+ /*
+ * New Link Schema: https://link.tospotify.com/kqTBblrjQbb,
+ * Fetching Standard Link: https://open.spotify.com/playlist/37i9dQZF1DX9RwfGbeGQwP?si=iWz7B1tETiunDntnDo3lSQ&_branch_match_id=862039436205270630
+ * */
+ if (!spotifyLink.contains("open.spotify")) {
+ val resolvedLink = viewModel.resolveLink(spotifyLink)
+ Log.d("Spotify Resolved Link", resolvedLink)
+ spotifyLink = resolvedLink
}
- else -> {
- if(type == "episode" || type == "show"){//TODO Implementation
- showMessage("Implementing Soon, Stay Tuned!")
+ link = spotifyLink.substringAfterLast('/', "Error").substringBefore('?')
+ type = spotifyLink.substringBeforeLast('/', "Error").substringAfterLast('/')
+
+ Log.i("Spotify Fragment", "$type : $link")
+
+ if (sharedViewModel.spotifyService.value == null) {//Authentication pending!!
+ if (isOnline()) mainActivity.authenticateSpotify()
+ }
+
+ when {
+ type == "Error" || link == "Error" -> {
+ showMessage("Please Check Your Link!")
+ mainActivity.onBackPressed()
}
- else{
- this.viewModel.spotifySearch(type,link)
- binding.btnDownloadAll.setOnClickListener {
- if(!isOnline()){
- showNoConnectionAlert()
- return@setOnClickListener
- }
- binding.btnDownloadAll.visibility = View.GONE
- binding.downloadingFab.visibility = View.VISIBLE
+ else -> {
+ if (type == "episode" || type == "show") {//TODO Implementation
+ showMessage("Implementing Soon, Stay Tuned!")
+ } else {
+ viewModel.spotifySearch(type, link)
- rotateAnim(binding.downloadingFab)
- for (track in this.viewModel.trackList.value ?: listOf()){
- if(track.downloaded != DownloadStatus.Downloaded){
- track.downloaded = DownloadStatus.Downloading
- adapter.notifyItemChanged(this.viewModel.trackList.value!!.indexOf(track))
+ binding.btnDownloadAll.setOnClickListener {
+ if (!isOnline()) {
+ showNoConnectionAlert()
+ return@setOnClickListener
+ }
+ binding.btnDownloadAll.gone()
+ binding.downloadingFab.apply {
+ visible()
+ rotate()
+ }
+ showMessage("Processing!")
+ sharedViewModel.viewModelScope.launch(Dispatchers.Default) {
+ loadAllImages(
+ requireActivity(),
+ viewModel.trackList.value?.map { it.albumArtURL },
+ Source.Spotify
+ )
+ }
+ viewModelScope.launch {
+ val finalList = viewModel.trackList.value?.filter{it.downloaded == DownloadStatus.NotDownloaded}
+ if (finalList.isNullOrEmpty()) showMessage("Not Downloading Any Song")
+ else downloadTracks(finalList as ArrayList)
+ for (track in viewModel.trackList.value ?: listOf()) {
+ if (track.downloaded == DownloadStatus.NotDownloaded) {
+ track.downloaded = DownloadStatus.Queued
+ //adapter.notifyItemChanged(viewModel.trackList.value!!.indexOf(track))
+ }
+ }
+ adapter.notifyDataSetChanged()
}
- }
- showMessage("Processing!")
- sharedViewModel.uiScope.launch(Dispatchers.Default){
- val urlList = arrayListOf()
- this@SpotifyFragment.viewModel.trackList.value?.forEach { urlList.add(it.albumArtURL) }
- //Appending Source
- urlList.add("spotify")
- loadAllImages(
- requireActivity(),
- urlList
- )
- }
- this.viewModel.uiScope.launch {
- val finalList = viewModel.trackList.value
- if(finalList.isNullOrEmpty())showMessage("Not Downloading Any Song")
- DownloadHelper.downloadAllTracks(
- viewModel.folderType,
- viewModel.subFolder,
- finalList ?: listOf(),
- )
}
}
}
}
}
-
return binding.root
}
@@ -127,15 +135,10 @@ class SpotifyFragment : TrackListFragment(
* Basic Initialization
**/
private fun initializeAll() {
- this.viewModel = ViewModelProvider(this).get(SpotifyViewModel::class.java)
- adapter = TrackListAdapter(this.viewModel)
sharedViewModel.spotifyService.observe(viewLifecycleOwner, {
this.viewModel.spotifyService = it
})
- DownloadHelper.youtubeMusicApi = youtubeMusicApi
- DownloadHelper.sharedViewModel = sharedViewModel
- DownloadHelper.statusBar = binding.statusBar
- binding.trackList.adapter = adapter
- (binding.trackList.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
+ viewModel.spotifyService = spotifyService //Temp Initialisation
+ adapter = TrackListAdapter(this.viewModel)
}
}
\ 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 705166f9..bfac5bf5 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
@@ -17,167 +17,193 @@
package com.shabinder.spotiflyer.ui.spotify
-import android.os.Environment
import android.util.Log
import androidx.hilt.lifecycle.ViewModelInject
+import androidx.lifecycle.viewModelScope
import com.shabinder.spotiflyer.database.DatabaseDAO
import com.shabinder.spotiflyer.database.DownloadRecord
import com.shabinder.spotiflyer.models.DownloadStatus
import com.shabinder.spotiflyer.models.TrackDetails
-import com.shabinder.spotiflyer.models.spotify.*
+import com.shabinder.spotiflyer.models.spotify.Album
+import com.shabinder.spotiflyer.models.spotify.Image
+import com.shabinder.spotiflyer.models.spotify.Source
+import com.shabinder.spotiflyer.models.spotify.Track
+import com.shabinder.spotiflyer.networking.GaanaInterface
import com.shabinder.spotiflyer.networking.SpotifyService
-import com.shabinder.spotiflyer.utils.Provider
-import com.shabinder.spotiflyer.utils.TrackListViewModel
+import com.shabinder.spotiflyer.ui.base.tracklistbase.TrackListViewModel
+import com.shabinder.spotiflyer.utils.Provider.imageDir
import com.shabinder.spotiflyer.utils.finalOutputDir
+import com.shabinder.spotiflyer.utils.queryActiveTracks
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
-class SpotifyViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) : TrackListViewModel(){
+class SpotifyViewModel @ViewModelInject constructor(
+ val databaseDAO: DatabaseDAO,
+ val gaanaInterface : GaanaInterface
+) : TrackListViewModel(){
override var folderType:String = ""
override var subFolder:String = ""
var spotifyService : SpotifyService? = null
+ fun resolveLink(url:String):String {
+ val response = gaanaInterface.getResponse(url).execute().body()?.string().toString()
+ val regex = """https://open\.spotify\.com.+\w""".toRegex()
+ return regex.find(response)?.value.toString()
+ }
+
fun spotifySearch(type:String,link: String){
- when (type) {
- "track" -> {
- uiScope.launch {
- getTrackDetails(link)?.also {
+ viewModelScope.launch {
+ when (type) {
+ "track" -> {
+ spotifyService?.getTrack(link)?.value?.also {
folderType = "Tracks"
- if(File(finalOutputDir(it.name,folderType,subFolder)).exists()){//Download Already Present!!
+ subFolder = ""
+ if (File(
+ finalOutputDir(
+ it.name.toString(),
+ folderType,
+ subFolder
+ )
+ ).exists()
+ ) {//Download Already Present!!
it.downloaded = DownloadStatus.Downloaded
}
- trackList.value = listOf(it).toTrackDetailsList()
+ trackList.value = listOf(it).toTrackDetailsList(folderType, subFolder)
title.value = it.name
- coverUrl.value = it.album!!.images?.elementAtOrNull(1)?.url ?: it.album!!.images?.elementAtOrNull(0)?.url
- withContext(Dispatchers.IO){
- databaseDAO.insert(DownloadRecord(
- type = "Track",
- name = title.value!!,
- link = "https://open.spotify.com/$type/$link",
- coverUrl = coverUrl.value!!,
- totalFiles = 1,
- downloaded = it.downloaded == DownloadStatus.Downloaded,
- directory = finalOutputDir(it.name,folderType,subFolder)
- ))
+ coverUrl.value = it.album!!.images?.elementAtOrNull(1)?.url
+ ?: it.album!!.images?.elementAtOrNull(0)?.url
+ withContext(Dispatchers.IO) {
+ databaseDAO.insert(
+ DownloadRecord(
+ type = "Track",
+ name = title.value!!,
+ link = "https://open.spotify.com/$type/$link",
+ coverUrl = coverUrl.value!!,
+ totalFiles = 1,
+ )
+ )
}
}
}
- }
- "album" -> {
- uiScope.launch {
- val albumObject = getAlbumDetails(link)
+ "album" -> {
+ val albumObject = spotifyService?.getAlbum(link)?.value
folderType = "Albums"
subFolder = albumObject?.name.toString()
albumObject?.tracks?.items?.forEach {
- if(File(finalOutputDir(it.name!!,folderType,subFolder)).exists()){//Download Already Present!!
+ if (File(
+ finalOutputDir(
+ it.name!!,
+ folderType,
+ subFolder
+ )
+ ).exists()
+ ) {//Download Already Present!!
it.downloaded = DownloadStatus.Downloaded
}
- it.album = Album(images = listOf(Image(url = albumObject.images?.elementAtOrNull(1)?.url ?: albumObject.images?.elementAtOrNull(0)?.url )))
+ it.album = Album(
+ images = listOf(
+ Image(
+ url = albumObject.images?.elementAtOrNull(1)?.url
+ ?: albumObject.images?.elementAtOrNull(0)?.url
+ )
+ )
+ )
}
- trackList.value = albumObject?.tracks?.items?.toTrackDetailsList()
+ trackList.value = albumObject?.tracks?.items?.toTrackDetailsList(folderType, subFolder)
title.value = albumObject?.name
- coverUrl.value = albumObject?.images?.elementAtOrNull(1)?.url ?: albumObject?.images?.elementAtOrNull(0)?.url
- withContext(Dispatchers.IO){
- databaseDAO.insert(DownloadRecord(
- type = "Album",
- name = title.value!!,
- link = "https://open.spotify.com/$type/$link",
- coverUrl = coverUrl.value.toString(),
- totalFiles = trackList.value?.size ?: 0,
- downloaded = File(finalOutputDir(type = folderType,subFolder = subFolder)).listFiles()?.size == trackList.value?.size,
- directory = finalOutputDir(type = folderType,subFolder = subFolder)
- ))
+ coverUrl.value = albumObject?.images?.elementAtOrNull(1)?.url
+ ?: albumObject?.images?.elementAtOrNull(0)?.url
+ withContext(Dispatchers.IO) {
+ databaseDAO.insert(
+ DownloadRecord(
+ type = "Album",
+ name = title.value!!,
+ link = "https://open.spotify.com/$type/$link",
+ coverUrl = coverUrl.value.toString(),
+ totalFiles = trackList.value?.size ?: 0,
+ )
+ )
}
}
- }
- "playlist" -> {
- uiScope.launch {
- val playlistObject = getPlaylistDetails(link)
+ "playlist" -> {
+ Log.i("Spotify Service",spotifyService.toString())
+ val playlistObject = spotifyService?.getPlaylist(link)?.value
folderType = "Playlists"
subFolder = playlistObject?.name.toString()
val tempTrackList = mutableListOf