mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-25 02:14:32 +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 "androidx.room:room-runtime:2.2.5"
|
||||||
implementation project(path: ':mobile-ffmpeg')
|
implementation project(path: ':mobile-ffmpeg')
|
||||||
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
kapt "androidx.room:room-compiler:2.2.5"
|
kapt "androidx.room:room-compiler:2.2.5"
|
||||||
implementation "androidx.room:room-ktx:2.2.5"
|
implementation "androidx.room:room-ktx:2.2.5"
|
||||||
implementation ("com.github.bumptech.glide:recyclerview-integration:4.11.0") {
|
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.javiersantos.appupdater.enums.UpdateFrom
|
||||||
import com.github.kiulian.downloader.YoutubeDownloader
|
import com.github.kiulian.downloader.YoutubeDownloader
|
||||||
import com.shabinder.spotiflyer.databinding.MainActivityBinding
|
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.SpotifyService
|
||||||
import com.shabinder.spotiflyer.utils.SpotifyServiceToken
|
import com.shabinder.spotiflyer.utils.SpotifyServiceToken
|
||||||
import com.shabinder.spotiflyer.utils.createDirectory
|
import com.shabinder.spotiflyer.utils.createDirectory
|
||||||
@ -74,7 +74,7 @@ class MainActivity : AppCompatActivity(){
|
|||||||
sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)
|
sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)
|
||||||
sharedPref = this.getPreferences(Context.MODE_PRIVATE)
|
sharedPref = this.getPreferences(Context.MODE_PRIVATE)
|
||||||
//starting Notification and Downloader Service!
|
//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)) ){
|
/* if(sharedPref?.contains("token")!! && (sharedPref?.getLong("time",System.currentTimeMillis()/1000/60/60)!! < (System.currentTimeMillis()/1000/60/60)) ){
|
||||||
val savedToken = sharedPref?.getString("token","error")!!
|
val savedToken = sharedPref?.getString("token","error")!!
|
||||||
@ -85,7 +85,7 @@ class MainActivity : AppCompatActivity(){
|
|||||||
implementSpotifyService(savedToken)
|
implementSpotifyService(savedToken)
|
||||||
}else{authenticateSpotify()}*/
|
}else{authenticateSpotify()}*/
|
||||||
|
|
||||||
if(sharedViewModel.spotifyService == null){
|
if(sharedViewModel.spotifyService.value == null){
|
||||||
authenticateSpotify()
|
authenticateSpotify()
|
||||||
}else{
|
}else{
|
||||||
implementSpotifyService(sharedViewModel.accessToken.value!!)
|
implementSpotifyService(sharedViewModel.accessToken.value!!)
|
||||||
@ -102,7 +102,7 @@ class MainActivity : AppCompatActivity(){
|
|||||||
|
|
||||||
//Object to download From Youtube {"https://github.com/sealedtx/java-youtube-downloader"}
|
//Object to download From Youtube {"https://github.com/sealedtx/java-youtube-downloader"}
|
||||||
ytDownloader = YoutubeDownloader()
|
ytDownloader = YoutubeDownloader()
|
||||||
sharedViewModel.ytDownloader = ytDownloader
|
sharedViewModel.ytDownloader.value = ytDownloader
|
||||||
|
|
||||||
handleIntentFromExternalActivity()
|
handleIntentFromExternalActivity()
|
||||||
}
|
}
|
||||||
@ -168,7 +168,7 @@ class MainActivity : AppCompatActivity(){
|
|||||||
.build()
|
.build()
|
||||||
|
|
||||||
spotifyService = retrofit.create(SpotifyService::class.java)
|
spotifyService = retrofit.create(SpotifyService::class.java)
|
||||||
sharedViewModel.spotifyService = spotifyService
|
sharedViewModel.spotifyService.value = spotifyService
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSpotifyToken(){
|
private fun getSpotifyToken(){
|
||||||
@ -283,12 +283,12 @@ class MainActivity : AppCompatActivity(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createDir() {
|
private fun createDir() {
|
||||||
createDirectory(DownloadHelper.defaultDir)
|
createDirectory(SpotifyDownloadHelper.defaultDir)
|
||||||
createDirectory(DownloadHelper.defaultDir+".Images/")
|
createDirectory(SpotifyDownloadHelper.defaultDir+".Images/")
|
||||||
createDirectory(DownloadHelper.defaultDir+"Tracks/")
|
createDirectory(SpotifyDownloadHelper.defaultDir+"Tracks/")
|
||||||
createDirectory(DownloadHelper.defaultDir+"Albums/")
|
createDirectory(SpotifyDownloadHelper.defaultDir+"Albums/")
|
||||||
createDirectory(DownloadHelper.defaultDir+"Playlists/")
|
createDirectory(SpotifyDownloadHelper.defaultDir+"Playlists/")
|
||||||
createDirectory(DownloadHelper.defaultDir+"YT_Downloads/")
|
createDirectory(SpotifyDownloadHelper.defaultDir+"YT_Downloads/")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkIfLatestVersion() {
|
private fun checkIfLatestVersion() {
|
||||||
|
@ -24,9 +24,6 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.github.kiulian.downloader.YoutubeDownloader
|
import com.github.kiulian.downloader.YoutubeDownloader
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
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.shabinder.spotiflyer.utils.SpotifyService
|
||||||
import com.shreyaspatil.EasyUpiPayment.EasyUpiPayment
|
import com.shreyaspatil.EasyUpiPayment.EasyUpiPayment
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@ -36,11 +33,11 @@ import java.io.File
|
|||||||
|
|
||||||
class SharedViewModel : ViewModel() {
|
class SharedViewModel : ViewModel() {
|
||||||
var intentString = ""
|
var intentString = ""
|
||||||
var accessToken = MutableLiveData<String>().apply { value = "" }
|
var spotifyService = MutableLiveData<SpotifyService>()
|
||||||
var spotifyService : SpotifyService? = null
|
var ytDownloader = MutableLiveData<YoutubeDownloader>()
|
||||||
var ytDownloader : YoutubeDownloader? = null
|
|
||||||
var isConnected = MutableLiveData<Boolean>().apply { value = false }
|
|
||||||
var easyUpiPayment: EasyUpiPayment? = null
|
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
|
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)
|
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() {
|
override fun onCleared() {
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
viewModelJob.cancel()
|
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.formats.Format
|
||||||
import com.github.kiulian.downloader.model.quality.AudioQuality
|
import com.github.kiulian.downloader.model.quality.AudioQuality
|
||||||
import com.shabinder.spotiflyer.SharedViewModel
|
import com.shabinder.spotiflyer.SharedViewModel
|
||||||
import com.shabinder.spotiflyer.fragments.MainFragment
|
|
||||||
import com.shabinder.spotiflyer.models.DownloadObject
|
import com.shabinder.spotiflyer.models.DownloadObject
|
||||||
import com.shabinder.spotiflyer.models.Track
|
import com.shabinder.spotiflyer.models.Track
|
||||||
|
import com.shabinder.spotiflyer.ui.spotify.SpotifyFragment
|
||||||
import com.shabinder.spotiflyer.worker.ForegroundService
|
import com.shabinder.spotiflyer.worker.ForegroundService
|
||||||
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
|
||||||
|
|
||||||
object DownloadHelper {
|
object SpotifyDownloadHelper {
|
||||||
var webView:WebView? = null
|
var webView:WebView? = null
|
||||||
var context : Context? = null
|
var context : Context? = null
|
||||||
var statusBar:TextView? = null
|
var statusBar:TextView? = null
|
||||||
val defaultDir = Environment.DIRECTORY_MUSIC + File.separator + "SpotiFlyer" + File.separator
|
val defaultDir = Environment.DIRECTORY_MUSIC + File.separator + "SpotiFlyer" + File.separator
|
||||||
private var downloadList = arrayListOf<DownloadObject>()
|
|
||||||
var sharedViewModel:SharedViewModel? = null
|
var sharedViewModel:SharedViewModel? = null
|
||||||
private var isBrowserLoading = false
|
private var isBrowserLoading = false
|
||||||
private var total = 0
|
private var total = 0
|
||||||
private var Processed = 0
|
private var Processed = 0
|
||||||
|
private var listProcessed:Boolean = false
|
||||||
var youtubeList = mutableListOf<YoutubeRequest>()
|
var youtubeList = mutableListOf<YoutubeRequest>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,32 +64,27 @@ object DownloadHelper {
|
|||||||
subFolder: String?,
|
subFolder: String?,
|
||||||
trackList: List<Track>, ytDownloader: YoutubeDownloader?) {
|
trackList: List<Track>, ytDownloader: YoutubeDownloader?) {
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
var size = trackList.size
|
total += trackList.size // Adding New Download List Count to StatusBar
|
||||||
total += size
|
|
||||||
animateStatusBar()
|
|
||||||
trackList.forEach {
|
trackList.forEach {
|
||||||
size--
|
|
||||||
val outputFile:String = Environment.getExternalStorageDirectory().toString() + File.separator +
|
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")
|
defaultDir + removeIllegalChars(type) + File.separator + (if(subFolder == null){""}else{ removeIllegalChars(subFolder) + File.separator} + removeIllegalChars(it.name!!)+".mp3")
|
||||||
if(File(outputFile).exists()){//Download Already Present!!
|
if(File(outputFile).exists()){//Download Already Present!!
|
||||||
Processed++
|
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{
|
}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))
|
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{
|
}else{
|
||||||
getYTLink(null,type,subFolder,ytDownloader,"${it.name} ${it.artists?.get(0)?.name ?:""}", it)
|
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!!
|
//TODO CleanUp here and there!!
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
suspend fun getYTLink(mainFragment: MainFragment? = null,
|
suspend fun getYTLink(spotifyFragment: SpotifyFragment? = null,
|
||||||
type:String,
|
type:String,
|
||||||
subFolder:String?,
|
subFolder:String?,
|
||||||
ytDownloader: YoutubeDownloader?,
|
ytDownloader: YoutubeDownloader?,
|
||||||
searchQuery: String,
|
searchQuery: String,
|
||||||
track: Track,
|
track: Track){
|
||||||
index: Int? = null){
|
|
||||||
val searchText = searchQuery.replace("\\s".toRegex(), "+")
|
val searchText = searchQuery.replace("\\s".toRegex(), "+")
|
||||||
val url = "https://www.youtube.com/results?sp=EgIQAQ%253D%253D&q=$searchText"
|
val url = "https://www.youtube.com/results?sp=EgIQAQ%253D%253D&q=$searchText"
|
||||||
Log.i("DH YT LINK ",url)
|
Log.i("DH YT LINK ",url)
|
||||||
@ -122,15 +116,16 @@ object DownloadHelper {
|
|||||||
val id = value!!.substringAfterLast("=", "error").replace("\"","")
|
val id = value!!.substringAfterLast("=", "error").replace("\"","")
|
||||||
Log.i("YT-id",id)
|
Log.i("YT-id",id)
|
||||||
if(id !="error"){//Link extracting error
|
if(id !="error"){//Link extracting error
|
||||||
mainFragment?.showToast("Starting Download")
|
spotifyFragment?.showToast("Starting Download")
|
||||||
Processed++
|
Processed++
|
||||||
|
if(Processed == total)listProcessed = true //List Processesd
|
||||||
updateStatusBar()
|
updateStatusBar()
|
||||||
downloadFile(subFolder, type, track, index,ytDownloader,id)
|
downloadFile(subFolder, type, track,ytDownloader,id)
|
||||||
}
|
}
|
||||||
if(youtubeList.isNotEmpty()){
|
if(youtubeList.isNotEmpty()){
|
||||||
val request = youtubeList[0]
|
val request = youtubeList[0]
|
||||||
sharedViewModel!!.uiScope.launch {
|
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)
|
youtubeList.remove(request)
|
||||||
if(youtubeList.size == 0){//list processing completed , webView is free again!
|
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() {
|
private fun updateStatusBar() {
|
||||||
statusBar!!.visibility = View.VISIBLE
|
statusBar!!.visibility = View.VISIBLE
|
||||||
statusBar?.text = "Total: $total Processed: $Processed"
|
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 {
|
sharedViewModel!!.uiScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val video = ytDownloader?.getVideo(id)
|
val video = ytDownloader?.getVideo(id)
|
||||||
|
val detail = video?.details()
|
||||||
val format:Format? =try {
|
val format:Format? =try {
|
||||||
video?.findAudioWithQuality(AudioQuality.high)?.get(0) as Format
|
video?.findAudioWithQuality(AudioQuality.high)?.get(0) as Format
|
||||||
}catch (e:java.lang.IndexOutOfBoundsException){
|
}catch (e:java.lang.IndexOutOfBoundsException){
|
||||||
@ -196,9 +167,7 @@ object DownloadHelper {
|
|||||||
}
|
}
|
||||||
format?.let {
|
format?.let {
|
||||||
val url:String = format.url()
|
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 +
|
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")
|
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)
|
Log.i("DH",outputFile)
|
||||||
startService(context!!, downloadObject)
|
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
|
anim.repeatCount = Animation.INFINITE
|
||||||
statusBar?.animation = anim
|
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(
|
data class YoutubeRequest(
|
||||||
val mainFragment: MainFragment? = null,
|
val spotifyFragment: SpotifyFragment? = null,
|
||||||
val type:String,
|
val type:String,
|
||||||
val subFolder:String?,
|
val subFolder:String?,
|
||||||
val ytDownloader: YoutubeDownloader?,
|
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
|
@Parcelize
|
||||||
data class DownloadObject(
|
data class DownloadObject(
|
||||||
var track: Track,
|
var ytVideo: YTTrack?=null,
|
||||||
|
var track: Track?=null,
|
||||||
var url:String,
|
var url:String,
|
||||||
var outputDir:String
|
var outputDir:String
|
||||||
):Parcelable
|
):Parcelable
|
@ -26,20 +26,20 @@ import android.widget.TextView
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.shabinder.spotiflyer.R
|
import com.shabinder.spotiflyer.R
|
||||||
import com.shabinder.spotiflyer.SharedViewModel
|
import com.shabinder.spotiflyer.SharedViewModel
|
||||||
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper.getYTLink
|
import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper.getYTLink
|
||||||
import com.shabinder.spotiflyer.fragments.MainFragment
|
|
||||||
import com.shabinder.spotiflyer.models.Track
|
import com.shabinder.spotiflyer.models.Track
|
||||||
|
import com.shabinder.spotiflyer.ui.spotify.SpotifyFragment
|
||||||
import com.shabinder.spotiflyer.utils.bindImage
|
import com.shabinder.spotiflyer.utils.bindImage
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
class TrackListAdapter:RecyclerView.Adapter<TrackListAdapter.ViewHolder>() {
|
class SpotifyTrackListAdapter:RecyclerView.Adapter<SpotifyTrackListAdapter.ViewHolder>() {
|
||||||
|
|
||||||
var trackList = listOf<Track>()
|
var trackList = listOf<Track>()
|
||||||
var totalItems:Int = 0
|
var totalItems:Int = 0
|
||||||
var sharedViewModel = SharedViewModel()
|
var sharedViewModel = SharedViewModel()
|
||||||
var isAlbum:Boolean = false
|
var isAlbum:Boolean = false
|
||||||
var mainFragment:MainFragment? = null
|
var spotifyFragment: SpotifyFragment? = null
|
||||||
|
|
||||||
override fun getItemCount():Int = totalItems
|
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.duration.text = "${item.duration_ms/1000/60} minutes, ${(item.duration_ms/1000)%60} sec"
|
||||||
holder.downloadBtn.setOnClickListener{
|
holder.downloadBtn.setOnClickListener{
|
||||||
sharedViewModel.uiScope.launch {
|
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)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.splash_screen)
|
setContentView(R.layout.splash_screen)
|
||||||
|
|
||||||
val splashTimeout = 600
|
val splashTimeout = 500
|
||||||
val homeIntent = Intent(this@SplashScreen, MainActivity::class.java)
|
val homeIntent = Intent(this@SplashScreen, MainActivity::class.java)
|
||||||
Handler().postDelayed({
|
Handler().postDelayed({
|
||||||
//TODO:Bring Initial Setup here
|
//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.RequestListener
|
||||||
import com.bumptech.glide.request.target.Target
|
import com.bumptech.glide.request.target.Target
|
||||||
import com.shabinder.spotiflyer.R
|
import com.shabinder.spotiflyer.R
|
||||||
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper
|
import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -72,7 +72,7 @@ fun bindImage(imgView: ImageView, imgUrl: String?) {
|
|||||||
try {
|
try {
|
||||||
val file = File(
|
val file = File(
|
||||||
Environment.getExternalStorageDirectory(),
|
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.
|
) // the File to save , append increasing numeric counter to prevent files from getting overwritten.
|
||||||
val options = BitmapFactory.Options()
|
val options = BitmapFactory.Options()
|
||||||
options.inPreferredConfig = Bitmap.Config.ARGB_8888
|
options.inPreferredConfig = Bitmap.Config.ARGB_8888
|
||||||
|
@ -41,19 +41,19 @@ import retrofit2.http.*
|
|||||||
interface SpotifyService {
|
interface SpotifyService {
|
||||||
|
|
||||||
@GET("playlists/{playlist_id}")
|
@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}")
|
@GET("tracks/{id}")
|
||||||
suspend fun getTrack(@Path("id") var1: String?): Track?
|
suspend fun getTrack(@Path("id") trackId: String?): Track
|
||||||
|
|
||||||
@GET("episodes/{id}")
|
@GET("episodes/{id}")
|
||||||
suspend fun getEpisode(@Path("id") var1: String?): Track?
|
suspend fun getEpisode(@Path("id") episodeId: String?): Track
|
||||||
|
|
||||||
@GET("shows/{id}")
|
@GET("shows/{id}")
|
||||||
suspend fun getShow(@Path("id") var1: String?): Track?
|
suspend fun getShow(@Path("id") showId: String?): Track
|
||||||
|
|
||||||
@GET("albums/{id}")
|
@GET("albums/{id}")
|
||||||
suspend fun getAlbum(@Path("id") var1: String?): Album?
|
suspend fun getAlbum(@Path("id") albumId: String?): Album
|
||||||
|
|
||||||
@GET("me")
|
@GET("me")
|
||||||
suspend fun getMe(): UserPrivate?
|
suspend fun getMe(): UserPrivate?
|
||||||
|
@ -40,7 +40,7 @@ import com.mpatric.mp3agic.ID3v24Tag
|
|||||||
import com.mpatric.mp3agic.Mp3File
|
import com.mpatric.mp3agic.Mp3File
|
||||||
import com.shabinder.spotiflyer.MainActivity
|
import com.shabinder.spotiflyer.MainActivity
|
||||||
import com.shabinder.spotiflyer.R
|
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.DownloadObject
|
||||||
import com.shabinder.spotiflyer.models.Track
|
import com.shabinder.spotiflyer.models.Track
|
||||||
import com.tonyodev.fetch2.*
|
import com.tonyodev.fetch2.*
|
||||||
@ -74,11 +74,9 @@ class ForegroundService : Service(){
|
|||||||
)
|
)
|
||||||
private var wakeLock: PowerManager.WakeLock? = null
|
private var wakeLock: PowerManager.WakeLock? = null
|
||||||
private var isServiceStarted = false
|
private var isServiceStarted = false
|
||||||
private var messageSnippet1 = ""
|
var notificationLine = 0
|
||||||
private var messageSnippet2 = ""
|
val messageList = mutableListOf<String>("","","","")
|
||||||
private var messageSnippet3 = ""
|
private var pendingIntent:PendingIntent? = null
|
||||||
private var messageSnippet4 = ""
|
|
||||||
var notificationLine = 1
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -89,7 +87,7 @@ class ForegroundService : Service(){
|
|||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
val notificationIntent = Intent(this, MainActivity::class.java)
|
val notificationIntent = Intent(this, MainActivity::class.java)
|
||||||
val pendingIntent = PendingIntent.getActivity(
|
pendingIntent = PendingIntent.getActivity(
|
||||||
this,
|
this,
|
||||||
0, notificationIntent, 0
|
0, notificationIntent, 0
|
||||||
)
|
)
|
||||||
@ -120,18 +118,16 @@ class ForegroundService : Service(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
val notification = NotificationCompat.Builder(this, channelId)
|
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)
|
.setSmallIcon(R.drawable.down_arrowbw)
|
||||||
|
.setNotificationSilent()
|
||||||
|
.setSubText("Speed: $speed KB/s")
|
||||||
.setStyle(NotificationCompat.InboxStyle()
|
.setStyle(NotificationCompat.InboxStyle()
|
||||||
.addLine(messageSnippet1)
|
.setBigContentTitle("Total: $total Completed:$converted")
|
||||||
.addLine(messageSnippet2)
|
.addLine(messageList[0])
|
||||||
.addLine(messageSnippet3)
|
.addLine(messageList[1])
|
||||||
.addLine(messageSnippet4))
|
.addLine(messageList[2])
|
||||||
|
.addLine(messageList[3]))
|
||||||
|
.setContentIntent(pendingIntent)
|
||||||
.build()
|
.build()
|
||||||
startForeground(notificationId, notification)
|
startForeground(notificationId, notification)
|
||||||
}
|
}
|
||||||
@ -149,7 +145,7 @@ class ForegroundService : Service(){
|
|||||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||||
// Send a notification that service is started
|
// Send a notification that service is started
|
||||||
Log.i(tag,"Service Started.")
|
Log.i(tag,"Service Started.")
|
||||||
|
startForeground()
|
||||||
//do heavy work on a background thread
|
//do heavy work on a background thread
|
||||||
//val list = intent.getSerializableExtra("list") as List<Any?>
|
//val list = intent.getSerializableExtra("list") as List<Any?>
|
||||||
// val list = intent.getParcelableArrayListExtra<DownloadObject>("list") ?: intent.extras?.getParcelableArrayList<DownloadObject>("list")
|
// val list = intent.getParcelableArrayListExtra<DownloadObject>("list") ?: intent.extras?.getParcelableArrayList<DownloadObject>("list")
|
||||||
@ -166,7 +162,7 @@ class ForegroundService : Service(){
|
|||||||
|
|
||||||
fetch!!.enqueue(request,
|
fetch!!.enqueue(request,
|
||||||
Func {
|
Func {
|
||||||
requestMap[it] = obj.track
|
obj.track?.let { it1 -> requestMap.put(it, it1) }
|
||||||
downloadList.remove(obj)
|
downloadList.remove(obj)
|
||||||
Log.i(tag, "Enqueuing Download")
|
Log.i(tag, "Enqueuing Download")
|
||||||
},
|
},
|
||||||
@ -197,7 +193,6 @@ class ForegroundService : Service(){
|
|||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
if(downloadMap.isEmpty() && converted == total){
|
if(downloadMap.isEmpty() && converted == total){
|
||||||
Log.i(tag,"Service destroyed.")
|
Log.i(tag,"Service destroyed.")
|
||||||
fetch?.close()
|
|
||||||
deleteFile(parentDirectory)
|
deleteFile(parentDirectory)
|
||||||
releaseWakeLock()
|
releaseWakeLock()
|
||||||
stopForeground(true)
|
stopForeground(true)
|
||||||
@ -223,8 +218,11 @@ class ForegroundService : Service(){
|
|||||||
super.onTaskRemoved(rootIntent)
|
super.onTaskRemoved(rootIntent)
|
||||||
if(downloadMap.isEmpty() && converted == total ){
|
if(downloadMap.isEmpty() && converted == total ){
|
||||||
Log.i(tag,"Service Removed.")
|
Log.i(tag,"Service Removed.")
|
||||||
fetch?.close()
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
stopSelf()
|
stopForeground(true)
|
||||||
|
} else {
|
||||||
|
stopSelf()//System will automatically close it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +237,7 @@ class ForegroundService : Service(){
|
|||||||
if (file.isDirectory) {
|
if (file.isDirectory) {
|
||||||
deleteFile(file)
|
deleteFile(file)
|
||||||
} else if(file.isFile) {
|
} 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}")
|
// Log.i(tag,"deleting ${file.path}")
|
||||||
file.delete()
|
file.delete()
|
||||||
}
|
}
|
||||||
@ -274,21 +272,21 @@ class ForegroundService : Service(){
|
|||||||
) {
|
) {
|
||||||
val track = requestMap[download.request]
|
val track = requestMap[download.request]
|
||||||
when(notificationLine){
|
when(notificationLine){
|
||||||
|
0 -> {
|
||||||
|
messageList[0] = "Downloading ${track?.name}"
|
||||||
|
notificationLine = 1
|
||||||
|
}
|
||||||
1 -> {
|
1 -> {
|
||||||
messageSnippet1 = "Downloading ${track?.name}"
|
messageList[1] = "Downloading ${track?.name}"
|
||||||
notificationLine = 2
|
notificationLine = 2
|
||||||
}
|
}
|
||||||
2 -> {
|
2-> {
|
||||||
messageSnippet2 = "Downloading ${track?.name}"
|
messageList[2] = "Downloading ${track?.name}"
|
||||||
notificationLine = 3
|
notificationLine = 3
|
||||||
}
|
}
|
||||||
3-> {
|
3 -> {
|
||||||
messageSnippet3 = "Downloading ${track?.name}"
|
messageList[3] = "Downloading ${track?.name}"
|
||||||
notificationLine = 4
|
notificationLine = 0
|
||||||
}
|
|
||||||
4 -> {
|
|
||||||
messageSnippet4 = "Downloading ${track?.name}"
|
|
||||||
notificationLine = 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.i(tag,"${track?.name} Download Started")
|
Log.i(tag,"${track?.name} Download Started")
|
||||||
@ -309,6 +307,13 @@ class ForegroundService : Service(){
|
|||||||
|
|
||||||
override fun onCompleted(download: Download) {
|
override fun onCompleted(download: Download) {
|
||||||
val track = requestMap[download.request]
|
val track = requestMap[download.request]
|
||||||
|
|
||||||
|
for (message in messageList){
|
||||||
|
if( message == "Downloading ${track?.name}"){
|
||||||
|
messageList[messageList.indexOf(message)] = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
serviceScope.launch {
|
serviceScope.launch {
|
||||||
try{
|
try{
|
||||||
convertToMp3(download.file, track!!)
|
convertToMp3(download.file, track!!)
|
||||||
@ -370,7 +375,7 @@ class ForegroundService : Service(){
|
|||||||
/**
|
/**
|
||||||
* If fetch Fails , Android Download Manager To RESCUE!!
|
* 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 uri = Uri.parse(url)
|
||||||
val request = DownloadManager.Request(uri)
|
val request = DownloadManager.Request(uri)
|
||||||
.setAllowedNetworkTypes(
|
.setAllowedNetworkTypes(
|
||||||
@ -407,7 +412,7 @@ class ForegroundService : Service(){
|
|||||||
/**
|
/**
|
||||||
*Converting Downloaded Audio (m4a) to Mp3.( Also Applying Metadata)
|
*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)
|
val m4aFile = File(filePath)
|
||||||
|
|
||||||
FFmpeg.executeAsync(
|
FFmpeg.executeAsync(
|
||||||
@ -457,17 +462,16 @@ class ForegroundService : Service(){
|
|||||||
val mNotificationManager: NotificationManager =
|
val mNotificationManager: NotificationManager =
|
||||||
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
val notification = NotificationCompat.Builder(this, channelId)
|
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)
|
.setSmallIcon(R.drawable.down_arrowbw)
|
||||||
|
.setSubText("Speed: $speed KB/s")
|
||||||
|
.setNotificationSilent()
|
||||||
.setStyle(NotificationCompat.InboxStyle()
|
.setStyle(NotificationCompat.InboxStyle()
|
||||||
.addLine(messageSnippet1)
|
.setBigContentTitle("Total: $total Completed:$converted")
|
||||||
.addLine(messageSnippet2)
|
.addLine(messageList[0])
|
||||||
.addLine(messageSnippet3)
|
.addLine(messageList[1])
|
||||||
.addLine(messageSnippet4))
|
.addLine(messageList[2])
|
||||||
|
.addLine(messageList[3]))
|
||||||
|
.setContentIntent(pendingIntent)
|
||||||
.build()
|
.build()
|
||||||
mNotificationManager.notify(notificationId, notification)
|
mNotificationManager.notify(notificationId, notification)
|
||||||
}
|
}
|
||||||
@ -506,7 +510,7 @@ class ForegroundService : Service(){
|
|||||||
track.ytCoverUrl?.let {
|
track.ytCoverUrl?.let {
|
||||||
val file = File(
|
val file = File(
|
||||||
Environment.getExternalStorageDirectory(),
|
Environment.getExternalStorageDirectory(),
|
||||||
DownloadHelper.defaultDir +".Images/" + it.substringAfterLast('/',it) + ".jpeg")
|
SpotifyDownloadHelper.defaultDir +".Images/" + it.substringAfterLast('/',it) + ".jpeg")
|
||||||
Log.i("Mp3Tags editing Tags",file.path)
|
Log.i("Mp3Tags editing Tags",file.path)
|
||||||
//init array with file length
|
//init array with file length
|
||||||
val bytesArray = ByteArray(file.length().toInt())
|
val bytesArray = ByteArray(file.length().toInt())
|
||||||
@ -518,7 +522,7 @@ class ForegroundService : Service(){
|
|||||||
track.album?.let {
|
track.album?.let {
|
||||||
val file = File(
|
val file = File(
|
||||||
Environment.getExternalStorageDirectory(),
|
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)
|
Log.i("Mp3Tags editing Tags",file.path)
|
||||||
//init array with file length
|
//init array with file length
|
||||||
val bytesArray = ByteArray(file.length().toInt())
|
val bytesArray = ByteArray(file.length().toInt())
|
||||||
|
@ -17,5 +17,6 @@
|
|||||||
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="38dp"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="38dp"
|
||||||
android:height="38dp" android:viewportWidth="512" android:viewportHeight="512">
|
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>
|
</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">
|
app:startDestination="@id/mainFragment">
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/mainFragment"
|
android:id="@+id/spotifyFragment"
|
||||||
android:name="com.shabinder.spotiflyer.fragments.MainFragment"
|
android:name="com.shabinder.spotiflyer.ui.spotify.SpotifyFragment"
|
||||||
android:label="main_fragment"
|
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>
|
</navigation>
|
@ -19,7 +19,8 @@
|
|||||||
<string name="app_name">SpotiFlyer</string>
|
<string name="app_name">SpotiFlyer</string>
|
||||||
<string name="d_one"><b><i>Usage Instructions:</i></b></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_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_not_installed">It Seems you don\'t have Spotify Installed</string>
|
||||||
<string name="spotify_web_link">Open Spotify Web Player</string>
|
<string name="spotify_web_link">Open Spotify Web Player</string>
|
||||||
<string name="title"><b>No</b> Internet Connectivity!</string>
|
<string name="title"><b>No</b> Internet Connectivity!</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user