mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-24 18:04:33 +01:00
Apk size reduced,MVVM Approach,UI Changes
This commit is contained in:
parent
eecb7c3b43
commit
5f1176ead5
@ -90,6 +90,7 @@ dependencies {
|
||||
|
||||
implementation "androidx.room:room-runtime:2.2.5"
|
||||
implementation project(path: ':mobile-ffmpeg')
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
kapt "androidx.room:room-compiler:2.2.5"
|
||||
implementation "androidx.room:room-ktx:2.2.5"
|
||||
implementation ("com.github.bumptech.glide:recyclerview-integration:4.11.0") {
|
||||
|
@ -36,7 +36,7 @@ import com.github.javiersantos.appupdater.AppUpdater
|
||||
import com.github.javiersantos.appupdater.enums.UpdateFrom
|
||||
import com.github.kiulian.downloader.YoutubeDownloader
|
||||
import com.shabinder.spotiflyer.databinding.MainActivityBinding
|
||||
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper
|
||||
import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper
|
||||
import com.shabinder.spotiflyer.utils.SpotifyService
|
||||
import com.shabinder.spotiflyer.utils.SpotifyServiceToken
|
||||
import com.shabinder.spotiflyer.utils.createDirectory
|
||||
@ -74,7 +74,7 @@ class MainActivity : AppCompatActivity(){
|
||||
sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)
|
||||
sharedPref = this.getPreferences(Context.MODE_PRIVATE)
|
||||
//starting Notification and Downloader Service!
|
||||
DownloadHelper.startService(this)
|
||||
SpotifyDownloadHelper.startService(this)
|
||||
|
||||
/* if(sharedPref?.contains("token")!! && (sharedPref?.getLong("time",System.currentTimeMillis()/1000/60/60)!! < (System.currentTimeMillis()/1000/60/60)) ){
|
||||
val savedToken = sharedPref?.getString("token","error")!!
|
||||
@ -85,7 +85,7 @@ class MainActivity : AppCompatActivity(){
|
||||
implementSpotifyService(savedToken)
|
||||
}else{authenticateSpotify()}*/
|
||||
|
||||
if(sharedViewModel.spotifyService == null){
|
||||
if(sharedViewModel.spotifyService.value == null){
|
||||
authenticateSpotify()
|
||||
}else{
|
||||
implementSpotifyService(sharedViewModel.accessToken.value!!)
|
||||
@ -102,7 +102,7 @@ class MainActivity : AppCompatActivity(){
|
||||
|
||||
//Object to download From Youtube {"https://github.com/sealedtx/java-youtube-downloader"}
|
||||
ytDownloader = YoutubeDownloader()
|
||||
sharedViewModel.ytDownloader = ytDownloader
|
||||
sharedViewModel.ytDownloader.value = ytDownloader
|
||||
|
||||
handleIntentFromExternalActivity()
|
||||
}
|
||||
@ -168,7 +168,7 @@ class MainActivity : AppCompatActivity(){
|
||||
.build()
|
||||
|
||||
spotifyService = retrofit.create(SpotifyService::class.java)
|
||||
sharedViewModel.spotifyService = spotifyService
|
||||
sharedViewModel.spotifyService.value = spotifyService
|
||||
}
|
||||
|
||||
private fun getSpotifyToken(){
|
||||
@ -283,12 +283,12 @@ class MainActivity : AppCompatActivity(){
|
||||
}
|
||||
|
||||
private fun createDir() {
|
||||
createDirectory(DownloadHelper.defaultDir)
|
||||
createDirectory(DownloadHelper.defaultDir+".Images/")
|
||||
createDirectory(DownloadHelper.defaultDir+"Tracks/")
|
||||
createDirectory(DownloadHelper.defaultDir+"Albums/")
|
||||
createDirectory(DownloadHelper.defaultDir+"Playlists/")
|
||||
createDirectory(DownloadHelper.defaultDir+"YT_Downloads/")
|
||||
createDirectory(SpotifyDownloadHelper.defaultDir)
|
||||
createDirectory(SpotifyDownloadHelper.defaultDir+".Images/")
|
||||
createDirectory(SpotifyDownloadHelper.defaultDir+"Tracks/")
|
||||
createDirectory(SpotifyDownloadHelper.defaultDir+"Albums/")
|
||||
createDirectory(SpotifyDownloadHelper.defaultDir+"Playlists/")
|
||||
createDirectory(SpotifyDownloadHelper.defaultDir+"YT_Downloads/")
|
||||
}
|
||||
|
||||
private fun checkIfLatestVersion() {
|
||||
|
@ -24,9 +24,6 @@ import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.github.kiulian.downloader.YoutubeDownloader
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.shabinder.spotiflyer.models.Album
|
||||
import com.shabinder.spotiflyer.models.Playlist
|
||||
import com.shabinder.spotiflyer.models.Track
|
||||
import com.shabinder.spotiflyer.utils.SpotifyService
|
||||
import com.shreyaspatil.EasyUpiPayment.EasyUpiPayment
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@ -36,11 +33,11 @@ import java.io.File
|
||||
|
||||
class SharedViewModel : ViewModel() {
|
||||
var intentString = ""
|
||||
var accessToken = MutableLiveData<String>().apply { value = "" }
|
||||
var spotifyService : SpotifyService? = null
|
||||
var ytDownloader : YoutubeDownloader? = null
|
||||
var isConnected = MutableLiveData<Boolean>().apply { value = false }
|
||||
var spotifyService = MutableLiveData<SpotifyService>()
|
||||
var ytDownloader = MutableLiveData<YoutubeDownloader>()
|
||||
var easyUpiPayment: EasyUpiPayment? = null
|
||||
var accessToken = MutableLiveData<String>().apply { value = "" }
|
||||
var isConnected = MutableLiveData<Boolean>().apply { value = false }
|
||||
val defaultDir = Environment.DIRECTORY_MUSIC + File.separator + "SpotiFlyer" + File.separator + ".Images" + File.separator
|
||||
|
||||
|
||||
@ -48,17 +45,6 @@ class SharedViewModel : ViewModel() {
|
||||
|
||||
val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
|
||||
|
||||
suspend fun getTrackDetails(trackLink:String): Track?{
|
||||
return spotifyService?.getTrack(trackLink)
|
||||
}
|
||||
suspend fun getAlbumDetails(albumLink:String): Album?{
|
||||
return spotifyService?.getAlbum(albumLink)
|
||||
}
|
||||
suspend fun getPlaylistDetails(link:String): Playlist?{
|
||||
return spotifyService?.getPlaylist(link)
|
||||
}
|
||||
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
viewModelJob.cancel()
|
||||
|
@ -35,25 +35,25 @@ import com.github.kiulian.downloader.YoutubeDownloader
|
||||
import com.github.kiulian.downloader.model.formats.Format
|
||||
import com.github.kiulian.downloader.model.quality.AudioQuality
|
||||
import com.shabinder.spotiflyer.SharedViewModel
|
||||
import com.shabinder.spotiflyer.fragments.MainFragment
|
||||
import com.shabinder.spotiflyer.models.DownloadObject
|
||||
import com.shabinder.spotiflyer.models.Track
|
||||
import com.shabinder.spotiflyer.ui.spotify.SpotifyFragment
|
||||
import com.shabinder.spotiflyer.worker.ForegroundService
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
object DownloadHelper {
|
||||
object SpotifyDownloadHelper {
|
||||
var webView:WebView? = null
|
||||
var context : Context? = null
|
||||
var statusBar:TextView? = null
|
||||
val defaultDir = Environment.DIRECTORY_MUSIC + File.separator + "SpotiFlyer" + File.separator
|
||||
private var downloadList = arrayListOf<DownloadObject>()
|
||||
var sharedViewModel:SharedViewModel? = null
|
||||
private var isBrowserLoading = false
|
||||
private var total = 0
|
||||
private var Processed = 0
|
||||
private var listProcessed:Boolean = false
|
||||
var youtubeList = mutableListOf<YoutubeRequest>()
|
||||
|
||||
/**
|
||||
@ -64,32 +64,27 @@ object DownloadHelper {
|
||||
subFolder: String?,
|
||||
trackList: List<Track>, ytDownloader: YoutubeDownloader?) {
|
||||
withContext(Dispatchers.Main){
|
||||
var size = trackList.size
|
||||
total += size
|
||||
animateStatusBar()
|
||||
total += trackList.size // Adding New Download List Count to StatusBar
|
||||
trackList.forEach {
|
||||
size--
|
||||
val outputFile:String = Environment.getExternalStorageDirectory().toString() + File.separator +
|
||||
defaultDir + removeIllegalChars(type) + File.separator + (if(subFolder == null){""}else{ removeIllegalChars(subFolder) + File.separator} + removeIllegalChars(it.name!!)+".mp3")
|
||||
if(File(outputFile).exists()){//Download Already Present!!
|
||||
Processed++
|
||||
updateStatusBar()
|
||||
}else{
|
||||
if(isBrowserLoading){
|
||||
if(size == 0){
|
||||
youtubeList.add(YoutubeRequest(null,type,subFolder,ytDownloader,"${it.name} ${it.artists?.get(0)?.name ?:""}", it ,0 ))
|
||||
}else{
|
||||
if(isBrowserLoading){//WebView Busy!!
|
||||
if (listProcessed){//Previous List request progress check
|
||||
getYTLink(null,type,subFolder,ytDownloader,"${it.name} ${it.artists?.get(0)?.name ?:""}", it)
|
||||
listProcessed = false//Notifying A list Processing Started
|
||||
}else{//Adding Requests to a Queue
|
||||
youtubeList.add(YoutubeRequest(null,type,subFolder,ytDownloader,"${it.name} ${it.artists?.get(0)?.name ?:""}", it))
|
||||
}
|
||||
}else{
|
||||
if(size == 0){
|
||||
getYTLink(null,type,subFolder,ytDownloader,"${it.name} ${it.artists?.get(0)?.name ?:""}", it ,0 )
|
||||
}else{
|
||||
getYTLink(null,type,subFolder,ytDownloader,"${it.name} ${it.artists?.get(0)?.name ?:""}", it)
|
||||
}
|
||||
}
|
||||
updateStatusBar()
|
||||
}
|
||||
}
|
||||
animateStatusBar()
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,13 +92,12 @@ object DownloadHelper {
|
||||
|
||||
//TODO CleanUp here and there!!
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
suspend fun getYTLink(mainFragment: MainFragment? = null,
|
||||
suspend fun getYTLink(spotifyFragment: SpotifyFragment? = null,
|
||||
type:String,
|
||||
subFolder:String?,
|
||||
ytDownloader: YoutubeDownloader?,
|
||||
searchQuery: String,
|
||||
track: Track,
|
||||
index: Int? = null){
|
||||
track: Track){
|
||||
val searchText = searchQuery.replace("\\s".toRegex(), "+")
|
||||
val url = "https://www.youtube.com/results?sp=EgIQAQ%253D%253D&q=$searchText"
|
||||
Log.i("DH YT LINK ",url)
|
||||
@ -122,15 +116,16 @@ object DownloadHelper {
|
||||
val id = value!!.substringAfterLast("=", "error").replace("\"","")
|
||||
Log.i("YT-id",id)
|
||||
if(id !="error"){//Link extracting error
|
||||
mainFragment?.showToast("Starting Download")
|
||||
spotifyFragment?.showToast("Starting Download")
|
||||
Processed++
|
||||
if(Processed == total)listProcessed = true //List Processesd
|
||||
updateStatusBar()
|
||||
downloadFile(subFolder, type, track, index,ytDownloader,id)
|
||||
downloadFile(subFolder, type, track,ytDownloader,id)
|
||||
}
|
||||
if(youtubeList.isNotEmpty()){
|
||||
val request = youtubeList[0]
|
||||
sharedViewModel!!.uiScope.launch {
|
||||
getYTLink(request.mainFragment,request.type,request.subFolder,request.ytDownloader,request.searchQuery,request.track,request.index)
|
||||
getYTLink(request.spotifyFragment,request.type,request.subFolder,request.ytDownloader,request.searchQuery,request.track)
|
||||
}
|
||||
youtubeList.remove(request)
|
||||
if(youtubeList.size == 0){//list processing completed , webView is free again!
|
||||
@ -145,41 +140,17 @@ object DownloadHelper {
|
||||
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
fun applyWebViewSettings(webView: WebView) {
|
||||
val desktopUserAgent =
|
||||
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.4) Gecko/20100101 Firefox/4.0"
|
||||
val mobileUserAgent =
|
||||
"Mozilla/5.0 (Linux; U; Android 4.4; en-us; Nexus 4 Build/JOP24G) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30"
|
||||
|
||||
//Choose Mobile/Desktop client.
|
||||
webView.settings.userAgentString = desktopUserAgent
|
||||
webView.settings.loadWithOverviewMode = true
|
||||
webView.settings.loadWithOverviewMode = true
|
||||
webView.settings.builtInZoomControls = true
|
||||
webView.settings.setSupportZoom(true)
|
||||
webView.isScrollbarFadingEnabled = false
|
||||
webView.scrollBarStyle = WebView.SCROLLBARS_OUTSIDE_OVERLAY
|
||||
webView.settings.displayZoomControls = false
|
||||
webView.settings.useWideViewPort = true
|
||||
webView.settings.javaScriptEnabled = true
|
||||
webView.settings.loadsImagesAutomatically = false
|
||||
webView.settings.blockNetworkImage = true
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
webView.settings.safeBrowsingEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateStatusBar() {
|
||||
statusBar!!.visibility = View.VISIBLE
|
||||
statusBar?.text = "Total: $total Processed: $Processed"
|
||||
}
|
||||
|
||||
|
||||
fun downloadFile(subFolder: String?, type: String, track:Track, index:Int? = null,ytDownloader: YoutubeDownloader?,id: String) {
|
||||
fun downloadFile(subFolder: String?, type: String, track:Track, ytDownloader: YoutubeDownloader?, id: String) {
|
||||
sharedViewModel!!.uiScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
val video = ytDownloader?.getVideo(id)
|
||||
val detail = video?.details()
|
||||
val format:Format? =try {
|
||||
video?.findAudioWithQuality(AudioQuality.high)?.get(0) as Format
|
||||
}catch (e:java.lang.IndexOutOfBoundsException){
|
||||
@ -196,9 +167,7 @@ object DownloadHelper {
|
||||
}
|
||||
format?.let {
|
||||
val url:String = format.url()
|
||||
|
||||
Log.i("DHelper Link Found", url)
|
||||
|
||||
// Log.i("DHelper Link Found", url)
|
||||
val outputFile:String = Environment.getExternalStorageDirectory().toString() + File.separator +
|
||||
defaultDir + removeIllegalChars(type) + File.separator + (if(subFolder == null){""}else{ removeIllegalChars(subFolder) + File.separator} + removeIllegalChars(track.name!!)+".m4a")
|
||||
|
||||
@ -209,17 +178,6 @@ object DownloadHelper {
|
||||
)
|
||||
Log.i("DH",outputFile)
|
||||
startService(context!!, downloadObject)
|
||||
|
||||
/*if(index==null){
|
||||
downloadList.add(downloadObject)
|
||||
}else{
|
||||
downloadList.add(downloadObject)
|
||||
startService(context!!, downloadList)
|
||||
Log.i("DH No of Songs", downloadList.size.toString())
|
||||
downloadList = arrayListOf()
|
||||
}*/
|
||||
// downloadList.add(downloadObject)
|
||||
// downloadList = arrayListOf()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -281,10 +239,33 @@ object DownloadHelper {
|
||||
anim.repeatCount = Animation.INFINITE
|
||||
statusBar?.animation = anim
|
||||
}
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
fun applyWebViewSettings(webView: WebView) {
|
||||
val desktopUserAgent =
|
||||
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.4) Gecko/20100101 Firefox/4.0"
|
||||
val mobileUserAgent =
|
||||
"Mozilla/5.0 (Linux; U; Android 4.4; en-us; Nexus 4 Build/JOP24G) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30"
|
||||
|
||||
//Choose Mobile/Desktop client.
|
||||
webView.settings.userAgentString = desktopUserAgent
|
||||
webView.settings.loadWithOverviewMode = true
|
||||
webView.settings.loadWithOverviewMode = true
|
||||
webView.settings.builtInZoomControls = true
|
||||
webView.settings.setSupportZoom(true)
|
||||
webView.isScrollbarFadingEnabled = false
|
||||
webView.scrollBarStyle = WebView.SCROLLBARS_OUTSIDE_OVERLAY
|
||||
webView.settings.displayZoomControls = false
|
||||
webView.settings.useWideViewPort = true
|
||||
webView.settings.javaScriptEnabled = true
|
||||
webView.settings.loadsImagesAutomatically = false
|
||||
webView.settings.blockNetworkImage = true
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
webView.settings.safeBrowsingEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
data class YoutubeRequest(
|
||||
val mainFragment: MainFragment? = null,
|
||||
val spotifyFragment: SpotifyFragment? = null,
|
||||
val type:String,
|
||||
val subFolder:String?,
|
||||
val ytDownloader: YoutubeDownloader?,
|
@ -1,445 +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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.shabinder.spotiflyer.fragments
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.ValueCallback
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import android.widget.Toast
|
||||
import androidx.core.net.toUri
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.shabinder.spotiflyer.MainActivity
|
||||
import com.shabinder.spotiflyer.R
|
||||
import com.shabinder.spotiflyer.SharedViewModel
|
||||
import com.shabinder.spotiflyer.databinding.MainFragmentBinding
|
||||
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper
|
||||
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper.applyWebViewSettings
|
||||
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper.downloadAllTracks
|
||||
import com.shabinder.spotiflyer.models.Track
|
||||
import com.shabinder.spotiflyer.recyclerView.TrackListAdapter
|
||||
import com.shabinder.spotiflyer.utils.SpotifyService
|
||||
import com.shabinder.spotiflyer.utils.bindImage
|
||||
import com.shabinder.spotiflyer.utils.copyTo
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
class MainFragment : Fragment() {
|
||||
private lateinit var binding:MainFragmentBinding
|
||||
private lateinit var mainViewModel: MainViewModel
|
||||
private lateinit var sharedViewModel: SharedViewModel
|
||||
private lateinit var adapter:TrackListAdapter
|
||||
private var spotifyService : SpotifyService? = null
|
||||
private var type:String = ""
|
||||
private var spotifyLink = ""
|
||||
private var i: Intent? = null
|
||||
private var webView: WebView? = null
|
||||
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = DataBindingUtil.inflate(inflater,R.layout.main_fragment,container,false)
|
||||
webView = binding.webView
|
||||
DownloadHelper.webView = binding.webView
|
||||
DownloadHelper.context = requireContext()
|
||||
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
|
||||
mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
|
||||
spotifyService = sharedViewModel.spotifyService
|
||||
DownloadHelper.sharedViewModel = sharedViewModel
|
||||
DownloadHelper.statusBar = binding.StatusBar
|
||||
|
||||
setUpUsageText()
|
||||
openSpotifyButton()
|
||||
openGithubButton()
|
||||
openInstaButton()
|
||||
|
||||
binding.btnDonate.setOnClickListener {
|
||||
sharedViewModel.easyUpiPayment?.startPayment()
|
||||
}
|
||||
|
||||
binding.btnSearch.setOnClickListener {
|
||||
val link = binding.linkSearch.text.toString()
|
||||
if(link.contains("open.spotify",true)){
|
||||
spotifySearch()
|
||||
}
|
||||
if(link.contains("youtube.com",true) || link.contains("youtu.be",true) ){
|
||||
youtubeSearch()
|
||||
}
|
||||
|
||||
}
|
||||
handleIntent()
|
||||
//Handling Device Configuration Change
|
||||
if(savedInstanceState != null && savedInstanceState["searchLink"].toString() != ""){
|
||||
binding.linkSearch.setText(savedInstanceState["searchLink"].toString())
|
||||
binding.btnSearch.performClick()
|
||||
setUiVisibility()
|
||||
}
|
||||
return binding.root
|
||||
}
|
||||
|
||||
private fun youtubeSearch() {
|
||||
val youtubeLink = binding.linkSearch.text.toString()
|
||||
var title = ""
|
||||
val link = youtubeLink.removePrefix("https://").removePrefix("http://")
|
||||
val sampleDomain1 = "youtube.com"
|
||||
val sampleDomain2 = "youtu.be"
|
||||
if(!link.contains("playlist",true)){
|
||||
var searchId = "error"
|
||||
if(link.contains(sampleDomain1,true) ){
|
||||
searchId = link.substringAfterLast("=","error")
|
||||
}
|
||||
if(link.contains(sampleDomain2,true) && !link.contains("playlist",true) ){
|
||||
searchId = link.substringAfterLast("/","error")
|
||||
}
|
||||
if(searchId != "error"){
|
||||
val coverLink = "https://i.ytimg.com/vi/$searchId/maxresdefault.jpg"
|
||||
applyWebViewSettings(webView!!)
|
||||
sharedViewModel.uiScope.launch {
|
||||
webView!!.loadUrl(youtubeLink)
|
||||
webView!!.webViewClient = object : WebViewClient() {
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
super.onPageFinished(view, url)
|
||||
view?.evaluateJavascript(
|
||||
"document.getElementsByTagName(\"h1\")[0].textContent"
|
||||
,object : ValueCallback<String> {
|
||||
override fun onReceiveValue(value: String?) {
|
||||
title = DownloadHelper.removeIllegalChars(value.toString()).toString()
|
||||
Log.i("YT-id", title)
|
||||
Log.i("YT-id", value)
|
||||
Log.i("YT-id", coverLink)
|
||||
setUiVisibility()
|
||||
bindImage(binding.imageView,coverLink)
|
||||
binding.btnDownloadAll.setOnClickListener {
|
||||
showToast("Starting Download in Few Seconds")
|
||||
//TODO Clean This Code!
|
||||
DownloadHelper.downloadFile(null,"YT_Downloads",Track(name = value,ytCoverUrl = coverLink),0,sharedViewModel.ytDownloader,searchId)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}else(showToast("Your Youtube Link is not of a Video!!"))
|
||||
}
|
||||
|
||||
private fun spotifySearch(){
|
||||
spotifyLink = binding.linkSearch.text.toString()
|
||||
|
||||
val link = spotifyLink.substringAfterLast('/', "Error").substringBefore('?')
|
||||
type = spotifyLink.substringBeforeLast('/', "Error").substringAfterLast('/')
|
||||
|
||||
Log.i("Fragment", "$type : $link")
|
||||
|
||||
if(sharedViewModel.spotifyService == null && !isOnline()){
|
||||
(activity as MainActivity).authenticateSpotify()
|
||||
}
|
||||
|
||||
if (type == "Error" || link == "Error") {
|
||||
showToast("Please Check Your Link!")
|
||||
} else if(!isOnline()){
|
||||
sharedViewModel.showAlertDialog(resources,requireContext())
|
||||
} else {
|
||||
adapter = TrackListAdapter()
|
||||
binding.trackList.adapter = adapter
|
||||
adapter.sharedViewModel = sharedViewModel
|
||||
adapter.mainFragment = this
|
||||
setUiVisibility()
|
||||
|
||||
if(mainViewModel.searchLink == spotifyLink){
|
||||
//it's a Device Configuration Change
|
||||
adapterConfig(mainViewModel.trackList)
|
||||
sharedViewModel.uiScope.launch {
|
||||
bindImage(binding.imageView,mainViewModel.coverUrl)
|
||||
}
|
||||
}else{
|
||||
when (type) {
|
||||
"track" -> {
|
||||
mainViewModel.searchLink = spotifyLink
|
||||
sharedViewModel.uiScope.launch {
|
||||
val trackObject = sharedViewModel.getTrackDetails(link)
|
||||
val trackList = mutableListOf<Track>()
|
||||
trackList.add(trackObject!!)
|
||||
mainViewModel.trackList = trackList
|
||||
mainViewModel.coverUrl = trackObject.album!!.images?.get(0)!!.url!!
|
||||
bindImage(binding.imageView,mainViewModel.coverUrl)
|
||||
adapterConfig(trackList)
|
||||
|
||||
binding.btnDownloadAll.setOnClickListener {
|
||||
showToast("Starting Download in Few Seconds")
|
||||
sharedViewModel.uiScope.launch {
|
||||
downloadAllTracks(
|
||||
"Tracks",
|
||||
null,
|
||||
trackList,
|
||||
sharedViewModel.ytDownloader
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
"album" -> {
|
||||
mainViewModel.searchLink = spotifyLink
|
||||
sharedViewModel.uiScope.launch {
|
||||
val albumObject = sharedViewModel.getAlbumDetails(link)
|
||||
val trackList = mutableListOf<Track>()
|
||||
albumObject!!.tracks?.items?.forEach { trackList.add(it) }
|
||||
mainViewModel.trackList = trackList
|
||||
mainViewModel.coverUrl = albumObject.images?.get(0)!!.url!!
|
||||
bindImage(binding.imageView,mainViewModel.coverUrl)
|
||||
adapter.isAlbum = true
|
||||
adapterConfig(trackList)
|
||||
binding.btnDownloadAll.setOnClickListener {
|
||||
showToast("Starting Download in Few Seconds")
|
||||
sharedViewModel.uiScope.launch {
|
||||
loadAllImages(trackList)
|
||||
downloadAllTracks(
|
||||
"Albums",
|
||||
albumObject.name,
|
||||
trackList,
|
||||
sharedViewModel.ytDownloader
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
"playlist" -> {
|
||||
mainViewModel.searchLink = spotifyLink
|
||||
sharedViewModel.uiScope.launch {
|
||||
val playlistObject = sharedViewModel.getPlaylistDetails(link)
|
||||
val trackList = mutableListOf<Track>()
|
||||
playlistObject!!.tracks?.items!!.forEach { trackList.add(it.track!!) }
|
||||
mainViewModel.trackList = trackList
|
||||
mainViewModel.coverUrl = playlistObject.images?.get(0)!!.url!!
|
||||
bindImage(binding.imageView,mainViewModel.coverUrl)
|
||||
adapterConfig(trackList)
|
||||
binding.btnDownloadAll.setOnClickListener {
|
||||
showToast("Starting Download in Few Seconds")
|
||||
sharedViewModel.uiScope.launch {
|
||||
loadAllImages(trackList)
|
||||
downloadAllTracks(
|
||||
"Playlists",
|
||||
playlistObject.name,
|
||||
trackList,
|
||||
sharedViewModel.ytDownloader
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"episode" -> {
|
||||
showToast("Implementation Pending")
|
||||
}
|
||||
"show" -> {
|
||||
showToast("Implementation Pending ")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to fetch all Images for using in mp3 tag.
|
||||
**/
|
||||
private fun loadAllImages(trackList: List<Track>) {
|
||||
trackList.forEach {
|
||||
val imgUrl = it.album!!.images?.get(0)?.url
|
||||
imgUrl?.let {
|
||||
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
|
||||
Glide
|
||||
.with(requireContext())
|
||||
.asFile()
|
||||
.load(imgUri)
|
||||
.listener(object: RequestListener<File> {
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any?,
|
||||
target: Target<File>?,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
Log.i("Glide","LoadFailed")
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: File?,
|
||||
model: Any?,
|
||||
target: Target<File>?,
|
||||
dataSource: DataSource?,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
sharedViewModel.uiScope.launch {
|
||||
withContext(Dispatchers.IO){
|
||||
try {
|
||||
val file = File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
DownloadHelper.defaultDir+".Images/" + imgUrl.substringAfterLast('/') + ".jpeg"
|
||||
)
|
||||
resource?.copyTo(file)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}).submit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementing button to Open Spotify App
|
||||
**/
|
||||
private fun openSpotifyButton() {
|
||||
val manager: PackageManager = requireActivity().packageManager
|
||||
try {
|
||||
i = manager.getLaunchIntentForPackage("com.spotify.music")
|
||||
if (i == null) throw PackageManager.NameNotFoundException()
|
||||
i?.addCategory(Intent.CATEGORY_LAUNCHER)
|
||||
binding.btnOpenSpotify.setOnClickListener { startActivity(i) }
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
binding.textView.text = getString(R.string.spotify_not_installed)
|
||||
binding.btnOpenSpotify.text = getString(R.string.spotify_web_link)
|
||||
val uri: Uri =
|
||||
Uri.parse("http://open.spotify.com")
|
||||
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||
binding.btnOpenSpotify.setOnClickListener {
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openGithubButton() {
|
||||
val uri: Uri =
|
||||
Uri.parse("http://github.com/Shabinder/SpotiFlyer")
|
||||
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||
binding.btnGithub.setOnClickListener {
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
private fun openInstaButton() {
|
||||
val uri: Uri =
|
||||
Uri.parse("http://www.instagram.com/mr.shabinder")
|
||||
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||
binding.developerInsta.setOnClickListener {
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configure Recycler View Adapter
|
||||
**/
|
||||
private fun adapterConfig(trackList: List<Track>){
|
||||
adapter.trackList = trackList.toList()
|
||||
adapter.totalItems = trackList.size
|
||||
adapter.mainFragment = this
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
/**
|
||||
* Make Ui elements Visible
|
||||
**/
|
||||
private fun setUiVisibility() {
|
||||
binding.btnDownloadAll.visibility =View.VISIBLE
|
||||
binding.titleView.visibility = View.GONE
|
||||
binding.openSpotify.visibility = View.GONE
|
||||
binding.trackList.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Intent If there is any!
|
||||
**/
|
||||
private fun handleIntent() {
|
||||
binding.linkSearch.setText(sharedViewModel.intentString)
|
||||
sharedViewModel.accessToken.observe(viewLifecycleOwner, Observer {
|
||||
//Waiting for Authentication to Finish with Spotify
|
||||
if (it != ""){
|
||||
if(sharedViewModel.intentString != ""){
|
||||
binding.btnSearch.performClick()
|
||||
setUiVisibility()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setUpUsageText() {
|
||||
val spanStringBuilder = SpannableStringBuilder()
|
||||
spanStringBuilder.append(getText(R.string.d_one)).append("\n")
|
||||
spanStringBuilder.append(getText(R.string.d_two)).append("\n")
|
||||
spanStringBuilder.append(getText(R.string.d_three)).append("\n")
|
||||
binding.usage.text = spanStringBuilder
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Util. Function to create toasts!
|
||||
**/
|
||||
fun showToast(message:String){
|
||||
Toast.makeText(context,message,Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Util. Function To Check Connection Status
|
||||
**/
|
||||
private fun isOnline(): Boolean {
|
||||
val cm =
|
||||
requireActivity().getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
val netInfo = cm.activeNetworkInfo
|
||||
return netInfo != null && netInfo.isConnectedOrConnecting
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putCharSequence("searchLink",mainViewModel.searchLink)
|
||||
}
|
||||
}
|
@ -1,27 +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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.shabinder.spotiflyer.fragments
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.shabinder.spotiflyer.models.Track
|
||||
|
||||
class MainViewModel: ViewModel() {
|
||||
var searchLink: String = ""
|
||||
var trackList = mutableListOf<Track>()
|
||||
var coverUrl: String = ""
|
||||
}
|
@ -22,7 +22,8 @@ import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class DownloadObject(
|
||||
var track: Track,
|
||||
var ytVideo: YTTrack?=null,
|
||||
var track: Track?=null,
|
||||
var url:String,
|
||||
var outputDir:String
|
||||
):Parcelable
|
@ -26,20 +26,20 @@ import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.shabinder.spotiflyer.R
|
||||
import com.shabinder.spotiflyer.SharedViewModel
|
||||
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper.getYTLink
|
||||
import com.shabinder.spotiflyer.fragments.MainFragment
|
||||
import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper.getYTLink
|
||||
import com.shabinder.spotiflyer.models.Track
|
||||
import com.shabinder.spotiflyer.ui.spotify.SpotifyFragment
|
||||
import com.shabinder.spotiflyer.utils.bindImage
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class TrackListAdapter:RecyclerView.Adapter<TrackListAdapter.ViewHolder>() {
|
||||
class SpotifyTrackListAdapter:RecyclerView.Adapter<SpotifyTrackListAdapter.ViewHolder>() {
|
||||
|
||||
var trackList = listOf<Track>()
|
||||
var totalItems:Int = 0
|
||||
var sharedViewModel = SharedViewModel()
|
||||
var isAlbum:Boolean = false
|
||||
var mainFragment:MainFragment? = null
|
||||
var spotifyFragment: SpotifyFragment? = null
|
||||
|
||||
override fun getItemCount():Int = totalItems
|
||||
|
||||
@ -63,7 +63,7 @@ class TrackListAdapter:RecyclerView.Adapter<TrackListAdapter.ViewHolder>() {
|
||||
holder.duration.text = "${item.duration_ms/1000/60} minutes, ${(item.duration_ms/1000)%60} sec"
|
||||
holder.downloadBtn.setOnClickListener{
|
||||
sharedViewModel.uiScope.launch {
|
||||
getYTLink(mainFragment,"Tracks",null,sharedViewModel.ytDownloader,"${item.name} ${item.artists?.get(0)!!.name?:""}",track = item,index = 0)
|
||||
getYTLink(spotifyFragment,"Tracks",null,sharedViewModel.ytDownloader.value,"${item.name} ${item.artists?.get(0)!!.name?:""}",track = item)
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ class SplashScreen : AppCompatActivity(){
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.splash_screen)
|
||||
|
||||
val splashTimeout = 600
|
||||
val splashTimeout = 500
|
||||
val homeIntent = Intent(this@SplashScreen, MainActivity::class.java)
|
||||
Handler().postDelayed({
|
||||
//TODO:Bring Initial Setup here
|
||||
|
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.shabinder.spotiflyer.ui.spotify
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.WebView
|
||||
import android.widget.Toast
|
||||
import androidx.core.net.toUri
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.shabinder.spotiflyer.MainActivity
|
||||
import com.shabinder.spotiflyer.R
|
||||
import com.shabinder.spotiflyer.SharedViewModel
|
||||
import com.shabinder.spotiflyer.databinding.SpotifyFragmentBinding
|
||||
import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper
|
||||
import com.shabinder.spotiflyer.models.Track
|
||||
import com.shabinder.spotiflyer.recyclerView.SpotifyTrackListAdapter
|
||||
import com.shabinder.spotiflyer.utils.bindImage
|
||||
import com.shabinder.spotiflyer.utils.copyTo
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
class SpotifyFragment : Fragment() {
|
||||
private lateinit var binding:SpotifyFragmentBinding
|
||||
private lateinit var spotifyViewModel: SpotifyViewModel
|
||||
private lateinit var sharedViewModel: SharedViewModel
|
||||
private lateinit var adapterSpotify:SpotifyTrackListAdapter
|
||||
private var webView: WebView? = null
|
||||
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = DataBindingUtil.inflate(inflater,R.layout.spotify_fragment,container,false)
|
||||
adapterSpotify = SpotifyTrackListAdapter()
|
||||
initializeAll()
|
||||
initializeLiveDataObservers()
|
||||
|
||||
val args = SpotifyFragmentArgs.fromBundle(requireArguments())
|
||||
val spotifyLink = args.link
|
||||
|
||||
val link = spotifyLink.substringAfterLast('/', "Error").substringBefore('?')
|
||||
val type = spotifyLink.substringBeforeLast('/', "Error").substringAfterLast('/')
|
||||
|
||||
Log.i("Fragment", "$type : $link")
|
||||
|
||||
if(sharedViewModel.spotifyService.value == null && isNotOnline()){//Authentication pending!!
|
||||
(activity as MainActivity).authenticateSpotify()
|
||||
}
|
||||
if(!isNotOnline()){//Device Offline
|
||||
sharedViewModel.showAlertDialog(resources,requireContext())
|
||||
}else if (type == "Error" || link == "Error") {//Incorrect Link
|
||||
showToast("Please Check Your Link!")
|
||||
}else if(spotifyLink.contains("open.spotify",true)){//Link Validation!!
|
||||
if(type == "episode" || type == "show"){//TODO Implementation
|
||||
showToast("Implementing Soon, Stay Tuned!")
|
||||
}
|
||||
else{
|
||||
spotifyViewModel.spotifySearch(type,link)
|
||||
if(type=="album")adapterSpotify.isAlbum = true
|
||||
binding.btnDownloadAllSpotify.setOnClickListener {
|
||||
showToast("Starting Download in Few Seconds")
|
||||
loadAllImages(spotifyViewModel.trackList.value!!)
|
||||
spotifyViewModel.uiScope.launch {
|
||||
SpotifyDownloadHelper.downloadAllTracks(
|
||||
spotifyViewModel.folderType,
|
||||
spotifyViewModel.subFolder,
|
||||
spotifyViewModel.trackList.value!!,
|
||||
spotifyViewModel.ytDownloader
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return binding.root
|
||||
}
|
||||
|
||||
/**
|
||||
*Live Data Observers
|
||||
**/
|
||||
private fun initializeLiveDataObservers() {
|
||||
/**
|
||||
* CoverUrl Binding Observer!
|
||||
**/
|
||||
spotifyViewModel.coverUrl.observe(viewLifecycleOwner, Observer {
|
||||
if(it!="Loading") bindImage(binding.spotifyCoverImage,it)
|
||||
})
|
||||
|
||||
/**
|
||||
* TrackList Binding Observer!
|
||||
**/
|
||||
spotifyViewModel.trackList.observe(viewLifecycleOwner, Observer {
|
||||
if (it.isNotEmpty()){
|
||||
Log.i("SpotifyFragment","TrackList Fetched!")
|
||||
adapterConfig(it)
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Title Binding Observer!
|
||||
**/
|
||||
spotifyViewModel.title.observe(viewLifecycleOwner, Observer {
|
||||
binding.titleViewSpotify.text = it
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic Initialization
|
||||
**/
|
||||
private fun initializeAll() {
|
||||
webView = binding.webViewSpotify
|
||||
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
|
||||
spotifyViewModel = ViewModelProvider(this).get(SpotifyViewModel::class.java)
|
||||
sharedViewModel.spotifyService.observe(viewLifecycleOwner, Observer {
|
||||
spotifyViewModel.spotifyService = it
|
||||
})
|
||||
sharedViewModel.ytDownloader.observe(viewLifecycleOwner, Observer {
|
||||
spotifyViewModel.ytDownloader = it
|
||||
})
|
||||
SpotifyDownloadHelper.webView = binding.webViewSpotify
|
||||
SpotifyDownloadHelper.context = requireContext()
|
||||
SpotifyDownloadHelper.sharedViewModel = sharedViewModel
|
||||
SpotifyDownloadHelper.statusBar = binding.StatusBarSpotify
|
||||
binding.trackListSpotify.adapter = adapterSpotify
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to fetch all Images for using in mp3 tag.
|
||||
**/
|
||||
private fun loadAllImages(trackList: List<Track>) {
|
||||
trackList.forEach {
|
||||
val imgUrl = it.album!!.images?.get(0)?.url
|
||||
imgUrl?.let {
|
||||
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
|
||||
Glide
|
||||
.with(requireContext())
|
||||
.asFile()
|
||||
.load(imgUri)
|
||||
.listener(object: RequestListener<File> {
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any?,
|
||||
target: Target<File>?,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
Log.i("Glide","LoadFailed")
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: File?,
|
||||
model: Any?,
|
||||
target: Target<File>?,
|
||||
dataSource: DataSource?,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
sharedViewModel.uiScope.launch {
|
||||
withContext(Dispatchers.IO){
|
||||
try {
|
||||
val file = File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
SpotifyDownloadHelper.defaultDir+".Images/" + imgUrl.substringAfterLast('/') + ".jpeg"
|
||||
)
|
||||
resource?.copyTo(file)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}).submit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure Recycler View Adapter
|
||||
**/
|
||||
private fun adapterConfig(trackList: List<Track>){
|
||||
adapterSpotify.trackList = trackList
|
||||
adapterSpotify.totalItems = trackList.size
|
||||
adapterSpotify.spotifyFragment = this
|
||||
adapterSpotify.sharedViewModel = sharedViewModel
|
||||
adapterSpotify.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Util. Function to create toasts!
|
||||
**/
|
||||
fun showToast(message:String){
|
||||
Toast.makeText(context,message,Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Util. Function To Check Connection Status
|
||||
**/
|
||||
private fun isNotOnline(): Boolean {
|
||||
val cm =
|
||||
requireActivity().getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
val netInfo = cm.activeNetworkInfo
|
||||
return netInfo != null && netInfo.isConnectedOrConnecting
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.shabinder.spotiflyer.ui.spotify
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.github.kiulian.downloader.YoutubeDownloader
|
||||
import com.shabinder.spotiflyer.models.Album
|
||||
import com.shabinder.spotiflyer.models.Playlist
|
||||
import com.shabinder.spotiflyer.models.Track
|
||||
import com.shabinder.spotiflyer.utils.SpotifyService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SpotifyViewModel: ViewModel() {
|
||||
|
||||
var folderType:String = ""
|
||||
var subFolder:String = ""
|
||||
var trackList = MutableLiveData<List<Track>>()
|
||||
private val loading = "Loading"
|
||||
var title = MutableLiveData<String>().apply { value = "\"SpotiFlyer\"" }
|
||||
var coverUrl = MutableLiveData<String>().apply { value = loading }
|
||||
var spotifyService : SpotifyService? = null
|
||||
var ytDownloader : YoutubeDownloader? = null
|
||||
|
||||
private var viewModelJob = Job()
|
||||
val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
|
||||
|
||||
|
||||
fun spotifySearch(type:String,link: String){
|
||||
when (type) {
|
||||
"track" -> {
|
||||
uiScope.launch {
|
||||
val trackObject = getTrackDetails(link)
|
||||
val tempTrackList = mutableListOf<Track>()
|
||||
tempTrackList.add(trackObject!!)
|
||||
trackList.value = tempTrackList
|
||||
title.value = trackObject.name
|
||||
coverUrl.value = trackObject.album!!.images?.get(0)!!.url!!
|
||||
folderType = "Tracks"
|
||||
}
|
||||
}
|
||||
|
||||
"album" -> {
|
||||
uiScope.launch {
|
||||
val albumObject = getAlbumDetails(link)
|
||||
val tempTrackList = mutableListOf<Track>()
|
||||
albumObject!!.tracks?.items?.forEach { tempTrackList.add(it) }
|
||||
trackList.value = tempTrackList
|
||||
title.value = albumObject.name
|
||||
coverUrl.value = albumObject.images?.get(0)!!.url!!
|
||||
folderType = "Albums"
|
||||
subFolder = albumObject.name!!
|
||||
}
|
||||
}
|
||||
|
||||
"playlist" -> {
|
||||
uiScope.launch {
|
||||
val playlistObject = getPlaylistDetails(link)
|
||||
val tempTrackList = mutableListOf<Track>()
|
||||
playlistObject!!.tracks?.items?.forEach {
|
||||
it.track?.let { it1 -> tempTrackList.add(it1) }
|
||||
}
|
||||
trackList.value = tempTrackList
|
||||
Log.i("VIEW MODEL",playlistObject.tracks?.items!!.toString())
|
||||
Log.i("VIEW MODEL",trackList.value?.size.toString())
|
||||
title.value = playlistObject.name
|
||||
coverUrl.value = playlistObject.images?.get(0)!!.url!!
|
||||
folderType = "Playlists"
|
||||
subFolder = playlistObject.name!!
|
||||
}
|
||||
}
|
||||
"episode" -> {//TODO
|
||||
}
|
||||
"show" -> {//TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getTrackDetails(trackLink:String): Track?{
|
||||
Log.i("Requesting","https://api.spotify.com/v1/tracks/$trackLink")
|
||||
return spotifyService?.getTrack(trackLink)
|
||||
}
|
||||
private suspend fun getAlbumDetails(albumLink:String): Album?{
|
||||
Log.i("Requesting","https://api.spotify.com/v1/albums/$albumLink")
|
||||
return spotifyService?.getAlbum(albumLink)
|
||||
}
|
||||
private suspend fun getPlaylistDetails(link:String): Playlist?{
|
||||
Log.i("Requesting","https://api.spotify.com/v1/playlists/$link")
|
||||
return spotifyService?.getPlaylist(link)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
viewModelJob.cancel()
|
||||
}
|
||||
|
||||
}
|
@ -30,7 +30,7 @@ import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.shabinder.spotiflyer.R
|
||||
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper
|
||||
import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@ -72,7 +72,7 @@ fun bindImage(imgView: ImageView, imgUrl: String?) {
|
||||
try {
|
||||
val file = File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
DownloadHelper.defaultDir+".Images/" + imgUrl.substringAfterLast('/',imgUrl) + ".jpeg"
|
||||
SpotifyDownloadHelper.defaultDir+".Images/" + imgUrl.substringAfterLast('/',imgUrl) + ".jpeg"
|
||||
) // the File to save , append increasing numeric counter to prevent files from getting overwritten.
|
||||
val options = BitmapFactory.Options()
|
||||
options.inPreferredConfig = Bitmap.Config.ARGB_8888
|
||||
|
@ -41,19 +41,19 @@ import retrofit2.http.*
|
||||
interface SpotifyService {
|
||||
|
||||
@GET("playlists/{playlist_id}")
|
||||
suspend fun getPlaylist(@Path("playlist_id") playlistId: String?): Playlist?
|
||||
suspend fun getPlaylist(@Path("playlist_id") playlistId: String?): Playlist
|
||||
|
||||
@GET("tracks/{id}")
|
||||
suspend fun getTrack(@Path("id") var1: String?): Track?
|
||||
suspend fun getTrack(@Path("id") trackId: String?): Track
|
||||
|
||||
@GET("episodes/{id}")
|
||||
suspend fun getEpisode(@Path("id") var1: String?): Track?
|
||||
suspend fun getEpisode(@Path("id") episodeId: String?): Track
|
||||
|
||||
@GET("shows/{id}")
|
||||
suspend fun getShow(@Path("id") var1: String?): Track?
|
||||
suspend fun getShow(@Path("id") showId: String?): Track
|
||||
|
||||
@GET("albums/{id}")
|
||||
suspend fun getAlbum(@Path("id") var1: String?): Album?
|
||||
suspend fun getAlbum(@Path("id") albumId: String?): Album
|
||||
|
||||
@GET("me")
|
||||
suspend fun getMe(): UserPrivate?
|
||||
|
@ -40,7 +40,7 @@ import com.mpatric.mp3agic.ID3v24Tag
|
||||
import com.mpatric.mp3agic.Mp3File
|
||||
import com.shabinder.spotiflyer.MainActivity
|
||||
import com.shabinder.spotiflyer.R
|
||||
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper
|
||||
import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper
|
||||
import com.shabinder.spotiflyer.models.DownloadObject
|
||||
import com.shabinder.spotiflyer.models.Track
|
||||
import com.tonyodev.fetch2.*
|
||||
@ -74,11 +74,9 @@ class ForegroundService : Service(){
|
||||
)
|
||||
private var wakeLock: PowerManager.WakeLock? = null
|
||||
private var isServiceStarted = false
|
||||
private var messageSnippet1 = ""
|
||||
private var messageSnippet2 = ""
|
||||
private var messageSnippet3 = ""
|
||||
private var messageSnippet4 = ""
|
||||
var notificationLine = 1
|
||||
var notificationLine = 0
|
||||
val messageList = mutableListOf<String>("","","","")
|
||||
private var pendingIntent:PendingIntent? = null
|
||||
|
||||
|
||||
|
||||
@ -89,7 +87,7 @@ class ForegroundService : Service(){
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
val notificationIntent = Intent(this, MainActivity::class.java)
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
pendingIntent = PendingIntent.getActivity(
|
||||
this,
|
||||
0, notificationIntent, 0
|
||||
)
|
||||
@ -120,18 +118,16 @@ class ForegroundService : Service(){
|
||||
}
|
||||
|
||||
val notification = NotificationCompat.Builder(this, channelId)
|
||||
.setOngoing(true)
|
||||
.setContentTitle("SpotiFlyer: Downloading Your Music")
|
||||
.setSubText("Speed: $speed KB/s ")
|
||||
.setNotificationSilent()
|
||||
.setOnlyAlertOnce(true)
|
||||
.setContentText("Total: $total Downloaded: $downloaded Completed:$converted ")
|
||||
.setSmallIcon(R.drawable.down_arrowbw)
|
||||
.setNotificationSilent()
|
||||
.setSubText("Speed: $speed KB/s")
|
||||
.setStyle(NotificationCompat.InboxStyle()
|
||||
.addLine(messageSnippet1)
|
||||
.addLine(messageSnippet2)
|
||||
.addLine(messageSnippet3)
|
||||
.addLine(messageSnippet4))
|
||||
.setBigContentTitle("Total: $total Completed:$converted")
|
||||
.addLine(messageList[0])
|
||||
.addLine(messageList[1])
|
||||
.addLine(messageList[2])
|
||||
.addLine(messageList[3]))
|
||||
.setContentIntent(pendingIntent)
|
||||
.build()
|
||||
startForeground(notificationId, notification)
|
||||
}
|
||||
@ -149,7 +145,7 @@ class ForegroundService : Service(){
|
||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||
// Send a notification that service is started
|
||||
Log.i(tag,"Service Started.")
|
||||
|
||||
startForeground()
|
||||
//do heavy work on a background thread
|
||||
//val list = intent.getSerializableExtra("list") as List<Any?>
|
||||
// val list = intent.getParcelableArrayListExtra<DownloadObject>("list") ?: intent.extras?.getParcelableArrayList<DownloadObject>("list")
|
||||
@ -166,7 +162,7 @@ class ForegroundService : Service(){
|
||||
|
||||
fetch!!.enqueue(request,
|
||||
Func {
|
||||
requestMap[it] = obj.track
|
||||
obj.track?.let { it1 -> requestMap.put(it, it1) }
|
||||
downloadList.remove(obj)
|
||||
Log.i(tag, "Enqueuing Download")
|
||||
},
|
||||
@ -197,7 +193,6 @@ class ForegroundService : Service(){
|
||||
super.onDestroy()
|
||||
if(downloadMap.isEmpty() && converted == total){
|
||||
Log.i(tag,"Service destroyed.")
|
||||
fetch?.close()
|
||||
deleteFile(parentDirectory)
|
||||
releaseWakeLock()
|
||||
stopForeground(true)
|
||||
@ -223,8 +218,11 @@ class ForegroundService : Service(){
|
||||
super.onTaskRemoved(rootIntent)
|
||||
if(downloadMap.isEmpty() && converted == total ){
|
||||
Log.i(tag,"Service Removed.")
|
||||
fetch?.close()
|
||||
stopSelf()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
stopForeground(true)
|
||||
} else {
|
||||
stopSelf()//System will automatically close it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,7 +237,7 @@ class ForegroundService : Service(){
|
||||
if (file.isDirectory) {
|
||||
deleteFile(file)
|
||||
} else if(file.isFile) {
|
||||
if(file.path.toString().substringAfterLast(".") != "mp3"){
|
||||
if(file.path.toString().substringAfterLast(".") != "mp3" && file.path.toString().substringAfterLast(".") != "jpeg"){
|
||||
// Log.i(tag,"deleting ${file.path}")
|
||||
file.delete()
|
||||
}
|
||||
@ -274,21 +272,21 @@ class ForegroundService : Service(){
|
||||
) {
|
||||
val track = requestMap[download.request]
|
||||
when(notificationLine){
|
||||
0 -> {
|
||||
messageList[0] = "Downloading ${track?.name}"
|
||||
notificationLine = 1
|
||||
}
|
||||
1 -> {
|
||||
messageSnippet1 = "Downloading ${track?.name}"
|
||||
messageList[1] = "Downloading ${track?.name}"
|
||||
notificationLine = 2
|
||||
}
|
||||
2 -> {
|
||||
messageSnippet2 = "Downloading ${track?.name}"
|
||||
2-> {
|
||||
messageList[2] = "Downloading ${track?.name}"
|
||||
notificationLine = 3
|
||||
}
|
||||
3-> {
|
||||
messageSnippet3 = "Downloading ${track?.name}"
|
||||
notificationLine = 4
|
||||
}
|
||||
4 -> {
|
||||
messageSnippet4 = "Downloading ${track?.name}"
|
||||
notificationLine = 1
|
||||
3 -> {
|
||||
messageList[3] = "Downloading ${track?.name}"
|
||||
notificationLine = 0
|
||||
}
|
||||
}
|
||||
Log.i(tag,"${track?.name} Download Started")
|
||||
@ -309,6 +307,13 @@ class ForegroundService : Service(){
|
||||
|
||||
override fun onCompleted(download: Download) {
|
||||
val track = requestMap[download.request]
|
||||
|
||||
for (message in messageList){
|
||||
if( message == "Downloading ${track?.name}"){
|
||||
messageList[messageList.indexOf(message)] = ""
|
||||
}
|
||||
}
|
||||
|
||||
serviceScope.launch {
|
||||
try{
|
||||
convertToMp3(download.file, track!!)
|
||||
@ -370,7 +375,7 @@ class ForegroundService : Service(){
|
||||
/**
|
||||
* If fetch Fails , Android Download Manager To RESCUE!!
|
||||
**/
|
||||
fun downloadUsingDM(url:String,outputDir:String,track: Track){
|
||||
fun downloadUsingDM(url:String, outputDir:String, track: Track){
|
||||
val uri = Uri.parse(url)
|
||||
val request = DownloadManager.Request(uri)
|
||||
.setAllowedNetworkTypes(
|
||||
@ -407,7 +412,7 @@ class ForegroundService : Service(){
|
||||
/**
|
||||
*Converting Downloaded Audio (m4a) to Mp3.( Also Applying Metadata)
|
||||
**/
|
||||
fun convertToMp3(filePath: String,track: Track){
|
||||
fun convertToMp3(filePath: String, track: Track){
|
||||
val m4aFile = File(filePath)
|
||||
|
||||
FFmpeg.executeAsync(
|
||||
@ -457,17 +462,16 @@ class ForegroundService : Service(){
|
||||
val mNotificationManager: NotificationManager =
|
||||
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
val notification = NotificationCompat.Builder(this, channelId)
|
||||
.setContentTitle("SpotiFlyer: Downloading Your Music")
|
||||
.setContentText("Total: $total Completed:$converted ")
|
||||
.setSubText("Speed: $speed KB/s ")
|
||||
.setNotificationSilent()
|
||||
.setOnlyAlertOnce(true)
|
||||
.setSmallIcon(R.drawable.down_arrowbw)
|
||||
.setSubText("Speed: $speed KB/s")
|
||||
.setNotificationSilent()
|
||||
.setStyle(NotificationCompat.InboxStyle()
|
||||
.addLine(messageSnippet1)
|
||||
.addLine(messageSnippet2)
|
||||
.addLine(messageSnippet3)
|
||||
.addLine(messageSnippet4))
|
||||
.setBigContentTitle("Total: $total Completed:$converted")
|
||||
.addLine(messageList[0])
|
||||
.addLine(messageList[1])
|
||||
.addLine(messageList[2])
|
||||
.addLine(messageList[3]))
|
||||
.setContentIntent(pendingIntent)
|
||||
.build()
|
||||
mNotificationManager.notify(notificationId, notification)
|
||||
}
|
||||
@ -506,7 +510,7 @@ class ForegroundService : Service(){
|
||||
track.ytCoverUrl?.let {
|
||||
val file = File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
DownloadHelper.defaultDir +".Images/" + it.substringAfterLast('/',it) + ".jpeg")
|
||||
SpotifyDownloadHelper.defaultDir +".Images/" + it.substringAfterLast('/',it) + ".jpeg")
|
||||
Log.i("Mp3Tags editing Tags",file.path)
|
||||
//init array with file length
|
||||
val bytesArray = ByteArray(file.length().toInt())
|
||||
@ -518,7 +522,7 @@ class ForegroundService : Service(){
|
||||
track.album?.let {
|
||||
val file = File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
DownloadHelper.defaultDir +".Images/" + (it.images?.get(0)?.url!!).substringAfterLast('/') + ".jpeg")
|
||||
SpotifyDownloadHelper.defaultDir +".Images/" + (it.images?.get(0)?.url!!).substringAfterLast('/') + ".jpeg")
|
||||
Log.i("Mp3Tags editing Tags",file.path)
|
||||
//init array with file length
|
||||
val bytesArray = ByteArray(file.length().toInt())
|
||||
|
@ -17,5 +17,6 @@
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="38dp"
|
||||
android:height="38dp" android:viewportWidth="512" android:viewportHeight="512">
|
||||
<path android:fillColor="#5C6BC0" android:pathData="M255.968,5.329C114.624,5.329 0,120.401 0,262.353c0,113.536 73.344,209.856 175.104,243.872c12.8,2.368 17.472,-5.568 17.472,-12.384c0,-6.112 -0.224,-22.272 -0.352,-43.712c-71.2,15.52 -86.24,-34.464 -86.24,-34.464c-11.616,-29.696 -28.416,-37.6 -28.416,-37.6c-23.264,-15.936 1.728,-15.616 1.728,-15.616c25.696,1.824 39.2,26.496 39.2,26.496c22.848,39.264 59.936,27.936 74.528,21.344c2.304,-16.608 8.928,-27.936 16.256,-34.368c-56.832,-6.496 -116.608,-28.544 -116.608,-127.008c0,-28.064 9.984,-51.008 26.368,-68.992c-2.656,-6.496 -11.424,-32.64 2.496,-68c0,0 21.504,-6.912 70.4,26.336c20.416,-5.696 42.304,-8.544 64.096,-8.64c21.728,0.128 43.648,2.944 64.096,8.672c48.864,-33.248 70.336,-26.336 70.336,-26.336c13.952,35.392 5.184,61.504 2.56,68c16.416,17.984 26.304,40.928 26.304,68.992c0,98.72 -59.84,120.448 -116.864,126.816c9.184,7.936 17.376,23.616 17.376,47.584c0,34.368 -0.32,62.08 -0.32,70.496c0,6.88 4.608,14.88 17.6,12.352C438.72,472.145 512,375.857 512,262.353C512,120.401 397.376,5.329 255.968,5.329z"/>
|
||||
<path android:fillColor="#4EA4FF" android:pathData="M255.968,5.329C114.624,5.329 0,120.401 0,262.353c0,113.536 73.344,209.856 175.104,243.872c12.8,2.368 17.472,-5.568 17.472,-12.384c0,-6.112 -0.224,-22.272 -0.352,-43.712c-71.2,15.52 -86.24,-34.464 -86.24,-34.464c-11.616,-29.696 -28.416,-37.6 -28.416,-37.6c-23.264,-15.936 1.728,-15.616 1.728,-15.616c25.696,1.824 39.2,26.496 39.2,26.496c22.848,39.264 59.936,27.936 74.528,21.344c2.304,-16.608 8.928,-27.936 16.256,-34.368c-56.832,-6.496 -116.608,-28.544 -116.608,-127.008c0,-28.064 9.984,-51.008 26.368,-68.992c-2.656,-6.496 -11.424,-32.64 2.496,-68c0,0 21.504,-6.912 70.4,26.336c20.416,-5.696 42.304,-8.544 64.096,-8.64c21.728,0.128 43.648,2.944 64.096,8.672c48.864,-33.248 70.336,-26.336 70.336,-26.336c13.952,35.392 5.184,61.504 2.56,68c16.416,17.984 26.304,40.928 26.304,68.992c0,98.72 -59.84,120.448 -116.864,126.816c9.184,7.936 17.376,23.616 17.376,47.584c0,34.368 -0.32,62.08 -0.32,70.496c0,6.88 4.608,14.88 17.6,12.352C438.72,472.145 512,375.857 512,262.353C512,120.401 397.376,5.329 255.968,5.329z"/>
|
||||
</vector>
|
||||
|
||||
|
@ -1,362 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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 <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
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="25dp"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".fragments.MainFragment">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_download_all"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="44dp"
|
||||
android:background="@drawable/btn_design"
|
||||
android:drawableEnd="@drawable/ic_arrow_slim"
|
||||
android:drawablePadding="4dp"
|
||||
android:drawableTint="@color/black"
|
||||
android:padding="12dp"
|
||||
android:text="Download All |"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp"
|
||||
android:visibility="gone"
|
||||
app:layout_anchor="@+id/appbar"
|
||||
app:layout_anchorGravity="bottom|center" />
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="280dp">
|
||||
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:contentScrim="#F2C102B7"
|
||||
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
|
||||
app:layout_scrollInterpolator="@android:anim/decelerate_interpolator"
|
||||
app:toolbarId="@+id/toolbar">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/TopLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
||||
<EditText
|
||||
android:id="@+id/linkSearch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/text_background_accented"
|
||||
android:ems="10"
|
||||
android:hint="Link From Spotify"
|
||||
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_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_search"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@drawable/btn_design"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:text="Search"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/linkSearch"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/linkSearch"
|
||||
app:layout_constraintTop_toTopOf="@id/linkSearch" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="3dp"
|
||||
android:contentDescription="Album Cover"
|
||||
android:foreground="@drawable/gradient"
|
||||
android:padding="20dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:src="@drawable/spotify_download"
|
||||
android:visibility="visible"
|
||||
app:layout_collapseMode="parallax"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linkSearch" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/StatusBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/text_background_accented"
|
||||
android:fontFamily="@font/raleway_semibold"
|
||||
android:foreground="@drawable/rounded_gradient"
|
||||
android:gravity="center"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingTop="1dp"
|
||||
android:paddingRight="12dp"
|
||||
android:paddingBottom="1dp"
|
||||
android:text="Total: 100 Processed: 50"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/grey"
|
||||
android:textSize="16sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="@+id/btn_search"
|
||||
app:layout_constraintStart_toStartOf="@+id/linkSearch"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linkSearch" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/open_spotify"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="12dp"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||
app:layout_constraintTop_toBottomOf="@id/appbar">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/constraint_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="visible">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:fontFamily="@font/raleway_semibold"
|
||||
android:gravity="end"
|
||||
android:text='"SpotiFlyer"'
|
||||
android:textAlignment="viewEnd"
|
||||
android:textColor="#9AB3FF"
|
||||
android:textSize="34sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:fontFamily="@font/raleway_semibold"
|
||||
android:text=" -Spotify Downloader"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/colorPrimary"
|
||||
app:layout_constraintEnd_toEndOf="@+id/title_view"
|
||||
app:layout_constraintStart_toStartOf="@+id/title_view"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title_view" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:fontFamily="@font/raleway"
|
||||
android:text="Forgot To Copy Link ?"
|
||||
android:textColor="@color/grey"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@+id/btn_openSpotify"
|
||||
app:layout_constraintEnd_toEndOf="@+id/btn_openSpotify"
|
||||
app:layout_constraintStart_toStartOf="@+id/btn_openSpotify" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_openSpotify"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="42dp"
|
||||
android:layout_marginTop="80dp"
|
||||
android:background="@drawable/text_background"
|
||||
android:fontFamily="@font/raleway_semibold"
|
||||
android:padding="12dp"
|
||||
android:text="Open Spotify App"
|
||||
android:textColor="@color/grey"
|
||||
android:textSize="13sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView2" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_donate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="44dp"
|
||||
android:background="@drawable/text_background_accented"
|
||||
android:drawableEnd="@drawable/ic_mug"
|
||||
android:drawablePadding="5dp"
|
||||
android:fontFamily="@font/capriola"
|
||||
android:foreground="@drawable/rounded_gradient"
|
||||
android:gravity="end|center_vertical"
|
||||
android:padding="5dp"
|
||||
android:text="Buy Me a Coffee"
|
||||
android:textSize="13sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView4" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/usage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="90dp"
|
||||
android:text="Usage Instructions!"
|
||||
android:textColor="#D0838383"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/btn_donate"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/btn_openSpotify" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView4"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:padding="4dp"
|
||||
android:text="Like This App?"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/btn_donate"
|
||||
app:layout_constraintEnd_toEndOf="@+id/btn_donate"
|
||||
app:layout_constraintStart_toStartOf="@+id/btn_donate"
|
||||
app:layout_constraintTop_toBottomOf="@+id/usage" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_github"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginTop="130dp"
|
||||
android:background="@drawable/text_background"
|
||||
android:drawableStart="@drawable/ic_github"
|
||||
android:drawablePadding="5dp"
|
||||
android:fontFamily="@font/capriola"
|
||||
android:gravity="end|center_vertical"
|
||||
android:padding="5dp"
|
||||
android:text=" Github "
|
||||
android:textSize="13sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/btn_donate" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/developer_insta"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginTop="130dp"
|
||||
android:background="@drawable/text_background"
|
||||
android:drawableEnd="@drawable/ic_instagram"
|
||||
android:drawablePadding="5dp"
|
||||
android:fontFamily="@font/capriola"
|
||||
android:gravity="end|center_vertical"
|
||||
android:padding="5dp"
|
||||
android:text=" Follow Me "
|
||||
android:textSize="13sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/btn_donate" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tagLine"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="80dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="@font/raleway_semibold"
|
||||
android:text="Made with "
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="22sp"
|
||||
app:layout_constraintEnd_toStartOf="@id/heart"
|
||||
app:layout_constraintTop_toBottomOf="@+id/developer_insta" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/heart"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="80dp"
|
||||
android:contentDescription="Made With Love In India"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/developer_insta"
|
||||
app:srcCompat="@drawable/ic_heart" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tagLine2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginTop="80dp"
|
||||
android:fontFamily="@font/raleway_semibold"
|
||||
android:text=" in India"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="22sp"
|
||||
app:layout_constraintStart_toEndOf="@id/heart"
|
||||
app:layout_constraintTop_toBottomOf="@+id/developer_insta" />
|
||||
|
||||
<WebView
|
||||
android:id="@+id/webView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="300dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/track_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="26dp"
|
||||
android:visibility="gone"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/open_spotify" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</layout>
|
148
app/src/main/res/layout/spotify_fragment.xml
Normal file
148
app/src/main/res/layout/spotify_fragment.xml
Normal file
@ -0,0 +1,148 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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 <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
|
||||
android:id="@+id/main_youtube"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="25dp"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".ui.spotify.SpotifyFragment">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btn_download_all_spotify"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="44dp"
|
||||
android:background="@drawable/btn_design"
|
||||
android:drawableEnd="@drawable/ic_arrow_slim"
|
||||
android:drawablePadding="4dp"
|
||||
android:drawableTint="@color/black"
|
||||
android:padding="12dp"
|
||||
android:text="Download All |"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp"
|
||||
android:visibility="visible"
|
||||
app:layout_anchor="@+id/appbar_spotify"
|
||||
app:layout_anchorGravity="bottom|center" />
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar_spotify"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="280dp">
|
||||
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:contentScrim="#F2C102B7"
|
||||
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
|
||||
app:layout_scrollInterpolator="@android:anim/decelerate_interpolator"
|
||||
app:toolbarId="@+id/toolbar">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/TopLayout_spotify"
|
||||
android:layout_width="match_parent"
|
||||
android:foreground="@drawable/gradient"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/spotify_cover_image"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="23dp"
|
||||
android:layout_marginBottom="3dp"
|
||||
android:contentDescription="Album Cover"
|
||||
android:padding="15dp"
|
||||
android:src="@drawable/spotify_download"
|
||||
android:visibility="visible"
|
||||
app:layout_collapseMode="parallax"
|
||||
app:layout_constraintBottom_toTopOf="@id/title_view_spotify"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/StatusBar_spotify"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:background="@drawable/text_background_accented"
|
||||
android:fontFamily="@font/raleway_semibold"
|
||||
android:foreground="@drawable/rounded_gradient"
|
||||
android:gravity="center"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingTop="1dp"
|
||||
android:paddingRight="12dp"
|
||||
android:paddingBottom="1dp"
|
||||
android:text="Total: 100 Processed: 50"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/grey"
|
||||
android:textSize="16sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@+id/spotify_cover_image"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
<TextView
|
||||
android:id="@+id/title_view_spotify"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:background="#00000000"
|
||||
android:fontFamily="@font/raleway_semibold"
|
||||
android:gravity="end"
|
||||
android:text='"Loading..."'
|
||||
android:textAlignment="viewEnd"
|
||||
android:textColor="#9AB3FF"
|
||||
android:textSize="28sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/track_list_spotify"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="26dp"
|
||||
android:visibility="visible"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/appbar_spotify" />
|
||||
|
||||
<WebView
|
||||
android:id="@+id/webView_spotify"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</layout>
|
@ -23,8 +23,37 @@
|
||||
app:startDestination="@id/mainFragment">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/mainFragment"
|
||||
android:name="com.shabinder.spotiflyer.fragments.MainFragment"
|
||||
android:id="@+id/spotifyFragment"
|
||||
android:name="com.shabinder.spotiflyer.ui.spotify.SpotifyFragment"
|
||||
android:label="main_fragment"
|
||||
tools:layout="@layout/main_fragment" />
|
||||
tools:layout="@layout/spotify_fragment" >
|
||||
<argument
|
||||
android:name="link"
|
||||
app:argType="string" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/mainFragment"
|
||||
android:name="com.shabinder.spotiflyer.ui.mainfragment.MainFragment"
|
||||
android:label="MainFragment"
|
||||
tools:layout="@layout/main_fragment">
|
||||
<action
|
||||
android:id="@+id/action_mainFragment_to_spotifyFragment"
|
||||
app:destination="@id/spotifyFragment"
|
||||
app:enterAnim="@android:anim/slide_in_left"
|
||||
app:exitAnim="@android:anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_mainFragment_to_youtubeFragment"
|
||||
app:destination="@id/youtubeFragment"
|
||||
app:enterAnim="@android:anim/slide_in_left"
|
||||
app:exitAnim="@android:anim/slide_out_right" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/youtubeFragment"
|
||||
android:name="com.shabinder.spotiflyer.ui.youtube.YoutubeFragment"
|
||||
android:label="YoutubeFragment"
|
||||
tools:layout="@layout/youtube_fragment">
|
||||
<argument
|
||||
android:name="link"
|
||||
app:argType="string" />
|
||||
</fragment>
|
||||
</navigation>
|
@ -19,7 +19,8 @@
|
||||
<string name="app_name">SpotiFlyer</string>
|
||||
<string name="d_one"><b><i>Usage Instructions:</i></b></string>
|
||||
<string name="d_two"><b>1. Paste</b>, Your Copied Link in Search Box at Top.</string>
|
||||
<string name="d_three"><b>2. Share directly</b> from <b>Spotify</b> App to this App.</string>
|
||||
<string name="d_three">or Open platform using <b>Logo Button Above.</b></string>
|
||||
<string name="d_four"><b>2. Share directly</b> to this App.</string>
|
||||
<string name="spotify_not_installed">It Seems you don\'t have Spotify Installed</string>
|
||||
<string name="spotify_web_link">Open Spotify Web Player</string>
|
||||
<string name="title"><b>No</b> Internet Connectivity!</string>
|
||||
|
Loading…
Reference in New Issue
Block a user