Set Up base TrackListFragmentBaseClass and TrackListViewModelBase

This commit is contained in:
Shabinder 2020-11-10 14:13:41 +05:30
parent f869725953
commit b3b2e6ed51
25 changed files with 365 additions and 386 deletions

View File

@ -16,6 +16,7 @@
<w>instagram</w> <w>instagram</w>
<w>jetbrains</w> <w>jetbrains</w>
<w>kotlinx</w> <w>kotlinx</w>
<w>linkedin</w>
<w>mainfragment</w> <w>mainfragment</w>
<w>maxresdefault</w> <w>maxresdefault</w>
<w>moshi</w> <w>moshi</w>

View File

@ -28,7 +28,8 @@ android {
buildToolsVersion "30.0.2" buildToolsVersion "30.0.2"
buildFeatures{ buildFeatures{
dataBinding = true //dataBinding = true
viewBinding = true
} }
defaultConfig { defaultConfig {

View File

@ -30,7 +30,6 @@ import android.util.Log
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.findNavController import androidx.navigation.findNavController
@ -69,12 +68,13 @@ class MainActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
//Enabling Dark Mode
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
binding = DataBindingUtil.setContentView(this, R.layout.main_activity) binding = MainActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java) sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)
navController = findNavController(R.id.navHostFragment) navController = findNavController(R.id.navHostFragment)
snackBarAnchor = binding.snackBarPosition snackBarAnchor = binding.snackBarPosition
//Enabling Dark Mode
authenticateSpotify() authenticateSpotify()

View File

@ -33,7 +33,7 @@ import com.shabinder.spotiflyer.models.spotify.Source
import com.shabinder.spotiflyer.utils.* import com.shabinder.spotiflyer.utils.*
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class TrackListAdapter(private val viewModel :BaseViewModel): ListAdapter<TrackDetails, TrackListAdapter.ViewHolder>(TrackDiffCallback()) { class TrackListAdapter(private val viewModel :TrackListViewModel): ListAdapter<TrackDetails, TrackListAdapter.ViewHolder>(TrackDiffCallback()) {
var source:Source =Source.Spotify var source:Source =Source.Spotify

View File

@ -21,11 +21,9 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import com.shabinder.spotiflyer.R
import com.shabinder.spotiflyer.databinding.DownloadRecordFragmentBinding import com.shabinder.spotiflyer.databinding.DownloadRecordFragmentBinding
import com.shabinder.spotiflyer.models.spotify.Source import com.shabinder.spotiflyer.models.spotify.Source
import com.shabinder.spotiflyer.recyclerView.DownloadRecordAdapter import com.shabinder.spotiflyer.recyclerView.DownloadRecordAdapter
@ -42,7 +40,7 @@ class DownloadRecordFragment : Fragment() {
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
binding = DataBindingUtil.inflate(inflater,R.layout.download_record_fragment,container,false) binding = DownloadRecordFragmentBinding.inflate(inflater,container,false)
downloadRecordViewModel = ViewModelProvider(this).get(DownloadRecordViewModel::class.java) downloadRecordViewModel = ViewModelProvider(this).get(DownloadRecordViewModel::class.java)
adapter = DownloadRecordAdapter() adapter = DownloadRecordAdapter()
binding.downloadRecordList.adapter = adapter binding.downloadRecordList.adapter = adapter

View File

@ -23,24 +23,30 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.SimpleItemAnimator import androidx.recyclerview.widget.SimpleItemAnimator
import com.shabinder.spotiflyer.SharedViewModel import com.shabinder.spotiflyer.SharedViewModel
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper import com.shabinder.spotiflyer.downloadHelper.DownloadHelper
import com.shabinder.spotiflyer.models.DownloadStatus import com.shabinder.spotiflyer.models.DownloadStatus
import com.shabinder.spotiflyer.models.spotify.Source import com.shabinder.spotiflyer.models.spotify.Source
import com.shabinder.spotiflyer.networking.GaanaInterface
import com.shabinder.spotiflyer.networking.YoutubeMusicApi
import com.shabinder.spotiflyer.recyclerView.TrackListAdapter import com.shabinder.spotiflyer.recyclerView.TrackListAdapter
import com.shabinder.spotiflyer.utils.* import com.shabinder.spotiflyer.utils.*
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject
class GaanaFragment : BaseFragment() { @AndroidEntryPoint
class GaanaFragment : TrackListFragment<GaanaViewModel,GaanaFragmentArgs>() {
override lateinit var baseViewModel: BaseViewModel @Inject lateinit var youtubeMusicApi: YoutubeMusicApi
@Inject lateinit var gaanaInterface: GaanaInterface
override lateinit var viewModel: GaanaViewModel
override lateinit var adapter: TrackListAdapter override lateinit var adapter: TrackListAdapter
override var source: Source = Source.Gaana override var source: Source = Source.Gaana
private val viewModel:GaanaViewModel override val args: GaanaFragmentArgs by navArgs()
get() = baseViewModel as GaanaViewModel
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
@ -75,16 +81,16 @@ class GaanaFragment : BaseFragment() {
binding.downloadingFab.visibility = View.VISIBLE binding.downloadingFab.visibility = View.VISIBLE
rotateAnim(binding.downloadingFab) rotateAnim(binding.downloadingFab)
for (track in baseViewModel.trackList.value!!){ for (track in viewModel.trackList.value!!){
if(track.downloaded != DownloadStatus.Downloaded){ if(track.downloaded != DownloadStatus.Downloaded){
track.downloaded = DownloadStatus.Downloading track.downloaded = DownloadStatus.Downloading
adapter.notifyItemChanged(baseViewModel.trackList.value!!.indexOf(track)) adapter.notifyItemChanged(viewModel.trackList.value!!.indexOf(track))
} }
} }
showMessage("Processing!") showMessage("Processing!")
sharedViewModel.uiScope.launch(Dispatchers.Default){ sharedViewModel.uiScope.launch(Dispatchers.Default){
val urlList = arrayListOf<String>() val urlList = arrayListOf<String>()
baseViewModel.trackList.value?.forEach { urlList.add(it.albumArtURL) } viewModel.trackList.value?.forEach { urlList.add(it.albumArtURL) }
//Appending Source //Appending Source
urlList.add("gaana") urlList.add("gaana")
loadAllImages( loadAllImages(
@ -92,12 +98,12 @@ class GaanaFragment : BaseFragment() {
urlList urlList
) )
} }
baseViewModel.uiScope.launch { viewModel.uiScope.launch {
val finalList = baseViewModel.trackList.value val finalList = viewModel.trackList.value
if(finalList.isNullOrEmpty())showMessage("Not Downloading Any Song") if(finalList.isNullOrEmpty())showMessage("Not Downloading Any Song")
DownloadHelper.downloadAllTracks( DownloadHelper.downloadAllTracks(
baseViewModel.folderType, viewModel.folderType,
baseViewModel.subFolder, viewModel.subFolder,
finalList ?: listOf(), finalList ?: listOf(),
) )
} }
@ -112,7 +118,7 @@ class GaanaFragment : BaseFragment() {
**/ **/
private fun initializeAll() { private fun initializeAll() {
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java) sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
baseViewModel = ViewModelProvider(this).get(GaanaViewModel::class.java) viewModel = ViewModelProvider(this).get(GaanaViewModel::class.java)
viewModel.gaanaInterface = gaanaInterface viewModel.gaanaInterface = gaanaInterface
adapter = TrackListAdapter(viewModel) adapter = TrackListAdapter(viewModel)
DownloadHelper.youtubeMusicApi = youtubeMusicApi DownloadHelper.youtubeMusicApi = youtubeMusicApi

View File

@ -27,15 +27,15 @@ import com.shabinder.spotiflyer.models.TrackDetails
import com.shabinder.spotiflyer.models.gaana.* import com.shabinder.spotiflyer.models.gaana.*
import com.shabinder.spotiflyer.models.spotify.Source import com.shabinder.spotiflyer.models.spotify.Source
import com.shabinder.spotiflyer.networking.GaanaInterface import com.shabinder.spotiflyer.networking.GaanaInterface
import com.shabinder.spotiflyer.utils.BaseViewModel
import com.shabinder.spotiflyer.utils.Provider import com.shabinder.spotiflyer.utils.Provider
import com.shabinder.spotiflyer.utils.TrackListViewModel
import com.shabinder.spotiflyer.utils.finalOutputDir import com.shabinder.spotiflyer.utils.finalOutputDir
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File
class GaanaViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) : BaseViewModel(){ class GaanaViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) : TrackListViewModel(){
override var folderType:String = "" override var folderType:String = ""
override var subFolder:String = "" override var subFolder:String = ""

View File

@ -25,7 +25,6 @@ import android.text.SpannableStringBuilder
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
@ -57,7 +56,7 @@ class MainFragment : Fragment() {
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
binding = DataBindingUtil.inflate(inflater,R.layout.main_fragment,container,false) binding = MainFragmentBinding.inflate(inflater,container,false)
initializeAll() initializeAll()
binding.btnSearch.setOnClickListener { binding.btnSearch.setOnClickListener {
if(!isOnline()){ if(!isOnline()){

View File

@ -24,26 +24,29 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.SimpleItemAnimator import androidx.recyclerview.widget.SimpleItemAnimator
import com.shabinder.spotiflyer.MainActivity import com.shabinder.spotiflyer.MainActivity
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper import com.shabinder.spotiflyer.downloadHelper.DownloadHelper
import com.shabinder.spotiflyer.models.DownloadStatus import com.shabinder.spotiflyer.models.DownloadStatus
import com.shabinder.spotiflyer.models.spotify.Source import com.shabinder.spotiflyer.models.spotify.Source
import com.shabinder.spotiflyer.networking.YoutubeMusicApi
import com.shabinder.spotiflyer.recyclerView.TrackListAdapter import com.shabinder.spotiflyer.recyclerView.TrackListAdapter
import com.shabinder.spotiflyer.utils.* import com.shabinder.spotiflyer.utils.*
import com.shabinder.spotiflyer.utils.Provider.mainActivity import com.shabinder.spotiflyer.utils.Provider.mainActivity
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject
@Suppress("DEPRECATION") @AndroidEntryPoint
class SpotifyFragment : BaseFragment() { class SpotifyFragment : TrackListFragment<SpotifyViewModel,SpotifyFragmentArgs>() {
override lateinit var baseViewModel: BaseViewModel @Inject lateinit var youtubeMusicApi: YoutubeMusicApi
override lateinit var viewModel: SpotifyViewModel
override lateinit var adapter: TrackListAdapter override lateinit var adapter: TrackListAdapter
override var source: Source = Source.Spotify override var source: Source = Source.Spotify
private val viewModel: SpotifyViewModel override val args: SpotifyFragmentArgs by navArgs()
get() = baseViewModel as SpotifyViewModel
@SuppressLint("SetJavaScriptEnabled") @SuppressLint("SetJavaScriptEnabled")
override fun onCreateView( override fun onCreateView(
@ -53,7 +56,7 @@ class SpotifyFragment : BaseFragment() {
super.onCreateView(inflater, container, savedInstanceState) super.onCreateView(inflater, container, savedInstanceState)
initializeAll() initializeAll()
val spotifyLink = SpotifyFragmentArgs.fromBundle(requireArguments()).link.substringAfter("open.spotify.com/") val spotifyLink = args.link.substringAfter("open.spotify.com/")
val link = spotifyLink.substringAfterLast('/', "Error").substringBefore('?') val link = spotifyLink.substringAfterLast('/', "Error").substringBefore('?')
val type = spotifyLink.substringBeforeLast('/', "Error").substringAfterLast('/') val type = spotifyLink.substringBeforeLast('/', "Error").substringAfterLast('/')
@ -75,7 +78,7 @@ class SpotifyFragment : BaseFragment() {
showMessage("Implementing Soon, Stay Tuned!") showMessage("Implementing Soon, Stay Tuned!")
} }
else{ else{
viewModel.spotifySearch(type,link) this.viewModel.spotifySearch(type,link)
binding.btnDownloadAll.setOnClickListener { binding.btnDownloadAll.setOnClickListener {
if(!isOnline()){ if(!isOnline()){
@ -86,16 +89,16 @@ class SpotifyFragment : BaseFragment() {
binding.downloadingFab.visibility = View.VISIBLE binding.downloadingFab.visibility = View.VISIBLE
rotateAnim(binding.downloadingFab) rotateAnim(binding.downloadingFab)
for (track in viewModel.trackList.value!!){ for (track in this.viewModel.trackList.value!!){
if(track.downloaded != DownloadStatus.Downloaded){ if(track.downloaded != DownloadStatus.Downloaded){
track.downloaded = DownloadStatus.Downloading track.downloaded = DownloadStatus.Downloading
adapter.notifyItemChanged(viewModel.trackList.value!!.indexOf(track)) adapter.notifyItemChanged(this.viewModel.trackList.value!!.indexOf(track))
} }
} }
showMessage("Processing!") showMessage("Processing!")
sharedViewModel.uiScope.launch(Dispatchers.Default){ sharedViewModel.uiScope.launch(Dispatchers.Default){
val urlList = arrayListOf<String>() val urlList = arrayListOf<String>()
viewModel.trackList.value?.forEach { urlList.add(it.albumArtURL) } this@SpotifyFragment.viewModel.trackList.value?.forEach { urlList.add(it.albumArtURL) }
//Appending Source //Appending Source
urlList.add("spotify") urlList.add("spotify")
loadAllImages( loadAllImages(
@ -103,7 +106,7 @@ class SpotifyFragment : BaseFragment() {
urlList urlList
) )
} }
viewModel.uiScope.launch { this.viewModel.uiScope.launch {
val finalList = viewModel.trackList.value val finalList = viewModel.trackList.value
if(finalList.isNullOrEmpty())showMessage("Not Downloading Any Song") if(finalList.isNullOrEmpty())showMessage("Not Downloading Any Song")
DownloadHelper.downloadAllTracks( DownloadHelper.downloadAllTracks(
@ -124,10 +127,10 @@ class SpotifyFragment : BaseFragment() {
* Basic Initialization * Basic Initialization
**/ **/
private fun initializeAll() { private fun initializeAll() {
baseViewModel = ViewModelProvider(this).get(SpotifyViewModel::class.java) this.viewModel = ViewModelProvider(this).get(SpotifyViewModel::class.java)
adapter = TrackListAdapter(viewModel) adapter = TrackListAdapter(this.viewModel)
sharedViewModel.spotifyService.observe(viewLifecycleOwner, { sharedViewModel.spotifyService.observe(viewLifecycleOwner, {
viewModel.spotifyService = it this.viewModel.spotifyService = it
}) })
DownloadHelper.youtubeMusicApi = youtubeMusicApi DownloadHelper.youtubeMusicApi = youtubeMusicApi
DownloadHelper.sharedViewModel = sharedViewModel DownloadHelper.sharedViewModel = sharedViewModel

View File

@ -26,15 +26,15 @@ import com.shabinder.spotiflyer.models.DownloadStatus
import com.shabinder.spotiflyer.models.TrackDetails import com.shabinder.spotiflyer.models.TrackDetails
import com.shabinder.spotiflyer.models.spotify.* import com.shabinder.spotiflyer.models.spotify.*
import com.shabinder.spotiflyer.networking.SpotifyService import com.shabinder.spotiflyer.networking.SpotifyService
import com.shabinder.spotiflyer.utils.BaseViewModel
import com.shabinder.spotiflyer.utils.Provider import com.shabinder.spotiflyer.utils.Provider
import com.shabinder.spotiflyer.utils.TrackListViewModel
import com.shabinder.spotiflyer.utils.finalOutputDir import com.shabinder.spotiflyer.utils.finalOutputDir
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File
class SpotifyViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) : BaseViewModel(){ class SpotifyViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) : TrackListViewModel(){
override var folderType:String = "" override var folderType:String = ""
override var subFolder:String = "" override var subFolder:String = ""
@ -145,6 +145,7 @@ class SpotifyViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO
} }
} }
@Suppress("DEPRECATION")
private fun List<Track>.toTrackDetailsList() = this.map { private fun List<Track>.toTrackDetailsList() = this.map {
TrackDetails( TrackDetails(
title = it.name.toString(), title = it.name.toString(),

View File

@ -22,31 +22,37 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.navArgs
import com.github.kiulian.downloader.YoutubeDownloader
import com.shabinder.spotiflyer.downloadHelper.YTDownloadHelper import com.shabinder.spotiflyer.downloadHelper.YTDownloadHelper
import com.shabinder.spotiflyer.models.DownloadStatus import com.shabinder.spotiflyer.models.DownloadStatus
import com.shabinder.spotiflyer.models.spotify.Source import com.shabinder.spotiflyer.models.spotify.Source
import com.shabinder.spotiflyer.recyclerView.TrackListAdapter import com.shabinder.spotiflyer.recyclerView.TrackListAdapter
import com.shabinder.spotiflyer.utils.* import com.shabinder.spotiflyer.utils.*
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject
class YoutubeFragment : BaseFragment() { private const val sampleDomain2 = "youtu.be"
private const val sampleDomain1 = "youtube.com"
override lateinit var baseViewModel: BaseViewModel @AndroidEntryPoint
class YoutubeFragment : TrackListFragment<YoutubeViewModel,YoutubeFragmentArgs>() {
@Inject lateinit var ytDownloader: YoutubeDownloader
override lateinit var viewModel: YoutubeViewModel
override lateinit var adapter : TrackListAdapter override lateinit var adapter : TrackListAdapter
override var source: Source = Source.YouTube override var source: Source = Source.YouTube
private val viewModel: YoutubeViewModel override val args: YoutubeFragmentArgs by navArgs()
get() = baseViewModel as YoutubeViewModel
private val sampleDomain2 = "youtu.be"
private val sampleDomain1 = "youtube.com"
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
super.onCreateView(inflater, container, savedInstanceState) super.onCreateView(inflater, container, savedInstanceState)
baseViewModel = ViewModelProvider(this).get(YoutubeViewModel::class.java) this.viewModel = ViewModelProvider(this).get(YoutubeViewModel::class.java)
adapter = TrackListAdapter(viewModel) adapter = TrackListAdapter(this.viewModel)
binding.trackList.adapter = adapter binding.trackList.adapter = adapter
val args = YoutubeFragmentArgs.fromBundle(requireArguments()) val args = YoutubeFragmentArgs.fromBundle(requireArguments())
@ -60,7 +66,7 @@ class YoutubeFragment : BaseFragment() {
if(link.contains("playlist",true) || link.contains("list",true)){ if(link.contains("playlist",true) || link.contains("list",true)){
// Given Link is of a Playlist // Given Link is of a Playlist
val playlistId = link.substringAfter("?list=").substringAfter("&list=").substringBefore("&") val playlistId = link.substringAfter("?list=").substringAfter("&list=").substringBefore("&")
viewModel.getYTPlaylist(playlistId,ytDownloader) this.viewModel.getYTPlaylist(playlistId,ytDownloader)
}else{//Given Link is of a Video }else{//Given Link is of a Video
var searchId = "error" var searchId = "error"
if(link.contains(sampleDomain1,true) ){ if(link.contains(sampleDomain1,true) ){
@ -70,7 +76,7 @@ class YoutubeFragment : BaseFragment() {
searchId = link.substringAfterLast("/","error") searchId = link.substringAfterLast("/","error")
} }
if(searchId != "error") { if(searchId != "error") {
viewModel.getYTTrack(searchId,ytDownloader) this.viewModel.getYTTrack(searchId,ytDownloader)
}else{showMessage("Your Youtube Link is not of a Video!!")} }else{showMessage("Your Youtube Link is not of a Video!!")}
} }
@ -87,10 +93,10 @@ class YoutubeFragment : BaseFragment() {
rotateAnim(binding.downloadingFab) rotateAnim(binding.downloadingFab)
for (track in viewModel.trackList.value?: listOf()){ for (track in this.viewModel.trackList.value?: listOf()){
if(track.downloaded != DownloadStatus.Downloaded){ if(track.downloaded != DownloadStatus.Downloaded){
track.downloaded = DownloadStatus.Downloading track.downloaded = DownloadStatus.Downloading
adapter.notifyItemChanged(viewModel.trackList.value!!.indexOf(track)) adapter.notifyItemChanged(this.viewModel.trackList.value!!.indexOf(track))
} }
} }
showMessage("Processing!") showMessage("Processing!")

View File

@ -27,8 +27,8 @@ import com.shabinder.spotiflyer.database.DownloadRecord
import com.shabinder.spotiflyer.models.DownloadStatus import com.shabinder.spotiflyer.models.DownloadStatus
import com.shabinder.spotiflyer.models.TrackDetails import com.shabinder.spotiflyer.models.TrackDetails
import com.shabinder.spotiflyer.models.spotify.Source 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.Provider.defaultDir
import com.shabinder.spotiflyer.utils.TrackListViewModel
import com.shabinder.spotiflyer.utils.finalOutputDir import com.shabinder.spotiflyer.utils.finalOutputDir
import com.shabinder.spotiflyer.utils.removeIllegalChars import com.shabinder.spotiflyer.utils.removeIllegalChars
import com.shabinder.spotiflyer.utils.showMessage import com.shabinder.spotiflyer.utils.showMessage
@ -37,7 +37,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File
class YoutubeViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) : BaseViewModel(){ class YoutubeViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) : TrackListViewModel(){
/* /*
* YT Album Art Schema * YT Album Art Schema
* HI-RES Url: https://i.ytimg.com/vi/$searchId/maxresdefault.jpg" * HI-RES Url: https://i.ytimg.com/vi/$searchId/maxresdefault.jpg"

View File

@ -26,35 +26,27 @@ import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.github.kiulian.downloader.YoutubeDownloader import androidx.navigation.NavArgs
import com.shabinder.spotiflyer.R import com.shabinder.spotiflyer.R
import com.shabinder.spotiflyer.SharedViewModel import com.shabinder.spotiflyer.SharedViewModel
import com.shabinder.spotiflyer.databinding.TrackListFragmentBinding import com.shabinder.spotiflyer.databinding.TrackListFragmentBinding
import com.shabinder.spotiflyer.models.DownloadStatus import com.shabinder.spotiflyer.models.DownloadStatus
import com.shabinder.spotiflyer.models.TrackDetails import com.shabinder.spotiflyer.models.TrackDetails
import com.shabinder.spotiflyer.models.spotify.Source import com.shabinder.spotiflyer.models.spotify.Source
import com.shabinder.spotiflyer.networking.GaanaInterface
import com.shabinder.spotiflyer.networking.YoutubeMusicApi
import com.shabinder.spotiflyer.recyclerView.TrackListAdapter import com.shabinder.spotiflyer.recyclerView.TrackListAdapter
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@AndroidEntryPoint abstract class TrackListFragment<VM : TrackListViewModel , args: NavArgs> : Fragment() {
abstract class BaseFragment : Fragment() {
@Inject open lateinit var youtubeMusicApi: YoutubeMusicApi protected lateinit var sharedViewModel: SharedViewModel
@Inject open lateinit var ytDownloader: YoutubeDownloader protected lateinit var binding: TrackListFragmentBinding
@Inject open lateinit var gaanaInterface: GaanaInterface protected abstract var viewModel: VM
open lateinit var sharedViewModel: SharedViewModel protected abstract var adapter: TrackListAdapter
open lateinit var binding: TrackListFragmentBinding protected abstract var source: Source
abstract var baseViewModel: BaseViewModel
abstract var adapter: TrackListAdapter
abstract var source: Source
private var intentFilter: IntentFilter? = null private var intentFilter: IntentFilter? = null
private var updateUIReceiver: BroadcastReceiver? = null private var updateUIReceiver: BroadcastReceiver? = null
protected abstract val args:NavArgs
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -66,7 +58,7 @@ abstract class BaseFragment : Fragment() {
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
binding = DataBindingUtil.inflate(inflater,R.layout.track_list_fragment, container, false) binding = TrackListFragmentBinding.inflate(inflater,container,false)
return binding.root return binding.root
} }
@ -74,11 +66,12 @@ abstract class BaseFragment : Fragment() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
initializeLiveDataObservers() initializeLiveDataObservers()
} }
/** /**
*Live Data Observers *Live Data Observers
**/ **/
open fun initializeLiveDataObservers() { private fun initializeLiveDataObservers() {
baseViewModel.trackList.observe(viewLifecycleOwner, { viewModel.trackList.observe(viewLifecycleOwner, {
if (it.isNotEmpty()){ if (it.isNotEmpty()){
Log.i("GaanaFragment","TrackList Updated") Log.i("GaanaFragment","TrackList Updated")
adapter.submitList(it, source) adapter.submitList(it, source)
@ -86,16 +79,16 @@ abstract class BaseFragment : Fragment() {
} }
}) })
baseViewModel.coverUrl.observe(viewLifecycleOwner, { viewModel.coverUrl.observe(viewLifecycleOwner, {
if(it!="Loading") bindImage(binding.coverImage,it, source) if(it!="Loading") bindImage(binding.coverImage,it, source)
}) })
baseViewModel.title.observe(viewLifecycleOwner, { viewModel.title.observe(viewLifecycleOwner, {
binding.titleView.text = it binding.titleView.text = it
}) })
} }
open fun initializeBroadcast() { private fun initializeBroadcast() {
intentFilter = IntentFilter() intentFilter = IntentFilter()
intentFilter?.addAction("track_download_completed") intentFilter?.addAction("track_download_completed")
@ -105,13 +98,13 @@ abstract class BaseFragment : Fragment() {
if (intent != null){ if (intent != null){
val trackDetails = intent.getParcelableExtra<TrackDetails?>("track") val trackDetails = intent.getParcelableExtra<TrackDetails?>("track")
trackDetails?.let { trackDetails?.let {
val position: Int = baseViewModel.trackList.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") Log.i("Track","Download Completed Intent :$position")
if(position != -1) { if(position != -1) {
val track = baseViewModel.trackList.value?.get(position) val track = viewModel.trackList.value?.get(position)
track?.let{ track?.let{
it.downloaded = DownloadStatus.Downloaded it.downloaded = DownloadStatus.Downloaded
baseViewModel.trackList.value?.set(position, it) viewModel.trackList.value?.set(position, it)
adapter.notifyItemChanged(position) adapter.notifyItemChanged(position)
checkIfAllDownloaded() checkIfAllDownloaded()
} }
@ -133,8 +126,8 @@ abstract class BaseFragment : Fragment() {
requireActivity().unregisterReceiver(updateUIReceiver) requireActivity().unregisterReceiver(updateUIReceiver)
} }
open fun checkIfAllDownloaded() { private fun checkIfAllDownloaded() {
if(!baseViewModel.trackList.value!!.any { it.downloaded != DownloadStatus.Downloaded }){ if(!viewModel.trackList.value!!.any { it.downloaded != DownloadStatus.Downloaded }){
//All Tracks Downloaded //All Tracks Downloaded
binding.btnDownloadAll.visibility = View.GONE binding.btnDownloadAll.visibility = View.GONE
binding.downloadingFab.apply{ binding.downloadingFab.apply{

View File

@ -25,7 +25,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
abstract class BaseViewModel:ViewModel() { abstract class TrackListViewModel:ViewModel() {
abstract var folderType:String abstract var folderType:String
abstract var subFolder:String abstract var subFolder:String
open val trackList = MutableLiveData<MutableList<TrackDetails>>() open val trackList = MutableLiveData<MutableList<TrackDetails>>()

View File

@ -16,7 +16,8 @@
~ along with this program. If not, see <https://www.gnu.org/licenses/>. ~ along with this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
<shape xmlns:android="http://schemas.android.com/apk/res/android" > <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
<solid android:color="@color/black"/>
<gradient <gradient
android:angle="90" android:angle="90"
android:centerColor="#0F6200FF" android:centerColor="#0F6200FF"

Binary file not shown.

Binary file not shown.

View File

@ -15,12 +15,10 @@
~ You should have received a copy of the GNU General Public License ~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>. ~ along with this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:background="@color/black"
android:layout_height="match_parent"> android:layout_height="match_parent">
<TextView <TextView
@ -28,14 +26,10 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:background="@drawable/text_background_accented" style="@style/Widget.AppCompat.EditText.Gradient"
android:drawablePadding="5dp" android:drawablePadding="5dp"
android:fontFamily="@font/raleway_semibold" android:fontFamily="@font/raleway_semibold"
android:gravity="center"
android:padding="8dp"
android:text=" Download History " android:text=" Download History "
android:textAlignment="center"
android:textColor="#E1FFFFFF"
android:textSize="21sp" android:textSize="21sp"
app:drawableStartCompat="@drawable/ic_history" app:drawableStartCompat="@drawable/ic_history"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@ -83,5 +77,4 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tabLayout" /> app:layout_constraintTop_toBottomOf="@+id/tabLayout" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@ -17,89 +17,81 @@
~ along with this program. If not, see <https://www.gnu.org/licenses/>. ~ along with this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<variable android:layout_width="match_parent"
name="downloadRecord" android:layout_height="92dp"
type="com.shabinder.spotiflyer.database.DownloadRecord" /> android:background="#000000"
</data> android:paddingBottom="12dp">
<RelativeLayout <ImageView
android:layout_width="match_parent" android:id="@+id/coverUrl"
android:layout_height="92dp" android:layout_width="100dp"
android:background="#000000" android:layout_height="80dp"
android:paddingBottom="12dp"> android:layout_alignParentStart="true"
android:contentDescription="Track Image"
android:scaleType="centerInside"
android:src="@drawable/ic_song_placeholder" />
<ImageView <TextView
android:id="@+id/coverUrl" android:id="@+id/item_name"
android:layout_width="100dp" android:layout_width="0dp"
android:layout_height="80dp" android:layout_height="wrap_content"
android:layout_alignParentStart="true" android:layout_alignParentTop="true"
android:contentDescription="Track Image" android:layout_marginStart="12dp"
android:scaleType="centerInside" android:layout_marginTop="14dp"
android:src="@drawable/ic_song_placeholder" /> android:layout_marginEnd="12dp"
android:layout_toStartOf="@+id/btn_action"
android:layout_toEndOf="@+id/coverUrl"
android:fontFamily="@font/raleway_semibold"
android:letterSpacing="0.04"
android:lines="1"
android:text="Weekend Chills"
android:textAllCaps="false"
android:textAppearance="@style/TextAppearance.AppTheme.Headline4"
android:textColor="#9AB3FF"
android:textSize="20sp" />
<TextView <TextView
android:id="@+id/item_name" android:id="@+id/type"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentTop="true" android:layout_alignBottom="@+id/coverUrl"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="14dp" android:layout_marginEnd="0dp"
android:layout_marginEnd="12dp" android:layout_marginBottom="15dp"
android:layout_toStartOf="@+id/btn_action" android:layout_toStartOf="@+id/totalItems"
android:layout_toEndOf="@+id/coverUrl" android:layout_toEndOf="@+id/coverUrl"
android:fontFamily="@font/raleway_semibold" android:paddingLeft="9dp"
android:letterSpacing="0.04" android:text="Playlist"
android:lines="1" android:textSize="12sp" />
android:text="Weekend Chills"
android:textAllCaps="false"
android:textAppearance="@style/TextAppearance.AppTheme.Headline4"
android:textColor="#9AB3FF"
android:textSize="20sp" />
<TextView
android:id="@+id/type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/coverUrl"
android:layout_marginStart="12dp"
android:layout_marginEnd="0dp"
android:layout_marginBottom="15dp"
android:layout_toStartOf="@+id/totalItems"
android:layout_toEndOf="@+id/coverUrl"
android:paddingLeft="9dp"
android:text="Playlist"
android:textSize="12sp" />
<TextView <TextView
android:id="@+id/totalItems" android:id="@+id/totalItems"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignBottom="@+id/coverUrl" android:layout_alignBottom="@+id/coverUrl"
android:layout_marginTop="7dp" android:layout_marginTop="7dp"
android:layout_marginEnd="25dp" android:layout_marginEnd="25dp"
android:layout_marginBottom="15dp" android:layout_marginBottom="15dp"
android:layout_toStartOf="@+id/btn_action" android:layout_toStartOf="@+id/btn_action"
android:paddingLeft="9dp" android:paddingLeft="9dp"
android:text="50 Tracks" android:text="50 Tracks"
android:textSize="12sp" /> android:textSize="12sp" />
<ImageButton <ImageButton
android:id="@+id/btn_action" android:id="@+id/btn_action"
android:layout_width="60dp" android:layout_width="60dp"
android:layout_height="80dp" android:layout_height="80dp"
android:layout_alignBottom="@+id/coverUrl" android:layout_alignBottom="@+id/coverUrl"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
android:backgroundTint="@color/black" android:backgroundTint="@color/black"
android:scaleType="centerInside" android:scaleType="centerInside"
android:src="@drawable/ic_share_open" android:src="@drawable/ic_share_open"
android:tint="@null" /> android:tint="@null" />
</RelativeLayout> </RelativeLayout>
</layout>

View File

@ -16,39 +16,36 @@
~ along with this program. If not, see <https://www.gnu.org/licenses/>. ~ along with this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mainActivity"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/snackBarPosition"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:background="@drawable/transparent"
android:textStyle="bold"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout <fragment
android:id="@+id/mainActivity" android:id="@+id/navHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:background="@color/black" android:layout_height="0dp"
android:layout_height="match_parent"> app:defaultNavHost="true"
<View app:layout_constraintBottom_toBottomOf="parent"
android:id="@+id/snackBarPosition" app:layout_constraintEnd_toEndOf="parent"
android:layout_width="wrap_content" app:layout_constraintStart_toStartOf="parent"
android:layout_height="wrap_content" app:layout_constraintTop_toTopOf="parent"
android:layout_marginBottom="16dp" app:navGraph="@navigation/navigation"
android:background="@drawable/transparent" tools:ignore="FragmentTagUsage" />
android:visibility="invisible"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<fragment </androidx.constraintlayout.widget.ConstraintLayout>
android:id="@+id/navHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/navigation"
tools:ignore="FragmentTagUsage" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@ -15,47 +15,40 @@
~ You should have received a copy of the GNU General Public License ~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>. ~ along with this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ScrollView <ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fillViewport="true" android:fillViewport="true"
android:background="@color/black" tools:ignore="HardcodedText"
> >
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<EditText <EditText
android:id="@+id/linkSearch" android:id="@+id/linkSearch"
android:layout_width="wrap_content" style="@style/Widget.AppCompat.EditText.Gradient"
android:layout_height="46dp" android:layout_height="46dp"
android:layout_marginTop="24dp" android:layout_marginTop="24dp"
android:background="@drawable/text_background_accented"
android:ems="10" android:ems="10"
android:hint="Paste Link here" android:hint="Paste Link here"
android:inputType="text"
android:padding="8dp"
android:textAlignment="center"
android:textColor="@color/white"
android:textColorHint="@color/grey"
android:textSize="19sp"
app:layout_constraintEnd_toStartOf="@+id/btn_search" app:layout_constraintEnd_toStartOf="@+id/btn_search"
app:layout_constraintHorizontal_chainStyle="spread" app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
android:importantForAutofill="no"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatButton <androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_search" android:id="@+id/btn_search"
android:layout_width="wrap_content" style="@style/Widget.AppCompat.Button.Colored.Gradient"
android:layout_height="44dp" android:layout_height="44dp"
android:background="@drawable/btn_design" android:fontFamily="@font/nunito_sans_light"
android:paddingLeft="4dp" android:text=" Search "
android:paddingRight="4dp" android:textStyle="bold"
android:text="Search"
android:textColor="@color/black"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@+id/linkSearch" app:layout_constraintBottom_toBottomOf="@+id/linkSearch"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/linkSearch" app:layout_constraintStart_toEndOf="@+id/linkSearch"
@ -75,19 +68,16 @@
app:layout_collapseMode="parallax" app:layout_collapseMode="parallax"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linkSearch" /> app:layout_constraintTop_toBottomOf="@+id/linkSearch"
tools:ignore="UnusedAttribute" />
<ImageButton <ImageButton
android:id="@+id/btn_history" android:id="@+id/btn_history"
android:layout_width="40dp" style="@style/Widget.AppCompat.ImageButton.40dp"
android:layout_height="40dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:background="@drawable/transparent"
android:scaleType="fitCenter"
app:layout_constraintBottom_toBottomOf="@+id/appLogo" app:layout_constraintBottom_toBottomOf="@+id/appLogo"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/ic_history" /> android:contentDescription="Open Download History Button"
app:srcCompat="@drawable/ic_history"/>
<TextView <TextView
android:id="@+id/appName" android:id="@+id/appName"
@ -95,16 +85,13 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:fontFamily="@font/raleway_semibold" android:fontFamily="@font/raleway_semibold"
android:gravity="end"
android:text='"SpotiFlyer"' android:text='"SpotiFlyer"'
android:textAlignment="viewEnd" android:textColor="@color/colorAccent"
android:textColor="#9AB3FF"
android:textSize="40sp" android:textSize="40sp"
android:typeface="normal"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/appLogo" /> app:layout_constraintTop_toBottomOf="@id/appLogo"
/>
<TextView <TextView
android:id="@+id/appSubTitle" android:id="@+id/appSubTitle"
@ -120,50 +107,26 @@
app:layout_constraintStart_toStartOf="@+id/appName" app:layout_constraintStart_toStartOf="@+id/appName"
app:layout_constraintTop_toBottomOf="@+id/appName" /> app:layout_constraintTop_toBottomOf="@+id/appName" />
<TextView
android:id="@+id/platforms"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginEnd="2dp"
android:fontFamily="@font/raleway_semibold"
android:text="Supports: "
android:textAlignment="center"
android:textColor="#9AB3FF"
android:textSize="16sp"
android:textStyle="bold"
android:visibility="visible"
app:layout_constraintEnd_toStartOf="@+id/btn_spotify"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appSubTitle" />
<ImageButton <ImageButton
android:id="@+id/btn_spotify" android:id="@+id/btn_spotify"
android:layout_width="46dp" style="@style/Widget.AppCompat.ImageButton.platformIcon"
android:layout_height="46dp"
android:layout_marginEnd="2dp" android:layout_marginEnd="2dp"
android:background="@color/black" android:contentDescription="Open Spotify App Button"
android:padding="5dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_spotify_logo" android:src="@drawable/ic_spotify_logo"
app:layout_constraintBottom_toBottomOf="@+id/platforms"
app:layout_constraintEnd_toStartOf="@+id/btn_youtube" app:layout_constraintEnd_toStartOf="@+id/btn_youtube"
app:layout_constraintStart_toEndOf="@+id/platforms" android:padding="6dp"
app:layout_constraintTop_toTopOf="@+id/platforms" /> app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="@+id/appSubTitle"
app:layout_constraintTop_toBottomOf="@+id/appSubTitle"/>
<ImageButton <ImageButton
android:id="@+id/btn_youtube" android:id="@+id/btn_youtube"
android:layout_width="52dp" style="@style/Widget.AppCompat.ImageButton.platformIcon"
android:layout_height="52dp"
android:background="@color/black"
android:padding="5dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_youtube" android:src="@drawable/ic_youtube"
app:layout_constraintBottom_toBottomOf="@+id/btn_spotify" app:layout_constraintBottom_toBottomOf="@+id/btn_spotify"
app:layout_constraintEnd_toEndOf="parent" android:contentDescription="Open Youtube App Button"
app:layout_constraintStart_toEndOf="@+id/btn_spotify" app:layout_constraintStart_toEndOf="@+id/btn_spotify"
app:layout_constraintEnd_toEndOf="@+id/appSubTitle"
app:layout_constraintTop_toTopOf="@+id/btn_spotify" /> app:layout_constraintTop_toTopOf="@+id/btn_spotify" />
<TextView <TextView
@ -171,11 +134,11 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginEnd="24dp" android:layout_marginEnd="24dp"
android:gravity="center"
android:text="Usage Instructions!" android:text="Usage Instructions!"
android:textAlignment="center" android:textAlignment="center"
android:textColor="#D0838383" android:textColor="#D0838383"
android:textSize="14sp" android:textSize="14sp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="@+id/developer_insta_spotify" app:layout_constraintBottom_toBottomOf="@+id/developer_insta_spotify"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btn_linkedin" app:layout_constraintStart_toEndOf="@+id/btn_linkedin"
@ -189,6 +152,7 @@
android:background="@drawable/text_background_accented" android:background="@drawable/text_background_accented"
android:drawableEnd="@drawable/ic_mug" android:drawableEnd="@drawable/ic_mug"
android:drawablePadding="5dp" android:drawablePadding="5dp"
android:contentDescription="Donate Money Button"
android:fontFamily="@font/capriola" android:fontFamily="@font/capriola"
android:foreground="@drawable/rounded_gradient" android:foreground="@drawable/rounded_gradient"
android:gravity="end|center_vertical" android:gravity="end|center_vertical"
@ -214,39 +178,30 @@
<ImageButton <ImageButton
android:id="@+id/btn_github_spotify" android:id="@+id/btn_github_spotify"
android:layout_width="48dp" style="@style/Widget.AppCompat.ImageButton.platformIcon"
android:layout_height="wrap_content"
android:background="@color/black"
android:padding="5dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_github" android:src="@drawable/ic_github"
app:layout_constraintBottom_toTopOf="@+id/btn_linkedin" app:layout_constraintBottom_toTopOf="@+id/btn_linkedin"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_youtube" app:layout_constraintTop_toBottomOf="@+id/btn_youtube"
app:layout_constraintVertical_chainStyle="packed" /> android:contentDescription="Open Github App Button"
app:layout_constraintVertical_chainStyle="packed"/>
<ImageButton <ImageButton
android:id="@+id/btn_linkedin" android:id="@+id/btn_linkedin"
android:layout_width="48dp" style="@style/Widget.AppCompat.ImageButton.platformIcon"
android:layout_height="wrap_content"
android:background="@color/black"
android:padding="5dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_linkedin" android:src="@drawable/ic_linkedin"
app:layout_constraintBottom_toTopOf="@+id/developer_insta_spotify" app:layout_constraintBottom_toTopOf="@+id/developer_insta_spotify"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_github_spotify" /> android:contentDescription="Open LinkedIN App Button"
app:layout_constraintTop_toBottomOf="@+id/btn_github_spotify"/>
<ImageButton <ImageButton
android:id="@+id/developer_insta_spotify" android:id="@+id/developer_insta_spotify"
android:layout_width="48dp" style="@style/Widget.AppCompat.ImageButton.platformIcon"
android:layout_height="wrap_content"
android:background="@color/black"
android:padding="5dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_instagram" android:src="@drawable/ic_instagram"
app:layout_constraintBottom_toTopOf="@+id/btn_donate" app:layout_constraintBottom_toTopOf="@+id/btn_donate"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
android:contentDescription="Open Instagram App Button"
app:layout_constraintTop_toBottomOf="@+id/btn_linkedin" /> app:layout_constraintTop_toBottomOf="@+id/btn_linkedin" />
<TextView <TextView
@ -289,5 +244,4 @@
app:layout_constraintStart_toEndOf="@+id/heart" /> app:layout_constraintStart_toEndOf="@+id/heart" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView> </ScrollView>
</layout>

View File

@ -16,16 +16,16 @@
~ along with this program. If not, see <https://www.gnu.org/licenses/>. ~ along with this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main" android:id="@+id/main"
android:layout_width="match_parent" android:layout_width="match_parent"
android:background="@color/black" android:background="@color/black"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginTop="25dp"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
android:paddingTop="16dp"
tools:context=".ui.spotify.SpotifyFragment"> tools:context=".ui.spotify.SpotifyFragment">
<androidx.appcompat.widget.AppCompatButton <androidx.appcompat.widget.AppCompatButton
@ -155,4 +155,4 @@
app:layout_constraintTop_toBottomOf="@id/appbar" /> app:layout_constraintTop_toBottomOf="@id/appbar" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>

View File

@ -16,97 +16,84 @@
~ You should have received a copy of the GNU General Public License ~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>. ~ along with this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_marginBottom="12dp"
android:background="#000000">
<layout xmlns:android="http://schemas.android.com/apk/res/android" <ImageView
xmlns:app="http://schemas.android.com/apk/res-auto"> android:id="@+id/imageUrl"
android:layout_width="100dp"
<data>
<variable
name="track"
type="com.shabinder.spotiflyer.models.spotify.Track" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="80dp" android:layout_height="80dp"
android:background="#000000" android:contentDescription="Track Image"
android:layout_marginBottom="12dp" android:scaleType="centerInside"
> app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/artist"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_song_placeholder" />
<ImageView <TextView
android:id="@+id/imageUrl" android:id="@+id/track_name"
android:layout_width="100dp" android:layout_width="0dp"
android:layout_height="80dp" android:layout_height="wrap_content"
android:contentDescription="Track Image" android:layout_marginStart="8dp"
android:scaleType="centerInside" android:layout_marginTop="14dp"
app:layout_constraintBottom_toBottomOf="parent" android:fontFamily="@font/raleway_semibold"
app:layout_constraintEnd_toStartOf="@+id/artist" android:letterSpacing="0.04"
app:layout_constraintStart_toStartOf="parent" android:lines="1"
app:layout_constraintTop_toTopOf="parent" android:text="The Spectre"
app:srcCompat="@drawable/ic_song_placeholder" /> android:textAllCaps="false"
android:textAppearance="@style/TextAppearance.AppTheme.Headline4"
android:textColor="#9AB3FF"
android:textSize="20sp"
app:layout_constraintEnd_toStartOf="@+id/btn_download"
app:layout_constraintStart_toStartOf="@+id/artist"
app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
android:id="@+id/track_name" android:id="@+id/artist"
android:layout_width="0dp" style="@style/TextAppearance.AppCompat.Body2"
android:layout_height="wrap_content" android:layout_width="0dp"
android:layout_marginStart="8dp" android:layout_height="wrap_content"
android:layout_marginTop="14dp" android:layout_marginStart="12dp"
android:fontFamily="@font/raleway_semibold" android:layout_marginTop="8dp"
android:letterSpacing="0.04" android:layout_marginBottom="8dp"
android:lines="1" android:paddingLeft="9dp"
android:text="The Spectre" android:text="Alan Walker"
android:textAllCaps="false" android:textSize="12sp"
android:textAppearance="@style/TextAppearance.AppTheme.Headline4" app:layout_constraintBottom_toBottomOf="parent"
android:textColor="#9AB3FF" app:layout_constraintEnd_toStartOf="@+id/duration"
android:textSize="20sp" app:layout_constraintStart_toEndOf="@+id/imageUrl"
app:layout_constraintEnd_toStartOf="@+id/btn_download" app:layout_constraintTop_toBottomOf="@+id/track_name" />
app:layout_constraintStart_toStartOf="@+id/artist"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/artist"
style="@style/TextAppearance.AppCompat.Body2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:paddingLeft="9dp"
android:text="Alan Walker"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/duration"
app:layout_constraintStart_toEndOf="@+id/imageUrl"
app:layout_constraintTop_toBottomOf="@+id/track_name" />
<TextView <TextView
android:id="@+id/duration" android:id="@+id/duration"
style="@style/TextAppearance.AppCompat.Body2" style="@style/TextAppearance.AppCompat.Body2"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="3dp" android:layout_marginEnd="3dp"
android:paddingLeft="9dp" android:paddingLeft="9dp"
android:text="4 minutes, 20 sec" android:text="4 minutes, 20 sec"
android:textSize="12sp" android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="@+id/artist" app:layout_constraintBottom_toBottomOf="@+id/artist"
app:layout_constraintEnd_toStartOf="@+id/btn_download" app:layout_constraintEnd_toStartOf="@+id/btn_download"
app:layout_constraintStart_toEndOf="@+id/artist" app:layout_constraintStart_toEndOf="@+id/artist"
app:layout_constraintTop_toTopOf="@+id/artist" /> app:layout_constraintTop_toTopOf="@+id/artist" />
<ImageButton <ImageButton
android:id="@+id/btn_download" android:id="@+id/btn_download"
android:layout_width="60dp" android:layout_width="60dp"
android:layout_height="0dp" android:layout_height="0dp"
android:backgroundTint="@color/black" android:backgroundTint="@color/black"
android:scaleType="fitCenter" android:scaleType="fitCenter"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_arrow" /> app:srcCompat="@drawable/ic_arrow" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@ -20,10 +20,12 @@
<resources> <resources>
<color name="colorPrimary">#FC5C7D</color> <color name="colorPrimary">#FC5C7D</color>
<color name="colorPrimaryDark">#CE1CFF</color> <color name="colorPrimaryDark">#CE1CFF</color>
<color name="colorAccent">#799BFF</color> <color name="colorAccent">#9AB3FF</color>
<color name="white">#FFFFFF</color> <color name="white">#FFFFFF</color>
<color name="grey">#99FFFFFF</color> <color name="grey">#99FFFFFF</color>
<color name="black">#000000</color> <color name="black">#000000</color>
<color name="dark">#121212</color>
<color name="successGreen">#4BB543</color>
<color name="errorRed">#FF9494</color>
</resources> </resources>

View File

@ -19,12 +19,22 @@
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar"> <style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
<item name="colorPrimaryDark">#000000</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="android:colorBackground">@color/black</item>
<item name="colorOnBackground">@color/white</item>
<item name="colorError">#FF5E5E</item>
<item name="colorOnError">@color/black</item>
<item name="colorSurface">@color/dark</item>
<item name="colorOnSurface">@color/white</item>
<item name="colorPrimary">#FC5C7D</item> <item name="colorPrimary">#FC5C7D</item>
<item name="colorOnPrimary">@color/white</item>
<item name="colorSecondary">@color/colorPrimaryDark</item>
<item name="colorOnSecondary">@color/white</item>
<item name="android:textColor">#FFFFFF</item> <item name="android:textColor">#FFFFFF</item>
<item name="colorAccent">#6A82FB</item> <item name="android:textColorHint">@color/grey</item>
<item name="android:outlineAmbientShadowColor" tools:targetApi="p">#A9B200FF</item> <item name="colorAccent">@color/colorAccent</item>
<item name="android:radius">11dp</item> <item name="android:outlineAmbientShadowColor" tools:targetApi="p">@color/colorPrimaryDark</item>
<item name="android:radius">12dp</item>
<!-- Text Appearances !--> <!-- Text Appearances !-->
<!-- use our brand's custom TextAppearance4 !--> <!-- use our brand's custom TextAppearance4 !-->
<item name="textAppearanceHeadline4">@style/TextAppearance.AppTheme.Headline4</item> <item name="textAppearanceHeadline4">@style/TextAppearance.AppTheme.Headline4</item>
@ -32,7 +42,42 @@
<item name="textAppearanceBody2">@style/TextAppearance.MaterialComponents.Body2</item> <item name="textAppearanceBody2">@style/TextAppearance.MaterialComponents.Body2</item>
</style> </style>
<style name="Widget.AppCompat.Button.Colored.Gradient" parent="Widget.AppCompat.ActionButton">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:background">@drawable/btn_design</item>
<item name="android:textColor">@color/black</item>
<item name="android:textAllCaps">true</item>
<item name="android:textAlignment">center</item>
<item name="android:paddingLeft">4dp</item>
<item name="android:paddingRight">4dp</item>
<item name="android:textSize">16sp</item>
</style>
<style name="Widget.AppCompat.ImageButton.40dp" parent="Widget.AppCompat.ImageButton">
<item name="android:layout_width">40dp</item>
<item name="android:layout_height">40dp</item>
<item name="android:background">@drawable/transparent</item>
<item name="android:scaleType">fitCenter</item>
<item name="android:layout_margin">8dp</item>
</style>
<style name="Widget.AppCompat.EditText.Gradient" parent="Widget.AppCompat.EditText">
<item name="android:background">@drawable/text_background_accented</item>
<item name="android:textAlignment">center</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:inputType">text</item>
<item name="android:padding">8dp</item>
<item name="android:textSize">18sp</item>
<item name="android:textColor">@color/white</item>
</style>
<style name="Widget.AppCompat.ImageButton.platformIcon" parent="Widget.AppCompat.ImageButton">
<item name="android:layout_width">48dp</item>
<item name="android:layout_height">48dp</item>
<item name="android:background">@color/black</item>
<item name="android:scaleType">fitCenter</item>
<item name="android:padding">4dp</item>
</style>
<style name="AlertDialogTheme" parent="ThemeOverlay.MaterialComponents.Dialog.Alert"> <style name="AlertDialogTheme" parent="ThemeOverlay.MaterialComponents.Dialog.Alert">
<item name="shapeAppearanceMediumComponent">@style/CutShapeAppearance</item> <item name="shapeAppearanceMediumComponent">@style/CutShapeAppearance</item>
<item name="buttonBarPositiveButtonStyle">@style/Alert.Button.Positive</item> <item name="buttonBarPositiveButtonStyle">@style/Alert.Button.Positive</item>