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 ac6b0aec..23a89bbb 100755 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -15,6 +15,7 @@ diff --git a/.idea/misc.xml b/.idea/misc.xml index c340fa86..d10a516c 100755 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,7 +8,7 @@ - + diff --git a/app/build.gradle b/app/build.gradle index 38466056..2844c795 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,7 +19,7 @@ plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-kapt' - id 'kotlin-android-extensions' + id 'kotlin-parcelize' id 'androidx.navigation.safeargs.kotlin' id 'dagger.hilt.android.plugin' id 'kotlinx-serialization' @@ -39,8 +39,8 @@ android { applicationId 'com.shabinder.spotiflyer' minSdkVersion 22 targetSdkVersion 30 - versionCode 8 - versionName "1.6" + versionCode 9 + versionName "1.7" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -79,6 +79,7 @@ android { exclude 'META-INF/ASL2.0' exclude("META-INF/*.kotlin_module") } + ndkVersion '21.3.6528147' } @@ -101,7 +102,7 @@ dependencies { 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.1' + 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" diff --git a/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt b/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt index 974efb7a..b0496bc3 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/MainActivity.kt @@ -37,7 +37,6 @@ import androidx.navigation.findNavController import com.github.javiersantos.appupdater.AppUpdater import com.github.javiersantos.appupdater.enums.UpdateFrom import com.shabinder.spotiflyer.databinding.MainActivityBinding -import com.shabinder.spotiflyer.downloadHelper.DownloadHelper import com.shabinder.spotiflyer.networking.SpotifyService import com.shabinder.spotiflyer.networking.SpotifyServiceTokenRequest import com.shabinder.spotiflyer.utils.NetworkInterceptor @@ -45,6 +44,7 @@ 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 @@ -59,6 +59,8 @@ import javax.inject.Inject @AndroidEntryPoint class MainActivity : AppCompatActivity(){ private var spotifyService : SpotifyService? = null + val viewModelScope : CoroutineScope + get() = sharedViewModel.viewModelScope private lateinit var binding: MainActivityBinding private lateinit var sharedViewModel: SharedViewModel lateinit var snackBarAnchor: View @@ -75,7 +77,6 @@ class MainActivity : AppCompatActivity(){ sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java) navController = findNavController(R.id.navHostFragment) snackBarAnchor = binding.snackBarPosition - DownloadHelper.youtubeMusicApi = sharedViewModel.youtubeMusicApi authenticateSpotify() } 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 cfcb33e3..00000000 --- a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/DownloadHelper.kt +++ /dev/null @@ -1,168 +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.content.Intent -import android.os.Handler -import android.os.Looper -import android.util.Log -import android.view.View -import android.view.animation.AlphaAnimation -import android.view.animation.Animation -import android.widget.TextView -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 com.tonyodev.fetch2.Status -import kotlinx.coroutines.CoroutineScope -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 - - 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.IO){ - 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(Looper.myLooper()!!).postDelayed({ - //Delay is Added ,if a request is in processing it may finish - Log.i("Spotify Helper","Download Request Sent") - 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) { - 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()) { - //Track Not Found - notFound++ ; updateStatusBar() - val intent = Intent() - .setAction(Status.FAILED.name) - .putExtra("track",it) - statusBar?.context?.sendBroadcast(intent) - } - else {//Found Youtube Video ID - val outputFile: String = - 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++ - updateStatusBar() - downloadList.add(downloadObject) - } - if(index == (trackList.size-1)){//LastElement - statusBar?.clearAnimation() - if(downloadList.size > 0) { - Handler(Looper.myLooper()!!).postDelayed({ - //Delay is Added ,if a request is in processing it may finish - Log.i("Spotify Helper", "Download Request Sent") - showMessage("Download Started, Now You can leave the App!") - startService(mainActivity, downloadList) - }, 3000) - } - } - } - 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") - private fun updateStatusBar() { - CoroutineScope(Dispatchers.Main).launch{ - 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 ff0e53d7..00000000 --- a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YTDownloadHelper.kt +++ /dev/null @@ -1,63 +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.util.Log -import com.shabinder.spotiflyer.models.DownloadObject -import com.shabinder.spotiflyer.models.TrackDetails -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.withContext -import java.io.File - -interface YTDownloadHelper { - suspend fun downloadYTTracks( - type:String, - subFolder: String?, - tracks:List, - ){ - val downloadList = ArrayList() - tracks.forEach { - if(!isOnline()){ - showNoConnectionAlert() - return - } - val outputFile: String = 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){ - showMessage("Download Started, Now You can leave the App!") - 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 caec1f40..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, @@ -43,7 +36,9 @@ data class TrackDetails( var albumArtURL: String, var source: Source, var downloaded: DownloadStatus = DownloadStatus.NotDownloaded, - var progress: Int = 0 + var progress: Int = 0, + var outputFile: String, + var videoID:String? = null ):Parcelable enum class DownloadStatus{ 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/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/recyclerView/TrackListAdapter.kt b/app/src/main/java/com/shabinder/spotiflyer/recyclerView/TrackListAdapter.kt index dd85ae03..f45ef001 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/recyclerView/TrackListAdapter.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/recyclerView/TrackListAdapter.kt @@ -27,8 +27,6 @@ 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 @@ -36,7 +34,7 @@ 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()),YTDownloadHelper { +class TrackListAdapter(private val viewModel : TrackListViewModel): ListAdapter(TrackDiffCallback()){ var source:Source =Source.Spotify @@ -115,20 +113,12 @@ class TrackListAdapter(private val viewModel : TrackListViewModel): ListAdapter< when(source){ Source.YouTube -> { viewModel.viewModelScope.launch { - downloadYTTracks( - viewModel.folderType, - viewModel.subFolder, - listOf(item) - ) + downloadTracks(arrayListOf(item)) } } else -> { viewModel.viewModelScope.launch { - DownloadHelper.downloadAllTracks( - viewModel.folderType, - viewModel.subFolder, - listOf(item) - ) + downloadTracks(arrayListOf(item)) } } } 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 index fb82fffb..d3ae1050 100644 --- a/app/src/main/java/com/shabinder/spotiflyer/ui/base/BaseFragment.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/ui/base/BaseFragment.kt @@ -32,5 +32,6 @@ abstract class BaseFragment : Fragment() { protected abstract val viewModel: VM protected val viewModelScope by lazy{viewModel.viewModelScope} - open fun applicationContext(): Context = requireActivity().applicationContext + 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 index 61904b98..cb316551 100644 --- 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 @@ -29,7 +29,6 @@ import android.view.ViewGroup import androidx.navigation.NavArgs import com.shabinder.spotiflyer.R import com.shabinder.spotiflyer.databinding.TrackListFragmentBinding -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 @@ -63,15 +62,9 @@ abstract class TrackListFragment : BaseF savedInstanceState: Bundle? ): View? { binding = TrackListFragmentBinding.inflate(inflater,container,false) - initializeAll() return binding.root } - private fun initializeAll() { - DownloadHelper.youtubeMusicApi = sharedViewModel.youtubeMusicApi - DownloadHelper.statusBar = binding.statusBar - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.trackList.adapter = adapter @@ -84,7 +77,7 @@ abstract class TrackListFragment : BaseF private fun initializeLiveDataObservers() { viewModel.trackList.observe(viewLifecycleOwner, { if (!it.isNullOrEmpty()){ - Log.i("GaanaFragment","TrackList Updated") + Log.i("TrackListFragment","TrackList Updated") adapter.submitList(it, source) updateTracksStatus() } 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 699aff5f..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 @@ -25,8 +25,8 @@ import android.view.ViewGroup import androidx.fragment.app.viewModels import androidx.lifecycle.viewModelScope import androidx.navigation.fragment.navArgs -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.recyclerView.TrackListAdapter import com.shabinder.spotiflyer.ui.base.tracklistbase.TrackListFragment @@ -46,7 +46,7 @@ class GaanaFragment : TrackListFragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { super.onCreateView(inflater, container, savedInstanceState) adapter = TrackListAdapter(viewModel) @@ -76,24 +76,21 @@ class GaanaFragment : TrackListFragment() { visible() rotate() } - for (track in viewModel.trackList.value!!){ - if(track.downloaded != DownloadStatus.Downloaded){ - track.downloaded = DownloadStatus.Queued - adapter.notifyItemChanged(viewModel.trackList.value!!.indexOf(track)) - } - } showMessage("Processing!") sharedViewModel.viewModelScope.launch(Dispatchers.Default){ loadAllImages(requireActivity(), viewModel.trackList.value?.map{it.albumArtURL}, Source.Gaana) } viewModel.viewModelScope.launch { - val finalList = viewModel.trackList.value + 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() } } } 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 c410c55f..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 @@ -37,7 +37,7 @@ import java.io.File class GaanaViewModel @ViewModelInject constructor( val databaseDAO: DatabaseDAO, - val gaanaInterface : GaanaInterface + private val gaanaInterface : GaanaInterface ) : TrackListViewModel(){ override var folderType:String = "" @@ -51,6 +51,7 @@ class GaanaViewModel @ViewModelInject constructor( "song" -> { gaanaInterface.getGaanaSong(seokey = link).value?.tracks?.firstOrNull()?.also { folderType = "Tracks" + subFolder = "" if (File( finalOutputDir( it.track_title, @@ -61,7 +62,7 @@ class GaanaViewModel @ViewModelInject constructor( ) {//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) { @@ -72,12 +73,6 @@ class GaanaViewModel @ViewModelInject constructor( link = "https://gaana.com/$type/$link", coverUrl = coverUrl.value!!, totalFiles = 1, - downloaded = it.downloaded == DownloadStatus.Downloaded, - directory = finalOutputDir( - it.track_title, - folderType, - subFolder - ) ) ) } @@ -99,7 +94,7 @@ class GaanaViewModel @ViewModelInject constructor( 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) { @@ -110,16 +105,6 @@ class GaanaViewModel @ViewModelInject constructor( 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 - ) ) ) } @@ -141,7 +126,7 @@ class GaanaViewModel @ViewModelInject constructor( 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 @@ -153,16 +138,6 @@ class GaanaViewModel @ViewModelInject constructor( 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 - ) ) ) } @@ -190,7 +165,7 @@ class GaanaViewModel @ViewModelInject constructor( track.downloaded = DownloadStatus.Downloaded } } - trackList.value = it.tracks.toTrackDetailsList() + trackList.value = it.tracks.toTrackDetailsList(folderType, subFolder) withContext(Dispatchers.IO) { databaseDAO.insert( DownloadRecord( @@ -199,16 +174,6 @@ class GaanaViewModel @ViewModelInject constructor( 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 - ) ) ) } @@ -219,7 +184,7 @@ class GaanaViewModel @ViewModelInject constructor( } } - 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() }, @@ -232,7 +197,8 @@ class GaanaViewModel @ViewModelInject constructor( 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() } \ 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 2995093f..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 @@ -51,7 +51,7 @@ class MainFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { binding = MainFragmentBinding.inflate(inflater,container,false) initializeAll() binding.btnSearch.setOnClickListener { @@ -86,12 +86,6 @@ class MainFragment : Fragment() { return binding.root } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - //starting Notification and Downloader Service! - startService(requireContext()) - } - /** * Handle Intent If there is any! **/ 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 a5d0a309..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 @@ -26,8 +26,8 @@ import android.view.ViewGroup import androidx.fragment.app.viewModels import androidx.lifecycle.viewModelScope import androidx.navigation.fragment.navArgs -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.SpotifyService import com.shabinder.spotiflyer.recyclerView.TrackListAdapter @@ -103,16 +103,6 @@ class SpotifyFragment : TrackListFragment visible() rotate() } - for (track in viewModel.trackList.value ?: listOf()) { - if (track.downloaded != DownloadStatus.Downloaded) { - track.downloaded = DownloadStatus.Queued - adapter.notifyItemChanged( - viewModel.trackList.value!!.indexOf( - track - ) - ) - } - } showMessage("Processing!") sharedViewModel.viewModelScope.launch(Dispatchers.Default) { loadAllImages( @@ -122,13 +112,16 @@ class SpotifyFragment : TrackListFragment ) } viewModelScope.launch { - val finalList = viewModel.trackList.value + 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(), - ) + 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() } } } 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 fb8006e2..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 @@ -61,9 +61,10 @@ class SpotifyViewModel @ViewModelInject constructor( "track" -> { spotifyService?.getTrack(link)?.value?.also { folderType = "Tracks" + subFolder = "" if (File( finalOutputDir( - it.name, + it.name.toString(), folderType, subFolder ) @@ -71,7 +72,7 @@ class SpotifyViewModel @ViewModelInject constructor( ) {//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 @@ -83,8 +84,6 @@ class SpotifyViewModel @ViewModelInject constructor( link = "https://open.spotify.com/$type/$link", coverUrl = coverUrl.value!!, totalFiles = 1, - downloaded = it.downloaded == DownloadStatus.Downloaded, - directory = finalOutputDir(it.name, folderType, subFolder) ) ) } @@ -115,7 +114,7 @@ class SpotifyViewModel @ViewModelInject constructor( ) ) } - 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 @@ -127,13 +126,6 @@ class SpotifyViewModel @ViewModelInject constructor( 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) ) ) } @@ -172,7 +164,7 @@ class SpotifyViewModel @ViewModelInject constructor( moreTracksAvailable = !moreTracks?.next.isNullOrBlank() } Log.i("Total Tracks Fetched", tempTrackList.size.toString()) - trackList.value = tempTrackList.toTrackDetailsList() + trackList.value = tempTrackList.toTrackDetailsList(folderType, subFolder) title.value = playlistObject?.name coverUrl.value = playlistObject?.images?.elementAtOrNull(1)?.url ?: playlistObject?.images?.firstOrNull()?.url.toString() @@ -184,13 +176,6 @@ class SpotifyViewModel @ViewModelInject constructor( link = "https://open.spotify.com/$type/$link", coverUrl = coverUrl.value.toString(), totalFiles = tempTrackList.size, - downloaded = File( - finalOutputDir( - type = folderType, - subFolder = subFolder - ) - ).listFiles()?.size == tempTrackList.size, - directory = finalOutputDir(type = folderType, subFolder = subFolder) ) ) } @@ -204,8 +189,7 @@ class SpotifyViewModel @ViewModelInject constructor( } } - @Suppress("DEPRECATION") - private fun List.toTrackDetailsList() = this.map { + private fun List.toTrackDetailsList(type:String , subFolder:String) = this.map { TrackDetails( title = it.name.toString(), artists = it.artists?.map { artist -> artist?.name.toString() } ?: listOf(), @@ -218,7 +202,8 @@ class SpotifyViewModel @ViewModelInject constructor( trackUrl = it.href, downloaded = it.downloaded, source = Source.Spotify, - albumArtURL = it.album?.images?.elementAtOrNull(1)?.url ?: it.album?.images?.firstOrNull()?.url.toString() + albumArtURL = it.album?.images?.elementAtOrNull(1)?.url ?: it.album?.images?.firstOrNull()?.url.toString(), + outputFile = finalOutputDir(it.name.toString(),type, subFolder,".m4a") ) }.toMutableList() } \ No newline at end of file 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 f4b5c021..75bb3a9d 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 @@ -24,8 +24,8 @@ import android.view.ViewGroup import androidx.fragment.app.viewModels import androidx.lifecycle.viewModelScope import androidx.navigation.fragment.navArgs -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.recyclerView.TrackListAdapter import com.shabinder.spotiflyer.ui.base.tracklistbase.TrackListFragment @@ -38,7 +38,7 @@ private const val sampleDomain2 = "youtu.be" private const val sampleDomain1 = "youtube.com" @AndroidEntryPoint -class YoutubeFragment : TrackListFragment() , YTDownloadHelper { +class YoutubeFragment : TrackListFragment(){ override val viewModel: YoutubeViewModel by viewModels() override lateinit var adapter : TrackListAdapter @@ -48,7 +48,7 @@ class YoutubeFragment : TrackListFragment override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { super.onCreateView(inflater, container, savedInstanceState) adapter = TrackListAdapter(viewModel) @@ -90,24 +90,19 @@ class YoutubeFragment : TrackListFragment visible() rotate() } - - for (track in this.viewModel.trackList.value?: listOf()){ - if(track.downloaded != DownloadStatus.Downloaded){ - track.downloaded = DownloadStatus.Queued - //adapter.notifyItemChanged(this.viewModel.trackList.value!!.indexOf(track)) - } - } - adapter.notifyDataSetChanged() showMessage("Processing!") sharedViewModel.viewModelScope.launch(Dispatchers.Default){ loadAllImages(requireActivity(), viewModel.trackList.value?.map{it.albumArtURL}, Source.YouTube) } viewModel.viewModelScope.launch { - downloadYTTracks( - type = viewModel.folderType, - subFolder = viewModel.subFolder, - tracks = viewModel.trackList.value ?: listOf() - ) + downloadTracks((viewModel.trackList.value?.filter { it.downloaded == DownloadStatus.NotDownloaded } ?: arrayListOf()) 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() } } } 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 4a72b0e7..340fe754 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 @@ -82,7 +82,9 @@ class YoutubeViewModel @ViewModelInject constructor( DownloadStatus.Downloaded else { DownloadStatus.NotDownloaded - } + }, + outputFile = finalOutputDir(it.title(),folderType, subFolder,".m4a"), + videoID = it.videoId() ) }.toMutableList()) @@ -93,8 +95,6 @@ class YoutubeViewModel @ViewModelInject constructor( link = "https://www.youtube.com/playlist?list=$searchId", coverUrl = "https://i.ytimg.com/vi/${videos.firstOrNull()?.videoId()}/hqdefault.jpg", totalFiles = videos.size, - directory = finalOutputDir(itemName = removeIllegalChars(name),type = folderType,subFolder = subFolder), - downloaded = File(finalOutputDir(itemName = removeIllegalChars(name),type = folderType,subFolder = subFolder)).exists() )) } queryActiveTracks() @@ -124,8 +124,21 @@ class YoutubeViewModel @ViewModelInject constructor( durationSec = detail?.lengthSeconds()?:0, albumArt = File(imageDir,"$searchId.jpeg"), source = Source.YouTube, - albumArtURL = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg" - ) + albumArtURL = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg", + downloaded = if (File( + finalOutputDir( + itemName = name, + type = folderType, + subFolder = subFolder + )).exists() + ) + DownloadStatus.Downloaded + else { + DownloadStatus.NotDownloaded + }, + outputFile = finalOutputDir(name,folderType, subFolder,".m4a"), + videoID = searchId + ) ).toMutableList() ) title.postValue( @@ -139,8 +152,6 @@ class YoutubeViewModel @ViewModelInject constructor( link = "https://www.youtube.com/watch?v=$searchId", coverUrl = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg", totalFiles = 1, - downloaded = false, - directory = finalOutputDir(type = "YT_Downloads") )) } queryActiveTracks() 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 d9c1829f..46c0b174 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/utils/Utils.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/utils/Utils.kt @@ -34,7 +34,7 @@ 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.TrackDetails import com.shabinder.spotiflyer.models.spotify.Source import com.shabinder.spotiflyer.utils.Provider.defaultDir import com.shabinder.spotiflyer.utils.Provider.imageDir @@ -52,11 +52,17 @@ fun loadAllImages(context: Context?, images:List? = null,source:Source) context?.let { ContextCompat.startForegroundService(it, serviceIntent) } } -fun startService(context:Context? = mainActivity,objects:ArrayList? = null ) { - val serviceIntent = Intent(context, ForegroundService::class.java) - objects?.let { serviceIntent.putParcelableArrayListExtra("object",it) } - context?.let { ContextCompat.startForegroundService(it, serviceIntent) } +fun downloadTracks( + trackList: ArrayList, + context: Context? = mainActivity +) { + if(!trackList.isNullOrEmpty()){ + val serviceIntent = Intent(context, ForegroundService::class.java) + serviceIntent.putParcelableArrayListExtra("object",trackList) + context?.let { ContextCompat.startForegroundService(it, serviceIntent) } + } } + fun queryActiveTracks(context:Context? = mainActivity) { val serviceIntent = Intent(context, ForegroundService::class.java).apply { action = "query" @@ -64,10 +70,10 @@ fun queryActiveTracks(context:Context? = mainActivity) { context?.let { ContextCompat.startForegroundService(it, serviceIntent) } } -fun finalOutputDir(itemName:String? = null,type:String, subFolder:String?=null,extension:String? = ".mp3"): String{ +fun finalOutputDir(itemName:String ,type:String, subFolder:String,extension:String = ".mp3"): String{ return defaultDir + removeIllegalChars(type) + File.separator + - (if(subFolder == null){""}else{ removeIllegalChars(subFolder) + File.separator} - + itemName?.let { removeIllegalChars(it) + extension}) + if(subFolder.isEmpty())"" else { removeIllegalChars(subFolder) + File.separator} + + removeIllegalChars(itemName) + extension } /** @@ -220,7 +226,7 @@ fun createDirectory(dir:String){ /** * Removing Illegal Chars from File Name * **/ -fun removeIllegalChars(fileName: String): String? { +fun removeIllegalChars(fileName: String): String { val illegalCharArray = charArrayOf( '/', '\n', @@ -265,7 +271,4 @@ fun createDirectories() { createDirectory(defaultDir + "Albums/") createDirectory(defaultDir + "Playlists/") createDirectory(defaultDir + "YT_Downloads/") -} -fun getEmojiByUnicode(unicode: Int): String? { - return String(Character.toChars(unicode)) } \ No newline at end of file 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 828f51ac..a492f63e 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/worker/ForegroundService.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/worker/ForegroundService.kt @@ -24,6 +24,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.media.MediaScannerConnection import android.net.Uri import android.os.* import android.util.Log @@ -46,21 +47,29 @@ import com.mpatric.mp3agic.ID3v1Tag import com.mpatric.mp3agic.ID3v24Tag import com.mpatric.mp3agic.Mp3File import com.shabinder.spotiflyer.R -import com.shabinder.spotiflyer.models.DownloadObject +import com.shabinder.spotiflyer.downloadHelper.getYTTracks +import com.shabinder.spotiflyer.downloadHelper.sortByBestMatch import com.shabinder.spotiflyer.models.DownloadStatus import com.shabinder.spotiflyer.models.TrackDetails import com.shabinder.spotiflyer.models.spotify.Source -import com.shabinder.spotiflyer.utils.Provider +import com.shabinder.spotiflyer.networking.YoutubeMusicApi +import com.shabinder.spotiflyer.networking.makeJsonBody +import com.shabinder.spotiflyer.utils.* import com.shabinder.spotiflyer.utils.Provider.imageDir -import com.shabinder.spotiflyer.utils.copyTo import com.tonyodev.fetch2.* import com.tonyodev.fetch2core.DownloadBlock +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.* +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response import java.io.File import java.io.FileInputStream import java.io.IOException import java.util.* - +import javax.inject.Inject +@AndroidEntryPoint +@RequiresApi(Build.VERSION_CODES.O) class ForegroundService : Service(){ private val tag = "Foreground Service" private val channelId = "ForegroundDownloaderService" @@ -68,9 +77,6 @@ class ForegroundService : Service(){ private var total = 0 //Total Downloads Requested private var converted = 0//Total Files Converted private var downloaded = 0//Total Files downloaded - private lateinit var fetch:Fetch - private lateinit var ytDownloader: YoutubeDownloader - private lateinit var downloadManager : DownloadManager private var serviceJob = Job() private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob) private val requestMap = mutableMapOf() @@ -80,12 +86,17 @@ class ForegroundService : Service(){ private var isServiceStarted = false private var notificationLine = 0 private var messageList = mutableListOf("", "", "", "") - private var cancelIntent:PendingIntent? = null + private lateinit var cancelIntent:PendingIntent + private lateinit var fetch:Fetch + private lateinit var ytDownloader: YoutubeDownloader + private lateinit var downloadManager : DownloadManager + @Inject lateinit var youtubeMusicApi: YoutubeMusicApi override fun onBind(intent: Intent): IBinder? = null override fun onCreate() { super.onCreate() + createNotificationChannel(channelId,"Downloader Service") val intent = Intent( this, ForegroundService::class.java @@ -94,26 +105,26 @@ class ForegroundService : Service(){ downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager ytDownloader = YoutubeDownloader() initialiseFetch() - startForeground() } @SuppressLint("WakelockTimeout") override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { // Send a notification that service is started Log.i(tag, "Service Started.") - startForeground() + startForeground(notificationId, getNotification()) - if(intent.action == "kill") killService() - - if(intent.action == "query"){ - val response = Intent().apply { - action = "query_result" - putParcelableArrayListExtra("tracks", allTracksDetails as ArrayList ) + when (intent.action) { + "kill" -> killService() + "query" -> { + val response = Intent().apply { + action = "query_result" + putParcelableArrayListExtra("tracks", allTracksDetails as ArrayList ) + } + sendBroadcast(response) } - sendBroadcast(response) } - val downloadObjects: ArrayList? = (intent.getParcelableArrayListExtra("object") ?: intent.extras?.getParcelableArrayList( + val downloadObjects: ArrayList? = (intent.getParcelableArrayListExtra("object") ?: intent.extras?.getParcelableArrayList( "object" )) val imagesList: ArrayList? = (intent.getStringArrayListExtra("imagesList") ?: intent.extras?.getStringArrayList( @@ -130,9 +141,9 @@ class ForegroundService : Service(){ total += downloadObjects.size updateNotification() it.forEach { it1 -> - allTracksDetails.add(it1.trackDetails.apply { downloaded = DownloadStatus.Queued }) + allTracksDetails.add(it1.apply { downloaded = DownloadStatus.Queued }) } - downloadAllTracks(downloadObjects) + downloadAllTracks(it) } //Wake locks and misc tasks from here : @@ -152,66 +163,96 @@ class ForegroundService : Service(){ } } - private fun downloadAllTracks(downloadObjects: List){ - serviceScope.launch(Dispatchers.IO) { - for(downloadObj in downloadObjects){ - try { - val video = ytDownloader.getVideo(downloadObj.ytVideoId) - val format: Format? = try { - video?.findAudioWithQuality(AudioQuality.medium)?.get(0) as Format - } catch (e: java.lang.IndexOutOfBoundsException) { - try { - video?.findAudioWithQuality(AudioQuality.high)?.get(0) as Format - } catch (e: java.lang.IndexOutOfBoundsException) { - try { - video?.findAudioWithQuality(AudioQuality.low)?.get(0) as Format - } catch (e: java.lang.IndexOutOfBoundsException) { - Log.i("YTDownloader", e.toString()) - null - } - } - } - format?.let { - val url: String = format.url() - Log.i("DHelper Link Found", url) - val request= Request(url, downloadObj.outputFile).apply{ - priority = Priority.NORMAL - networkType = NetworkType.ALL - } - fetch.enqueue(request, - { - requestMap[it] = downloadObj.trackDetails - Log.i(tag, "Enqueuing Download") - }, - { - Log.i(tag, "Enqueuing Error:${it.throwable.toString()}") + /** + * Function To Download All Tracks Available in a List + **/ + private fun downloadAllTracks(trackList: List) { + serviceScope.launch(Dispatchers.Default){ + trackList.forEach { + if(it.downloaded == DownloadStatus.Downloaded){//Download Already Present!! + }else { + if (!it.videoID.isNullOrBlank()) {//Video ID already known! + downloadTrack(it.videoID!!, it) + } 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 + ) { + val videoId = sortByBestMatch( + getYTTracks(response.body().toString()), + trackName = it.title, + trackArtists = it.artists, + trackDurationSec = it.durationSec + ).keys.firstOrNull() + Log.i("Service VideoID", videoId ?: "Not Found") + if (videoId.isNullOrBlank()) sendTrackBroadcast( + Status.FAILED.name, + it + ) + else {//Found Youtube Video ID + downloadTrack(videoId, it) + } + } + + 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()) + } } ) } - }catch (e: com.github.kiulian.downloader.YoutubeException){ - Log.i("Service YT Error", e.message.toString()) } } } } - override fun onDestroy() { - super.onDestroy() - if(converted == total){ - Handler(Looper.myLooper()!!).postDelayed({ - killService() - }, 5000) + fun downloadTrack(videoID:String,track: TrackDetails){ + serviceScope.launch(Dispatchers.IO) { + try { + val video = ytDownloader.getVideo(videoID) + val format: Format? = try { + video?.findAudioWithQuality(AudioQuality.medium)?.get(0) as Format + } catch (e: java.lang.IndexOutOfBoundsException) { + try { + video?.findAudioWithQuality(AudioQuality.high)?.get(0) as Format + } catch (e: java.lang.IndexOutOfBoundsException) { + try { + video?.findAudioWithQuality(AudioQuality.low)?.get(0) as Format + } catch (e: java.lang.IndexOutOfBoundsException) { + Log.i("YTDownloader", e.toString()) + null + } + } + } + format?.let { + val url: String = format.url() + Log.i("DHelper Link Found", url) + val request= Request(url, track.outputFile).apply{ + priority = Priority.NORMAL + networkType = NetworkType.ALL + } + fetch.enqueue(request, + { + requestMap[it] = track + Log.i(tag, "Enqueuing Download") + }, + { + Log.i(tag, "Enqueuing Error:${it.throwable.toString()}") + } + ) + } + }catch (e: com.github.kiulian.downloader.YoutubeException){ + Log.i("Service YT Error", e.message.toString()) + } } } - override fun onTaskRemoved(rootIntent: Intent?) { - super.onTaskRemoved(rootIntent) - if(converted == total ){ - killService() - } - } - - /** * Fetch Listener/ Responsible for Fetch Behaviour **/ @@ -220,10 +261,7 @@ class ForegroundService : Service(){ download: Download, waitingOnNetwork: Boolean ) { - val intent = Intent() - .setAction(Status.QUEUED.name) - .putExtra("track", requestMap[download.request]) - this@ForegroundService.sendBroadcast(intent) + requestMap[download.request]?.let { sendTrackBroadcast(Status.QUEUED.name, it) } } override fun onRemoved(download: Download) { @@ -262,12 +300,9 @@ class ForegroundService : Service(){ track?.let{ allTracksDetails[allTracksDetails.map{ trackDetails -> trackDetails.title}.indexOf(it.title)] = it.apply { downloaded = DownloadStatus.Downloading } + updateNotification() + sendTrackBroadcast(Status.DOWNLOADING.name,track) } - updateNotification() - val intent = Intent() - .setAction(Status.DOWNLOADING.name) - .putExtra("track", track) - this@ForegroundService.sendBroadcast(intent) } override fun onWaitingNetwork(download: Download) { @@ -346,12 +381,12 @@ class ForegroundService : Service(){ ) { val track = requestMap[download.request] Log.i(tag, "${track?.title} ETA: ${etaInMilliSeconds / 1000} sec") - val intent = Intent() - .setAction("Progress") - .putExtra("progress", download.progress) - .putExtra("track", requestMap[download.request]) - this@ForegroundService.sendBroadcast(intent) -// updateNotification() + val intent = Intent().apply { + action = "Progress" + putExtra("progress", download.progress) + putExtra("track", requestMap[download.request]) + } + sendBroadcast(intent) } } @@ -360,16 +395,18 @@ class ForegroundService : Service(){ **/ fun downloadUsingDM(url: String, outputDir: String, track: TrackDetails){ val uri = Uri.parse(url) - val request = DownloadManager.Request(uri) - .setAllowedNetworkTypes( + val request = DownloadManager.Request(uri).apply { + setAllowedNetworkTypes( DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE ) - .setAllowedOverRoaming(false) - .setTitle(track.title) - .setDescription("Spotify Downloader Working Up here...") - .setDestinationUri(File(outputDir).toUri()) - .setNotificationVisibility(VISIBILITY_VISIBLE_NOTIFY_COMPLETED) + setAllowedOverRoaming(false) + setTitle(track.title) + setDescription("Spotify Downloader Working Up here...") + setDestinationUri(File(outputDir).toUri()) + setNotificationVisibility(VISIBILITY_VISIBLE_NOTIFY_COMPLETED) + } + //Start Download val downloadID = downloadManager.enqueue(request) Log.i("DownloadManager", "Download Request Sent") @@ -396,11 +433,7 @@ class ForegroundService : Service(){ *Converting Downloaded Audio (m4a) to Mp3.( Also Applying Metadata) **/ fun convertToMp3(filePath: String, track: TrackDetails){ - val intent = Intent() - .setAction("Converting") - .putExtra("track", track) - this@ForegroundService.sendBroadcast(intent) - + sendTrackBroadcast("Converting",track) val m4aFile = File(filePath) FFmpeg.executeAsync( @@ -441,13 +474,10 @@ class ForegroundService : Service(){ newFile.renameTo(file) converted++ updateNotification() + addToLibrary(file.absolutePath) allTracksDetails.removeAt(allTracksDetails.map{ trackDetails -> trackDetails.title}.indexOf(track.title)) //Notify Download Completed - val intent = Intent() - .setAction("track_download_completed") - .putExtra("track", track) - this@ForegroundService.sendBroadcast(intent) - + sendTrackBroadcast("track_download_completed",track) //All tasks completed (REST IN PEACE) if(converted == total){ onDestroy() @@ -460,21 +490,7 @@ class ForegroundService : Service(){ private fun updateNotification() { val mNotificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - val notification = NotificationCompat.Builder(this, channelId) - .setSmallIcon(R.drawable.down_arrowbw) - .setSubText("Total: $total Completed:$converted") - .setNotificationSilent() - .setStyle( - NotificationCompat.InboxStyle() -// .setBigContentTitle("Speed: $speed KB/s") - .addLine(messageList[0]) - .addLine(messageList[1]) - .addLine(messageList[2]) - .addLine(messageList[3]) - ) - .addAction(R.drawable.ic_baseline_cancel_24,"Exit",cancelIntent) - .build() - mNotificationManager.notify(notificationId, notification) + mNotificationManager.notify(notificationId, getNotification()) } /** @@ -541,47 +557,16 @@ class ForegroundService : Service(){ isServiceStarted = false } - /** - *Starting Service with Notification as Foreground! - **/ - private fun startForeground() { - val channelId = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - createNotificationChannel(channelId, "Downloader Service") - } else { - // If earlier version channel ID is not used - // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context) - "" - } - - val notification = NotificationCompat.Builder(this, channelId) - .setSmallIcon(R.drawable.down_arrowbw) - .setNotificationSilent() - .setSubText("Total: $total Completed:$converted") - .setStyle( - NotificationCompat.InboxStyle() -// .setBigContentTitle("Speed: $speed KB/s") - .addLine(messageList[0]) - .addLine(messageList[1]) - .addLine(messageList[2]) - .addLine(messageList[3]) - ) - .addAction(R.drawable.ic_baseline_cancel_24,"Exit",cancelIntent) - .build() - startForeground(notificationId, notification) - } - @Suppress("SameParameterValue") @RequiresApi(Build.VERSION_CODES.O) - private fun createNotificationChannel(channelId: String, channelName: String): String{ - val chan = NotificationChannel( + private fun createNotificationChannel(channelId: String, channelName: String){ + val channel = NotificationChannel( channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT ) - chan.lockscreenVisibility = Notification.VISIBILITY_PUBLIC + channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - service.createNotificationChannel(chan) - return channelId + service.createNotificationChannel(channel) } /** @@ -604,6 +589,15 @@ class ForegroundService : Service(){ } } + /* + * Add File to Android's Media Library. + * */ + private fun addToLibrary(path:String) { + Log.i(tag,"Scanning File") + MediaScannerConnection.scanFile(this, + listOf(path).toTypedArray(), null,null) + } + /** * Function to fetch all Images for use in mp3 tags. **/ @@ -681,6 +675,7 @@ class ForegroundService : Service(){ private fun killService() { serviceScope.launch{ + Log.i(tag,"Killing Self") messageList = mutableListOf("Cleaning And Exiting","","","") fetch.cancelAll() fetch.removeAll() @@ -696,6 +691,22 @@ class ForegroundService : Service(){ } } + override fun onDestroy() { + super.onDestroy() + if(converted == total){ + Handler(Looper.myLooper()!!).postDelayed({ + killService() + }, 5000) + } + } + + override fun onTaskRemoved(rootIntent: Intent?) { + super.onTaskRemoved(rootIntent) + if(converted == total ){ + killService() + } + } + private fun initialiseFetch() { val fetchConfiguration = FetchConfiguration.Builder(this) @@ -711,4 +722,28 @@ class ForegroundService : Service(){ //Starting fresh fetch.removeAll() } -} + + private fun getNotification():Notification = NotificationCompat.Builder(this, channelId).run { + setSmallIcon(R.drawable.down_arrowbw) + setSubText("Total: $total Completed:$converted") + setNotificationSilent() + setStyle( + NotificationCompat.InboxStyle().run { + addLine(messageList[0]) + addLine(messageList[1]) + addLine(messageList[2]) + addLine(messageList[3]) + } + ) + addAction(R.drawable.ic_baseline_cancel_24,"Exit",cancelIntent) + build() + } + + fun sendTrackBroadcast(action:String,track:TrackDetails){ + val intent = Intent().apply{ + setAction(action) + putExtra("track", track) + } + this@ForegroundService.sendBroadcast(intent) + } +} \ No newline at end of file diff --git a/app/src/main/res/xml/app_update.xml b/app/src/main/res/xml/app_update.xml index 4417913b..354335fa 100755 --- a/app/src/main/res/xml/app_update.xml +++ b/app/src/main/res/xml/app_update.xml @@ -18,8 +18,8 @@ - 1.6 - 8 + 1.7 + 9 https://github.com/Shabinder/SpotiFlyer/releases/ \ No newline at end of file diff --git a/build.gradle b/build.gradle index f8a61458..226d3cf2 100755 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext{ - kotlin_version = "1.4.10" + kotlin_version = "1.4.20" navigationVersion = '2.3.0' ext.hilt_version = '2.29.1-alpha' } @@ -28,7 +28,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.2.0-alpha16' + classpath 'com.android.tools.build:gradle:4.1.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" //Safe-Args classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion" diff --git a/gradle.properties b/gradle.properties index bc97821e..fc5b2999 100755 --- a/gradle.properties +++ b/gradle.properties @@ -15,24 +15,20 @@ # along with this program. If not, see . # -# Project-wide Gradle settings. -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. -# For more details on how to configure your build environment visit +## For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html +# # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2048m +# Default value: -Xmx1024m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +# # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true -# AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app"s APK -# https://developer.android.com/topic/libraries/support-library/androidx-rn +#Wed Dec 02 11:11:59 IST 2020 +kotlin.code.style=official +org.gradle.jvmargs=-Xmx1536M -Dkotlin.daemon.jvm.options\="-Xmx2048M" android.useAndroidX=true -# Automatically convert third-party libraries to use AndroidX android.enableJetifier=true -# Kotlin code style for this project: "official" or "obsolete": -kotlin.code.style=official \ No newline at end of file