mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-25 02:14:32 +01:00
Ui Anim ,Database,List View updated
This commit is contained in:
parent
2455684416
commit
72fd6a4c73
@ -89,10 +89,10 @@ dependencies {
|
|||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7"
|
||||||
|
|
||||||
implementation "androidx.room:room-runtime:2.2.5"
|
implementation "androidx.room:room-runtime:2.2.5"
|
||||||
implementation project(path: ':mobile-ffmpeg')
|
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
|
||||||
kapt "androidx.room:room-compiler:2.2.5"
|
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 project(path: ':mobile-ffmpeg')
|
||||||
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
implementation ("com.github.bumptech.glide:recyclerview-integration:4.11.0") {
|
implementation ("com.github.bumptech.glide:recyclerview-integration:4.11.0") {
|
||||||
transitive = true
|
transitive = true
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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.database
|
||||||
|
|
||||||
|
import androidx.room.*
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface DatabaseDAO {
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
suspend fun insert(record: DownloadRecord)
|
||||||
|
|
||||||
|
@Update
|
||||||
|
fun update(record: DownloadRecord)
|
||||||
|
|
||||||
|
@Query("SELECT * from download_record_table ORDER BY id DESC")
|
||||||
|
suspend fun getRecord():List<DownloadRecord>
|
||||||
|
|
||||||
|
@Query("DELETE FROM download_record_table")
|
||||||
|
suspend fun deleteAll()
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* 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.database
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Index
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import kotlinx.android.parcel.Parcelize
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
@Entity(
|
||||||
|
tableName = "download_record_table",
|
||||||
|
indices = [Index(value = ["id","link"], unique = true)]
|
||||||
|
)
|
||||||
|
data class DownloadRecord(
|
||||||
|
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
var id:Int = 0,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "type")
|
||||||
|
var type:String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "name")
|
||||||
|
var name:String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "link")
|
||||||
|
var link:String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "coverUrl")
|
||||||
|
var coverUrl:String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "totalFiles")
|
||||||
|
var totalFiles:Int = 1,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "downloaded")
|
||||||
|
var downloaded:Boolean=false,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "directory")
|
||||||
|
var directory:String?=null
|
||||||
|
):Parcelable
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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.database
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.room.Database
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
|
||||||
|
@Database(entities = [DownloadRecord::class], version = 1, exportSchema = false)
|
||||||
|
abstract class DownloadRecordDatabase:RoomDatabase() {
|
||||||
|
|
||||||
|
abstract val databaseDAO: DatabaseDAO
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@Volatile
|
||||||
|
private var INSTANCE: DownloadRecordDatabase? = null
|
||||||
|
|
||||||
|
fun getInstance(context: Context): DownloadRecordDatabase {
|
||||||
|
synchronized(this) {
|
||||||
|
var instance = INSTANCE
|
||||||
|
if (instance == null) {
|
||||||
|
instance = Room.databaseBuilder(
|
||||||
|
context.applicationContext,
|
||||||
|
DownloadRecordDatabase::class.java,
|
||||||
|
"download_record_database")
|
||||||
|
.fallbackToDestructiveMigration()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
INSTANCE = instance
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -34,10 +34,10 @@ import androidx.core.content.ContextCompat
|
|||||||
import com.github.kiulian.downloader.YoutubeDownloader
|
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.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.ui.spotify.SpotifyFragment
|
||||||
|
import com.shabinder.spotiflyer.ui.spotify.SpotifyViewModel
|
||||||
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
|
||||||
@ -49,7 +49,7 @@ object SpotifyDownloadHelper {
|
|||||||
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
|
||||||
var sharedViewModel:SharedViewModel? = null
|
var spotifyViewModel: SpotifyViewModel? = 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
|
||||||
@ -66,9 +66,7 @@ object SpotifyDownloadHelper {
|
|||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
total += trackList.size // Adding New Download List Count to StatusBar
|
total += trackList.size // Adding New Download List Count to StatusBar
|
||||||
trackList.forEach {
|
trackList.forEach {
|
||||||
val outputFile:String = Environment.getExternalStorageDirectory().toString() + File.separator +
|
if(it.downloaded == "Downloaded"){//Download Already Present!!
|
||||||
defaultDir + removeIllegalChars(type) + File.separator + (if(subFolder == null){""}else{ removeIllegalChars(subFolder) + File.separator} + removeIllegalChars(it.name!!)+".mp3")
|
|
||||||
if(File(outputFile).exists()){//Download Already Present!!
|
|
||||||
Processed++
|
Processed++
|
||||||
}else{
|
}else{
|
||||||
if(isBrowserLoading){//WebView Busy!!
|
if(isBrowserLoading){//WebView Busy!!
|
||||||
@ -124,7 +122,7 @@ object SpotifyDownloadHelper {
|
|||||||
}
|
}
|
||||||
if(youtubeList.isNotEmpty()){
|
if(youtubeList.isNotEmpty()){
|
||||||
val request = youtubeList[0]
|
val request = youtubeList[0]
|
||||||
sharedViewModel!!.uiScope.launch {
|
spotifyViewModel!!.uiScope.launch {
|
||||||
getYTLink(request.spotifyFragment,request.type,request.subFolder,request.ytDownloader,request.searchQuery,request.track)
|
getYTLink(request.spotifyFragment,request.type,request.subFolder,request.ytDownloader,request.searchQuery,request.track)
|
||||||
}
|
}
|
||||||
youtubeList.remove(request)
|
youtubeList.remove(request)
|
||||||
@ -147,7 +145,7 @@ object SpotifyDownloadHelper {
|
|||||||
|
|
||||||
|
|
||||||
fun downloadFile(subFolder: String?, type: String, track:Track, ytDownloader: YoutubeDownloader?, id: String) {
|
fun downloadFile(subFolder: String?, type: String, track:Track, ytDownloader: YoutubeDownloader?, id: String) {
|
||||||
sharedViewModel!!.uiScope.launch {
|
spotifyViewModel!!.uiScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val video = ytDownloader?.getVideo(id)
|
val video = ytDownloader?.getVideo(id)
|
||||||
val detail = video?.details()
|
val detail = video?.details()
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* 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.downloadHelper
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Environment
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import com.github.kiulian.downloader.model.formats.Format
|
||||||
|
import com.shabinder.spotiflyer.models.DownloadObject
|
||||||
|
import com.shabinder.spotiflyer.models.Track
|
||||||
|
import com.shabinder.spotiflyer.worker.ForegroundService
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object YTDownloadHelper {
|
||||||
|
var context : Context? = null
|
||||||
|
var statusBar: TextView? = null
|
||||||
|
|
||||||
|
fun downloadFile(subFolder: String?, type: String,ytTrack: Track,format: Format?) {
|
||||||
|
format?.let {
|
||||||
|
val url:String = format.url()
|
||||||
|
// Log.i("DHelper Link Found", url)
|
||||||
|
val outputFile:String = Environment.getExternalStorageDirectory().toString() + File.separator +
|
||||||
|
SpotifyDownloadHelper.defaultDir + SpotifyDownloadHelper.removeIllegalChars(type) + File.separator + (if(subFolder == null){""}else{ SpotifyDownloadHelper.removeIllegalChars(subFolder) + File.separator} + SpotifyDownloadHelper.removeIllegalChars(
|
||||||
|
ytTrack.name!!
|
||||||
|
) +".m4a")
|
||||||
|
|
||||||
|
val downloadObject = DownloadObject(
|
||||||
|
track = ytTrack,
|
||||||
|
url = url,
|
||||||
|
outputDir = outputFile
|
||||||
|
)
|
||||||
|
Log.i("DH",outputFile)
|
||||||
|
startService(context!!, downloadObject)
|
||||||
|
statusBar?.visibility= View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private fun startService(context:Context, obj: DownloadObject? = null ) {
|
||||||
|
val serviceIntent = Intent(context, ForegroundService::class.java)
|
||||||
|
serviceIntent.putExtra("object",obj)
|
||||||
|
ContextCompat.startForegroundService(context, serviceIntent)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -40,4 +40,5 @@ data class Track(
|
|||||||
var album: Album? = null,
|
var album: Album? = null,
|
||||||
var external_ids: Map<String?, String?>? = null,
|
var external_ids: Map<String?, String?>? = null,
|
||||||
var popularity: Int? = null,
|
var popularity: Int? = null,
|
||||||
var ytCoverUrl:String? = null):Parcelable
|
var ytCoverUrl:String? = null,
|
||||||
|
var downloaded:String? = "notDownloaded"):Parcelable
|
31
app/src/main/java/com/shabinder/spotiflyer/models/YTTrack.kt
Normal file
31
app/src/main/java/com/shabinder/spotiflyer/models/YTTrack.kt
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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.models
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import kotlinx.android.parcel.Parcelize
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class YTTrack(
|
||||||
|
var id:String?,
|
||||||
|
var title:String?,
|
||||||
|
var duration:Int?,
|
||||||
|
var author:String?,
|
||||||
|
var viewCount:Long?,
|
||||||
|
var thumbnails:List<String?>?
|
||||||
|
):Parcelable
|
@ -20,59 +20,84 @@ package com.shabinder.spotiflyer.recyclerView
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageButton
|
import android.widget.Toast
|
||||||
import android.widget.ImageView
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import android.widget.TextView
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
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.databinding.TrackListItemBinding
|
||||||
|
import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper.context
|
||||||
import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper.getYTLink
|
import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper.getYTLink
|
||||||
import com.shabinder.spotiflyer.models.Track
|
import com.shabinder.spotiflyer.models.Track
|
||||||
import com.shabinder.spotiflyer.ui.spotify.SpotifyFragment
|
import com.shabinder.spotiflyer.ui.spotify.SpotifyFragment
|
||||||
|
import com.shabinder.spotiflyer.ui.spotify.SpotifyViewModel
|
||||||
import com.shabinder.spotiflyer.utils.bindImage
|
import com.shabinder.spotiflyer.utils.bindImage
|
||||||
|
import com.shabinder.spotiflyer.utils.rotateAnim
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
class SpotifyTrackListAdapter:RecyclerView.Adapter<SpotifyTrackListAdapter.ViewHolder>() {
|
class SpotifyTrackListAdapter: ListAdapter<Track,SpotifyTrackListAdapter.ViewHolder>(SpotifyTrackDiffCallback()) {
|
||||||
|
|
||||||
var trackList = listOf<Track>()
|
var spotifyViewModel = SpotifyViewModel()
|
||||||
var totalItems:Int = 0
|
|
||||||
var sharedViewModel = SharedViewModel()
|
|
||||||
var isAlbum:Boolean = false
|
var isAlbum:Boolean = false
|
||||||
var spotifyFragment: SpotifyFragment? = null
|
var spotifyFragment: SpotifyFragment? = null
|
||||||
|
|
||||||
override fun getItemCount():Int = totalItems
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
val layoutInflater = LayoutInflater.from(parent.context)
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
|
|
||||||
val view = layoutInflater.inflate(R.layout.track_list_item,parent,false)
|
val binding = TrackListItemBinding.inflate(layoutInflater,parent,false)
|
||||||
return ViewHolder(view)
|
// val view = layoutInflater.inflate(R.layout.track_list_item,parent,false)
|
||||||
|
return ViewHolder(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
val item = trackList[position]
|
val item = getItem(position)
|
||||||
if(totalItems == 1 || isAlbum){holder.coverImage.visibility = View.GONE}else{
|
if(itemCount ==1 || isAlbum){
|
||||||
sharedViewModel.uiScope.launch {
|
holder.binding.imageUrl.visibility = View.GONE}else{
|
||||||
bindImage(holder.coverImage, item.album!!.images?.get(0)?.url)
|
spotifyViewModel.uiScope.launch {
|
||||||
|
bindImage(holder.binding.imageUrl, item.album!!.images?.get(0)?.url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.trackName.text = "${if(item.name!!.length > 17){"${item.name!!.subSequence(0,16)}..."}else{item.name}}"
|
holder.binding.trackName.text = "${if(item.name!!.length > 17){"${item.name!!.subSequence(0,16)}..."}else{item.name}}"
|
||||||
holder.artistName.text = "${item.artists?.get(0)?.name?:""}..."
|
holder.binding.artist.text = "${item.artists?.get(0)?.name?:""}..."
|
||||||
holder.duration.text = "${item.duration_ms/1000/60} minutes, ${(item.duration_ms/1000)%60} sec"
|
holder.binding.duration.text = "${item.duration_ms/1000/60} minutes, ${(item.duration_ms/1000)%60} sec"
|
||||||
holder.downloadBtn.setOnClickListener{
|
when (item.downloaded) {
|
||||||
sharedViewModel.uiScope.launch {
|
"Downloaded" -> {
|
||||||
getYTLink(spotifyFragment,"Tracks",null,sharedViewModel.ytDownloader.value,"${item.name} ${item.artists?.get(0)!!.name?:""}",track = item)
|
holder.binding.btnDownload.setImageResource(R.drawable.ic_tick)
|
||||||
|
holder.binding.btnDownload.clearAnimation()
|
||||||
}
|
}
|
||||||
|
"Downloading" -> {
|
||||||
|
holder.binding.btnDownload.setImageResource(R.drawable.ic_refresh)
|
||||||
|
rotateAnim(holder.binding.btnDownload)
|
||||||
|
}
|
||||||
|
"notDownloaded" -> {
|
||||||
|
holder.binding.btnDownload.setImageResource(R.drawable.ic_arrow)
|
||||||
|
holder.binding.btnDownload.clearAnimation()
|
||||||
|
holder.binding.btnDownload.setOnClickListener{
|
||||||
|
Toast.makeText(context,"Starting Download",Toast.LENGTH_SHORT).show()
|
||||||
|
holder.binding.btnDownload.setImageResource(R.drawable.ic_refresh)
|
||||||
|
rotateAnim(it)
|
||||||
|
item.downloaded = "Downloading"
|
||||||
|
spotifyViewModel.uiScope.launch {
|
||||||
|
getYTLink(spotifyFragment,"Tracks",null,spotifyViewModel.ytDownloader,"${item.name} ${item.artists?.get(0)!!.name?:""}",track = item)
|
||||||
|
}
|
||||||
|
notifyItemChanged(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class ViewHolder(val binding: TrackListItemBinding) : RecyclerView.ViewHolder(binding.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
class SpotifyTrackDiffCallback: DiffUtil.ItemCallback<Track>(){
|
||||||
|
override fun areItemsTheSame(oldItem: Track, newItem: Track): Boolean {
|
||||||
|
return oldItem.name == newItem.name
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: Track, newItem: Track): Boolean {
|
||||||
|
return oldItem == newItem //Downloaded Check
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
|
|
||||||
val trackName:TextView = itemView.findViewById(R.id.track_name)
|
|
||||||
val artistName:TextView = itemView.findViewById(R.id.artist)
|
|
||||||
val duration:TextView = itemView.findViewById(R.id.duration)
|
|
||||||
val downloadBtn:ImageButton = itemView.findViewById(R.id.btn_download)
|
|
||||||
val coverImage:ImageView = itemView.findViewById(R.id.imageUrl)
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* 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.recyclerView
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
|
import com.github.kiulian.downloader.model.formats.Format
|
||||||
|
import com.shabinder.spotiflyer.SharedViewModel
|
||||||
|
import com.shabinder.spotiflyer.databinding.TrackListItemBinding
|
||||||
|
import com.shabinder.spotiflyer.downloadHelper.YTDownloadHelper
|
||||||
|
import com.shabinder.spotiflyer.models.Track
|
||||||
|
import com.shabinder.spotiflyer.utils.bindImage
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class YoutubeTrackListAdapter: ListAdapter<Track,SpotifyTrackListAdapter.ViewHolder>(YouTubeTrackDiffCallback()) {
|
||||||
|
|
||||||
|
var format:Format? = null
|
||||||
|
var sharedViewModel = SharedViewModel()
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(
|
||||||
|
parent: ViewGroup,
|
||||||
|
viewType: Int
|
||||||
|
): SpotifyTrackListAdapter.ViewHolder {
|
||||||
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
|
val binding = TrackListItemBinding.inflate(layoutInflater,parent,false)
|
||||||
|
// val view = layoutInflater.inflate(R.layout.track_list_item,parent,false)
|
||||||
|
return SpotifyTrackListAdapter.ViewHolder(binding)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: SpotifyTrackListAdapter.ViewHolder, position: Int) {
|
||||||
|
val item = getItem(position)
|
||||||
|
if(itemCount == 1){
|
||||||
|
holder.binding.imageUrl.visibility = View.GONE}else{
|
||||||
|
sharedViewModel.uiScope.launch {
|
||||||
|
bindImage(holder.binding.imageUrl, item.ytCoverUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.binding.trackName.text = "${if(item.name!!.length > 17){"${item.name!!.subSequence(0,16)}..."}else{item.name}}"
|
||||||
|
holder.binding.artist.text = "${item.artists?.get(0)?.name?:""}..."
|
||||||
|
holder.binding.duration.text = "${item.duration_ms/1000/60} minutes, ${(item.duration_ms/1000)%60} sec"
|
||||||
|
holder.binding.btnDownload.setOnClickListener{
|
||||||
|
sharedViewModel.uiScope.launch {
|
||||||
|
YTDownloadHelper.downloadFile(null,"YT_Downloads",item,format)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class YouTubeTrackDiffCallback: DiffUtil.ItemCallback<Track>(){
|
||||||
|
override fun areItemsTheSame(oldItem: Track, newItem: Track): Boolean {
|
||||||
|
return oldItem.name == newItem.name
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: Track, newItem: Track): Boolean {
|
||||||
|
return oldItem == newItem
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* 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.mainfragment
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.SpannableStringBuilder
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.databinding.DataBindingUtil
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import com.shabinder.spotiflyer.R
|
||||||
|
import com.shabinder.spotiflyer.SharedViewModel
|
||||||
|
import com.shabinder.spotiflyer.databinding.MainFragmentBinding
|
||||||
|
|
||||||
|
class MainFragment : Fragment() {
|
||||||
|
|
||||||
|
private lateinit var viewModel: MainViewModel
|
||||||
|
private lateinit var sharedViewModel: SharedViewModel
|
||||||
|
private lateinit var binding: MainFragmentBinding
|
||||||
|
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
binding = DataBindingUtil.inflate(inflater,R.layout.main_fragment,container,false)
|
||||||
|
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
|
||||||
|
initializeAll()
|
||||||
|
|
||||||
|
binding.btnSearch.setOnClickListener {
|
||||||
|
val link = binding.linkSearch.text.toString()
|
||||||
|
if (link.contains("spotify",true)){
|
||||||
|
findNavController().navigate(MainFragmentDirections.actionMainFragmentToSpotifyFragment(link))
|
||||||
|
}else if(link.contains("youtube.com",true) || link.contains("youtu.be",true) ){
|
||||||
|
findNavController().navigate(MainFragmentDirections.actionMainFragmentToYoutubeFragment(link))
|
||||||
|
}else{Toast.makeText(context,"Link is Not Valid",Toast.LENGTH_SHORT).show()}
|
||||||
|
}
|
||||||
|
handleIntent()
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializeAll() {
|
||||||
|
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
|
||||||
|
setUpUsageText()
|
||||||
|
openYTButton()
|
||||||
|
openSpotifyButton()
|
||||||
|
openGithubButton()
|
||||||
|
openInstaButton()
|
||||||
|
openLinkedInButton()
|
||||||
|
binding.btnDonate.setOnClickListener {
|
||||||
|
sharedViewModel.easyUpiPayment?.startPayment()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Intent If there is any!
|
||||||
|
**/
|
||||||
|
private fun handleIntent() {
|
||||||
|
sharedViewModel.accessToken.observe(viewLifecycleOwner, Observer {
|
||||||
|
//Waiting for Authentication to Finish with Spotify()Access Token Observe
|
||||||
|
if (it != ""){
|
||||||
|
if(sharedViewModel.intentString != ""){
|
||||||
|
binding.linkSearch.setText(sharedViewModel.intentString)
|
||||||
|
binding.btnSearch.performClick()
|
||||||
|
sharedViewModel.intentString = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
spanStringBuilder.append(getText(R.string.d_four)).append("\n")
|
||||||
|
binding.usage.text = spanStringBuilder
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementing buttons
|
||||||
|
**/
|
||||||
|
private fun openSpotifyButton() {
|
||||||
|
val manager: PackageManager = requireActivity().packageManager
|
||||||
|
try {
|
||||||
|
val i = manager.getLaunchIntentForPackage("com.spotify.music")
|
||||||
|
?: throw PackageManager.NameNotFoundException()
|
||||||
|
i.addCategory(Intent.CATEGORY_LAUNCHER)
|
||||||
|
binding.btnSpotify.setOnClickListener { startActivity(i) }
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
val uri: Uri =
|
||||||
|
Uri.parse("http://open.spotify.com")
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||||
|
binding.btnSpotify.setOnClickListener {
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun openYTButton() {
|
||||||
|
val manager: PackageManager = requireActivity().packageManager
|
||||||
|
try {
|
||||||
|
val i = manager.getLaunchIntentForPackage("com.google.android.youtube")
|
||||||
|
?: throw PackageManager.NameNotFoundException()
|
||||||
|
i.addCategory(Intent.CATEGORY_LAUNCHER)
|
||||||
|
binding.btnYoutube.setOnClickListener { startActivity(i) }
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
val uri: Uri =
|
||||||
|
Uri.parse("http://m.youtube.com")
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||||
|
binding.btnYoutube.setOnClickListener {
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun openGithubButton() {
|
||||||
|
val uri: Uri =
|
||||||
|
Uri.parse("http://github.com/Shabinder/SpotiFlyer")
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||||
|
binding.btnGithubSpotify.setOnClickListener {
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun openLinkedInButton() {
|
||||||
|
val uri: Uri =
|
||||||
|
Uri.parse("https://in.linkedin.com/in/shabinder")
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||||
|
binding.btnLinkedin.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.developerInstaSpotify.setOnClickListener {
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* 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.mainfragment
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
|
||||||
|
class MainViewModel : ViewModel() {
|
||||||
|
// TODO: Implement the ViewModel
|
||||||
|
}
|
@ -18,7 +18,10 @@
|
|||||||
package com.shabinder.spotiflyer.ui.spotify
|
package com.shabinder.spotiflyer.ui.spotify
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
@ -47,6 +50,7 @@ import com.shabinder.spotiflyer.models.Track
|
|||||||
import com.shabinder.spotiflyer.recyclerView.SpotifyTrackListAdapter
|
import com.shabinder.spotiflyer.recyclerView.SpotifyTrackListAdapter
|
||||||
import com.shabinder.spotiflyer.utils.bindImage
|
import com.shabinder.spotiflyer.utils.bindImage
|
||||||
import com.shabinder.spotiflyer.utils.copyTo
|
import com.shabinder.spotiflyer.utils.copyTo
|
||||||
|
import com.shabinder.spotiflyer.utils.rotateAnim
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -60,6 +64,8 @@ class SpotifyFragment : Fragment() {
|
|||||||
private lateinit var sharedViewModel: SharedViewModel
|
private lateinit var sharedViewModel: SharedViewModel
|
||||||
private lateinit var adapterSpotify:SpotifyTrackListAdapter
|
private lateinit var adapterSpotify:SpotifyTrackListAdapter
|
||||||
private var webView: WebView? = null
|
private var webView: WebView? = null
|
||||||
|
private var intentFilter:IntentFilter? = null
|
||||||
|
private var updateUIReceiver: BroadcastReceiver? = null
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
@ -71,6 +77,7 @@ class SpotifyFragment : Fragment() {
|
|||||||
adapterSpotify = SpotifyTrackListAdapter()
|
adapterSpotify = SpotifyTrackListAdapter()
|
||||||
initializeAll()
|
initializeAll()
|
||||||
initializeLiveDataObservers()
|
initializeLiveDataObservers()
|
||||||
|
initializeBroadcast()
|
||||||
|
|
||||||
val args = SpotifyFragmentArgs.fromBundle(requireArguments())
|
val args = SpotifyFragmentArgs.fromBundle(requireArguments())
|
||||||
val spotifyLink = args.link
|
val spotifyLink = args.link
|
||||||
@ -95,6 +102,19 @@ class SpotifyFragment : Fragment() {
|
|||||||
spotifyViewModel.spotifySearch(type,link)
|
spotifyViewModel.spotifySearch(type,link)
|
||||||
if(type=="album")adapterSpotify.isAlbum = true
|
if(type=="album")adapterSpotify.isAlbum = true
|
||||||
binding.btnDownloadAllSpotify.setOnClickListener {
|
binding.btnDownloadAllSpotify.setOnClickListener {
|
||||||
|
for (track in spotifyViewModel.trackList.value!!){
|
||||||
|
if(track.downloaded != "Downloaded"){
|
||||||
|
track.downloaded = "Downloading"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.btnDownloadAllSpotify.visibility = View.GONE
|
||||||
|
binding.downloadingFabSpotify.visibility = View.VISIBLE
|
||||||
|
rotateAnim(binding.downloadingFabSpotify)
|
||||||
|
for (track in spotifyViewModel.trackList.value!!){
|
||||||
|
if(track.downloaded != "Downloaded"){
|
||||||
|
adapterSpotify.notifyItemChanged(spotifyViewModel.trackList.value!!.indexOf(track))
|
||||||
|
}
|
||||||
|
}
|
||||||
showToast("Starting Download in Few Seconds")
|
showToast("Starting Download in Few Seconds")
|
||||||
loadAllImages(spotifyViewModel.trackList.value!!)
|
loadAllImages(spotifyViewModel.trackList.value!!)
|
||||||
spotifyViewModel.uiScope.launch {
|
spotifyViewModel.uiScope.launch {
|
||||||
@ -111,6 +131,41 @@ class SpotifyFragment : Fragment() {
|
|||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
initializeBroadcast()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializeBroadcast() {
|
||||||
|
intentFilter = IntentFilter()
|
||||||
|
intentFilter?.addAction("track_download_completed")
|
||||||
|
|
||||||
|
updateUIReceiver = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
|
//UI update here
|
||||||
|
if (intent != null){
|
||||||
|
val track = intent.getParcelableExtra<Track?>("track")
|
||||||
|
track?.let {
|
||||||
|
val position: Int = spotifyViewModel.trackList.value?.indexOf(track)!!
|
||||||
|
Log.i("Track","Download Completed Intent :$position")
|
||||||
|
track.downloaded = "Downloaded"
|
||||||
|
if(position != -1) {
|
||||||
|
spotifyViewModel.trackList.value?.set(position, track)
|
||||||
|
adapterSpotify.notifyItemChanged(position)
|
||||||
|
checkIfAllDownloaded()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requireActivity().registerReceiver(updateUIReceiver, intentFilter)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
requireActivity().unregisterReceiver(updateUIReceiver)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*Live Data Observers
|
*Live Data Observers
|
||||||
**/
|
**/
|
||||||
@ -127,8 +182,9 @@ class SpotifyFragment : Fragment() {
|
|||||||
**/
|
**/
|
||||||
spotifyViewModel.trackList.observe(viewLifecycleOwner, Observer {
|
spotifyViewModel.trackList.observe(viewLifecycleOwner, Observer {
|
||||||
if (it.isNotEmpty()){
|
if (it.isNotEmpty()){
|
||||||
Log.i("SpotifyFragment","TrackList Fetched!")
|
Log.i("SpotifyFragment","TrackList Updated")
|
||||||
adapterConfig(it)
|
adapterConfig(it)
|
||||||
|
checkIfAllDownloaded()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -140,6 +196,19 @@ class SpotifyFragment : Fragment() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkIfAllDownloaded() {
|
||||||
|
var allDownloaded = true
|
||||||
|
for (track in spotifyViewModel.trackList.value!!){
|
||||||
|
if (track.downloaded != "Downloaded")allDownloaded = false
|
||||||
|
}
|
||||||
|
if(allDownloaded){
|
||||||
|
binding.downloadingFabSpotify.setImageResource(R.drawable.ic_tick)
|
||||||
|
binding.btnDownloadAllSpotify.visibility = View.GONE
|
||||||
|
binding.downloadingFabSpotify.visibility = View.VISIBLE
|
||||||
|
binding.downloadingFabSpotify.clearAnimation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic Initialization
|
* Basic Initialization
|
||||||
**/
|
**/
|
||||||
@ -155,7 +224,7 @@ class SpotifyFragment : Fragment() {
|
|||||||
})
|
})
|
||||||
SpotifyDownloadHelper.webView = binding.webViewSpotify
|
SpotifyDownloadHelper.webView = binding.webViewSpotify
|
||||||
SpotifyDownloadHelper.context = requireContext()
|
SpotifyDownloadHelper.context = requireContext()
|
||||||
SpotifyDownloadHelper.sharedViewModel = sharedViewModel
|
SpotifyDownloadHelper.spotifyViewModel = spotifyViewModel
|
||||||
SpotifyDownloadHelper.statusBar = binding.StatusBarSpotify
|
SpotifyDownloadHelper.statusBar = binding.StatusBarSpotify
|
||||||
binding.trackListSpotify.adapter = adapterSpotify
|
binding.trackListSpotify.adapter = adapterSpotify
|
||||||
}
|
}
|
||||||
@ -214,11 +283,9 @@ class SpotifyFragment : Fragment() {
|
|||||||
* Configure Recycler View Adapter
|
* Configure Recycler View Adapter
|
||||||
**/
|
**/
|
||||||
private fun adapterConfig(trackList: List<Track>){
|
private fun adapterConfig(trackList: List<Track>){
|
||||||
adapterSpotify.trackList = trackList
|
|
||||||
adapterSpotify.totalItems = trackList.size
|
|
||||||
adapterSpotify.spotifyFragment = this
|
adapterSpotify.spotifyFragment = this
|
||||||
adapterSpotify.sharedViewModel = sharedViewModel
|
adapterSpotify.spotifyViewModel = spotifyViewModel
|
||||||
adapterSpotify.notifyDataSetChanged()
|
adapterSpotify.submitList(trackList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,16 +25,18 @@ import com.shabinder.spotiflyer.models.Album
|
|||||||
import com.shabinder.spotiflyer.models.Playlist
|
import com.shabinder.spotiflyer.models.Playlist
|
||||||
import com.shabinder.spotiflyer.models.Track
|
import com.shabinder.spotiflyer.models.Track
|
||||||
import com.shabinder.spotiflyer.utils.SpotifyService
|
import com.shabinder.spotiflyer.utils.SpotifyService
|
||||||
|
import com.shabinder.spotiflyer.utils.finalOutputDir
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class SpotifyViewModel: ViewModel() {
|
class SpotifyViewModel: ViewModel() {
|
||||||
|
|
||||||
var folderType:String = ""
|
var folderType:String = ""
|
||||||
var subFolder:String = ""
|
var subFolder:String = ""
|
||||||
var trackList = MutableLiveData<List<Track>>()
|
var trackList = MutableLiveData<MutableList<Track>>()
|
||||||
private val loading = "Loading"
|
private val loading = "Loading"
|
||||||
var title = MutableLiveData<String>().apply { value = loading }
|
var title = MutableLiveData<String>().apply { value = loading }
|
||||||
var coverUrl = MutableLiveData<String>().apply { value = loading }
|
var coverUrl = MutableLiveData<String>().apply { value = loading }
|
||||||
@ -50,42 +52,57 @@ class SpotifyViewModel: ViewModel() {
|
|||||||
"track" -> {
|
"track" -> {
|
||||||
uiScope.launch {
|
uiScope.launch {
|
||||||
val trackObject = getTrackDetails(link)
|
val trackObject = getTrackDetails(link)
|
||||||
|
folderType = "Tracks"
|
||||||
val tempTrackList = mutableListOf<Track>()
|
val tempTrackList = mutableListOf<Track>()
|
||||||
tempTrackList.add(trackObject!!)
|
if(File(finalOutputDir(trackObject?.name!!,folderType,subFolder)).exists()){//Download Already Present!!
|
||||||
|
trackObject.downloaded = "Downloaded"
|
||||||
|
}
|
||||||
|
tempTrackList.add(trackObject)
|
||||||
trackList.value = tempTrackList
|
trackList.value = tempTrackList
|
||||||
title.value = trackObject.name
|
title.value = trackObject.name
|
||||||
coverUrl.value = trackObject.album!!.images?.get(0)!!.url!!
|
coverUrl.value = trackObject.album!!.images?.get(0)!!.url!!
|
||||||
folderType = "Tracks"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"album" -> {
|
"album" -> {
|
||||||
uiScope.launch {
|
uiScope.launch {
|
||||||
val albumObject = getAlbumDetails(link)
|
val albumObject = getAlbumDetails(link)
|
||||||
|
folderType = "Albums"
|
||||||
|
subFolder = albumObject?.name!!
|
||||||
val tempTrackList = mutableListOf<Track>()
|
val tempTrackList = mutableListOf<Track>()
|
||||||
albumObject!!.tracks?.items?.forEach { tempTrackList.add(it) }
|
albumObject.tracks?.items?.forEach {
|
||||||
|
if(File(finalOutputDir(it.name!!,folderType,subFolder)).exists()){//Download Already Present!!
|
||||||
|
it.downloaded = "Downloaded"
|
||||||
|
}
|
||||||
|
tempTrackList.add(it)
|
||||||
|
}
|
||||||
trackList.value = tempTrackList
|
trackList.value = tempTrackList
|
||||||
title.value = albumObject.name
|
title.value = albumObject.name
|
||||||
coverUrl.value = albumObject.images?.get(0)!!.url!!
|
coverUrl.value = albumObject.images?.get(0)!!.url!!
|
||||||
folderType = "Albums"
|
|
||||||
subFolder = albumObject.name!!
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"playlist" -> {
|
"playlist" -> {
|
||||||
uiScope.launch {
|
uiScope.launch {
|
||||||
val playlistObject = getPlaylistDetails(link)
|
val playlistObject = getPlaylistDetails(link)
|
||||||
|
folderType = "Playlists"
|
||||||
|
subFolder = playlistObject?.name!!
|
||||||
val tempTrackList = mutableListOf<Track>()
|
val tempTrackList = mutableListOf<Track>()
|
||||||
playlistObject!!.tracks?.items?.forEach {
|
playlistObject.tracks?.items?.forEach {
|
||||||
it.track?.let { it1 -> tempTrackList.add(it1) }
|
it.track?.let {
|
||||||
|
it1 -> if(File(finalOutputDir(it1.name!!,folderType,subFolder)).exists()){//Download Already Present!!
|
||||||
|
it1.downloaded = "Downloaded"
|
||||||
|
Log.i("ViewModel123","${it1.name} Downloaded")
|
||||||
}
|
}
|
||||||
|
tempTrackList.add(it1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.i("ViewModel",tempTrackList.size.toString())
|
||||||
|
Log.i("ViewModel",playlistObject.tracks?.items?.size.toString())
|
||||||
trackList.value = tempTrackList
|
trackList.value = tempTrackList
|
||||||
Log.i("VIEW MODEL",playlistObject.tracks?.items!!.toString())
|
|
||||||
Log.i("VIEW MODEL",trackList.value?.size.toString())
|
|
||||||
title.value = playlistObject.name
|
title.value = playlistObject.name
|
||||||
coverUrl.value = playlistObject.images?.get(0)!!.url!!
|
coverUrl.value = playlistObject.images?.get(0)!!.url!!
|
||||||
folderType = "Playlists"
|
|
||||||
subFolder = playlistObject.name!!
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"episode" -> {//TODO
|
"episode" -> {//TODO
|
||||||
|
@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* 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.youtube
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.databinding.DataBindingUtil
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.shabinder.spotiflyer.R
|
||||||
|
import com.shabinder.spotiflyer.SharedViewModel
|
||||||
|
import com.shabinder.spotiflyer.databinding.YoutubeFragmentBinding
|
||||||
|
import com.shabinder.spotiflyer.downloadHelper.YTDownloadHelper
|
||||||
|
import com.shabinder.spotiflyer.models.Track
|
||||||
|
import com.shabinder.spotiflyer.recyclerView.YoutubeTrackListAdapter
|
||||||
|
import com.shabinder.spotiflyer.utils.bindImage
|
||||||
|
|
||||||
|
class YoutubeFragment : Fragment() {
|
||||||
|
|
||||||
|
private lateinit var binding:YoutubeFragmentBinding
|
||||||
|
private lateinit var youtubeViewModel: YoutubeViewModel
|
||||||
|
private lateinit var sharedViewModel: SharedViewModel
|
||||||
|
private lateinit var adapter : YoutubeTrackListAdapter
|
||||||
|
private val sampleDomain1 = "youtube.com"
|
||||||
|
private val sampleDomain2 = "youtu.be"
|
||||||
|
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
binding = DataBindingUtil.inflate(inflater,R.layout.youtube_fragment,container,false)
|
||||||
|
youtubeViewModel = ViewModelProvider(this).get(YoutubeViewModel::class.java)
|
||||||
|
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
|
||||||
|
adapter = YoutubeTrackListAdapter()
|
||||||
|
YTDownloadHelper.context = requireContext()
|
||||||
|
YTDownloadHelper.statusBar = binding.StatusBarYoutube
|
||||||
|
binding.trackListYoutube.adapter = adapter
|
||||||
|
sharedViewModel.ytDownloader.observe(viewLifecycleOwner, Observer {
|
||||||
|
youtubeViewModel.ytDownloader = it
|
||||||
|
})
|
||||||
|
initializeLiveDataObservers()
|
||||||
|
|
||||||
|
val args = YoutubeFragmentArgs.fromBundle(requireArguments())
|
||||||
|
val link = args.link
|
||||||
|
youtubeSearch(link)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun youtubeSearch(linkSearch:String) {
|
||||||
|
val link = linkSearch.removePrefix("https://").removePrefix("http://")
|
||||||
|
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 coverUrl = "https://i.ytimg.com/vi/$searchId/maxresdefault.jpg"
|
||||||
|
youtubeViewModel.getYTTrack(searchId)
|
||||||
|
binding.btnDownloadAllYoutube.setOnClickListener {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
}else{showToast("Your Youtube Link is not of a Video!!")}
|
||||||
|
}else(showToast("Your Youtube Link is not of a Video!!"))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializeLiveDataObservers() {
|
||||||
|
/**
|
||||||
|
* CoverUrl Binding Observer!
|
||||||
|
**/
|
||||||
|
youtubeViewModel.coverUrl.observe(viewLifecycleOwner, Observer {
|
||||||
|
if(it!="Loading") bindImage(binding.youtubeCoverImage,it)
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TrackList Binding Observer!
|
||||||
|
**/
|
||||||
|
youtubeViewModel.ytTrack.observe(viewLifecycleOwner, Observer {
|
||||||
|
val list = mutableListOf<Track>()
|
||||||
|
list.add(it)
|
||||||
|
adapterConfig(list)
|
||||||
|
})
|
||||||
|
|
||||||
|
youtubeViewModel.format.observe(viewLifecycleOwner, Observer {
|
||||||
|
adapter.format = it
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Title Binding Observer!
|
||||||
|
**/
|
||||||
|
youtubeViewModel.title.observe(viewLifecycleOwner, Observer {
|
||||||
|
binding.titleViewYoutube.text = it
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure Recycler View Adapter
|
||||||
|
**/
|
||||||
|
private fun adapterConfig(list:List<Track>){
|
||||||
|
adapter.sharedViewModel = sharedViewModel
|
||||||
|
adapter.submitList(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Util. Function to create toasts!
|
||||||
|
**/
|
||||||
|
private 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,82 @@
|
|||||||
|
/*
|
||||||
|
* 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.youtube
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.github.kiulian.downloader.YoutubeDownloader
|
||||||
|
import com.github.kiulian.downloader.model.formats.Format
|
||||||
|
import com.github.kiulian.downloader.model.quality.AudioQuality
|
||||||
|
import com.shabinder.spotiflyer.models.Artist
|
||||||
|
import com.shabinder.spotiflyer.models.Track
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
|
||||||
|
class YoutubeViewModel : ViewModel() {
|
||||||
|
|
||||||
|
val ytTrack = MutableLiveData<Track>()
|
||||||
|
val format = MutableLiveData<Format>()
|
||||||
|
private val loading = "Loading"
|
||||||
|
var title = MutableLiveData<String>().apply { value = "\"Loading!\"" }
|
||||||
|
var coverUrl = MutableLiveData<String>().apply { value = loading }
|
||||||
|
var ytDownloader: YoutubeDownloader? = null
|
||||||
|
|
||||||
|
|
||||||
|
private var viewModelJob = Job()
|
||||||
|
val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
|
||||||
|
|
||||||
|
|
||||||
|
fun getYTTrack(searchId:String) {
|
||||||
|
uiScope.launch {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
Log.i("YT View Model",searchId)
|
||||||
|
val video = ytDownloader?.getVideo(searchId)
|
||||||
|
val detail = video?.details()
|
||||||
|
val name = detail?.title()?.replace(detail.author()!!.toUpperCase(),"",true) ?: detail?.title()
|
||||||
|
Log.i("YT View Model",detail.toString())
|
||||||
|
ytTrack.postValue(
|
||||||
|
Track(
|
||||||
|
id = searchId,
|
||||||
|
name = name,
|
||||||
|
artists = listOf<Artist>(Artist(name = detail?.author())),
|
||||||
|
duration_ms = detail?.lengthSeconds()?.times(1000)?.toLong()?:0,
|
||||||
|
ytCoverUrl = "https://i.ytimg.com/vi/$searchId/maxresdefault.jpg"
|
||||||
|
))
|
||||||
|
coverUrl.postValue("https://i.ytimg.com/vi/$searchId/maxresdefault.jpg")
|
||||||
|
title.postValue(
|
||||||
|
if(name?.length!! > 17){"${name.subSequence(0,16)}..."}else{name}
|
||||||
|
)
|
||||||
|
format.postValue(try {
|
||||||
|
video?.findAudioWithQuality(AudioQuality.high)?.get(0) as Format
|
||||||
|
} catch (e: IndexOutOfBoundsException) {
|
||||||
|
try {
|
||||||
|
video?.findAudioWithQuality(AudioQuality.medium)?.get(0) as Format
|
||||||
|
} catch (e: IndexOutOfBoundsException) {
|
||||||
|
try {
|
||||||
|
video?.findAudioWithQuality(AudioQuality.low)?.get(0) as Format
|
||||||
|
} catch (e: IndexOutOfBoundsException) {
|
||||||
|
Log.i("YTDownloader", e.toString())
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -21,6 +21,10 @@ import android.graphics.Bitmap
|
|||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import android.view.animation.Animation
|
||||||
|
import android.view.animation.LinearInterpolator
|
||||||
|
import android.view.animation.RotateAnimation
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.databinding.BindingAdapter
|
import androidx.databinding.BindingAdapter
|
||||||
@ -39,6 +43,25 @@ import java.io.File
|
|||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
|
fun finalOutputDir(itemName:String,type:String, subFolder:String?=null): String{
|
||||||
|
return Environment.getExternalStorageDirectory().toString() + File.separator +
|
||||||
|
SpotifyDownloadHelper.defaultDir + SpotifyDownloadHelper.removeIllegalChars(type) + File.separator +
|
||||||
|
(if(subFolder == null){""}else{ SpotifyDownloadHelper.removeIllegalChars(subFolder) + File.separator}
|
||||||
|
+ SpotifyDownloadHelper.removeIllegalChars(itemName) +".mp3")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun rotateAnim(view: View){
|
||||||
|
val rotate = RotateAnimation(
|
||||||
|
0F, 360F,
|
||||||
|
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f
|
||||||
|
)
|
||||||
|
rotate.duration = 1000
|
||||||
|
rotate.repeatCount = Animation.INFINITE
|
||||||
|
rotate.repeatMode = Animation.INFINITE
|
||||||
|
rotate.interpolator = LinearInterpolator()
|
||||||
|
view.animation = rotate
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@BindingAdapter("imageUrl")
|
@BindingAdapter("imageUrl")
|
||||||
fun bindImage(imgView: ImageView, imgUrl: String?) {
|
fun bindImage(imgView: ImageView, imgUrl: String?) {
|
||||||
|
@ -98,9 +98,15 @@ class ForegroundService : Service(){
|
|||||||
.setDownloadConcurrentLimit(4)
|
.setDownloadConcurrentLimit(4)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fetch = Fetch.Impl.getInstance(fetchConfiguration)
|
Fetch.setDefaultInstanceConfiguration(fetchConfiguration)
|
||||||
|
|
||||||
|
fetch = Fetch.getDefaultInstance()
|
||||||
// fetch?.enableLogging(true)
|
// fetch?.enableLogging(true)
|
||||||
fetch?.addListener(fetchListener)
|
fetch?.addListener(fetchListener)
|
||||||
|
//clearing all not completed Downloads
|
||||||
|
//Starting fresh
|
||||||
|
fetch?.removeAll()
|
||||||
|
|
||||||
startForeground()
|
startForeground()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,11 +224,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.")
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
stopForeground(true)
|
// stopForeground(true)
|
||||||
} else {
|
// } else {
|
||||||
stopSelf()//System will automatically close it
|
stopSelf()//System will automatically close it
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,12 +313,17 @@ 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){
|
for (message in messageList){
|
||||||
if( message == "Downloading ${track?.name}"){
|
if( message == "Downloading ${track?.name}"){
|
||||||
messageList[messageList.indexOf(message)] = ""
|
messageList[messageList.indexOf(message)] = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Notify Download Completed
|
||||||
|
val intent = Intent()
|
||||||
|
.setAction("track_download_completed")
|
||||||
|
.putExtra("track",track)
|
||||||
|
this@ForegroundService.sendBroadcast(intent)
|
||||||
|
|
||||||
|
|
||||||
serviceScope.launch {
|
serviceScope.launch {
|
||||||
try{
|
try{
|
||||||
|
@ -1,4 +1,21 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
<gradient
|
<gradient
|
||||||
android:angle="90"
|
android:angle="90"
|
||||||
@ -6,5 +23,4 @@
|
|||||||
android:endColor="#0374FFF4"
|
android:endColor="#0374FFF4"
|
||||||
android:startColor="#45F700FF" />
|
android:startColor="#45F700FF" />
|
||||||
<corners android:radius="0dp" />
|
<corners android:radius="0dp" />
|
||||||
|
|
||||||
</shape>
|
</shape>
|
@ -1,10 +1,27 @@
|
|||||||
|
<!--
|
||||||
|
~ 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="26dp"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="26dp"
|
||||||
android:height="22dp" android:viewportWidth="512" android:viewportHeight="512">
|
android:height="22dp" android:viewportWidth="512" android:viewportHeight="512">
|
||||||
<path android:fillColor="#0AFF02" android:pathData="m296,288 l60,-60c7.73,-7.73 17.86,-11.6 28,-11.6 10.055,0 20.101,3.806 27.806,11.407 15.612,15.402 15.207,41.18 -0.3,56.687l-134.293,134.293c-11.716,11.716 -30.711,11.716 -42.426,0l-134.787,-134.787c-7.73,-7.73 -11.6,-17.86 -11.6,-28 0,-10.055 3.806,-20.101 11.407,-27.806 15.402,-15.612 41.18,-15.207 56.687,0.3l59.506,59.506v-232c0,-22.091 17.909,-40 40,-40 22.091,0 40,17.909 40,40z"/>
|
<path android:fillColor="#000000" android:pathData="m296,288 l60,-60c7.73,-7.73 17.86,-11.6 28,-11.6 10.055,0 20.101,3.806 27.806,11.407 15.612,15.402 15.207,41.18 -0.3,56.687l-134.293,134.293c-11.716,11.716 -30.711,11.716 -42.426,0l-134.787,-134.787c-7.73,-7.73 -11.6,-17.86 -11.6,-28 0,-10.055 3.806,-20.101 11.407,-27.806 15.402,-15.612 41.18,-15.207 56.687,0.3l59.506,59.506v-232c0,-22.091 17.909,-40 40,-40 22.091,0 40,17.909 40,40z"/>
|
||||||
<path android:fillColor="#218F02" android:pathData="m411.51,284.49 l-134.3,134.3c-11.71,11.71 -30.71,11.71 -42.42,0l-12.74,-12.74c10.69,4.06 23.23,1.77 31.84,-6.84l134.29,-134.29c12.51,-12.51 15.19,-31.7 7.57,-46.74 5.86,1.81 11.39,5.03 16.06,9.63 15.61,15.4 15.2,41.18 -0.3,56.68z"/>
|
<path android:fillColor="#000000" android:pathData="m411.51,284.49 l-134.3,134.3c-11.71,11.71 -30.71,11.71 -42.42,0l-12.74,-12.74c10.69,4.06 23.23,1.77 31.84,-6.84l134.29,-134.29c12.51,-12.51 15.19,-31.7 7.57,-46.74 5.86,1.81 11.39,5.03 16.06,9.63 15.61,15.4 15.2,41.18 -0.3,56.68z"/>
|
||||||
<path android:fillColor="#218F02" android:pathData="m251.88,27.72c-3.46,-3.46 -7.55,-6.29 -12.08,-8.3 4.95,-2.2 10.43,-3.42 16.2,-3.42 11.04,0 21.04,4.48 28.28,11.72s11.72,17.24 11.72,28.28v232l-15.329,15.329c-6.3,6.3 -17.071,1.838 -17.071,-7.071v-240.258c0,-11.04 -4.48,-21.04 -11.72,-28.28z"/>
|
<path android:fillColor="#000000" android:pathData="m251.88,27.72c-3.46,-3.46 -7.55,-6.29 -12.08,-8.3 4.95,-2.2 10.43,-3.42 16.2,-3.42 11.04,0 21.04,4.48 28.28,11.72s11.72,17.24 11.72,28.28v232l-15.329,15.329c-6.3,6.3 -17.071,1.838 -17.071,-7.071v-240.258c0,-11.04 -4.48,-21.04 -11.72,-28.28z"/>
|
||||||
<path android:fillColor="#41B632" android:pathData="m496,512h-24c-8.836,0 -16,-7.164 -16,-16s7.164,-16 16,-16h24c8.836,0 16,7.164 16,16s-7.164,16 -16,16z"/>
|
<path android:fillColor="#000000" android:pathData="m496,512h-24c-8.836,0 -16,-7.164 -16,-16s7.164,-16 16,-16h24c8.836,0 16,7.164 16,16s-7.164,16 -16,16z"/>
|
||||||
<path android:fillColor="#41B632" android:pathData="m40,512h-24c-8.836,0 -16,-7.164 -16,-16s7.164,-16 16,-16h24c8.836,0 16,7.164 16,16s-7.164,16 -16,16z"/>
|
<path android:fillColor="#000000" android:pathData="m40,512h-24c-8.836,0 -16,-7.164 -16,-16s7.164,-16 16,-16h24c8.836,0 16,7.164 16,16s-7.164,16 -16,16z"/>
|
||||||
<path android:fillColor="#41B632" android:pathData="m416,512h-320c-8.836,0 -16,-7.164 -16,-16s7.164,-16 16,-16h320c8.836,0 16,7.164 16,16s-7.164,16 -16,16z"/>
|
<path android:fillColor="#000000" android:pathData="m416,512h-320c-8.836,0 -16,-7.164 -16,-16s7.164,-16 16,-16h320c8.836,0 16,7.164 16,16s-7.164,16 -16,16z"/>
|
||||||
|
|
||||||
</vector>
|
</vector>
|
||||||
|
34
app/src/main/res/drawable/ic_linkedin.xml
Normal file
34
app/src/main/res/drawable/ic_linkedin.xml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<!--
|
||||||
|
~ 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="52dp" android:height="52dp"
|
||||||
|
android:viewportWidth="512" android:viewportHeight="512">
|
||||||
|
<path android:pathData="m140.008,423h-30c-11.047,0 -20,-8.953 -20,-20v-186c0,-11.047 8.953,-20 20,-20h30c11.047,0 20,8.953 20,20v186c0,11.047 -8.953,20 -20,20zM166.992,124.996c0,-22.629 -18.359,-40.996 -40.977,-40.996 -22.703,0 -41.016,18.367 -41.016,40.996 0,22.637 18.313,41.004 41.016,41.004 22.617,0 40.977,-18.367 40.977,-41.004zM422,403v-104.336c0,-60.668 -12.816,-105.664 -83.688,-105.664 -34.055,0 -56.914,17.031 -66.246,34.742h-0.066v-10.742c0,-11.047 -8.953,-20 -20,-20h-28c-11.047,0 -20,8.953 -20,20v186c0,11.047 8.953,20 20,20h28c11.047,0 20,-8.953 20,-20v-92.211c0,-29.387 7.48,-57.855 43.906,-57.855 35.93,0 37.094,33.605 37.094,59.723v90.344c0,11.047 8.953,20 20,20h29c11.047,0 20,-8.953 20,-20zM512,432c0,-11.047 -8.953,-20 -20,-20s-20,8.953 -20,20c0,22.055 -17.945,40 -40,40h-352c-22.055,0 -40,-17.945 -40,-40v-352c0,-22.055 17.945,-40 40,-40h352c22.055,0 40,17.945 40,40v251c0,11.047 8.953,20 20,20s20,-8.953 20,-20v-251c0,-44.113 -35.887,-80 -80,-80h-352c-44.113,0 -80,35.887 -80,80v352c0,44.113 35.887,80 80,80h352c44.113,0 80,-35.887 80,-80zM512,432">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient android:endX="512" android:endY="256"
|
||||||
|
android:startX="0" android:startY="256" android:type="linear">
|
||||||
|
<item android:color="#FF00F2FE" android:offset="0"/>
|
||||||
|
<item android:color="#FF03EFFE" android:offset="0.0208"/>
|
||||||
|
<item android:color="#FF24D2FE" android:offset="0.2931"/>
|
||||||
|
<item android:color="#FF3CBDFE" android:offset="0.5538"/>
|
||||||
|
<item android:color="#FF4AB0FE" android:offset="0.7956"/>
|
||||||
|
<item android:color="#FF4FACFE" android:offset="1"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
</vector>
|
30
app/src/main/res/drawable/ic_refresh.xml
Normal file
30
app/src/main/res/drawable/ic_refresh.xml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<!--
|
||||||
|
~ 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="42dp" android:height="42dp"
|
||||||
|
android:viewportWidth="512" android:viewportHeight="512">
|
||||||
|
<path android:pathData="m45,300.008c-24.813,0 -45,-20.188 -45,-45s20.188,-45 45,-45c24.816,0 45.004,20.188 45.004,45s-20.188,45 -45.004,45zM237.008,100.004c-27.57,0 -50.004,-22.43 -50.004,-50.004 0,-27.57 22.434,-50 50.004,-50 27.57,0 50,22.43 50,50 0,27.574 -22.43,50.004 -50,50.004zM237.008,40c-5.516,0 -10,4.488 -10,10 0,5.516 4.484,10 10,10 5.512,0 10,-4.484 10,-10 0,-5.512 -4.488,-10 -10,-10zM100.504,162.004c-12.957,0 -25.137,-5.043 -34.297,-14.207 -9.16,-9.16 -14.207,-21.34 -14.207,-34.293 0,-12.957 5.047,-25.137 14.207,-34.297 9.16,-9.16 21.34,-14.207 34.297,-14.207 12.953,0 25.133,5.047 34.297,14.207h-0.004,0.004c9.16,9.16 14.203,21.34 14.203,34.297 0,12.953 -5.043,25.133 -14.207,34.297 -9.16,9.16 -21.34,14.203 -34.293,14.203zM100.504,105.004c-1.316,0 -3.844,0.32 -6.012,2.488 -2.168,2.168 -2.488,4.695 -2.488,6.012s0.32,3.844 2.488,6.008c2.168,2.168 4.695,2.492 6.012,2.492 1.313,0 3.844,-0.324 6.008,-2.488 2.168,-2.168 2.492,-4.695 2.492,-6.012s-0.324,-3.844 -2.488,-6.012h-0.004c-2.164,-2.168 -4.695,-2.488 -6.008,-2.488zM382.512,164.004c-14.824,0 -28.762,-5.773 -39.246,-16.254 -10.484,-10.484 -16.258,-24.422 -16.258,-39.246 0,-14.824 5.773,-28.762 16.258,-39.246 10.484,-10.484 24.422,-16.258 39.246,-16.258s28.762,5.773 39.246,16.258c10.48,10.484 16.254,24.422 16.254,39.246 0,14.824 -5.773,28.762 -16.254,39.246 -10.484,10.48 -24.422,16.254 -39.246,16.254zM382.512,93.004c-4.141,0 -8.031,1.609 -10.961,4.539 -2.926,2.93 -4.539,6.82 -4.539,10.961s1.609,8.031 4.539,10.957c2.93,2.93 6.82,4.543 10.961,4.543s8.031,-1.613 10.957,-4.539c2.93,-2.93 4.543,-6.82 4.543,-10.961s-1.613,-8.031 -4.543,-10.961c-2.926,-2.93 -6.816,-4.539 -10.957,-4.539zM447.012,318.98c-16.645,0 -33.289,-6.336 -45.961,-19.008 -25.348,-25.344 -25.348,-66.586 0,-91.93 25.344,-25.344 66.582,-25.344 91.926,0s25.344,66.586 0,91.93c-12.672,12.672 -29.316,19.008 -45.965,19.008zM447.012,229.02c-6.402,0 -12.805,2.438 -17.676,7.309 -9.75,9.75 -9.75,25.609 0,35.359 9.746,9.746 25.605,9.746 35.355,0 9.746,-9.75 9.746,-25.609 0,-35.359 -4.875,-4.871 -11.277,-7.309 -17.68,-7.309zM383.012,484.98c-17.672,0 -35.34,-6.723 -48.793,-20.176 -26.902,-26.902 -26.902,-70.68 0,-97.582 26.902,-26.906 70.68,-26.906 97.582,0 26.906,26.902 26.906,70.68 0,97.582 -13.449,13.453 -31.121,20.176 -48.789,20.176zM383.012,387.023c-7.426,0 -14.852,2.828 -20.508,8.48 -11.305,11.309 -11.305,29.707 0,41.016 11.309,11.309 29.707,11.309 41.016,0 11.305,-11.309 11.305,-29.707 0,-41.016 -5.656,-5.652 -13.082,-8.48 -20.508,-8.48zM92.5,438.992c-11.137,0 -22.277,-4.242 -30.758,-12.719 -16.961,-16.965 -16.961,-44.563 0,-61.523 16.961,-16.961 44.559,-16.961 61.52,0 16.961,16.961 16.961,44.559 0,61.52 -8.48,8.48 -19.621,12.723 -30.762,12.723zM210.004,512c-7.684,0 -15.363,-2.922 -21.211,-8.77 -11.699,-11.699 -11.699,-30.734 -0.004,-42.43l0.004,-0.004c11.695,-11.695 30.73,-11.695 42.426,0 11.699,11.699 11.699,30.734 0.004,42.43 -5.852,5.848 -13.535,8.773 -21.219,8.773zM210.004,512">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient android:endX="255.99213" android:endY="512.00024"
|
||||||
|
android:startX="255.99213" android:startY="0" android:type="linear">
|
||||||
|
<item android:color="#FF7490" android:offset="0"/>
|
||||||
|
<item android:color="#4563FF" android:offset="1"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
</vector>
|
32
app/src/main/res/drawable/ic_refreshgradient.xml
Normal file
32
app/src/main/res/drawable/ic_refreshgradient.xml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<!--
|
||||||
|
~ 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="42dp"
|
||||||
|
android:height="42dp" android:viewportWidth="496" android:viewportHeight="496">
|
||||||
|
<path android:fillColor="#6C9DFF" android:pathData="M248,92c-13.6,0 -24,-10.4 -24,-24V24c0,-13.6 10.4,-24 24,-24s24,10.4 24,24v44C272,80.8 261.6,92 248,92z"/>
|
||||||
|
<path android:fillColor="#DA3B7A" android:pathData="M248,496c-13.6,0 -24,-10.4 -24,-24v-44c0,-13.6 10.4,-24 24,-24s24,10.4 24,24v44C272,485.6 261.6,496 248,496z"/>
|
||||||
|
<path android:fillColor="#63BBFF" android:pathData="M157.6,116c-8,0 -16,-4 -20.8,-12l-21.6,-37.6c-6.4,-11.2 -2.4,-26.4 8.8,-32.8s26.4,-2.4 32.8,8.8L178.4,80c6.4,11.2 2.4,26.4 -8.8,32.8C166.4,114.4 161.6,116 157.6,116z"/>
|
||||||
|
<path android:fillColor="#E542A9" android:pathData="M360,465.6c-8,0 -16,-4 -20.8,-12L317.6,416c-6.4,-11.2 -2.4,-26.4 8.8,-32.8c11.2,-6.4 26.4,-2.4 32.8,8.8l21.6,37.6c6.4,11.2 2.4,26.4 -8.8,32.8C368,464.8 364,465.6 360,465.6z"/>
|
||||||
|
<path android:fillColor="#A1DCEC" android:pathData="M92,181.6c-4,0 -8,-0.8 -12,-3.2l-37.6,-21.6c-11.2,-6.4 -15.2,-21.6 -8.8,-32.8s21.6,-15.2 32.8,-8.8l37.6,21.6c11.2,6.4 15.2,21.6 8.8,32.8C108,177.6 100,181.6 92,181.6z"/>
|
||||||
|
<path android:fillColor="#B135FF" android:pathData="M442.4,384c-4,0 -8,-0.8 -12,-3.2L392,359.2c-11.2,-6.4 -15.2,-21.6 -8.8,-32.8c6.4,-11.2 21.6,-15.2 32.8,-8.8l37.6,21.6c11.2,6.4 15.2,21.6 8.8,32.8C458.4,380 450.4,384 442.4,384z"/>
|
||||||
|
<path android:fillColor="#F3FFFD" android:pathData="M68,272H24c-13.6,0 -24,-10.4 -24,-24s10.4,-24 24,-24h44c13.6,0 24,10.4 24,24S80.8,272 68,272z"/>
|
||||||
|
<path android:fillColor="#9254C8" android:pathData="M472,272h-44c-13.6,0 -24,-10.4 -24,-24s10.4,-24 24,-24h44c13.6,0 24,10.4 24,24S485.6,272 472,272z"/>
|
||||||
|
<path android:fillColor="#CE1CFF" android:pathData="M53.6,384c-8,0 -16,-4 -20.8,-12c-6.4,-11.2 -2.4,-26.4 8.8,-32.8l37.6,-21.6c11.2,-6.4 26.4,-2.4 32.8,8.8c6.4,11.2 2.4,26.4 -8.8,32.8l-37.6,21.6C62.4,383.2 58.4,384 53.6,384z"/>
|
||||||
|
<path android:fillColor="#6953E5" android:pathData="M404,181.6c-8,0 -16,-4 -20.8,-12c-6.4,-11.2 -2.4,-26.4 8.8,-32.8l37.6,-21.6c11.2,-6.4 26.4,-2.4 32.8,8.8s2.4,26.4 -8.8,32.8L416,178.4C412,180.8 408,181.6 404,181.6z"/>
|
||||||
|
<path android:fillColor="#DE339F" android:pathData="M136,465.6c-4,0 -8,-0.8 -12,-3.2c-11.2,-6.4 -15.2,-21.6 -8.8,-32.8l21.6,-37.6c6.4,-11.2 21.6,-15.2 32.8,-8.8c11.2,6.4 15.2,21.6 8.8,32.8l-21.6,37.6C152,461.6 144,465.6 136,465.6z"/>
|
||||||
|
<path android:fillColor="#5681FF" android:pathData="M338.4,116c-4,0 -8,-0.8 -12,-3.2c-11.2,-6.4 -15.2,-21.6 -8.8,-32.8l21.6,-37.6c6.4,-11.2 21.6,-15.2 32.8,-8.8c11.2,6.4 15.2,21.6 8.8,32.8L359.2,104C354.4,111.2 346.4,116 338.4,116z"/>
|
||||||
|
</vector>
|
21
app/src/main/res/drawable/ic_spotify_logo.xml
Normal file
21
app/src/main/res/drawable/ic_spotify_logo.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<!--
|
||||||
|
~ 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="42dp"
|
||||||
|
android:height="42dp" android:viewportWidth="427.652" android:viewportHeight="427.652">
|
||||||
|
<path android:fillColor="#00D95F" android:pathData="M213.826,0C95.733,0 0,95.733 0,213.826s95.733,213.826 213.826,213.826s213.826,-95.733 213.826,-213.826S331.919,0 213.826,0zM306.886,310.32c-2.719,4.652 -7.612,7.246 -12.638,7.247c-2.506,0 -5.044,-0.645 -7.364,-2c-38.425,-22.456 -82.815,-26.065 -113.295,-25.138c-33.763,1.027 -58.523,7.692 -58.769,7.76c-7.783,2.126 -15.826,-2.454 -17.961,-10.236c-2.134,-7.781 2.43,-15.819 10.209,-17.962c1.116,-0.307 27.76,-7.544 64.811,-8.766c21.824,-0.72 42.834,0.801 62.438,4.52c24.83,4.71 47.48,12.978 67.322,24.574C308.612,294.393 310.96,303.349 306.886,310.32zM334.07,253.861c-3.22,5.511 -9.016,8.583 -14.97,8.584c-2.968,0 -5.975,-0.763 -8.723,-2.369c-45.514,-26.6 -98.097,-30.873 -134.2,-29.776c-39.994,1.217 -69.323,9.112 -69.614,9.192c-9.217,2.515 -18.746,-2.906 -21.275,-12.124c-2.528,-9.218 2.879,-18.738 12.093,-21.277c1.322,-0.364 32.882,-8.937 76.77,-10.384c25.853,-0.852 50.739,0.949 73.96,5.354c29.412,5.58 56.241,15.373 79.744,29.108C336.115,234.995 338.897,245.603 334.07,253.861zM350.781,202.526c-3.641,0 -7.329,-0.936 -10.7,-2.906c-108.207,-63.238 -248.572,-25.643 -249.977,-25.255c-11.313,3.117 -23.008,-3.527 -26.124,-14.839c-3.117,-11.312 3.527,-23.008 14.839,-26.124c1.621,-0.447 40.333,-10.962 94.166,-12.737c31.713,-1.044 62.237,1.164 90.72,6.567c36.077,6.844 68.987,18.856 97.815,35.704c10.13,5.92 13.543,18.931 7.623,29.061C365.193,198.757 358.084,202.526 350.781,202.526z"/>
|
||||||
|
</vector>
|
31
app/src/main/res/drawable/ic_tick.xml
Normal file
31
app/src/main/res/drawable/ic_tick.xml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<!--
|
||||||
|
~ 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="38dp" android:height="38dp"
|
||||||
|
android:viewportWidth="512" android:viewportHeight="512">
|
||||||
|
<path android:pathData="m512,256c0,141.387 -114.613,256 -256,256s-256,-114.613 -256,-256 114.613,-256 256,-256 256,114.613 256,256zM512,256">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient android:endX="512" android:endY="256"
|
||||||
|
android:startX="0" android:startY="256" android:type="linear">
|
||||||
|
<item android:color="#748AFF" android:offset="0"/>
|
||||||
|
<item android:color="#FF3C64" android:offset="1"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path android:fillColor="#000000" android:pathData="m175,395.246c-4.035,0 -7.902,-1.629 -10.727,-4.512l-81,-82.832c-5.789,-5.922 -5.684,-15.418 0.238,-21.211 5.922,-5.793 15.418,-5.688 21.211,0.238l70.273,71.859 232.277,-237.523c5.793,-5.922 15.289,-6.027 21.211,-0.234 5.926,5.789 6.031,15.289 0.238,21.211l-243,248.492c-2.82,2.883 -6.688,4.512 -10.723,4.512zM175,395.246"/>
|
||||||
|
</vector>
|
27
app/src/main/res/drawable/ic_youtube.xml
Normal file
27
app/src/main/res/drawable/ic_youtube.xml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<!--
|
||||||
|
~ 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="42dp"
|
||||||
|
android:height="42dp" android:viewportWidth="512.0006" android:viewportHeight="512">
|
||||||
|
<path android:fillColor="#fff" android:fillType="evenOdd" android:pathData="m271.277,218.113 l-61.887,-38.234v76.469l0.004,76.469 61.883,-38.234 61.891,-38.234zM271.277,218.113"/>
|
||||||
|
<path android:fillColor="#f00" android:fillType="evenOdd" android:pathData="m209.391,179.879 l61.887,38.234 61.891,38.234 -61.891,38.234 -61.883,38.234 -0.004,-76.469zM13.816,343.254c5.242,50.121 25.367,70.117 66.094,76.469 76.73,11.961 275.449,11.961 352.184,0 40.723,-6.352 60.848,-26.344 66.09,-76.469 5.129,-48.996 5.129,-124.816 0,-173.813 -5.242,-50.121 -25.367,-70.117 -66.09,-76.469 -76.734,-11.961 -275.453,-11.961 -352.184,0 -40.727,6.352 -60.852,26.348 -66.094,76.469 -5.125,48.996 -5.125,124.816 0,173.813zM13.816,343.254"/>
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="m329.848,417.492c-23.492,0.797 -48.336,1.199 -73.848,1.199 -73.746,0 -138.996,-3.309 -174.551,-8.852 -37.988,-5.922 -53.082,-23.621 -57.688,-67.629 -5.035,-48.148 -5.035,-123.582 0,-171.73 4.605,-44.008 19.699,-61.703 57.688,-67.625 6.168,-0.965 13.414,-1.879 21.543,-2.723 5.496,-0.57 9.488,-5.484 8.918,-10.98 -0.57,-5.492 -5.492,-9.488 -10.98,-8.914 -8.469,0.879 -16.059,1.84 -22.563,2.852 -23.695,3.695 -40.305,11.652 -52.266,25.039 -12.309,13.766 -19.371,32.918 -22.234,60.273 -5.156,49.316 -5.156,126.574 0,175.891 2.863,27.355 9.926,46.504 22.234,60.273 11.961,13.387 28.57,21.344 52.266,25.035 36.512,5.695 102.918,9.094 177.633,9.094 25.734,0 50.809,-0.406 74.523,-1.211 5.52,-0.188 9.844,-4.813 9.656,-10.332 -0.188,-5.52 -4.789,-9.844 -10.332,-9.66zM329.848,417.492"/>
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="m508.133,168.402c-2.863,-27.352 -9.926,-46.504 -22.23,-60.273 -11.965,-13.387 -28.574,-21.344 -52.27,-25.035 -36.516,-5.695 -102.922,-9.094 -177.633,-9.094 -25.734,0 -50.805,0.41 -74.52,1.211 -5.523,0.188 -9.848,4.813 -9.66,10.332 0.188,5.523 4.793,9.855 10.336,9.66 23.488,-0.797 48.336,-1.199 73.848,-1.199 73.738,0 138.992,3.309 174.551,8.852 37.984,5.926 53.082,23.621 57.684,67.629 5.039,48.148 5.039,123.582 0,171.73 -4.602,44.008 -19.699,61.703 -57.684,67.629 -6.168,0.961 -13.418,1.875 -21.547,2.719 -5.492,0.57 -9.484,5.484 -8.914,10.98 0.535,5.145 4.875,8.969 9.934,8.969 0.348,0 0.695,-0.02 1.047,-0.055 8.465,-0.879 16.055,-1.836 22.559,-2.852 23.695,-3.695 40.305,-11.648 52.27,-25.035 12.305,-13.77 19.367,-32.922 22.23,-60.273 5.156,-49.316 5.156,-126.578 0,-175.895zM508.133,168.402"/>
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="m338.422,247.84 l-123.773,-76.473c-3.086,-1.906 -6.961,-1.992 -10.129,-0.227 -3.164,1.766 -5.129,5.109 -5.129,8.734v152.941c0,3.625 1.965,6.969 5.129,8.734 1.52,0.844 3.195,1.266 4.871,1.266 1.828,0 3.648,-0.5 5.258,-1.492l123.773,-76.469c2.949,-1.824 4.746,-5.043 4.746,-8.508 0,-3.469 -1.797,-6.688 -4.746,-8.508zM219.395,314.883v-117.07l94.742,58.535zM219.395,314.883"/>
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="m141.844,97.035c0.223,0 0.449,-0.008 0.676,-0.02 5.512,-0.367 9.68,-5.133 9.313,-10.645 -0.367,-5.512 -5.141,-9.688 -10.645,-9.316h-0.008c-5.512,0.367 -9.676,5.133 -9.309,10.645 0.352,5.285 4.75,9.336 9.973,9.336zM141.844,97.035"/>
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="m369.484,415.684c-5.512,0.363 -9.684,5.129 -9.316,10.641 0.352,5.285 4.75,9.336 9.969,9.336 0.227,0 0.449,-0.008 0.676,-0.02h0.008c5.512,-0.367 9.676,-5.133 9.309,-10.645 -0.367,-5.512 -5.125,-9.676 -10.645,-9.313zM369.484,415.684"/>
|
||||||
|
</vector>
|
27
app/src/main/res/drawable/transparent.xml
Normal file
27
app/src/main/res/drawable/transparent.xml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="ring">
|
||||||
|
<solid android:color="#00000000"/>
|
||||||
|
<corners
|
||||||
|
android:bottomLeftRadius="180dp"
|
||||||
|
android:bottomRightRadius="180dp"
|
||||||
|
android:topLeftRadius="180dp"
|
||||||
|
android:topRightRadius="180dp" />
|
||||||
|
</shape>
|
274
app/src/main/res/layout/main_fragment.xml
Normal file
274
app/src/main/res/layout/main_fragment.xml
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/linkSearch"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="46dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:background="@drawable/text_background_accented"
|
||||||
|
android:ems="10"
|
||||||
|
android:hint="Paste Link here"
|
||||||
|
android:inputType="text"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textColorHint="@color/grey"
|
||||||
|
android:textSize="19sp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/btn_search"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="spread"
|
||||||
|
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: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_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/linkSearch"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/linkSearch" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/appLogo"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="200dp"
|
||||||
|
android:contentDescription="App Logo"
|
||||||
|
android:foreground="@drawable/gradient"
|
||||||
|
android:padding="20dp"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:src="@drawable/spotify_download"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_collapseMode="parallax"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/linkSearch" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/appName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:fontFamily="@font/raleway_semibold"
|
||||||
|
android:gravity="end"
|
||||||
|
android:text='"SpotiFlyer"'
|
||||||
|
android:textAlignment="viewEnd"
|
||||||
|
android:textColor="#9AB3FF"
|
||||||
|
android:textSize="40sp"
|
||||||
|
android:typeface="normal"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/appLogo" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/appSubTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:fontFamily="@font/raleway_semibold"
|
||||||
|
android:text=" -Music Downloader"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
android:textSize="17sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/appName"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/appName"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/appName" />
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/platforms"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:layout_marginEnd="2dp"
|
||||||
|
android:fontFamily="@font/raleway_semibold"
|
||||||
|
android:text="Supports: "
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textColor="#9AB3FF"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/btn_spotify"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/appSubTitle" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btn_spotify"
|
||||||
|
android:layout_width="46dp"
|
||||||
|
android:layout_height="46dp"
|
||||||
|
android:layout_marginEnd="2dp"
|
||||||
|
android:background="@color/black"
|
||||||
|
android:padding="5dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/ic_spotify_logo"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/platforms"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/btn_youtube"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/platforms"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/platforms" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btn_youtube"
|
||||||
|
android:layout_width="52dp"
|
||||||
|
android:layout_height="52dp"
|
||||||
|
android:background="@color/black"
|
||||||
|
android:padding="5dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/ic_youtube"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/btn_spotify"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/btn_spotify"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/btn_spotify" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/usage"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:text="Usage Instructions!"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textColor="#D0838383"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/developer_insta_spotify"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/btn_linkedin"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/btn_github_spotify" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatButton
|
||||||
|
android:id="@+id/btn_donate"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="42dp"
|
||||||
|
android:layout_marginBottom="32dp"
|
||||||
|
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_constraintBottom_toTopOf="@+id/heart"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/spotify_donate_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
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" />
|
||||||
|
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btn_github_spotify"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/black"
|
||||||
|
android:padding="5dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/ic_github"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/btn_linkedin"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/btn_youtube"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btn_linkedin"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/black"
|
||||||
|
android:padding="5dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/ic_linkedin"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/developer_insta_spotify"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/btn_github_spotify" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/developer_insta_spotify"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/black"
|
||||||
|
android:padding="5dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/ic_instagram"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/btn_donate"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/btn_linkedin" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tagLine1"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="@font/raleway_semibold"
|
||||||
|
android:text="Made with "
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/heart"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/heart"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="3dp"
|
||||||
|
android:layout_marginEnd="3dp"
|
||||||
|
android:contentDescription="Made With Love In India"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/tagline2"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/tagLine1"
|
||||||
|
app:srcCompat="@drawable/ic_heart" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tagline2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="@font/raleway_semibold"
|
||||||
|
android:text=" in India"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/heart" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</layout>
|
@ -34,7 +34,6 @@
|
|||||||
android:background="@drawable/btn_design"
|
android:background="@drawable/btn_design"
|
||||||
android:drawableEnd="@drawable/ic_arrow_slim"
|
android:drawableEnd="@drawable/ic_arrow_slim"
|
||||||
android:drawablePadding="4dp"
|
android:drawablePadding="4dp"
|
||||||
android:drawableTint="@color/black"
|
|
||||||
android:padding="12dp"
|
android:padding="12dp"
|
||||||
android:text="Download All |"
|
android:text="Download All |"
|
||||||
android:textColor="@color/black"
|
android:textColor="@color/black"
|
||||||
@ -43,6 +42,21 @@
|
|||||||
app:layout_anchor="@+id/appbar_spotify"
|
app:layout_anchor="@+id/appbar_spotify"
|
||||||
app:layout_anchorGravity="bottom|center" />
|
app:layout_anchorGravity="bottom|center" />
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/downloading_fab_spotify"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:backgroundTint="@color/black"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:borderWidth="0dp"
|
||||||
|
app:layout_anchor="@+id/appbar_spotify"
|
||||||
|
app:layout_anchorGravity="bottom|center"
|
||||||
|
app:maxImageSize="38dp"
|
||||||
|
app:rippleColor="@color/colorPrimaryDark"
|
||||||
|
app:srcCompat="@drawable/ic_refresh"
|
||||||
|
app:tint="@null" />
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/appbar_spotify"
|
android:id="@+id/appbar_spotify"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -105,7 +119,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="2dp"
|
android:layout_marginTop="2dp"
|
||||||
android:layout_marginBottom="20dp"
|
android:layout_marginBottom="22dp"
|
||||||
android:background="#00000000"
|
android:background="#00000000"
|
||||||
android:fontFamily="@font/raleway_semibold"
|
android:fontFamily="@font/raleway_semibold"
|
||||||
android:gravity="end"
|
android:gravity="end"
|
||||||
@ -144,5 +158,6 @@
|
|||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:visibility="gone"/>
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
</layout>
|
</layout>
|
||||||
|
@ -20,6 +20,12 @@
|
|||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<data>
|
||||||
|
<variable
|
||||||
|
name="track"
|
||||||
|
type="com.shabinder.spotiflyer.models.Track" />
|
||||||
|
</data>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="80dp"
|
android:layout_height="80dp"
|
||||||
|
152
app/src/main/res/layout/youtube_fragment.xml
Normal file
152
app/src/main/res/layout/youtube_fragment.xml
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
<?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">
|
||||||
|
<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">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatButton
|
||||||
|
android:id="@+id/btn_download_all_youtube"
|
||||||
|
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_youtube"
|
||||||
|
app:layout_anchorGravity="bottom|center" />
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/downloading_fab_youtube"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:backgroundTint="@color/black"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:borderWidth="0dp"
|
||||||
|
app:layout_anchor="@+id/appbar_youtube"
|
||||||
|
app:layout_anchorGravity="bottom|center"
|
||||||
|
app:maxImageSize="38dp"
|
||||||
|
app:rippleColor="@color/colorPrimaryDark"
|
||||||
|
app:srcCompat="@drawable/ic_refresh"
|
||||||
|
app:tint="@null" />
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/appbar_youtube"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="230dp">
|
||||||
|
|
||||||
|
<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_youtube"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:foreground="@drawable/gradient"
|
||||||
|
>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/youtube_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_youtube"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/StatusBar_youtube"
|
||||||
|
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/youtube_cover_image"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title_view_youtube"
|
||||||
|
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='"SpotiFlyer"'
|
||||||
|
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_youtube"
|
||||||
|
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_youtube" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
</layout>
|
19
mobile-ffmpeg/build.gradle
Normal file
19
mobile-ffmpeg/build.gradle
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
configurations.maybeCreate("default")
|
||||||
|
artifacts.add("default", file('mobile-ffmpeg.aar'))
|
@ -0,0 +1 @@
|
|||||||
|
o/jetified-mobile-ffmpeg-runtime
|
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
o/com.arthenica.mobileffmpeg-r.txt
|
@ -0,0 +1 @@
|
|||||||
|
com.arthenica.mobileffmpeg
|
@ -0,0 +1 @@
|
|||||||
|
o/com.arthenica.mobileffmpeg
|
@ -0,0 +1 @@
|
|||||||
|
i/jars/classes.jar
|
@ -0,0 +1 @@
|
|||||||
|
o/jetified-mobile-ffmpeg.aar
|
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
i/AndroidManifest.xml
|
@ -0,0 +1 @@
|
|||||||
|
o/jetified-mobile-ffmpeg-runtime
|
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
i/jni
|
@ -0,0 +1 @@
|
|||||||
|
o/jetified-mobile-ffmpeg-runtime
|
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
o/jetified-mobile-ffmpeg
|
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.arthenica.mobileffmpeg"
|
||||||
|
android:versionCode="160440"
|
||||||
|
android:versionName="4.4.LTS" >
|
||||||
|
|
||||||
|
<uses-sdk
|
||||||
|
android:minSdkVersion="16"
|
||||||
|
android:targetSdkVersion="29" />
|
||||||
|
|
||||||
|
</manifest>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,16 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
-keep class com.arthenica.mobileffmpeg.Config {
|
||||||
|
native <methods>;
|
||||||
|
void log(int, byte[]);
|
||||||
|
void statistics(int, float, float, long, int, double, double);
|
||||||
|
}
|
||||||
|
|
||||||
|
-keep class com.arthenica.mobileffmpeg.AbiDetect {
|
||||||
|
native <methods>;
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
o/jetified-mobile-ffmpeg-api.jar
|
Binary file not shown.
@ -0,0 +1 @@
|
|||||||
|
o/jetified-mobile-ffmpeg-runtime.jar
|
Binary file not shown.
BIN
mobile-ffmpeg/mobile-ffmpeg.aar
Normal file
BIN
mobile-ffmpeg/mobile-ffmpeg.aar
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user