From 180a284a547e9ba26330dc7b63071a926b10e3de Mon Sep 17 00:00:00 2001 From: Shabinder Date: Mon, 9 Nov 2020 14:11:22 +0530 Subject: [PATCH] Unifying All Things For Better Maintenance and Less Trouble. --- .../shabinder/spotiflyer/SharedViewModel.kt | 2 +- .../downloadHelper/DownloadHelper.kt | 10 +- .../downloadHelper/YTDownloadHelper.kt | 6 +- .../spotiflyer/models/DownloadObject.kt | 1 + .../spotiflyer/models/gaana/GaanaAlbum.kt | 2 +- .../models/gaana/GaanaArtistTracks.kt | 2 +- .../spotiflyer/models/gaana/GaanaPlaylist.kt | 14 +- .../spotiflyer/models/gaana/GaanaSong.kt | 2 +- .../models/gaana/{Tracks.kt => GaanaTrack.kt} | 4 +- .../spotiflyer/models/spotify/Track.kt | 2 +- .../recyclerView/SpotifyTrackListAdapter.kt | 125 ------------------ ...rackListAdapter.kt => TrackListAdapter.kt} | 62 +++++---- .../spotiflyer/ui/gaana/GaanaFragment.kt | 65 +++++++++ .../spotiflyer/ui/gaana/GaanaViewModel.kt | 15 ++- .../ui/mainfragment/MainFragment.kt | 91 +++++++------ .../spotiflyer/ui/spotify/SpotifyFragment.kt | 115 +++++++--------- .../spotiflyer/ui/spotify/SpotifyViewModel.kt | 58 ++++---- .../spotiflyer/ui/youtube/YoutubeFragment.kt | 24 ++-- .../spotiflyer/ui/youtube/YoutubeViewModel.kt | 54 ++++---- .../spotiflyer/utils/BaseViewModel.kt | 43 ++++++ .../shabinder/spotiflyer/utils/Provider.kt | 4 +- .../com/shabinder/spotiflyer/utils/Utils.kt | 28 ++-- app/src/main/res/navigation/navigation.xml | 52 +++++--- 23 files changed, 403 insertions(+), 378 deletions(-) rename app/src/main/java/com/shabinder/spotiflyer/models/gaana/{Tracks.kt => GaanaTrack.kt} (89%) delete mode 100755 app/src/main/java/com/shabinder/spotiflyer/recyclerView/SpotifyTrackListAdapter.kt rename app/src/main/java/com/shabinder/spotiflyer/recyclerView/{YoutubeTrackListAdapter.kt => TrackListAdapter.kt} (65%) create mode 100644 app/src/main/java/com/shabinder/spotiflyer/utils/BaseViewModel.kt diff --git a/app/src/main/java/com/shabinder/spotiflyer/SharedViewModel.kt b/app/src/main/java/com/shabinder/spotiflyer/SharedViewModel.kt index 1f9e0a45..7d8eb67f 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/SharedViewModel.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/SharedViewModel.kt @@ -25,7 +25,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job class SharedViewModel : ViewModel() { - var intentString = MutableLiveData().apply { value = "" } + var intentString = MutableLiveData() var spotifyService = MutableLiveData() private var viewModelJob = Job() diff --git a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/DownloadHelper.kt b/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/DownloadHelper.kt index 106750de..fedb4d13 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/DownloadHelper.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/DownloadHelper.kt @@ -33,8 +33,8 @@ import com.shabinder.spotiflyer.models.TrackDetails import com.shabinder.spotiflyer.networking.YoutubeMusicApi import com.shabinder.spotiflyer.networking.makeJsonBody import com.shabinder.spotiflyer.utils.* -import com.shabinder.spotiflyer.utils.Provider.activity import com.shabinder.spotiflyer.utils.Provider.defaultDir +import com.shabinder.spotiflyer.utils.Provider.mainActivity import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -76,9 +76,9 @@ object DownloadHelper { //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(activity,"Download Started, Now You can leave the App!", Toast.LENGTH_SHORT).show() + Toast.makeText(mainActivity,"Download Started, Now You can leave the App!", Toast.LENGTH_SHORT).show() } - startService(activity,downloadList) + startService(mainActivity,downloadList) },5000) } }else{ @@ -121,9 +121,9 @@ object DownloadHelper { //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(activity,"Download Started, Now You can leave the App!", Toast.LENGTH_SHORT).show() + Toast.makeText(mainActivity,"Download Started, Now You can leave the App!", Toast.LENGTH_SHORT).show() } - startService(activity,downloadList) + startService(mainActivity,downloadList) },5000) } } diff --git a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YTDownloadHelper.kt b/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YTDownloadHelper.kt index 453903ba..2e6637fa 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YTDownloadHelper.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/downloadHelper/YTDownloadHelper.kt @@ -22,8 +22,8 @@ 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.activity 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 @@ -63,8 +63,8 @@ object YTDownloadHelper { } Log.i("YT Downloader Helper","Download Request Sent") withContext(Dispatchers.Main){ - Toast.makeText(activity,"Download Started, Now You can leave the App!", Toast.LENGTH_SHORT).show() - startService(activity,downloadList) + 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 4fc8016f..f2448cbf 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/models/DownloadObject.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/DownloadObject.kt @@ -40,6 +40,7 @@ data class TrackDetails( var lyrics:String?=null, var trackUrl:String?=null, var albumArt: File, + var albumArtURL: String, var source: Source, var downloaded: DownloadStatus = DownloadStatus.NotDownloaded ):Parcelable diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaAlbum.kt b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaAlbum.kt index 06cecd0f..354e9e4d 100644 --- a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaAlbum.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaAlbum.kt @@ -18,7 +18,7 @@ package com.shabinder.spotiflyer.models.gaana data class GaanaAlbum ( - val tracks : List, + val tracks : List, val count : Int, val custom_artworks : CustomArtworks, val release_year : Int, diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaArtistTracks.kt b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaArtistTracks.kt index c9fa3050..2f31fb01 100644 --- a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaArtistTracks.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaArtistTracks.kt @@ -19,5 +19,5 @@ package com.shabinder.spotiflyer.models.gaana data class GaanaArtistTracks( val count : Int, - val tracks : List + val tracks : List ) \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaPlaylist.kt b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaPlaylist.kt index 268121c5..0e5454b2 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,11 +18,11 @@ package com.shabinder.spotiflyer.models.gaana data class GaanaPlaylist ( - val tags : String, - val fromCache : Int, - val modified_on : String, - val count : Int, - val created_on : String, - val favorite_count : Int, - val tracks : List, + val tags : String, + val fromCache : Int, + val modified_on : String, + val count : Int, + val created_on : String, + val favorite_count : Int, + val tracks : List, ) \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaSong.kt b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaSong.kt index 0d7a65df..8acbff96 100644 --- a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaSong.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaSong.kt @@ -18,5 +18,5 @@ package com.shabinder.spotiflyer.models.gaana data class GaanaSong( - val tracks : List + val tracks : List ) \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/Tracks.kt b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaTrack.kt similarity index 89% rename from app/src/main/java/com/shabinder/spotiflyer/models/gaana/Tracks.kt rename to app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaTrack.kt index 031e6687..00feb613 100644 --- a/app/src/main/java/com/shabinder/spotiflyer/models/gaana/Tracks.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/models/gaana/GaanaTrack.kt @@ -17,9 +17,10 @@ package com.shabinder.spotiflyer.models.gaana +import com.shabinder.spotiflyer.models.DownloadStatus import com.squareup.moshi.Json -data class Tracks ( +data class GaanaTrack ( val tags : List, val seokey : String, val albumseokey : String, @@ -35,4 +36,5 @@ data class Tracks ( val release_date : String, val play_ct : String, val secondary_language : String, + var downloaded: DownloadStatus? = DownloadStatus.NotDownloaded ) \ No newline at end of file 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 0be692b4..ecc0809e 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 @@ -40,6 +40,6 @@ data class Track( var album: Album? = null, var external_ids: Map? = null, var popularity: Int? = null, - var downloaded: DownloadStatus? = DownloadStatus.NotDownloaded + var downloaded: DownloadStatus = DownloadStatus.NotDownloaded ):Parcelable diff --git a/app/src/main/java/com/shabinder/spotiflyer/recyclerView/SpotifyTrackListAdapter.kt b/app/src/main/java/com/shabinder/spotiflyer/recyclerView/SpotifyTrackListAdapter.kt deleted file mode 100755 index c469c589..00000000 --- a/app/src/main/java/com/shabinder/spotiflyer/recyclerView/SpotifyTrackListAdapter.kt +++ /dev/null @@ -1,125 +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.recyclerView - -import android.annotation.SuppressLint -import android.os.Environment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import com.shabinder.spotiflyer.R -import com.shabinder.spotiflyer.databinding.TrackListItemBinding -import com.shabinder.spotiflyer.downloadHelper.DownloadHelper.downloadAllTracks -import com.shabinder.spotiflyer.models.DownloadStatus -import com.shabinder.spotiflyer.models.TrackDetails -import com.shabinder.spotiflyer.models.spotify.Source -import com.shabinder.spotiflyer.models.spotify.Track -import com.shabinder.spotiflyer.ui.spotify.SpotifyViewModel -import com.shabinder.spotiflyer.utils.* -import com.shabinder.spotiflyer.utils.Provider.activity -import kotlinx.coroutines.launch -import java.io.File - -class SpotifyTrackListAdapter(private val spotifyViewModel : SpotifyViewModel): ListAdapter(SpotifyTrackDiffCallback()) { - - var isAlbum:Boolean = false - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val layoutInflater = LayoutInflater.from(parent.context) - val binding = TrackListItemBinding.inflate(layoutInflater,parent,false) - return ViewHolder(binding) - } - - @SuppressLint("SetTextI18n") - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val item = getItem(position) - if(itemCount ==1 || isAlbum){ - holder.binding.imageUrl.visibility = View.GONE}else{ - spotifyViewModel.uiScope.launch { - //Placeholder Set - bindImage(holder.binding.imageUrl, item.album?.images?.get(0)?.url, Source.Spotify) - } - } - - holder.binding.trackName.text = "${if(item.name!!.length > 17){"${item.name!!.subSequence(0,16)}..."}else{item.name}}" - holder.binding.artist.text = "${item.artists?.get(0)?.name?:""}..." - holder.binding.duration.text = "${item.duration_ms/1000/60} minutes, ${(item.duration_ms/1000)%60} sec" - when (item.downloaded) { - DownloadStatus.Downloaded -> { - holder.binding.btnDownload.setImageResource(R.drawable.ic_tick) - holder.binding.btnDownload.clearAnimation() - } - DownloadStatus.Downloading -> { - holder.binding.btnDownload.setImageResource(R.drawable.ic_refresh) - rotateAnim(holder.binding.btnDownload) - } - DownloadStatus.NotDownloaded -> { - holder.binding.btnDownload.setImageResource(R.drawable.ic_arrow) - holder.binding.btnDownload.clearAnimation() - holder.binding.btnDownload.setOnClickListener{ - if(!isOnline()){ - showNoConnectionAlert() - return@setOnClickListener - } - Toast.makeText(activity,"Processing!",Toast.LENGTH_SHORT).show() - holder.binding.btnDownload.setImageResource(R.drawable.ic_refresh) - rotateAnim(it) - item.downloaded = DownloadStatus.Downloading - spotifyViewModel.uiScope.launch { - val itemList = mutableListOf() - itemList.add(item.let { track -> - val artistsList = mutableListOf() - track.artists?.forEach { artist -> artistsList.add(artist!!.name!!) } - TrackDetails( - title = track.name.toString(), - artists = artistsList, - durationSec = (track.duration_ms/1000).toInt(), - albumArt = File( - Environment.getExternalStorageDirectory(), - Provider.defaultDir +".Images/" + (track.album?.images?.get(0)?.url.toString()).substringAfterLast('/') + ".jpeg"), - albumName = track.album?.name, - year = track.album?.release_date, - comment = "Genres:${track.album?.genres?.joinToString()}", - trackUrl = track.href, - source = Source.Spotify - ) - } - ) - downloadAllTracks(spotifyViewModel.folderType,spotifyViewModel.subFolder,itemList) - } - notifyItemChanged(position)//start showing anim! - } - } - } - } - class ViewHolder(val binding: TrackListItemBinding) : RecyclerView.ViewHolder(binding.root) -} - -class SpotifyTrackDiffCallback: DiffUtil.ItemCallback(){ - override fun areItemsTheSame(oldItem: Track, newItem: Track): Boolean { - return oldItem.name == newItem.name - } - - override fun areContentsTheSame(oldItem: Track, newItem: Track): Boolean { - return oldItem == newItem //Downloaded Check - } -} \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/recyclerView/YoutubeTrackListAdapter.kt b/app/src/main/java/com/shabinder/spotiflyer/recyclerView/TrackListAdapter.kt similarity index 65% rename from app/src/main/java/com/shabinder/spotiflyer/recyclerView/YoutubeTrackListAdapter.kt rename to app/src/main/java/com/shabinder/spotiflyer/recyclerView/TrackListAdapter.kt index fc51cf8c..956bacc3 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/recyclerView/YoutubeTrackListAdapter.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/recyclerView/TrackListAdapter.kt @@ -22,39 +22,35 @@ import android.view.View import android.view.ViewGroup 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.youtube.YoutubeViewModel import com.shabinder.spotiflyer.utils.* import kotlinx.coroutines.launch -class YoutubeTrackListAdapter(private val youtubeViewModel :YoutubeViewModel): ListAdapter(YouTubeTrackDiffCallback()) { +class TrackListAdapter(private val viewModel :BaseViewModel): ListAdapter(TrackDiffCallback()) { + + var source:Source =Source.Spotify override fun onCreateViewHolder( parent: ViewGroup, viewType: Int - ): SpotifyTrackListAdapter.ViewHolder { + ): ViewHolder { val layoutInflater = LayoutInflater.from(parent.context) val binding = TrackListItemBinding.inflate(layoutInflater,parent,false) -// val view = layoutInflater.inflate(R.layout.track_list_item,parent,false) - return SpotifyTrackListAdapter.ViewHolder(binding) + return ViewHolder(binding) } - override fun onBindViewHolder(holder: SpotifyTrackListAdapter.ViewHolder, position: Int) { + override fun onBindViewHolder(holder: ViewHolder, position: Int) { val item = getItem(position) - if(itemCount == 1){ - holder.binding.imageUrl.visibility = View.GONE}else{ - youtubeViewModel.uiScope.launch { - bindImage(holder.binding.imageUrl, - "https://i.ytimg.com/vi/${item.albumArt.absolutePath.substringAfterLast("/") - .substringBeforeLast(".")}/hqdefault.jpg" - , - Source.YouTube - ) + if(itemCount == 1){ holder.binding.imageUrl.visibility = View.GONE}else{ + viewModel.uiScope.launch { + bindImage(holder.binding.imageUrl,item.albumArtURL, source) } } @@ -79,14 +75,25 @@ class YoutubeTrackListAdapter(private val youtubeViewModel :YoutubeViewModel): L holder.binding.btnDownload.setImageResource(R.drawable.ic_refresh) rotateAnim(it) item.downloaded = DownloadStatus.Downloading - youtubeViewModel.uiScope.launch { - val itemList = mutableListOf() - itemList.add(item) - YTDownloadHelper.downloadYTTracks( - youtubeViewModel.folderType, - youtubeViewModel.subFolder, - itemList - ) + when(source){ + Source.Spotify -> { + viewModel.uiScope.launch { + DownloadHelper.downloadAllTracks( + viewModel.folderType, + viewModel.subFolder, + listOf(item) + ) + } + } + Source.YouTube -> { + viewModel.uiScope.launch { + YTDownloadHelper.downloadYTTracks( + viewModel.folderType, + viewModel.subFolder, + listOf(item) + ) + } + } } notifyItemChanged(position)//start showing anim! } @@ -97,8 +104,15 @@ class YoutubeTrackListAdapter(private val youtubeViewModel :YoutubeViewModel): L holder.binding.artist.text = "${item.artists.get(0)}..." holder.binding.duration.text = "${item.durationSec/60} minutes, ${item.durationSec%60} sec" } + + class ViewHolder(val binding: TrackListItemBinding) : RecyclerView.ViewHolder(binding.root) + + fun submitList(list: MutableList?, source: Source) { + super.submitList(list) + this.source = source + } } -class YouTubeTrackDiffCallback: DiffUtil.ItemCallback(){ +class TrackDiffCallback: DiffUtil.ItemCallback(){ override fun areItemsTheSame(oldItem: TrackDetails, newItem: TrackDetails): Boolean { return oldItem.title == newItem.title } 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 7f80f9ca..8b4f3c5a 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 @@ -20,6 +20,7 @@ package com.shabinder.spotiflyer.ui.gaana import android.content.BroadcastReceiver import android.content.IntentFilter import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -29,8 +30,14 @@ import androidx.lifecycle.ViewModelProvider import com.shabinder.spotiflyer.R import com.shabinder.spotiflyer.SharedViewModel import com.shabinder.spotiflyer.databinding.TrackListFragmentBinding +import com.shabinder.spotiflyer.downloadHelper.DownloadHelper +import com.shabinder.spotiflyer.models.DownloadStatus import com.shabinder.spotiflyer.networking.GaanaInterface import com.shabinder.spotiflyer.networking.YoutubeMusicApi +import com.shabinder.spotiflyer.recyclerView.TrackListAdapter +import com.shabinder.spotiflyer.utils.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import javax.inject.Inject class GaanaFragment : Fragment() { @@ -39,6 +46,7 @@ class GaanaFragment : Fragment() { private lateinit var sharedViewModel: SharedViewModel @Inject lateinit var youtubeMusicApi: YoutubeMusicApi private lateinit var viewModel: GaanaViewModel + private lateinit var adapter: TrackListAdapter @Inject lateinit var gaanaInterface: GaanaInterface private var intentFilter: IntentFilter? = null private var updateUIReceiver: BroadcastReceiver? = null @@ -49,6 +57,63 @@ class GaanaFragment : Fragment() { ): View? { binding = DataBindingUtil.inflate(inflater,R.layout.track_list_fragment, container, false) viewModel = ViewModelProvider(this).get(GaanaViewModel::class.java) + adapter = TrackListAdapter(viewModel) + + val gaanaLink = GaanaFragmentArgs.fromBundle(requireArguments()).link.substringAfter("gaana.com/") + //Link Schema: https://gaana.com/type/link + val link = gaanaLink.substringAfterLast('/', "error") + val type = gaanaLink.substringBeforeLast('/', "error").substringAfterLast('/') + + Log.i("Gaana Fragment", "$type : $link") + + when{ + type == "Error" || link == "Error" -> { + showMessage("Please Check Your Link!") + Provider.mainActivity.onBackPressed() + } + + else -> { + viewModel.gaanaSearch(type,link) + + binding.btnDownloadAll.setOnClickListener { + if(!isOnline()){ + 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)) + } + } + showMessage("Processing!") + sharedViewModel.uiScope.launch(Dispatchers.Default){ + val urlList = arrayListOf() + viewModel.trackList.value?.forEach { urlList.add(it.albumArtURL) } + //Appending Source + urlList.add("spotify") + loadAllImages( + requireActivity(), + urlList + ) + } + 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 } } \ 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 e133edb7..3ff35af1 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 @@ -18,7 +18,18 @@ package com.shabinder.spotiflyer.ui.gaana import androidx.hilt.lifecycle.ViewModelInject -import androidx.lifecycle.ViewModel import com.shabinder.spotiflyer.database.DatabaseDAO +import com.shabinder.spotiflyer.networking.GaanaInterface +import com.shabinder.spotiflyer.utils.BaseViewModel -class GaanaViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) : ViewModel() \ No newline at end of file +class GaanaViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) : BaseViewModel(){ + + override var folderType:String = "" + override var subFolder:String = "" + var gaanaInterface : GaanaInterface? = null + + fun gaanaSearch(type:String,link:String){ + + } + +} \ 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 47e631c7..6d5af994 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 @@ -65,19 +65,60 @@ class MainFragment : Fragment() { return@setOnClickListener } val link = binding.linkSearch.text.toString() - if (link.contains("spotify",true)){ - if(sharedViewModel.spotifyService.value == null){//Authentication pending!! - (activity as MainActivity).authenticateSpotify() + when{ + //SPOTIFY + link.contains("spotify",true) -> { + if(sharedViewModel.spotifyService.value == null){//Authentication pending!! + (activity as MainActivity).authenticateSpotify() + } + findNavController().navigate(MainFragmentDirections.actionMainFragmentToSpotifyFragment(link)) } - findNavController().navigate(MainFragmentDirections.actionMainFragmentToSpotifyFragment(link)) - }else if(link.contains("youtube.com",true) || link.contains("youtu.be",true) ){ - findNavController().navigate(MainFragmentDirections.actionMainFragmentToYoutubeFragment(link)) - }else showMessage("Link is Not Valid",true) + + //YOUTUBE + link.contains("youtube.com",true) || link.contains("youtu.be",true) -> { + findNavController().navigate(MainFragmentDirections.actionMainFragmentToYoutubeFragment(link)) + } + + //GAANA + link.contains("gaana",true) -> { + findNavController().navigate(MainFragmentDirections.actionMainFragmentToGaanaFragment(link)) + } + + else -> showMessage("Link is Not Valid",true) + } } handleIntent() return binding.root } + /** + * Handle Intent If there is any! + **/ + private fun handleIntent() { + sharedViewModel.intentString.observe(viewLifecycleOwner,{ it?.let { + sharedViewModel.uiScope.launch(Dispatchers.IO) { + //Wait for any Authentication to Finish , + // this Wait prevents from multiple Authentication Requests + Thread.sleep(1000) + if(sharedViewModel.spotifyService.value == null){ + //Not Authenticated Yet + Provider.mainActivity.authenticateSpotify() + while (sharedViewModel.spotifyService.value == null) { + //Waiting for Authentication to Finish + Thread.sleep(1000) + } + } + + withContext(Dispatchers.Main){ + binding.linkSearch.setText(sharedViewModel.intentString.value) + binding.btnSearch.performClick() + //Intent Consumed + sharedViewModel.intentString.value = null + } + } + } + }) + } private fun initializeAll() { mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java) @@ -94,44 +135,14 @@ class MainFragment : Fragment() { } } + /** + * Implementing buttons + **/ private fun historyButton() { binding.btnHistory.setOnClickListener { findNavController().navigate(MainFragmentDirections.actionMainFragmentToDownloadRecord()) } } - - /** - * Handle Intent If there is any! - **/ - private fun handleIntent() { - sharedViewModel.intentString.observe(viewLifecycleOwner,{ - if(it != ""){ - sharedViewModel.uiScope.launch(Dispatchers.IO) { - //Wait for any Authentication to Finish , - // this Wait prevents from multiple Authentication Requests - Thread.sleep(1000) - if(sharedViewModel.spotifyService.value == null){ - //Not Authenticated Yet - Provider.activity.authenticateSpotify() - while (sharedViewModel.spotifyService.value == null) { - //Waiting for Authentication to Finish - Thread.sleep(1000) - } - } - - withContext(Dispatchers.Main){ - binding.linkSearch.setText(sharedViewModel.intentString.value) - binding.btnSearch.performClick() - sharedViewModel.intentString.value = "" - } - } - } - }) - } - - /** - * Implementing buttons - **/ private fun openSpotifyButton() { val manager: PackageManager = requireActivity().packageManager try { 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 97876314..e59600e4 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,7 +23,6 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.os.Bundle -import android.os.Environment import android.util.Log import android.view.LayoutInflater import android.view.View @@ -41,13 +40,12 @@ 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.recyclerView.SpotifyTrackListAdapter +import com.shabinder.spotiflyer.recyclerView.TrackListAdapter import com.shabinder.spotiflyer.utils.* -import com.shabinder.spotiflyer.utils.Provider.defaultDir +import com.shabinder.spotiflyer.utils.Provider.mainActivity import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import java.io.File import javax.inject.Inject @Suppress("DEPRECATION") @@ -58,7 +56,7 @@ class SpotifyFragment : Fragment() { private lateinit var sharedViewModel: SharedViewModel @Inject lateinit var youtubeMusicApi: YoutubeMusicApi private lateinit var viewModel: SpotifyViewModel - private lateinit var adapter:SpotifyTrackListAdapter + private lateinit var adapter:TrackListAdapter private var intentFilter:IntentFilter? = null private var updateUIReceiver: BroadcastReceiver? = null @@ -73,81 +71,70 @@ class SpotifyFragment : Fragment() { initializeLiveDataObservers() initializeBroadcast() - val args = SpotifyFragmentArgs.fromBundle(requireArguments()) - val spotifyLink = args.link + val spotifyLink = SpotifyFragmentArgs.fromBundle(requireArguments()).link.substringAfter("open.spotify.com/") val link = spotifyLink.substringAfterLast('/', "Error").substringBefore('?') val type = spotifyLink.substringBeforeLast('/', "Error").substringAfterLast('/') - Log.i("Fragment", "$type : $link") + Log.i("Spotify Fragment", "$type : $link") if(sharedViewModel.spotifyService.value == null){//Authentication pending!! (activity as MainActivity).authenticateSpotify() } - if (type == "Error" || link == "Error") {//Incorrect Link - showMessage("Please Check Your Link!") - }else if(spotifyLink.contains("open.spotify",true)){//Link Validation!! - if(type == "episode" || type == "show"){//TODO Implementation - showMessage("Implementing Soon, Stay Tuned!") + + when{ + type == "Error" || link == "Error" -> { + showMessage("Please Check Your Link!") + mainActivity.onBackPressed() } - else{ - viewModel.spotifySearch(type,link) - if(type=="album")adapter.isAlbum = true - binding.btnDownloadAll.setOnClickListener { - if(!isOnline()){ - showNoConnectionAlert() - return@setOnClickListener - } - binding.btnDownloadAll.visibility = View.GONE - binding.downloadingFab.visibility = View.VISIBLE + else -> { + if(type == "episode" || type == "show"){//TODO Implementation + showMessage("Implementing Soon, Stay Tuned!") + } + else{ + viewModel.spotifySearch(type,link) - 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.setOnClickListener { + if(!isOnline()){ + showNoConnectionAlert() + return@setOnClickListener } - } - showMessage("Processing!") - sharedViewModel.uiScope.launch(Dispatchers.Default){ - val urlList = arrayListOf() - viewModel.trackList.value?.forEach { urlList.add(it.album?.images?.get(0)?.url.toString()) } - //Appending Source - urlList.add("spotify") - loadAllImages( - requireActivity(), - urlList - ) - } - viewModel.uiScope.launch { - val finalList = viewModel.trackList.value?.map{ - val artistsList = mutableListOf() - it.artists?.forEach { artist -> artistsList.add(artist!!.name!!) } - TrackDetails( - title = it.name.toString(), - artists = artistsList, - durationSec = (it.duration_ms/1000).toInt(), - albumArt = File( - Environment.getExternalStorageDirectory(), - defaultDir +".Images/" + (it.album?.images?.get(0)?.url.toString()).substringAfterLast('/') + ".jpeg"), - albumName = it.album?.name, - year = it.album?.release_date, - comment = "Genres:${it.album?.genres?.joinToString()}", - trackUrl = it.href, - source = Source.Spotify + 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)) + } + } + showMessage("Processing!") + sharedViewModel.uiScope.launch(Dispatchers.Default){ + val urlList = arrayListOf() + viewModel.trackList.value?.forEach { urlList.add(it.albumArtURL) } + //Appending Source + urlList.add("spotify") + loadAllImages( + requireActivity(), + urlList + ) + } + viewModel.uiScope.launch { + val finalList = viewModel.trackList.value + if(finalList.isNullOrEmpty())showMessage("Not Downloading Any Song") + DownloadHelper.downloadAllTracks( + viewModel.folderType, + viewModel.subFolder, + finalList ?: listOf(), ) } - if(finalList.isNullOrEmpty())showMessage("Not Downloading Any Song") - DownloadHelper.downloadAllTracks( - viewModel.folderType, - viewModel.subFolder, - finalList ?: listOf(), - ) } } } } + return binding.root } @@ -160,7 +147,7 @@ class SpotifyFragment : Fragment() { sharedViewModel.spotifyService.observe(viewLifecycleOwner, { viewModel.spotifyService = it }) - adapter = SpotifyTrackListAdapter(viewModel) + adapter = TrackListAdapter(viewModel) DownloadHelper.youtubeMusicApi = youtubeMusicApi DownloadHelper.sharedViewModel = sharedViewModel DownloadHelper.statusBar = binding.statusBar @@ -211,7 +198,7 @@ class SpotifyFragment : Fragment() { if (intent != null){ val trackDetails = intent.getParcelableExtra("track") trackDetails?.let { - val position: Int = viewModel.trackList.value?.map { it.name }?.indexOf(trackDetails.title) ?: -1 + val position: Int = viewModel.trackList.value?.map { it.title }?.indexOf(trackDetails.title) ?: -1 Log.i("Track","Download Completed Intent :$position") if(position != -1) { val track = viewModel.trackList.value?.get(position) 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 f93ce2b9..a6ed9a03 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,34 +17,30 @@ package com.shabinder.spotiflyer.ui.spotify +import android.os.Environment import android.util.Log import androidx.hilt.lifecycle.ViewModelInject -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel import com.shabinder.spotiflyer.database.DatabaseDAO import com.shabinder.spotiflyer.database.DownloadRecord import com.shabinder.spotiflyer.models.DownloadStatus +import com.shabinder.spotiflyer.models.TrackDetails import com.shabinder.spotiflyer.models.spotify.* import com.shabinder.spotiflyer.networking.SpotifyService +import com.shabinder.spotiflyer.utils.BaseViewModel +import com.shabinder.spotiflyer.utils.Provider import com.shabinder.spotiflyer.utils.finalOutputDir -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.io.File -class SpotifyViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) : - ViewModel(){ +class SpotifyViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) : BaseViewModel(){ + + override var folderType:String = "" + override var subFolder:String = "" - var folderType:String = "" - var subFolder:String = "" - var trackList = MutableLiveData>() - private val loading = "Loading" - var title = MutableLiveData().apply { value = loading } - var coverUrl = MutableLiveData().apply { value = loading } var spotifyService : SpotifyService? = null - private var viewModelJob = Job() - val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob) - - fun spotifySearch(type:String,link: String){ when (type) { "track" -> { @@ -56,7 +52,7 @@ class SpotifyViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO trackObject.downloaded = DownloadStatus.Downloaded } tempTrackList.add(trackObject) - trackList.value = tempTrackList + trackList.value = tempTrackList.toTrackDetailsList() title.value = trackObject.name coverUrl.value = trackObject.album!!.images?.get(0)!!.url!! withContext(Dispatchers.IO){ @@ -86,7 +82,7 @@ class SpotifyViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO it.album = Album(images = listOf(Image(url = albumObject.images?.get(0)?.url))) tempTrackList.add(it) } - trackList.value = tempTrackList + trackList.value = tempTrackList.toTrackDetailsList() title.value = albumObject?.name coverUrl.value = albumObject?.images?.get(0)?.url withContext(Dispatchers.IO){ @@ -129,7 +125,7 @@ class SpotifyViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO moreTracksAvailable = !moreTracks?.next.isNullOrBlank() } Log.i("Total Tracks Fetched",tempTrackList.size.toString()) - trackList.value = tempTrackList + trackList.value = tempTrackList.toTrackDetailsList() title.value = playlistObject?.name coverUrl.value = playlistObject?.images?.get(0)?.url.toString() withContext(Dispatchers.IO){ @@ -152,6 +148,26 @@ class SpotifyViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO } } + private fun List.toTrackDetailsList() = this.map { + val artistsList = mutableListOf() + it.artists?.forEach { artist -> artistsList.add(artist!!.name!!) } + TrackDetails( + title = it.name.toString(), + artists = artistsList, + durationSec = (it.duration_ms/1000).toInt(), + albumArt = File( + Environment.getExternalStorageDirectory(), + Provider.defaultDir +".Images/" + (it.album?.images?.get(0)?.url.toString()).substringAfterLast('/') + ".jpeg"), + albumName = it.album?.name, + year = it.album?.release_date, + comment = "Genres:${it.album?.genres?.joinToString()}", + trackUrl = it.href, + downloaded = it.downloaded, + source = Source.Spotify, + albumArtURL = it.album?.images?.get(0)?.url.toString() + ) + }.toMutableList() + private suspend fun getTrackDetails(trackLink:String): Track?{ Log.i("Requesting","https://api.spotify.com/v1/tracks/$trackLink") return spotifyService?.getTrack(trackLink)?.value @@ -168,10 +184,4 @@ class SpotifyViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO Log.i("Requesting","https://api.spotify.com/v1/playlists/$link/tracks?offset=$offset&limit=$limit") return spotifyService?.getPlaylistTracks(link, offset, limit)?.value } - - override fun onCleared() { - super.onCleared() - viewModelJob.cancel() - } - } \ 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 6d2a4432..7daca1e4 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 @@ -37,7 +37,7 @@ 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.YoutubeTrackListAdapter +import com.shabinder.spotiflyer.recyclerView.TrackListAdapter import com.shabinder.spotiflyer.utils.* import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Dispatchers @@ -51,7 +51,7 @@ class YoutubeFragment : Fragment() { private lateinit var viewModel: YoutubeViewModel private lateinit var sharedViewModel: SharedViewModel @Inject lateinit var ytDownloader: YoutubeDownloader - private lateinit var adapter : YoutubeTrackListAdapter + private lateinit var adapter : TrackListAdapter private var intentFilter: IntentFilter? = null private var updateUIReceiver: BroadcastReceiver? = null private val sampleDomain2 = "youtu.be" @@ -64,7 +64,7 @@ class YoutubeFragment : Fragment() { binding = DataBindingUtil.inflate(inflater,R.layout.track_list_fragment,container,false) viewModel = ViewModelProvider(this).get(YoutubeViewModel::class.java) sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java) - adapter = YoutubeTrackListAdapter(viewModel) + adapter = TrackListAdapter(viewModel) binding.trackList.adapter = adapter initializeLiveDataObservers() @@ -108,16 +108,16 @@ class YoutubeFragment : Fragment() { rotateAnim(binding.downloadingFab) - for (track in viewModel.ytTrackList.value?: listOf()){ + for (track in viewModel.trackList.value?: listOf()){ if(track.downloaded != DownloadStatus.Downloaded){ track.downloaded = DownloadStatus.Downloading - adapter.notifyItemChanged(viewModel.ytTrackList.value!!.indexOf(track)) + adapter.notifyItemChanged(viewModel.trackList.value!!.indexOf(track)) } } showMessage("Processing!") sharedViewModel.uiScope.launch(Dispatchers.Default){ val urlList = arrayListOf() - viewModel.ytTrackList.value?.forEach { urlList.add("https://i.ytimg.com/vi/${it.albumArt.absolutePath.substringAfterLast("/") + viewModel.trackList.value?.forEach { urlList.add("https://i.ytimg.com/vi/${it.albumArt.absolutePath.substringAfterLast("/") .substringBeforeLast(".")}/hqdefault.jpg")} //Appending Source urlList.add("youtube") @@ -130,7 +130,7 @@ class YoutubeFragment : Fragment() { YTDownloadHelper.downloadYTTracks( type = viewModel.folderType, subFolder = viewModel.subFolder, - tracks = viewModel.ytTrackList.value ?: listOf() + tracks = viewModel.trackList.value ?: listOf() ) } } @@ -150,13 +150,13 @@ class YoutubeFragment : Fragment() { if (intent != null){ val trackDetails = intent.getParcelableExtra("track") trackDetails?.let { - val position: Int = viewModel.ytTrackList.value?.map { it.title }?.indexOf(trackDetails.title) ?: -1 + val position: Int = viewModel.trackList.value?.map { it.title }?.indexOf(trackDetails.title) ?: -1 Log.i("Track","Download Completed Intent :$position") if(position != -1) { - val track = viewModel.ytTrackList.value?.get(position) + val track = viewModel.trackList.value?.get(position) track?.let{ it.downloaded = DownloadStatus.Downloaded - viewModel.ytTrackList.value?.set(position, it) + viewModel.trackList.value?.set(position, it) adapter.notifyItemChanged(position) checkIfAllDownloaded() } @@ -174,7 +174,7 @@ class YoutubeFragment : Fragment() { } private fun checkIfAllDownloaded() { - if(!viewModel.ytTrackList.value!!.any { it.downloaded != DownloadStatus.Downloaded }){ + if(!viewModel.trackList.value!!.any { it.downloaded != DownloadStatus.Downloaded }){ //All Tracks Downloaded binding.btnDownloadAll.visibility = View.GONE binding.downloadingFab.apply{ @@ -195,7 +195,7 @@ class YoutubeFragment : Fragment() { /** * TrackList Binding Observer! **/ - viewModel.ytTrackList.observe(viewLifecycleOwner, { + viewModel.trackList.observe(viewLifecycleOwner, { adapter.submitList(it) }) 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 35f6b11d..467442bf 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 @@ -21,38 +21,31 @@ import android.annotation.SuppressLint import android.os.Environment import android.util.Log import androidx.hilt.lifecycle.ViewModelInject -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel import com.github.kiulian.downloader.YoutubeDownloader 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.Source +import com.shabinder.spotiflyer.utils.BaseViewModel import com.shabinder.spotiflyer.utils.Provider.defaultDir import com.shabinder.spotiflyer.utils.finalOutputDir import com.shabinder.spotiflyer.utils.removeIllegalChars import com.shabinder.spotiflyer.utils.showMessage -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.io.File -class YoutubeViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) : ViewModel(){ - +class YoutubeViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) : BaseViewModel(){ /* * YT Album Art Schema * HI-RES Url: https://i.ytimg.com/vi/$searchId/maxresdefault.jpg" * Normal Url: https://i.ytimg.com/vi/$searchId/hqdefault.jpg" * */ - val ytTrackList = MutableLiveData>() - private val loading = "Loading" - var title = MutableLiveData().apply { value = "\"Loading!\"" } - var coverUrl = MutableLiveData().apply { value = loading } - val folderType = "YT_Downloads" - var subFolder = "" - private var viewModelJob = Job() - val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob) - + override var folderType = "YT_Downloads" + override var subFolder = "" fun getYTPlaylist(searchId:String, ytDownloader:YoutubeDownloader){ try{ @@ -67,7 +60,7 @@ class YoutubeViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO title.postValue( if(name.length > 17){"${name.subSequence(0,16)}..."}else{name} ) - ytTrackList.postValue(videos.map { + this@YoutubeViewModel.trackList.postValue(videos.map { TrackDetails( title = it.title(), artists = listOf(it.author().toString()), @@ -77,13 +70,13 @@ class YoutubeViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO defaultDir + ".Images/" + it.videoId() + ".jpeg" ), source = Source.YouTube, + albumArtURL = "https://i.ytimg.com/vi/${it.videoId()}/hqdefault.jpg", downloaded = if (File( - finalOutputDir( - itemName = it.title(), - type = folderType, - subFolder = subFolder - ) - ).exists() + finalOutputDir( + itemName = it.title(), + type = folderType, + subFolder = subFolder + )).exists() ) DownloadStatus.Downloaded else { @@ -120,17 +113,18 @@ class YoutubeViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO val detail = video?.details() val name = detail?.title()?.replace(detail.author()!!.toUpperCase(),"",true) ?: detail?.title() ?: "" Log.i("YT View Model",detail.toString()) - ytTrackList.postValue( + this@YoutubeViewModel.trackList.postValue( listOf( TrackDetails( - title = name, - artists = listOf(detail?.author().toString()), - durationSec = detail?.lengthSeconds()?:0, - albumArt = File( - Environment.getExternalStorageDirectory(), - defaultDir +".Images/" + searchId + ".jpeg" - ), - source = Source.YouTube + title = name, + artists = listOf(detail?.author().toString()), + durationSec = detail?.lengthSeconds()?:0, + albumArt = File( + Environment.getExternalStorageDirectory(), + "$defaultDir.Images/$searchId.jpeg" + ), + source = Source.YouTube, + albumArtURL = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg" ) ).toMutableList() ) diff --git a/app/src/main/java/com/shabinder/spotiflyer/utils/BaseViewModel.kt b/app/src/main/java/com/shabinder/spotiflyer/utils/BaseViewModel.kt new file mode 100644 index 00000000..d652b171 --- /dev/null +++ b/app/src/main/java/com/shabinder/spotiflyer/utils/BaseViewModel.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2020 Shabinder Singh + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.shabinder.spotiflyer.utils + +import 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 BaseViewModel:ViewModel() { + abstract var folderType:String + abstract var subFolder:String + open val trackList = MutableLiveData>() + private val viewModelJob:CompletableJob = Job() + open val uiScope = CoroutineScope(Dispatchers.Default + viewModelJob) + + private val loading = "Loading!" + open var title = MutableLiveData().apply { value = loading } + open var coverUrl = MutableLiveData().apply { value = loading } + + override fun onCleared() { + super.onCleared() + viewModelJob.cancel() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/spotiflyer/utils/Provider.kt b/app/src/main/java/com/shabinder/spotiflyer/utils/Provider.kt index 15323e62..3fffe3f8 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/utils/Provider.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/utils/Provider.kt @@ -49,7 +49,7 @@ import javax.inject.Singleton @Module object Provider { - val activity: MainActivity = MainActivity.getInstance() + val mainActivity: MainActivity = MainActivity.getInstance() val defaultDir = Environment.DIRECTORY_MUSIC + File.separator + "SpotiFlyer" + File.separator @@ -68,7 +68,7 @@ object Provider { @Provides @Singleton fun provideUpi():EasyUpiPayment { - return EasyUpiPayment.Builder(activity) + return EasyUpiPayment.Builder(mainActivity) .setPayeeVpa("technoshab@paytm") .setPayeeName("Shabinder Singh") .setTransactionId("UNIQUE_TRANSACTION_ID") 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 e4e62be6..b1355efe 100755 --- a/app/src/main/java/com/shabinder/spotiflyer/utils/Utils.kt +++ b/app/src/main/java/com/shabinder/spotiflyer/utils/Utils.kt @@ -41,13 +41,12 @@ import com.google.android.material.snackbar.Snackbar import com.shabinder.spotiflyer.R import com.shabinder.spotiflyer.models.DownloadObject import com.shabinder.spotiflyer.models.spotify.Source -import com.shabinder.spotiflyer.utils.Provider.activity import com.shabinder.spotiflyer.utils.Provider.defaultDir +import com.shabinder.spotiflyer.utils.Provider.mainActivity import com.shabinder.spotiflyer.worker.ForegroundService import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import java.io.File import java.io.IOException @@ -77,7 +76,7 @@ fun finalOutputDir(itemName:String? = null,type:String, subFolder:String?=null,e fun isOnline(): Boolean { var result = false val connectivityManager = - activity.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager? + mainActivity.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager? connectivityManager?.let { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { it.getNetworkCapabilities(connectivityManager.activeNetwork)?.apply { @@ -90,7 +89,7 @@ fun isOnline(): Boolean { } } else { val netInfo = - (activity.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).activeNetworkInfo + (mainActivity.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).activeNetworkInfo result = netInfo != null && netInfo.isConnected } } @@ -100,12 +99,12 @@ fun isOnline(): Boolean { fun showMessage(message: String, long: Boolean = false){ CoroutineScope(Dispatchers.Main).launch{ Snackbar.make( - activity.snackBarAnchor, + mainActivity.snackBarAnchor, message, if (long) Snackbar.LENGTH_LONG else Snackbar.LENGTH_SHORT - ).also { snackbar -> - snackbar.setAction("Ok") { - snackbar.dismiss() + ).apply { + setAction("Ok") { + dismiss() } }.show() } @@ -126,7 +125,7 @@ fun rotateAnim(view: View){ fun showNoConnectionAlert(){ CoroutineScope(Dispatchers.Main).launch { - activity.apply { + mainActivity.apply { MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme) .setTitle(resources.getString(R.string.title)) .setMessage(resources.getString(R.string.supporting_text)) @@ -187,13 +186,10 @@ fun bindImage(imgView: ImageView, imgUrl: String?,source: Source?) { } // the File to save , append increasing numeric counter to prevent files from getting overwritten. resource?.copyTo(file) - withContext(Dispatchers.Main){ - Glide.with(imgView) - .load(file) - .placeholder(R.drawable.ic_song_placeholder) - .into(imgView) -// Log.i("Glide","imageSaved") - } + Glide.with(imgView) + .load(file) + .placeholder(R.drawable.ic_song_placeholder) + .into(imgView) } catch (e: IOException) { e.printStackTrace() } diff --git a/app/src/main/res/navigation/navigation.xml b/app/src/main/res/navigation/navigation.xml index 6c8b8bb3..c2c0d882 100755 --- a/app/src/main/res/navigation/navigation.xml +++ b/app/src/main/res/navigation/navigation.xml @@ -22,15 +22,6 @@ android:id="@+id/navigation" app:startDestination="@id/mainFragment"> - - - + - - - + + + + + + + + + + + \ No newline at end of file