mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-24 18:04:33 +01:00
Download Tab Added,
Some Force Closes causing Bugs Fixed, Implementing Dependency Injection(Hilt Android).
This commit is contained in:
parent
bee88f02c7
commit
abf6a6058a
@ -1,10 +1,12 @@
|
|||||||
<component name="ProjectDictionaryState">
|
<component name="ProjectDictionaryState">
|
||||||
<dictionary name="shabinder">
|
<dictionary name="shabinder">
|
||||||
<words>
|
<words>
|
||||||
|
<w>downloadrecord</w>
|
||||||
<w>ffmpeg</w>
|
<w>ffmpeg</w>
|
||||||
<w>flyer</w>
|
<w>flyer</w>
|
||||||
<w>insta</w>
|
<w>insta</w>
|
||||||
<w>instagram</w>
|
<w>instagram</w>
|
||||||
|
<w>mainfragment</w>
|
||||||
<w>maxresdefault</w>
|
<w>maxresdefault</w>
|
||||||
<w>moshi</w>
|
<w>moshi</w>
|
||||||
<w>musicforeveryone</w>
|
<w>musicforeveryone</w>
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
|
<component name="EntryPointsManager">
|
||||||
|
<list size="1">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="dagger.hilt.android.HiltAndroidApp" />
|
||||||
|
</list>
|
||||||
|
</component>
|
||||||
<component name="ProjectPlainTextFileTypeManager">
|
<component name="ProjectPlainTextFileTypeManager">
|
||||||
<file url="file://$PROJECT_DIR$/app/src/main/java/com/shabinder/spotiflyer/testing/YoutubeInterface.kt.backup" />
|
<file url="file://$PROJECT_DIR$/app/src/main/java/com/shabinder/spotiflyer/testing/YoutubeInterface.kt.backup" />
|
||||||
</component>
|
</component>
|
||||||
|
@ -20,6 +20,7 @@ apply plugin: 'kotlin-android'
|
|||||||
apply plugin: 'kotlin-android-extensions'
|
apply plugin: 'kotlin-android-extensions'
|
||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
apply plugin: "androidx.navigation.safeargs.kotlin"
|
apply plugin: "androidx.navigation.safeargs.kotlin"
|
||||||
|
apply plugin: 'dagger.hilt.android.plugin'
|
||||||
//apply plugin: 'kotlinx-serialization'
|
//apply plugin: 'kotlinx-serialization'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@ -34,8 +35,8 @@ android {
|
|||||||
applicationId 'com.shabinder.spotiflyer'
|
applicationId 'com.shabinder.spotiflyer'
|
||||||
minSdkVersion 22
|
minSdkVersion 22
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 4
|
versionCode 5
|
||||||
versionName "1.3"
|
versionName "1.4"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
@ -74,7 +75,7 @@ dependencies {
|
|||||||
implementation fileTree(dir: 'libs', include:['*.jar'])
|
implementation fileTree(dir: 'libs', include:['*.jar'])
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
implementation 'androidx.core:core-ktx:1.3.1'
|
implementation 'androidx.core:core-ktx:1.3.1'
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||||
implementation 'androidx.browser:browser:1.2.0'
|
implementation 'androidx.browser:browser:1.2.0'
|
||||||
implementation 'androidx.webkit:webkit:1.2.0'
|
implementation 'androidx.webkit:webkit:1.2.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
@ -83,14 +84,19 @@ dependencies {
|
|||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'
|
||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'
|
implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||||
|
implementation 'com.google.android.material:material:1.2.0'
|
||||||
implementation 'com.google.android.material:material:1.1.0'
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7"
|
||||||
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"
|
||||||
kapt "androidx.room:room-compiler:2.2.5"
|
kapt "androidx.room:room-compiler:2.2.5"
|
||||||
implementation "androidx.room:room-ktx:2.2.5"
|
implementation "androidx.room:room-ktx:2.2.5"
|
||||||
|
implementation "com.google.dagger:hilt-android:$hilt_version"
|
||||||
|
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
|
||||||
|
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
|
||||||
|
kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
|
||||||
|
|
||||||
|
|
||||||
implementation project(path: ':mobile-ffmpeg')
|
implementation project(path: ':mobile-ffmpeg')
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
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") {
|
||||||
|
@ -18,10 +18,7 @@
|
|||||||
package com.shabinder.spotiflyer
|
package com.shabinder.spotiflyer
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.app.NotificationChannel
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
import android.app.NotificationManager
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Build
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2020 Shabinder Singh
|
* Copyright (C) 2020 Shabinder Singh
|
||||||
@ -40,24 +37,10 @@ import android.os.Build
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@HiltAndroidApp
|
||||||
class App:Application(){
|
class App:Application(){
|
||||||
private val channelId = "ForegroundServiceChannel"
|
companion object{
|
||||||
|
const val clientId:String = "694d8bf4f6ec420fa66ea7fb4c68f89d"
|
||||||
override fun onCreate() {
|
const val clientSecret:String = "02ca2d4021a7452dae2328b47a6e8fe8"
|
||||||
super.onCreate()
|
|
||||||
createNotificationChannel()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createNotificationChannel() {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
val serviceChannel = NotificationChannel(
|
|
||||||
channelId,
|
|
||||||
"ForeGround Service Channel",
|
|
||||||
NotificationManager.IMPORTANCE_DEFAULT
|
|
||||||
)
|
|
||||||
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
||||||
manager.createNotificationChannel(serviceChannel)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -22,7 +22,6 @@ import android.annotation.SuppressLint
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.net.ConnectivityManager
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -34,15 +33,13 @@ import androidx.databinding.DataBindingUtil
|
|||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import com.github.javiersantos.appupdater.AppUpdater
|
import com.github.javiersantos.appupdater.AppUpdater
|
||||||
import com.github.javiersantos.appupdater.enums.UpdateFrom
|
import com.github.javiersantos.appupdater.enums.UpdateFrom
|
||||||
import com.github.kiulian.downloader.YoutubeDownloader
|
|
||||||
import com.shabinder.spotiflyer.databinding.MainActivityBinding
|
import com.shabinder.spotiflyer.databinding.MainActivityBinding
|
||||||
import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper
|
import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper
|
||||||
import com.shabinder.spotiflyer.utils.SpotifyService
|
import com.shabinder.spotiflyer.utils.SpotifyService
|
||||||
import com.shabinder.spotiflyer.utils.SpotifyServiceToken
|
import com.shabinder.spotiflyer.utils.SpotifyServiceTokenRequest
|
||||||
import com.shabinder.spotiflyer.utils.createDirectory
|
import com.shabinder.spotiflyer.utils.createDirectory
|
||||||
import com.shreyaspatil.EasyUpiPayment.EasyUpiPayment
|
|
||||||
import com.squareup.moshi.Moshi
|
import com.squareup.moshi.Moshi
|
||||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
@ -50,41 +47,40 @@ import okhttp3.Request
|
|||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
|
@AndroidEntryPoint
|
||||||
class MainActivity : AppCompatActivity(){
|
class MainActivity : AppCompatActivity(){
|
||||||
private lateinit var binding: MainActivityBinding
|
|
||||||
private var ytDownloader : YoutubeDownloader? = null
|
|
||||||
private var spotifyService : SpotifyService? = null
|
private var spotifyService : SpotifyService? = null
|
||||||
private var spotifyServiceToken : SpotifyServiceToken? = null
|
|
||||||
// private val redirectUri = "spotiflyer://callback"
|
|
||||||
private val clientId:String = "694d8bf4f6ec420fa66ea7fb4c68f89d"
|
|
||||||
private val clientSecret:String = "02ca2d4021a7452dae2328b47a6e8fe8"
|
|
||||||
private var isConnected: Boolean = false
|
private var isConnected: Boolean = false
|
||||||
private var sharedPref :SharedPreferences? = null
|
private var sharedPref :SharedPreferences? = null
|
||||||
private var easyUpiPayment:EasyUpiPayment? = null
|
|
||||||
private var token :String =""
|
private var token :String =""
|
||||||
|
private lateinit var binding: MainActivityBinding
|
||||||
private lateinit var sharedViewModel: SharedViewModel
|
private lateinit var sharedViewModel: SharedViewModel
|
||||||
|
@Inject lateinit var spotifyServiceTokenRequest: SpotifyServiceTokenRequest
|
||||||
|
@Inject lateinit var moshi: Moshi
|
||||||
|
|
||||||
|
companion object{
|
||||||
|
private var instance = MainActivity()
|
||||||
|
fun getInstance():MainActivity{
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init {
|
||||||
|
instance = this
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = DataBindingUtil.setContentView(this,R.layout.main_activity)
|
binding = DataBindingUtil.setContentView(this,R.layout.main_activity)
|
||||||
sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)
|
sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)
|
||||||
sharedPref = this.getPreferences(Context.MODE_PRIVATE)
|
sharedPref = this.getPreferences(Context.MODE_PRIVATE)
|
||||||
|
|
||||||
//starting Notification and Downloader Service!
|
//starting Notification and Downloader Service!
|
||||||
SpotifyDownloadHelper.startService(this)
|
SpotifyDownloadHelper.startService(this)
|
||||||
|
|
||||||
/* if(sharedPref?.contains("token")!! && (sharedPref?.getLong("time",System.currentTimeMillis()/1000/60/60)!! < (System.currentTimeMillis()/1000/60/60)) ){
|
|
||||||
val savedToken = sharedPref?.getString("token","error")!!
|
|
||||||
sharedViewModel.accessToken.value = savedToken
|
|
||||||
Log.i("SharedPrefs Token:",savedToken)
|
|
||||||
token = savedToken
|
|
||||||
|
|
||||||
implementSpotifyService(savedToken)
|
|
||||||
}else{authenticateSpotify()}*/
|
|
||||||
|
|
||||||
if(sharedViewModel.spotifyService.value == null){
|
if(sharedViewModel.spotifyService.value == null){
|
||||||
authenticateSpotify()
|
authenticateSpotify()
|
||||||
}else{
|
}else{
|
||||||
@ -94,16 +90,11 @@ class MainActivity : AppCompatActivity(){
|
|||||||
requestPermission()
|
requestPermission()
|
||||||
disableDozeMode()
|
disableDozeMode()
|
||||||
checkIfLatestVersion()
|
checkIfLatestVersion()
|
||||||
createDir()
|
createDirectories()
|
||||||
setUpi()
|
isConnected = sharedViewModel.isOnline(this)
|
||||||
isConnected = isOnline()
|
|
||||||
sharedViewModel.isConnected.value = isConnected
|
sharedViewModel.isConnected.value = isConnected
|
||||||
Log.i("Connection Status",isConnected.toString())
|
Log.i("Connection Status",isConnected.toString())
|
||||||
|
|
||||||
//Object to download From Youtube {"https://github.com/sealedtx/java-youtube-downloader"}
|
|
||||||
ytDownloader = YoutubeDownloader()
|
|
||||||
sharedViewModel.ytDownloader.value = ytDownloader
|
|
||||||
|
|
||||||
handleIntentFromExternalActivity()
|
handleIntentFromExternalActivity()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,10 +148,6 @@ class MainActivity : AppCompatActivity(){
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
val moshi = Moshi.Builder()
|
|
||||||
.add(KotlinJsonAdapterFactory())
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val retrofit = Retrofit.Builder()
|
val retrofit = Retrofit.Builder()
|
||||||
.baseUrl("https://api.spotify.com/v1/")
|
.baseUrl("https://api.spotify.com/v1/")
|
||||||
.client(httpClient.build())
|
.client(httpClient.build())
|
||||||
@ -171,45 +158,15 @@ class MainActivity : AppCompatActivity(){
|
|||||||
sharedViewModel.spotifyService.value = spotifyService
|
sharedViewModel.spotifyService.value = spotifyService
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSpotifyToken(){
|
|
||||||
val httpClient2: OkHttpClient.Builder = OkHttpClient.Builder()
|
|
||||||
httpClient2.addInterceptor(object : Interceptor {
|
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
|
||||||
val request: Request =
|
|
||||||
chain.request().newBuilder().addHeader(
|
|
||||||
"Authorization",
|
|
||||||
"Basic ${android.util.Base64.encodeToString("$clientId:$clientSecret".toByteArray(),android.util.Base64.NO_WRAP)}"
|
|
||||||
).build()
|
|
||||||
return chain.proceed(request)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
val moshi = Moshi.Builder()
|
|
||||||
.add(KotlinJsonAdapterFactory())
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val retrofit2 = Retrofit.Builder()
|
|
||||||
.baseUrl("https://accounts.spotify.com/")
|
|
||||||
.client(httpClient2.build())
|
|
||||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
|
||||||
.build()
|
|
||||||
|
|
||||||
spotifyServiceToken = retrofit2.create(SpotifyServiceToken::class.java)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fun authenticateSpotify() {
|
fun authenticateSpotify() {
|
||||||
if (spotifyServiceToken == null) {
|
|
||||||
getSpotifyToken()
|
|
||||||
}
|
|
||||||
sharedViewModel.uiScope.launch {
|
sharedViewModel.uiScope.launch {
|
||||||
if (isConnected) {
|
if (isConnected) {
|
||||||
Log.i("Post Request", "Made")
|
Log.i("Post Request", "Made")
|
||||||
token = spotifyServiceToken!!.getToken()!!.access_token
|
token = spotifyServiceTokenRequest.getToken()!!.access_token
|
||||||
implementSpotifyService(token)
|
implementSpotifyService(token)
|
||||||
Log.i("Post Request", token)
|
Log.i("Post Request", token)
|
||||||
sharedViewModel.accessToken.value = token
|
sharedViewModel.accessToken.value = token
|
||||||
saveToken(token)
|
|
||||||
}else{
|
}else{
|
||||||
Log.i("network", "unavailable")
|
Log.i("network", "unavailable")
|
||||||
// sharedViewModel.showAlertDialog(resources,this@MainActivity)
|
// sharedViewModel.showAlertDialog(resources,this@MainActivity)
|
||||||
@ -217,15 +174,6 @@ class MainActivity : AppCompatActivity(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveToken(token:String) {
|
|
||||||
with (sharedPref?.edit()) {
|
|
||||||
this?.let {
|
|
||||||
putString("token", token)
|
|
||||||
putLong("time",(System.currentTimeMillis()/1000/60/60))
|
|
||||||
commit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleIntentFromExternalActivity() {
|
private fun handleIntentFromExternalActivity() {
|
||||||
if (intent?.action == Intent.ACTION_SEND) {
|
if (intent?.action == Intent.ACTION_SEND) {
|
||||||
@ -238,13 +186,6 @@ class MainActivity : AppCompatActivity(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isOnline(): Boolean {
|
|
||||||
val cm =
|
|
||||||
getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
|
||||||
val netInfo = cm.activeNetworkInfo
|
|
||||||
return netInfo != null && netInfo.isConnectedOrConnecting
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun requestPermission() {
|
private fun requestPermission() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
requestPermissions(
|
requestPermissions(
|
||||||
@ -267,22 +208,7 @@ class MainActivity : AppCompatActivity(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpi() {
|
private fun createDirectories() {
|
||||||
easyUpiPayment = EasyUpiPayment.Builder()
|
|
||||||
.with(this)
|
|
||||||
.setPayeeVpa("technoshab@paytm")
|
|
||||||
.setPayeeName("Shabinder Singh")
|
|
||||||
.setTransactionId("UNIQUE_TRANSACTION_ID")
|
|
||||||
.setTransactionRefId("UNIQUE_TRANSACTION_REF_ID")
|
|
||||||
.setDescription("Thanks for donating")
|
|
||||||
.setAmount("39.00")
|
|
||||||
.build()
|
|
||||||
|
|
||||||
sharedViewModel.easyUpiPayment = easyUpiPayment
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createDir() {
|
|
||||||
createDirectory(SpotifyDownloadHelper.defaultDir)
|
createDirectory(SpotifyDownloadHelper.defaultDir)
|
||||||
createDirectory(SpotifyDownloadHelper.defaultDir+".Images/")
|
createDirectory(SpotifyDownloadHelper.defaultDir+".Images/")
|
||||||
createDirectory(SpotifyDownloadHelper.defaultDir+"Tracks/")
|
createDirectory(SpotifyDownloadHelper.defaultDir+"Tracks/")
|
||||||
@ -308,55 +234,4 @@ class MainActivity : AppCompatActivity(){
|
|||||||
}
|
}
|
||||||
appUpdater.start()
|
appUpdater.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
private fun authenticateSpotify() {
|
|
||||||
val builder = AuthenticationRequest.Builder(clientId,AuthenticationResponse.Type.TOKEN,redirectUri)
|
|
||||||
.setScopes(arrayOf("user-read-private"))
|
|
||||||
// .setScopes(arrayOf("user-read-private","streaming","user-read-email","user-modify-playback-state","user-top-read","user-library-modify","user-read-currently-playing","user-library-read","user-read-recently-played"))
|
|
||||||
val request: AuthenticationRequest = builder.build()
|
|
||||||
AuthenticationClient.openLoginActivity(this, LoginActivity.REQUEST_CODE, request)
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/*override fun onActivityResult(
|
|
||||||
requestCode: Int,
|
|
||||||
resultCode: Int,
|
|
||||||
intent: Intent?
|
|
||||||
) {
|
|
||||||
super.onActivityResult(requestCode, resultCode, intent)
|
|
||||||
// Check if result comes from the correct activity
|
|
||||||
if (requestCode == LoginActivity.REQUEST_CODE) {
|
|
||||||
val response = AuthenticationClient.getResponse(resultCode, intent)
|
|
||||||
when (response.type) {
|
|
||||||
AuthenticationResponse.Type.TOKEN -> {
|
|
||||||
Log.i("Network",response.accessToken.toString())
|
|
||||||
token = response.accessToken
|
|
||||||
sharedViewModel.accessToken = response.accessToken
|
|
||||||
|
|
||||||
//Implementing My Own Spotify Requests
|
|
||||||
implementSpotifyService(token)
|
|
||||||
|
|
||||||
sharedViewModel.uiScope.launch {
|
|
||||||
val me = spotifyService?.getMe()?.display_name
|
|
||||||
sharedViewModel.userName.value = "Logged in as: $me"
|
|
||||||
Log.i("Network","Hello, " + me!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
sharedViewModel.userName.observe(this, Observer {
|
|
||||||
binding.message.text = it
|
|
||||||
})
|
|
||||||
}
|
|
||||||
AuthenticationResponse.Type.ERROR -> {
|
|
||||||
Log.i("Network",response.error.toString())
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
Log.i("Network","Something Weird Happened While Authenticating")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -19,13 +19,12 @@ package com.shabinder.spotiflyer
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
|
import android.net.ConnectivityManager
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.github.kiulian.downloader.YoutubeDownloader
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.shabinder.spotiflyer.utils.SpotifyService
|
import com.shabinder.spotiflyer.utils.SpotifyService
|
||||||
import com.shreyaspatil.EasyUpiPayment.EasyUpiPayment
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@ -34,8 +33,6 @@ import java.io.File
|
|||||||
class SharedViewModel : ViewModel() {
|
class SharedViewModel : ViewModel() {
|
||||||
var intentString = ""
|
var intentString = ""
|
||||||
var spotifyService = MutableLiveData<SpotifyService>()
|
var spotifyService = MutableLiveData<SpotifyService>()
|
||||||
var ytDownloader = MutableLiveData<YoutubeDownloader>()
|
|
||||||
var easyUpiPayment: EasyUpiPayment? = null
|
|
||||||
var accessToken = MutableLiveData<String>().apply { value = "" }
|
var accessToken = MutableLiveData<String>().apply { value = "" }
|
||||||
var isConnected = MutableLiveData<Boolean>().apply { value = false }
|
var isConnected = MutableLiveData<Boolean>().apply { value = false }
|
||||||
val defaultDir = Environment.DIRECTORY_MUSIC + File.separator + "SpotiFlyer" + File.separator + ".Images" + File.separator
|
val defaultDir = Environment.DIRECTORY_MUSIC + File.separator + "SpotiFlyer" + File.separator + ".Images" + File.separator
|
||||||
@ -59,4 +56,10 @@ class SharedViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
fun isOnline(context: Context): Boolean {
|
||||||
|
val cm =
|
||||||
|
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
|
val netInfo = cm.activeNetworkInfo
|
||||||
|
return netInfo != null && netInfo.isConnectedOrConnecting
|
||||||
|
}
|
||||||
}
|
}
|
@ -25,7 +25,7 @@ interface DatabaseDAO {
|
|||||||
suspend fun insert(record: DownloadRecord)
|
suspend fun insert(record: DownloadRecord)
|
||||||
|
|
||||||
@Update
|
@Update
|
||||||
fun update(record: DownloadRecord)
|
suspend fun update(record: DownloadRecord)
|
||||||
|
|
||||||
@Query("SELECT * from download_record_table ORDER BY id DESC")
|
@Query("SELECT * from download_record_table ORDER BY id DESC")
|
||||||
suspend fun getRecord():List<DownloadRecord>
|
suspend fun getRecord():List<DownloadRecord>
|
||||||
|
@ -27,7 +27,7 @@ import kotlinx.android.parcel.Parcelize
|
|||||||
@Parcelize
|
@Parcelize
|
||||||
@Entity(
|
@Entity(
|
||||||
tableName = "download_record_table",
|
tableName = "download_record_table",
|
||||||
indices = [Index(value = ["id","link"], unique = true)]
|
indices = [Index(value = ["link"], unique = true)]
|
||||||
)
|
)
|
||||||
data class DownloadRecord(
|
data class DownloadRecord(
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
|
import android.os.Handler
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.animation.AlphaAnimation
|
import android.view.animation.AlphaAnimation
|
||||||
@ -126,13 +127,19 @@ object SpotifyDownloadHelper {
|
|||||||
isBrowserLoading = false
|
isBrowserLoading = false
|
||||||
listProcessed = true
|
listProcessed = true
|
||||||
}
|
}
|
||||||
|
}else{//YT List Empty....Maybe it was one Single Download
|
||||||
|
Handler().postDelayed({//Delay of 1.5 sec
|
||||||
|
if(youtubeList.isEmpty()){//Lets Make It sure , There are No more Downloads In Queue.....
|
||||||
|
isBrowserLoading = false
|
||||||
|
listProcessed = true
|
||||||
|
}
|
||||||
|
},1500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} )
|
} )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateStatusBar() {
|
private fun updateStatusBar() {
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* 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.ViewGroup
|
||||||
|
import androidx.navigation.findNavController
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.shabinder.spotiflyer.database.DownloadRecord
|
||||||
|
import com.shabinder.spotiflyer.databinding.DownloadRecordItemBinding
|
||||||
|
import com.shabinder.spotiflyer.ui.downloadrecord.DownloadRecordFragmentDirections
|
||||||
|
import com.shabinder.spotiflyer.utils.bindImage
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class DownloadRecordAdapter: ListAdapter<DownloadRecord,DownloadRecordAdapter.ViewHolder>(DownloadRecordDiffCallback()) {
|
||||||
|
|
||||||
|
private val adapterScope = CoroutineScope(Dispatchers.Default)
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
|
val binding =DownloadRecordItemBinding.inflate(layoutInflater)
|
||||||
|
return ViewHolder(binding)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
val item = getItem(position)
|
||||||
|
adapterScope.launch {
|
||||||
|
bindImage(holder.binding.coverUrl,item.coverUrl)
|
||||||
|
}
|
||||||
|
holder.binding.itemName.text = item.name
|
||||||
|
holder.binding.totalItems.text = "Tracks: ${item.totalFiles}"
|
||||||
|
holder.binding.type.text = item.type
|
||||||
|
holder.binding.btnAction.setOnClickListener {
|
||||||
|
if (item.link.contains("spotify",true)){
|
||||||
|
it.findNavController().navigate(DownloadRecordFragmentDirections.actionDownloadRecordToSpotifyFragment((item.link)))
|
||||||
|
}else if(item.link.contains("youtube.com",true) || item.link.contains("youtu.be",true) ){
|
||||||
|
it.findNavController().navigate(DownloadRecordFragmentDirections.actionDownloadRecordToYoutubeFragment(item.link))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class ViewHolder(val binding: DownloadRecordItemBinding) : RecyclerView.ViewHolder(binding.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
class DownloadRecordDiffCallback: DiffUtil.ItemCallback<DownloadRecord>(){
|
||||||
|
override fun areItemsTheSame(oldItem: DownloadRecord, newItem: DownloadRecord): Boolean {
|
||||||
|
return oldItem.coverUrl == newItem.coverUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: DownloadRecord, newItem: DownloadRecord): Boolean {
|
||||||
|
return oldItem == newItem
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,7 @@ import android.widget.Toast
|
|||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.github.kiulian.downloader.YoutubeDownloader
|
||||||
import com.shabinder.spotiflyer.R
|
import com.shabinder.spotiflyer.R
|
||||||
import com.shabinder.spotiflyer.databinding.TrackListItemBinding
|
import com.shabinder.spotiflyer.databinding.TrackListItemBinding
|
||||||
import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper.context
|
import com.shabinder.spotiflyer.downloadHelper.SpotifyDownloadHelper.context
|
||||||
@ -37,15 +38,14 @@ import kotlinx.coroutines.launch
|
|||||||
|
|
||||||
class SpotifyTrackListAdapter: ListAdapter<Track,SpotifyTrackListAdapter.ViewHolder>(SpotifyTrackDiffCallback()) {
|
class SpotifyTrackListAdapter: ListAdapter<Track,SpotifyTrackListAdapter.ViewHolder>(SpotifyTrackDiffCallback()) {
|
||||||
|
|
||||||
var spotifyViewModel = SpotifyViewModel()
|
var spotifyViewModel : SpotifyViewModel? = null
|
||||||
var isAlbum:Boolean = false
|
var isAlbum:Boolean = false
|
||||||
|
var ytDownloader: YoutubeDownloader? = null
|
||||||
|
|
||||||
|
|
||||||
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 binding = TrackListItemBinding.inflate(layoutInflater,parent,false)
|
val binding = TrackListItemBinding.inflate(layoutInflater,parent,false)
|
||||||
// val view = layoutInflater.inflate(R.layout.track_list_item,parent,false)
|
|
||||||
return ViewHolder(binding)
|
return ViewHolder(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ class SpotifyTrackListAdapter: ListAdapter<Track,SpotifyTrackListAdapter.ViewHol
|
|||||||
val item = getItem(position)
|
val item = getItem(position)
|
||||||
if(itemCount ==1 || isAlbum){
|
if(itemCount ==1 || isAlbum){
|
||||||
holder.binding.imageUrl.visibility = View.GONE}else{
|
holder.binding.imageUrl.visibility = View.GONE}else{
|
||||||
spotifyViewModel.uiScope.launch {
|
spotifyViewModel!!.uiScope.launch {
|
||||||
bindImage(holder.binding.imageUrl, item.album!!.images?.get(0)?.url)
|
bindImage(holder.binding.imageUrl, item.album!!.images?.get(0)?.url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -78,12 +78,12 @@ class SpotifyTrackListAdapter: ListAdapter<Track,SpotifyTrackListAdapter.ViewHol
|
|||||||
holder.binding.btnDownload.setImageResource(R.drawable.ic_refresh)
|
holder.binding.btnDownload.setImageResource(R.drawable.ic_refresh)
|
||||||
rotateAnim(it)
|
rotateAnim(it)
|
||||||
item.downloaded = "Downloading"
|
item.downloaded = "Downloading"
|
||||||
spotifyViewModel.uiScope.launch {
|
spotifyViewModel!!.uiScope.launch {
|
||||||
val itemList = mutableListOf<Track>()
|
val itemList = mutableListOf<Track>()
|
||||||
itemList.add(item)
|
itemList.add(item)
|
||||||
downloadAllTracks(spotifyViewModel.folderType,spotifyViewModel.subFolder,itemList,spotifyViewModel.ytDownloader)
|
downloadAllTracks(spotifyViewModel!!.folderType,spotifyViewModel!!.subFolder,itemList,ytDownloader)
|
||||||
}
|
}
|
||||||
notifyItemChanged(position)
|
notifyItemChanged(position)//start showing anim!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,5 +99,4 @@ class SpotifyTrackDiffCallback: DiffUtil.ItemCallback<Track>(){
|
|||||||
override fun areContentsTheSame(oldItem: Track, newItem: Track): Boolean {
|
override fun areContentsTheSame(oldItem: Track, newItem: Track): Boolean {
|
||||||
return oldItem == newItem //Downloaded Check
|
return oldItem == newItem //Downloaded Check
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -23,17 +23,18 @@ import android.view.ViewGroup
|
|||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import com.github.kiulian.downloader.model.formats.Format
|
import com.github.kiulian.downloader.model.formats.Format
|
||||||
import com.shabinder.spotiflyer.SharedViewModel
|
|
||||||
import com.shabinder.spotiflyer.databinding.TrackListItemBinding
|
import com.shabinder.spotiflyer.databinding.TrackListItemBinding
|
||||||
import com.shabinder.spotiflyer.downloadHelper.YTDownloadHelper
|
import com.shabinder.spotiflyer.downloadHelper.YTDownloadHelper
|
||||||
import com.shabinder.spotiflyer.models.Track
|
import com.shabinder.spotiflyer.models.Track
|
||||||
import com.shabinder.spotiflyer.utils.bindImage
|
import com.shabinder.spotiflyer.utils.bindImage
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class YoutubeTrackListAdapter: ListAdapter<Track,SpotifyTrackListAdapter.ViewHolder>(YouTubeTrackDiffCallback()) {
|
class YoutubeTrackListAdapter: ListAdapter<Track,SpotifyTrackListAdapter.ViewHolder>(YouTubeTrackDiffCallback()) {
|
||||||
|
|
||||||
var format:Format? = null
|
var format:Format? = null
|
||||||
var sharedViewModel = SharedViewModel()
|
private val adapterScope = CoroutineScope(Dispatchers.Default)
|
||||||
|
|
||||||
override fun onCreateViewHolder(
|
override fun onCreateViewHolder(
|
||||||
parent: ViewGroup,
|
parent: ViewGroup,
|
||||||
@ -49,7 +50,7 @@ class YoutubeTrackListAdapter: ListAdapter<Track,SpotifyTrackListAdapter.ViewHol
|
|||||||
val item = getItem(position)
|
val item = getItem(position)
|
||||||
if(itemCount == 1){
|
if(itemCount == 1){
|
||||||
holder.binding.imageUrl.visibility = View.GONE}else{
|
holder.binding.imageUrl.visibility = View.GONE}else{
|
||||||
sharedViewModel.uiScope.launch {
|
adapterScope.launch {
|
||||||
bindImage(holder.binding.imageUrl, item.ytCoverUrl)
|
bindImage(holder.binding.imageUrl, item.ytCoverUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,7 +59,7 @@ class YoutubeTrackListAdapter: ListAdapter<Track,SpotifyTrackListAdapter.ViewHol
|
|||||||
holder.binding.artist.text = "${item.artists?.get(0)?.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.duration.text = "${item.duration_ms/1000/60} minutes, ${(item.duration_ms/1000)%60} sec"
|
||||||
holder.binding.btnDownload.setOnClickListener{
|
holder.binding.btnDownload.setOnClickListener{
|
||||||
sharedViewModel.uiScope.launch {
|
adapterScope.launch {
|
||||||
YTDownloadHelper.downloadFile(null,"YT_Downloads",item,format)
|
YTDownloadHelper.downloadFile(null,"YT_Downloads",item,format)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,5 +73,4 @@ class YouTubeTrackDiffCallback: DiffUtil.ItemCallback<Track>(){
|
|||||||
override fun areContentsTheSame(oldItem: Track, newItem: Track): Boolean {
|
override fun areContentsTheSame(oldItem: Track, newItem: Track): Boolean {
|
||||||
return oldItem == newItem
|
return oldItem == newItem
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -31,7 +31,7 @@ class SplashScreen : AppCompatActivity(){
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.splash_screen)
|
setContentView(R.layout.splash_screen)
|
||||||
|
|
||||||
val splashTimeout = 500
|
val splashTimeout = 400
|
||||||
val homeIntent = Intent(this@SplashScreen, MainActivity::class.java)
|
val homeIntent = Intent(this@SplashScreen, MainActivity::class.java)
|
||||||
Handler().postDelayed({
|
Handler().postDelayed({
|
||||||
//TODO:Bring Initial Setup here
|
//TODO:Bring Initial Setup here
|
||||||
|
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* 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.downloadrecord
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.databinding.DataBindingUtil
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.google.android.material.tabs.TabLayout
|
||||||
|
import com.shabinder.spotiflyer.R
|
||||||
|
import com.shabinder.spotiflyer.databinding.DownloadRecordFragmentBinding
|
||||||
|
import com.shabinder.spotiflyer.recyclerView.DownloadRecordAdapter
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class DownloadRecordFragment : Fragment() {
|
||||||
|
|
||||||
|
private lateinit var downloadRecordViewModel: DownloadRecordViewModel
|
||||||
|
private lateinit var binding: DownloadRecordFragmentBinding
|
||||||
|
private lateinit var adapter: DownloadRecordAdapter
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
binding = DataBindingUtil.inflate(inflater,R.layout.download_record_fragment,container,false)
|
||||||
|
downloadRecordViewModel = ViewModelProvider(this).get(DownloadRecordViewModel::class.java)
|
||||||
|
adapter = DownloadRecordAdapter()
|
||||||
|
binding.downloadRecordList.adapter = adapter
|
||||||
|
downloadRecordViewModel.downloadRecordList.observe(viewLifecycleOwner, Observer {
|
||||||
|
if(it.isNotEmpty()){
|
||||||
|
downloadRecordViewModel.spotifyList = mutableListOf()
|
||||||
|
downloadRecordViewModel.ytList = mutableListOf()
|
||||||
|
for (downloadRecord in it) {
|
||||||
|
if(downloadRecord.link.contains("spotify",true)) downloadRecordViewModel.spotifyList.add(downloadRecord)
|
||||||
|
else downloadRecordViewModel.ytList.add(downloadRecord)
|
||||||
|
}
|
||||||
|
if(binding.tabLayout.selectedTabPosition == 0) adapter.submitList(downloadRecordViewModel.spotifyList)
|
||||||
|
else adapter.submitList(downloadRecordViewModel.ytList)
|
||||||
|
// adapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
binding.tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
|
||||||
|
|
||||||
|
override fun onTabSelected(tab: TabLayout.Tab?) {
|
||||||
|
if(tab?.text == "Spotify"){
|
||||||
|
adapter.submitList(downloadRecordViewModel.spotifyList)
|
||||||
|
} else adapter.submitList(downloadRecordViewModel.ytList)
|
||||||
|
// adapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTabReselected(tab: TabLayout.Tab?) {
|
||||||
|
// Handle tab reselect
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTabUnselected(tab: TabLayout.Tab?) {
|
||||||
|
// Handle tab unselect
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* 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.downloadrecord
|
||||||
|
|
||||||
|
import androidx.hilt.lifecycle.ViewModelInject
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.shabinder.spotiflyer.database.DatabaseDAO
|
||||||
|
import com.shabinder.spotiflyer.database.DownloadRecord
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class DownloadRecordViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) :
|
||||||
|
ViewModel(){
|
||||||
|
private var viewModelJob = Job()
|
||||||
|
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
|
||||||
|
var spotifyList = mutableListOf<DownloadRecord>()
|
||||||
|
var ytList = mutableListOf<DownloadRecord>()
|
||||||
|
val downloadRecordList = MutableLiveData<MutableList<DownloadRecord>>().apply {
|
||||||
|
value = mutableListOf()
|
||||||
|
}
|
||||||
|
init {
|
||||||
|
getDownloadRecordList()
|
||||||
|
}
|
||||||
|
private fun getDownloadRecordList() {
|
||||||
|
uiScope.launch {
|
||||||
|
downloadRecordList.postValue(databaseDAO.getRecord().toMutableList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun onCleared() {
|
||||||
|
super.onCleared()
|
||||||
|
viewModelJob.cancel()
|
||||||
|
}
|
||||||
|
}
|
@ -34,12 +34,17 @@ import androidx.navigation.fragment.findNavController
|
|||||||
import com.shabinder.spotiflyer.R
|
import com.shabinder.spotiflyer.R
|
||||||
import com.shabinder.spotiflyer.SharedViewModel
|
import com.shabinder.spotiflyer.SharedViewModel
|
||||||
import com.shabinder.spotiflyer.databinding.MainFragmentBinding
|
import com.shabinder.spotiflyer.databinding.MainFragmentBinding
|
||||||
|
import com.shreyaspatil.EasyUpiPayment.EasyUpiPayment
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
class MainFragment : Fragment() {
|
class MainFragment : Fragment() {
|
||||||
|
|
||||||
private lateinit var viewModel: MainViewModel
|
private lateinit var mainViewModel: MainViewModel
|
||||||
private lateinit var sharedViewModel: SharedViewModel
|
private lateinit var sharedViewModel: SharedViewModel
|
||||||
private lateinit var binding: MainFragmentBinding
|
private lateinit var binding: MainFragmentBinding
|
||||||
|
@Inject lateinit var easyUpiPayment: EasyUpiPayment
|
||||||
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
@ -47,7 +52,6 @@ class MainFragment : Fragment() {
|
|||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
binding = DataBindingUtil.inflate(inflater,R.layout.main_fragment,container,false)
|
binding = DataBindingUtil.inflate(inflater,R.layout.main_fragment,container,false)
|
||||||
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
|
|
||||||
initializeAll()
|
initializeAll()
|
||||||
|
|
||||||
binding.btnSearch.setOnClickListener {
|
binding.btnSearch.setOnClickListener {
|
||||||
@ -62,18 +66,26 @@ class MainFragment : Fragment() {
|
|||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun initializeAll() {
|
private fun initializeAll() {
|
||||||
|
mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
|
||||||
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
|
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
|
||||||
setUpUsageText()
|
|
||||||
openYTButton()
|
openYTButton()
|
||||||
openSpotifyButton()
|
openSpotifyButton()
|
||||||
openGithubButton()
|
openGithubButton()
|
||||||
openInstaButton()
|
openInstaButton()
|
||||||
openLinkedInButton()
|
openLinkedInButton()
|
||||||
|
historyButton()
|
||||||
|
binding.usage.text = usageText()
|
||||||
binding.btnDonate.setOnClickListener {
|
binding.btnDonate.setOnClickListener {
|
||||||
sharedViewModel.easyUpiPayment?.startPayment()
|
easyUpiPayment.startPayment()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun historyButton() {
|
||||||
|
binding.btnHistory.setOnClickListener {
|
||||||
|
findNavController().navigate(MainFragmentDirections.actionMainFragmentToDownloadRecord())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -92,16 +104,6 @@ class MainFragment : Fragment() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
* Implementing buttons
|
||||||
**/
|
**/
|
||||||
@ -161,5 +163,11 @@ class MainFragment : Fragment() {
|
|||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private fun usageText(): SpannableStringBuilder {
|
||||||
|
return SpannableStringBuilder()
|
||||||
|
.append(getText(R.string.d_one)).append("\n")
|
||||||
|
.append(getText(R.string.d_two)).append("\n")
|
||||||
|
.append(getText(R.string.d_three)).append("\n")
|
||||||
|
.append(getText(R.string.d_four)).append("\n")
|
||||||
|
}
|
||||||
}
|
}
|
@ -17,8 +17,11 @@
|
|||||||
|
|
||||||
package com.shabinder.spotiflyer.ui.mainfragment
|
package com.shabinder.spotiflyer.ui.mainfragment
|
||||||
|
|
||||||
|
import androidx.hilt.lifecycle.ViewModelInject
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.shabinder.spotiflyer.database.DatabaseDAO
|
||||||
|
|
||||||
class MainViewModel : ViewModel() {
|
class MainViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) :
|
||||||
// TODO: Implement the ViewModel
|
ViewModel(){
|
||||||
|
//TODO Refactoring Code Up here
|
||||||
}
|
}
|
@ -41,6 +41,7 @@ import com.bumptech.glide.load.DataSource
|
|||||||
import com.bumptech.glide.load.engine.GlideException
|
import com.bumptech.glide.load.engine.GlideException
|
||||||
import com.bumptech.glide.request.RequestListener
|
import com.bumptech.glide.request.RequestListener
|
||||||
import com.bumptech.glide.request.target.Target
|
import com.bumptech.glide.request.target.Target
|
||||||
|
import com.github.kiulian.downloader.YoutubeDownloader
|
||||||
import com.shabinder.spotiflyer.MainActivity
|
import com.shabinder.spotiflyer.MainActivity
|
||||||
import com.shabinder.spotiflyer.R
|
import com.shabinder.spotiflyer.R
|
||||||
import com.shabinder.spotiflyer.SharedViewModel
|
import com.shabinder.spotiflyer.SharedViewModel
|
||||||
@ -51,18 +52,23 @@ 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 com.shabinder.spotiflyer.utils.rotateAnim
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
class SpotifyFragment : Fragment() {
|
class SpotifyFragment : Fragment() {
|
||||||
private lateinit var binding:SpotifyFragmentBinding
|
private lateinit var binding:SpotifyFragmentBinding
|
||||||
private lateinit var spotifyViewModel: SpotifyViewModel
|
private lateinit var spotifyViewModel: SpotifyViewModel
|
||||||
private lateinit var sharedViewModel: SharedViewModel
|
private lateinit var sharedViewModel: SharedViewModel
|
||||||
private lateinit var adapterSpotify:SpotifyTrackListAdapter
|
private lateinit var adapterSpotify:SpotifyTrackListAdapter
|
||||||
|
@Inject lateinit var ytDownloader:YoutubeDownloader
|
||||||
private var webView: WebView? = null
|
private var webView: WebView? = null
|
||||||
private var intentFilter:IntentFilter? = null
|
private var intentFilter:IntentFilter? = null
|
||||||
private var updateUIReceiver: BroadcastReceiver? = null
|
private var updateUIReceiver: BroadcastReceiver? = null
|
||||||
@ -87,7 +93,7 @@ class SpotifyFragment : Fragment() {
|
|||||||
|
|
||||||
Log.i("Fragment", "$type : $link")
|
Log.i("Fragment", "$type : $link")
|
||||||
|
|
||||||
if(sharedViewModel.spotifyService.value == null && isNotOnline()){//Authentication pending!!
|
if(sharedViewModel.spotifyService.value == null){//Authentication pending!!
|
||||||
(activity as MainActivity).authenticateSpotify()
|
(activity as MainActivity).authenticateSpotify()
|
||||||
}
|
}
|
||||||
if(!isNotOnline()){//Device Offline
|
if(!isNotOnline()){//Device Offline
|
||||||
@ -122,7 +128,7 @@ class SpotifyFragment : Fragment() {
|
|||||||
spotifyViewModel.folderType,
|
spotifyViewModel.folderType,
|
||||||
spotifyViewModel.subFolder,
|
spotifyViewModel.subFolder,
|
||||||
spotifyViewModel.trackList.value!!,
|
spotifyViewModel.trackList.value!!,
|
||||||
spotifyViewModel.ytDownloader
|
ytDownloader
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -219,9 +225,6 @@ class SpotifyFragment : Fragment() {
|
|||||||
sharedViewModel.spotifyService.observe(viewLifecycleOwner, Observer {
|
sharedViewModel.spotifyService.observe(viewLifecycleOwner, Observer {
|
||||||
spotifyViewModel.spotifyService = it
|
spotifyViewModel.spotifyService = it
|
||||||
})
|
})
|
||||||
sharedViewModel.ytDownloader.observe(viewLifecycleOwner, Observer {
|
|
||||||
spotifyViewModel.ytDownloader = it
|
|
||||||
})
|
|
||||||
SpotifyDownloadHelper.webView = binding.webViewSpotify
|
SpotifyDownloadHelper.webView = binding.webViewSpotify
|
||||||
SpotifyDownloadHelper.context = requireContext()
|
SpotifyDownloadHelper.context = requireContext()
|
||||||
SpotifyDownloadHelper.spotifyViewModel = spotifyViewModel
|
SpotifyDownloadHelper.spotifyViewModel = spotifyViewModel
|
||||||
@ -283,7 +286,7 @@ class SpotifyFragment : Fragment() {
|
|||||||
* Configure Recycler View Adapter
|
* Configure Recycler View Adapter
|
||||||
**/
|
**/
|
||||||
private fun adapterConfig(trackList: List<Track>){
|
private fun adapterConfig(trackList: List<Track>){
|
||||||
adapterSpotify.spotifyFragment = this
|
adapterSpotify.ytDownloader = ytDownloader
|
||||||
adapterSpotify.spotifyViewModel = spotifyViewModel
|
adapterSpotify.spotifyViewModel = spotifyViewModel
|
||||||
adapterSpotify.submitList(trackList)
|
adapterSpotify.submitList(trackList)
|
||||||
}
|
}
|
||||||
|
@ -18,21 +18,21 @@
|
|||||||
package com.shabinder.spotiflyer.ui.spotify
|
package com.shabinder.spotiflyer.ui.spotify
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.hilt.lifecycle.ViewModelInject
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.github.kiulian.downloader.YoutubeDownloader
|
import com.shabinder.spotiflyer.database.DatabaseDAO
|
||||||
|
import com.shabinder.spotiflyer.database.DownloadRecord
|
||||||
import com.shabinder.spotiflyer.models.Album
|
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 com.shabinder.spotiflyer.utils.finalOutputDir
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class SpotifyViewModel: ViewModel() {
|
class SpotifyViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) :
|
||||||
|
ViewModel(){
|
||||||
|
|
||||||
var folderType:String = ""
|
var folderType:String = ""
|
||||||
var subFolder:String = ""
|
var subFolder:String = ""
|
||||||
@ -41,7 +41,6 @@ class SpotifyViewModel: ViewModel() {
|
|||||||
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 }
|
||||||
var spotifyService : SpotifyService? = null
|
var spotifyService : SpotifyService? = null
|
||||||
var ytDownloader : YoutubeDownloader? = null
|
|
||||||
|
|
||||||
private var viewModelJob = Job()
|
private var viewModelJob = Job()
|
||||||
val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
|
val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
|
||||||
@ -61,6 +60,17 @@ class SpotifyViewModel: ViewModel() {
|
|||||||
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!!
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
databaseDAO.insert(DownloadRecord(
|
||||||
|
type = "Track",
|
||||||
|
name = title.value!!,
|
||||||
|
link = "https://open.spotify.com/$type/$link",
|
||||||
|
coverUrl = coverUrl.value!!,
|
||||||
|
totalFiles = tempTrackList.size,
|
||||||
|
downloaded = trackObject.downloaded =="Downloaded",
|
||||||
|
directory = finalOutputDir(trackObject.name!!,folderType,subFolder)
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,6 +89,17 @@ class SpotifyViewModel: ViewModel() {
|
|||||||
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!!
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
databaseDAO.insert(DownloadRecord(
|
||||||
|
type = "Album",
|
||||||
|
name = title.value!!,
|
||||||
|
link = "https://open.spotify.com/$type/$link",
|
||||||
|
coverUrl = coverUrl.value!!,
|
||||||
|
totalFiles = tempTrackList.size,
|
||||||
|
downloaded = File(finalOutputDir(type = folderType,subFolder = subFolder)).listFiles()?.size == tempTrackList.size,
|
||||||
|
directory = finalOutputDir(type = folderType,subFolder = subFolder)
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,17 +113,24 @@ class SpotifyViewModel: ViewModel() {
|
|||||||
it.track?.let {
|
it.track?.let {
|
||||||
it1 -> if(File(finalOutputDir(it1.name!!,folderType,subFolder)).exists()){//Download Already Present!!
|
it1 -> if(File(finalOutputDir(it1.name!!,folderType,subFolder)).exists()){//Download Already Present!!
|
||||||
it1.downloaded = "Downloaded"
|
it1.downloaded = "Downloaded"
|
||||||
Log.i("ViewModel123","${it1.name} Downloaded")
|
|
||||||
}
|
}
|
||||||
tempTrackList.add(it1)
|
tempTrackList.add(it1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.i("ViewModel",tempTrackList.size.toString())
|
|
||||||
Log.i("ViewModel",playlistObject.tracks?.items?.size.toString())
|
|
||||||
trackList.value = tempTrackList
|
trackList.value = tempTrackList
|
||||||
title.value = playlistObject.name
|
title.value = playlistObject.name
|
||||||
coverUrl.value = playlistObject.images?.get(0)!!.url!!
|
coverUrl.value = playlistObject.images?.get(0)!!.url!!
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
databaseDAO.insert(DownloadRecord(
|
||||||
|
type = "Playlist",
|
||||||
|
name = title.value!!,
|
||||||
|
link = "https://open.spotify.com/$type/$link",
|
||||||
|
coverUrl = coverUrl.value!!,
|
||||||
|
totalFiles = tempTrackList.size,
|
||||||
|
downloaded = File(finalOutputDir(type = folderType,subFolder = subFolder)).listFiles()?.size == tempTrackList.size,
|
||||||
|
directory = finalOutputDir(type = folderType,subFolder = subFolder)
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"episode" -> {//TODO
|
"episode" -> {//TODO
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
|
|
||||||
package com.shabinder.spotiflyer.ui.youtube
|
package com.shabinder.spotiflyer.ui.youtube
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.net.ConnectivityManager
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -28,6 +26,7 @@ import androidx.databinding.DataBindingUtil
|
|||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.github.kiulian.downloader.YoutubeDownloader
|
||||||
import com.shabinder.spotiflyer.R
|
import com.shabinder.spotiflyer.R
|
||||||
import com.shabinder.spotiflyer.SharedViewModel
|
import com.shabinder.spotiflyer.SharedViewModel
|
||||||
import com.shabinder.spotiflyer.databinding.YoutubeFragmentBinding
|
import com.shabinder.spotiflyer.databinding.YoutubeFragmentBinding
|
||||||
@ -35,7 +34,10 @@ import com.shabinder.spotiflyer.downloadHelper.YTDownloadHelper
|
|||||||
import com.shabinder.spotiflyer.models.Track
|
import com.shabinder.spotiflyer.models.Track
|
||||||
import com.shabinder.spotiflyer.recyclerView.YoutubeTrackListAdapter
|
import com.shabinder.spotiflyer.recyclerView.YoutubeTrackListAdapter
|
||||||
import com.shabinder.spotiflyer.utils.bindImage
|
import com.shabinder.spotiflyer.utils.bindImage
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
class YoutubeFragment : Fragment() {
|
class YoutubeFragment : Fragment() {
|
||||||
|
|
||||||
private lateinit var binding:YoutubeFragmentBinding
|
private lateinit var binding:YoutubeFragmentBinding
|
||||||
@ -44,7 +46,7 @@ class YoutubeFragment : Fragment() {
|
|||||||
private lateinit var adapter : YoutubeTrackListAdapter
|
private lateinit var adapter : YoutubeTrackListAdapter
|
||||||
private val sampleDomain1 = "youtube.com"
|
private val sampleDomain1 = "youtube.com"
|
||||||
private val sampleDomain2 = "youtu.be"
|
private val sampleDomain2 = "youtu.be"
|
||||||
|
@Inject lateinit var ytDownloader: YoutubeDownloader
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
@ -57,9 +59,7 @@ class YoutubeFragment : Fragment() {
|
|||||||
YTDownloadHelper.context = requireContext()
|
YTDownloadHelper.context = requireContext()
|
||||||
YTDownloadHelper.statusBar = binding.StatusBarYoutube
|
YTDownloadHelper.statusBar = binding.StatusBarYoutube
|
||||||
binding.trackListYoutube.adapter = adapter
|
binding.trackListYoutube.adapter = adapter
|
||||||
sharedViewModel.ytDownloader.observe(viewLifecycleOwner, Observer {
|
|
||||||
youtubeViewModel.ytDownloader = it
|
|
||||||
})
|
|
||||||
initializeLiveDataObservers()
|
initializeLiveDataObservers()
|
||||||
|
|
||||||
val args = YoutubeFragmentArgs.fromBundle(requireArguments())
|
val args = YoutubeFragmentArgs.fromBundle(requireArguments())
|
||||||
@ -79,10 +79,10 @@ class YoutubeFragment : Fragment() {
|
|||||||
searchId = link.substringAfterLast("/","error")
|
searchId = link.substringAfterLast("/","error")
|
||||||
}
|
}
|
||||||
if(searchId != "error") {
|
if(searchId != "error") {
|
||||||
// val coverUrl = "https://i.ytimg.com/vi/$searchId/maxresdefault.jpg"
|
youtubeViewModel.getYTTrack(searchId,ytDownloader)
|
||||||
youtubeViewModel.getYTTrack(searchId)
|
|
||||||
binding.btnDownloadAllYoutube.setOnClickListener {
|
binding.btnDownloadAllYoutube.setOnClickListener {
|
||||||
//TODO
|
YTDownloadHelper.downloadFile(null,"YT_Downloads",
|
||||||
|
youtubeViewModel.ytTrack.value!!,youtubeViewModel.format.value)
|
||||||
}
|
}
|
||||||
}else{showToast("Your Youtube Link is not of a Video!!")}
|
}else{showToast("Your Youtube Link is not of a Video!!")}
|
||||||
}else(showToast("Your Youtube Link is not of a Video!!"))
|
}else(showToast("Your Youtube Link is not of a Video!!"))
|
||||||
@ -122,7 +122,6 @@ class YoutubeFragment : Fragment() {
|
|||||||
* Configure Recycler View Adapter
|
* Configure Recycler View Adapter
|
||||||
**/
|
**/
|
||||||
private fun adapterConfig(list:List<Track>){
|
private fun adapterConfig(list:List<Track>){
|
||||||
adapter.sharedViewModel = sharedViewModel
|
|
||||||
adapter.submitList(list)
|
adapter.submitList(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,13 +132,4 @@ class YoutubeFragment : Fragment() {
|
|||||||
Toast.makeText(context,message, Toast.LENGTH_SHORT).show()
|
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -18,34 +18,37 @@
|
|||||||
package com.shabinder.spotiflyer.ui.youtube
|
package com.shabinder.spotiflyer.ui.youtube
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.hilt.lifecycle.ViewModelInject
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.github.kiulian.downloader.YoutubeDownloader
|
import com.github.kiulian.downloader.YoutubeDownloader
|
||||||
import com.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.database.DatabaseDAO
|
||||||
|
import com.shabinder.spotiflyer.database.DownloadRecord
|
||||||
import com.shabinder.spotiflyer.models.Artist
|
import com.shabinder.spotiflyer.models.Artist
|
||||||
import com.shabinder.spotiflyer.models.Track
|
import com.shabinder.spotiflyer.models.Track
|
||||||
|
import com.shabinder.spotiflyer.utils.finalOutputDir
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
|
||||||
class YoutubeViewModel : ViewModel() {
|
class YoutubeViewModel @ViewModelInject constructor(val databaseDAO: DatabaseDAO) :
|
||||||
|
ViewModel(){
|
||||||
|
|
||||||
val ytTrack = MutableLiveData<Track>()
|
val ytTrack = MutableLiveData<Track>()
|
||||||
val format = MutableLiveData<Format>()
|
val format = MutableLiveData<Format>()
|
||||||
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 }
|
||||||
var ytDownloader: YoutubeDownloader? = null
|
|
||||||
|
|
||||||
|
|
||||||
private var viewModelJob = Job()
|
private var viewModelJob = Job()
|
||||||
val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
|
val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
|
||||||
|
|
||||||
|
|
||||||
fun getYTTrack(searchId:String) {
|
fun getYTTrack(searchId:String,ytDownloader:YoutubeDownloader) {
|
||||||
uiScope.launch {
|
uiScope.launch {
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
Log.i("YT View Model",searchId)
|
Log.i("YT View Model",searchId)
|
||||||
val video = ytDownloader?.getVideo(searchId)
|
val video = ytDownloader.getVideo(searchId)
|
||||||
val detail = video?.details()
|
val detail = video?.details()
|
||||||
val name = detail?.title()?.replace(detail.author()!!.toUpperCase(),"",true) ?: detail?.title()
|
val name = detail?.title()?.replace(detail.author()!!.toUpperCase(),"",true) ?: detail?.title()
|
||||||
Log.i("YT View Model",detail.toString())
|
Log.i("YT View Model",detail.toString())
|
||||||
@ -75,6 +78,17 @@ class YoutubeViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
databaseDAO.insert(DownloadRecord(
|
||||||
|
type = "Track",
|
||||||
|
name = if(name.length > 17){"${name.subSequence(0,16)}..."}else{name},
|
||||||
|
link = "https://www.youtube.com/watch?v=$searchId",
|
||||||
|
coverUrl = "https://i.ytimg.com/vi/$searchId/maxresdefault.jpg",
|
||||||
|
totalFiles = 1,
|
||||||
|
downloaded = false,
|
||||||
|
directory = finalOutputDir(type = "YT_Downloads")
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,11 +43,11 @@ 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{
|
fun finalOutputDir(itemName:String? = null,type:String, subFolder:String?=null,extension:String? = ".mp3"): String{
|
||||||
return Environment.getExternalStorageDirectory().toString() + File.separator +
|
return Environment.getExternalStorageDirectory().toString() + File.separator +
|
||||||
SpotifyDownloadHelper.defaultDir + SpotifyDownloadHelper.removeIllegalChars(type) + File.separator +
|
SpotifyDownloadHelper.defaultDir + SpotifyDownloadHelper.removeIllegalChars(type) + File.separator +
|
||||||
(if(subFolder == null){""}else{ SpotifyDownloadHelper.removeIllegalChars(subFolder) + File.separator}
|
(if(subFolder == null){""}else{ SpotifyDownloadHelper.removeIllegalChars(subFolder) + File.separator}
|
||||||
+ SpotifyDownloadHelper.removeIllegalChars(itemName) +".mp3")
|
+ itemName?.let { SpotifyDownloadHelper.removeIllegalChars(it) + extension})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun rotateAnim(view: View){
|
fun rotateAnim(view: View){
|
||||||
|
104
app/src/main/java/com/shabinder/spotiflyer/utils/Provider.kt
Normal file
104
app/src/main/java/com/shabinder/spotiflyer/utils/Provider.kt
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* 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.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.github.kiulian.downloader.YoutubeDownloader
|
||||||
|
import com.shabinder.spotiflyer.App
|
||||||
|
import com.shabinder.spotiflyer.MainActivity
|
||||||
|
import com.shabinder.spotiflyer.database.DatabaseDAO
|
||||||
|
import com.shabinder.spotiflyer.database.DownloadRecordDatabase
|
||||||
|
import com.shreyaspatil.EasyUpiPayment.EasyUpiPayment
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
|
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.android.components.ApplicationComponent
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@InstallIn(ApplicationComponent::class)
|
||||||
|
@Module
|
||||||
|
object Provider {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun databaseDAO(@ApplicationContext appContext: Context):DatabaseDAO{
|
||||||
|
return DownloadRecordDatabase.getInstance(appContext).databaseDAO
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideUpi():EasyUpiPayment {
|
||||||
|
return EasyUpiPayment.Builder()
|
||||||
|
.with(MainActivity.getInstance())
|
||||||
|
.setPayeeVpa("technoshab@paytm")
|
||||||
|
.setPayeeName("Shabinder Singh")
|
||||||
|
.setTransactionId("UNIQUE_TRANSACTION_ID")
|
||||||
|
.setTransactionRefId("UNIQUE_TRANSACTION_REF_ID")
|
||||||
|
.setDescription("Thanks for donating")
|
||||||
|
.setAmount("39.00")
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun getMoshi():Moshi{
|
||||||
|
return Moshi.Builder()
|
||||||
|
.add(KotlinJsonAdapterFactory())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun getYTDownloader():YoutubeDownloader{
|
||||||
|
return YoutubeDownloader()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun getSpotifyTokenInterface():SpotifyServiceTokenRequest{
|
||||||
|
val httpClient2: OkHttpClient.Builder = OkHttpClient.Builder()
|
||||||
|
httpClient2.addInterceptor(object : Interceptor {
|
||||||
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
|
val request: Request =
|
||||||
|
chain.request().newBuilder().addHeader(
|
||||||
|
"Authorization",
|
||||||
|
"Basic ${android.util.Base64.encodeToString("${App.clientId}:${App.clientSecret}".toByteArray(),android.util.Base64.NO_WRAP)}"
|
||||||
|
).build()
|
||||||
|
return chain.proceed(request)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
val retrofit = Retrofit.Builder()
|
||||||
|
.baseUrl("https://accounts.spotify.com/")
|
||||||
|
.client(httpClient2.build())
|
||||||
|
.addConverterFactory(MoshiConverterFactory.create(getMoshi()))
|
||||||
|
.build()
|
||||||
|
return retrofit.create(SpotifyServiceTokenRequest::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -17,7 +17,10 @@
|
|||||||
|
|
||||||
package com.shabinder.spotiflyer.utils
|
package com.shabinder.spotiflyer.utils
|
||||||
|
|
||||||
import com.shabinder.spotiflyer.models.*
|
import com.shabinder.spotiflyer.models.Album
|
||||||
|
import com.shabinder.spotiflyer.models.Playlist
|
||||||
|
import com.shabinder.spotiflyer.models.Token
|
||||||
|
import com.shabinder.spotiflyer.models.Track
|
||||||
import retrofit2.http.*
|
import retrofit2.http.*
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -54,13 +57,9 @@ interface SpotifyService {
|
|||||||
|
|
||||||
@GET("albums/{id}")
|
@GET("albums/{id}")
|
||||||
suspend fun getAlbum(@Path("id") albumId: String?): Album
|
suspend fun getAlbum(@Path("id") albumId: String?): Album
|
||||||
|
|
||||||
@GET("me")
|
|
||||||
suspend fun getMe(): UserPrivate?
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SpotifyServiceToken{
|
interface SpotifyServiceTokenRequest{
|
||||||
|
|
||||||
@POST("api/token")
|
@POST("api/token")
|
||||||
@FormUrlEncoded
|
@FormUrlEncoded
|
@ -24,10 +24,7 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.*
|
||||||
import android.os.Environment
|
|
||||||
import android.os.IBinder
|
|
||||||
import android.os.PowerManager
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
@ -198,10 +195,12 @@ class ForegroundService : Service(){
|
|||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
if(downloadMap.isEmpty() && converted == total){
|
if(downloadMap.isEmpty() && converted == total){
|
||||||
|
Handler().postDelayed({
|
||||||
Log.i(tag,"Service destroyed.")
|
Log.i(tag,"Service destroyed.")
|
||||||
deleteFile(parentDirectory)
|
deleteFile(parentDirectory)
|
||||||
releaseWakeLock()
|
releaseWakeLock()
|
||||||
stopForeground(true)
|
stopForeground(true)
|
||||||
|
},2000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,11 +223,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
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,7 +242,7 @@ class ForegroundService : Service(){
|
|||||||
if (file.isDirectory) {
|
if (file.isDirectory) {
|
||||||
deleteFile(file)
|
deleteFile(file)
|
||||||
} else if(file.isFile) {
|
} else if(file.isFile) {
|
||||||
if(file.path.toString().substringAfterLast(".") != "mp3" && file.path.toString().substringAfterLast(".") != "jpeg"){
|
if(file.path.toString().substringAfterLast(".") != "mp3"){
|
||||||
// Log.i(tag,"deleting ${file.path}")
|
// Log.i(tag,"deleting ${file.path}")
|
||||||
file.delete()
|
file.delete()
|
||||||
}
|
}
|
||||||
@ -427,7 +426,7 @@ class ForegroundService : Service(){
|
|||||||
val m4aFile = File(filePath)
|
val m4aFile = File(filePath)
|
||||||
|
|
||||||
FFmpeg.executeAsync(
|
FFmpeg.executeAsync(
|
||||||
"-i $filePath -b:a 160k -vn ${filePath.substringBeforeLast('.') + ".mp3"}"
|
"-i $filePath -y -b:a 160k -vn ${filePath.substringBeforeLast('.') + ".mp3"}"
|
||||||
) { _, returnCode ->
|
) { _, returnCode ->
|
||||||
when (returnCode) {
|
when (returnCode) {
|
||||||
RETURN_CODE_SUCCESS -> {
|
RETURN_CODE_SUCCESS -> {
|
||||||
|
31
app/src/main/res/drawable/ic_arrow_share.xml
Normal file
31
app/src/main/res/drawable/ic_arrow_share.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="m48,496a32.036,32.036 0,0 1,-32 -32,351.731 351.731,0 0,1 352,-351.995l-51.2,-38.4a32,32 0,1 1,38.4 -51.2l128,96a32,32 0,0 1,0 51.2l-128,96a31.736,31.736 0,0 1,-19.18 6.395,32 32,0 0,1 -19.22,-57.6l51.2,-38.4a288,288 0,0 0,-288 288,32.036 32.036,0 0,1 -32,32zM368,128a335.73,335.73 0,0 0,-336 336,16 16,0 0,0 32,0 304,304 0,0 1,304 -304h24a8,8 0,0 1,4.8 14.4l-70.4,52.8a16,16 0,1 0,19.2 25.6l128,-96a16,16 0,0 0,0 -25.6l-128,-96a16,16 0,1 0,-19.2 25.6l70.4,52.8a8,8 0,0 1,-4.8 14.4z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient android:endX="256" android:endY="16"
|
||||||
|
android:startX="256" android:startY="496" android:type="linear">
|
||||||
|
<item android:color="#8497FA" android:offset="0"/>
|
||||||
|
<item android:color="#CE1CFF" android:offset="0.6"/>
|
||||||
|
<item android:color="#FC5C7D" android:offset="0.9"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
</vector>
|
51
app/src/main/res/drawable/ic_history.xml
Normal file
51
app/src/main/res/drawable/ic_history.xml
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<!--
|
||||||
|
~ 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="38dp"
|
||||||
|
android:height="38dp" android:viewportWidth="512" android:viewportHeight="512">
|
||||||
|
<path android:fillColor="#F7F4FBFF" android:pathData="M256,256m-148.886,0a148.886,148.886 0,1 1,297.772 0a148.886,148.886 0,1 1,-297.772 0"/>
|
||||||
|
<path android:fillColor="#E9E4F6FF" android:pathData="m343.924,135.849c18.061,24.639 28.734,55.033 28.734,87.924 0,82.227 -66.658,148.886 -148.886,148.886 -32.891,0 -63.285,-10.673 -87.924,-28.734 27.091,36.959 70.815,60.962 120.151,60.962 82.227,0 148.886,-66.658 148.886,-148.886 0.001,-49.337 -24.003,-93.061 -60.961,-120.152z"/>
|
||||||
|
<path android:fillColor="#2b4d66" android:pathData="m256,412.537c-86.315,0 -156.537,-70.222 -156.537,-156.537s70.222,-156.537 156.537,-156.537 156.537,70.222 156.537,156.537 -70.222,156.537 -156.537,156.537zM256,114.765c-77.877,0 -141.234,63.357 -141.234,141.234 0,77.878 63.357,141.235 141.234,141.235s141.234,-63.357 141.234,-141.234 -63.357,-141.235 -141.234,-141.235z"/>
|
||||||
|
<path android:fillColor="@color/colorPrimary" android:pathData="m256,512c-78.938,0 -152.265,-35.6 -201.178,-97.671l24.037,-18.942c41.992,53.288 104.411,84.42 171.917,85.95 125.599,2.845 228.78,-96.388 230.596,-222.007 0.324,-22.421 -2.625,-44.569 -8.758,-65.858 -0.764,-2.653 0.749,-5.426 3.384,-6.249l19.479,-6.083c2.737,-0.855 5.634,0.717 6.432,3.471 6.698,23.109 10.091,47.098 10.091,71.389 0,68.38 -26.629,132.667 -74.98,181.019 -48.353,48.352 -112.64,74.981 -181.02,74.981z"/>
|
||||||
|
<path android:fillColor="@color/colorPrimaryDark" android:pathData="m15.922,328.908c-2.743,0.832 -5.628,-0.761 -6.403,-3.52 -6.318,-22.491 -9.519,-45.807 -9.519,-69.388 0,-68.38 26.629,-132.667 74.98,-181.019 48.353,-48.352 112.64,-74.981 181.02,-74.981 78.92,0 152.236,35.586 201.15,97.634l-24.034,18.946c-43.073,-54.639 -107.63,-85.976 -177.116,-85.976 -124.284,0 -225.396,101.112 -225.396,225.396 0,20.658 2.784,41.073 8.281,60.761 0.743,2.66 -0.791,5.422 -3.434,6.223z"/>
|
||||||
|
<path android:fillColor="@color/colorPrimaryDark" android:pathData="m501.909,184.611c-0.798,-2.754 -3.695,-4.326 -6.432,-3.471l-10,3.123c6.765,23.216 10.192,47.325 10.192,71.737 0,68.38 -26.629,132.667 -74.98,181.019 -46.438,46.439 -107.578,72.824 -172.922,74.841 2.738,0.086 5.481,0.14 8.233,0.14 68.38,0 132.667,-26.629 181.019,-74.981s74.981,-112.639 74.981,-181.019c0,-24.291 -3.393,-48.28 -10.091,-71.389z"/>
|
||||||
|
<path android:fillColor="@color/colorPrimary" android:pathData="m256,0c-16.62,0 -32.995,1.586 -48.962,4.662 9.424,-1.04 18.958,-1.586 28.578,-1.586 78.92,0 152.236,35.586 201.15,97.634l-10.161,8.01c2.216,2.569 4.397,5.178 6.512,7.86l24.034,-18.946c-48.915,-62.048 -122.231,-97.634 -201.151,-97.634z"/>
|
||||||
|
<path android:fillColor="@color/colorPrimary" android:pathData="m463.088,129.101h-81.951c-2.817,0 -5.101,-2.284 -5.101,-5.101v-20.403c0,-2.817 2.284,-5.101 5.101,-5.101h57.467v-57.466c0,-2.817 2.284,-5.101 5.101,-5.101h20.403c2.817,0 5.101,2.284 5.101,5.101v81.951c-0.001,3.38 -2.741,6.12 -6.121,6.12z"/>
|
||||||
|
<path android:fillColor="@color/colorPrimaryDark" android:pathData="m464.108,35.929h-10.064v71.887c0,3.38 -2.74,6.121 -6.121,6.121h-71.887v10.064c0,2.817 2.284,5.101 5.101,5.101h81.951c3.38,0 6.121,-2.74 6.121,-6.121v-81.951c-0.001,-2.817 -2.284,-5.101 -5.101,-5.101z"/>
|
||||||
|
<path android:fillColor="@color/colorPrimary" android:pathData="m68.295,476.043h-20.403c-2.817,0 -5.101,-2.284 -5.101,-5.101v-81.951c0,-3.38 2.74,-6.121 6.121,-6.121h81.951c2.817,0 5.101,2.284 5.101,5.101v20.403c0,2.817 -2.284,5.101 -5.101,5.101h-57.467v57.467c0,2.818 -2.284,5.101 -5.101,5.101z"/>
|
||||||
|
<path android:fillColor="@color/colorPrimaryDark" android:pathData="m130.863,382.871h-9.437v10.966c0,2.817 -2.284,5.101 -5.101,5.101h-51.346c-3.38,0 -6.121,2.74 -6.121,6.121v51.346c0,2.817 -2.284,5.101 -5.101,5.101h-10.965v9.437c0,2.817 2.284,5.101 5.101,5.101h20.403c2.817,0 5.101,-2.284 5.101,-5.101v-57.467h57.467c2.817,0 5.101,-2.284 5.101,-5.101v-20.403c-0.001,-2.817 -2.285,-5.101 -5.102,-5.101z"/>
|
||||||
|
<path android:fillColor="@color/colorPrimary" android:pathData="m271.302,256h-30.604v-68.167c0,-2.817 2.284,-5.101 5.101,-5.101h20.403c2.817,0 5.101,2.284 5.101,5.101v68.167z"/>
|
||||||
|
<path android:fillColor="@color/colorAccent" android:pathData="m266.201,182.732h-13.574c2.817,0 5.101,2.284 5.101,5.101v68.167h13.574v-68.167c0,-2.818 -2.284,-5.101 -5.101,-5.101z"/>
|
||||||
|
<path android:fillColor="@color/colorPrimary" android:pathData="m361.903,271.302h-105.903v-30.604h105.903c2.817,0 5.101,2.284 5.101,5.101v20.403c0,2.816 -2.284,5.1 -5.101,5.1z"/>
|
||||||
|
<path android:fillColor="@color/colorAccent" android:pathData="m361.903,240.698h-8.694v11.709c0,2.817 -2.284,5.101 -5.101,5.101h-92.108v13.795h105.903c2.817,0 5.101,-2.284 5.101,-5.101v-20.403c0,-2.818 -2.284,-5.101 -5.101,-5.101z"/>
|
||||||
|
<path android:fillColor="@color/colorPrimary" android:pathData="m276.625,256c0,5.514 10.78,10.71 8.374,15.27 -5.49,10.405 -16.416,17.497 -28.999,17.497 -18.097,0 -32.767,-14.67 -32.767,-32.767s14.67,-32.767 32.767,-32.767c12.441,0 23.263,6.934 28.812,17.148 2.522,4.643 -8.187,9.964 -8.187,15.619z"/>
|
||||||
|
<path android:fillColor="#2b4d66" android:pathData="m270.644,226.689c2.207,4.408 3.456,9.379 3.456,14.644 0,18.097 -14.67,32.767 -32.767,32.767 -5.265,0 -10.236,-1.249 -14.644,-3.456 5.378,10.743 16.48,18.123 29.311,18.123 18.097,0 32.767,-14.67 32.767,-32.767 0,-12.832 -7.38,-23.933 -18.123,-29.311z"/>
|
||||||
|
<path android:fillColor="#E6E4F6FF" android:pathData="M146.424,257.964m-9.518,0a9.518,9.518 0,1 1,19.036 0a9.518,9.518 0,1 1,-19.036 0"/>
|
||||||
|
<path android:fillColor="#E6E4F6FF" android:pathData="M162.087,312.489m-9.518,0a9.518,9.518 0,1 1,19.036 0a9.518,9.518 0,1 1,-19.036 0"/>
|
||||||
|
<path android:fillColor="#E6E4F6FF" android:pathData="M202.913,351.877m-9.518,0a9.518,9.518 0,1 1,19.036 0a9.518,9.518 0,1 1,-19.036 0"/>
|
||||||
|
<path android:fillColor="#E6E4F6FF" android:pathData="M257.964,365.575m-9.518,0a9.518,9.518 0,1 1,19.036 0a9.518,9.518 0,1 1,-19.036 0"/>
|
||||||
|
<path android:fillColor="#E6E4F6FF" android:pathData="M312.489,349.913m-9.518,0a9.518,9.518 0,1 1,19.036 0a9.518,9.518 0,1 1,-19.036 0"/>
|
||||||
|
<path android:fillColor="#E6E4F6FF" android:pathData="M351.877,309.087m-9.518,0a9.518,9.518 0,1 1,19.036 0a9.518,9.518 0,1 1,-19.036 0"/>
|
||||||
|
<path android:fillColor="#E6E4F6FF" android:pathData="m249.765,370.393c1.655,2.81 4.702,4.701 8.199,4.701 5.066,0 9.195,-3.961 9.49,-8.953 -5.756,1.764 -11.659,3.19 -17.689,4.252z"/>
|
||||||
|
<path android:fillColor="#E6E4F6FF" android:pathData="M349.913,199.511m-9.518,0a9.518,9.518 0,1 1,19.036 0a9.518,9.518 0,1 1,-19.036 0"/>
|
||||||
|
<path android:fillColor="#E6E4F6FF" android:pathData="M309.087,160.123m-9.518,0a9.518,9.518 0,1 1,19.036 0a9.518,9.518 0,1 1,-19.036 0"/>
|
||||||
|
<path android:fillColor="#E6E4F6FF" android:pathData="M254.036,146.424m-9.518,0a9.518,9.518 0,1 1,19.036 0a9.518,9.518 0,1 1,-19.036 0"/>
|
||||||
|
<path android:fillColor="#E6E4F6FF" android:pathData="M199.511,162.087m-9.518,0a9.518,9.518 0,1 1,19.036 0a9.518,9.518 0,1 1,-19.036 0"/>
|
||||||
|
<path android:fillColor="#E6E4F6FF" android:pathData="M160.123,202.913m-9.518,0a9.518,9.518 0,1 1,19.036 0a9.518,9.518 0,1 1,-19.036 0"/>
|
||||||
|
<path android:fillColor="#d3effb" android:pathData="m317.248,358.156c4.552,-2.628 6.112,-8.449 3.484,-13.002 -1.184,-2.05 -3.017,-3.487 -5.088,-4.214 -4.056,3.184 -8.286,6.155 -12.664,8.912 -0.012,1.638 0.388,3.299 1.266,4.82 2.628,4.552 8.449,6.112 13.002,3.484z"/>
|
||||||
|
<path android:fillColor="#d3effb" android:pathData="m356.636,300.844c-1.485,-0.857 -3.105,-1.262 -4.705,-1.269 -2.708,4.568 -5.644,8.984 -8.809,13.22 0.778,1.842 2.131,3.458 3.996,4.535 4.552,2.628 10.374,1.069 13.002,-3.484s1.069,-10.374 -3.484,-13.002z"/>
|
||||||
|
</vector>
|
23
app/src/main/res/drawable/ic_share_open.xml
Normal file
23
app/src/main/res/drawable/ic_share_open.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<!--
|
||||||
|
~ 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="30dp"
|
||||||
|
android:height="30dp" android:viewportWidth="512" android:viewportHeight="512">
|
||||||
|
<path android:fillColor="#FF3C64" android:pathData="m304,232a24,24 0,0 1,-16.971 -40.971l160,-160a24,24 0,0 1,33.942 33.942l-160,160a23.926,23.926 0,0 1,-16.971 7.029z"/>
|
||||||
|
<path android:fillColor="#FF3B63" android:pathData="m464,200a24,24 0,0 1,-24 -24v-104h-104a24,24 0,0 1,0 -48h128a24,24 0,0 1,24 24v128a24,24 0,0 1,-24 24z"/>
|
||||||
|
<path android:fillColor="#CE1CFF" android:pathData="m464,488h-416a24,24 0,0 1,-24 -24v-416a24,24 0,0 1,24 -24h176a24,24 0,0 1,0 48h-152v368h368v-152a24,24 0,0 1,48 0v176a24,24 0,0 1,-24 24z"/>
|
||||||
|
</vector>
|
@ -16,7 +16,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
android:width="38dp" android:height="38dp"
|
android:width="36dp" android:height="36dp"
|
||||||
android:viewportWidth="512" android:viewportHeight="512">
|
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">
|
<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">
|
<aapt:attr name="android:fillColor">
|
||||||
|
85
app/src/main/res/layout/download_record_fragment.xml
Normal file
85
app/src/main/res/layout/download_record_fragment.xml
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:background="@drawable/text_background_accented"
|
||||||
|
android:drawablePadding="5dp"
|
||||||
|
android:fontFamily="@font/raleway_semibold"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:text=" Download History "
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textColor="#E1FFFFFF"
|
||||||
|
android:textSize="21sp"
|
||||||
|
app:drawableStartCompat="@drawable/ic_history"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
android:id="@+id/tabLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/textView"
|
||||||
|
app:tabGravity="fill"
|
||||||
|
app:tabIconTint="@null"
|
||||||
|
app:tabInlineLabel="true"
|
||||||
|
app:tabMode="fixed"
|
||||||
|
app:tabSelectedTextColor="@color/colorPrimary"
|
||||||
|
app:tabTextColor="#B7FFFFFF">
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabItem
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:icon="@drawable/ic_spotify_logo"
|
||||||
|
android:text="Spotify" />
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabItem
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:icon="@drawable/ic_youtube"
|
||||||
|
android:text="Youtube" />
|
||||||
|
</com.google.android.material.tabs.TabLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/download_record_list"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginStart="1dp"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:layout_marginEnd="1dp"
|
||||||
|
android:layout_marginBottom="1dp"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/tabLayout" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</layout>
|
105
app/src/main/res/layout/download_record_item.xml
Normal file
105
app/src/main/res/layout/download_record_item.xml
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<data>
|
||||||
|
<variable
|
||||||
|
name="downloadRecord"
|
||||||
|
type="com.shabinder.spotiflyer.database.DownloadRecord" />
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="92dp"
|
||||||
|
android:background="#000000"
|
||||||
|
android:paddingBottom="12dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/coverUrl"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:contentDescription="Track Image"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:src="@drawable/ic_song_placeholder" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/item_name"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginTop="14dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:layout_toStartOf="@+id/btn_action"
|
||||||
|
android:layout_toEndOf="@+id/coverUrl"
|
||||||
|
android:fontFamily="@font/raleway_semibold"
|
||||||
|
android:letterSpacing="0.04"
|
||||||
|
android:lines="1"
|
||||||
|
android:text="Weekend Chills"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppTheme.Headline4"
|
||||||
|
android:textColor="#9AB3FF"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/type"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignBottom="@+id/coverUrl"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_marginEnd="0dp"
|
||||||
|
android:layout_marginBottom="15dp"
|
||||||
|
android:layout_toStartOf="@+id/totalItems"
|
||||||
|
android:layout_toEndOf="@+id/coverUrl"
|
||||||
|
android:paddingLeft="9dp"
|
||||||
|
android:text="Playlist"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/totalItems"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignBottom="@+id/coverUrl"
|
||||||
|
android:layout_marginTop="7dp"
|
||||||
|
android:layout_marginEnd="25dp"
|
||||||
|
android:layout_marginBottom="15dp"
|
||||||
|
android:layout_toStartOf="@+id/btn_action"
|
||||||
|
android:paddingLeft="9dp"
|
||||||
|
android:text="50 Tracks"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btn_action"
|
||||||
|
android:layout_width="60dp"
|
||||||
|
android:layout_height="80dp"
|
||||||
|
android:layout_alignBottom="@+id/coverUrl"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_marginBottom="5dp"
|
||||||
|
android:backgroundTint="@color/black"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:src="@drawable/ic_share_open"
|
||||||
|
android:tint="@null" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
</layout>
|
||||||
|
|
@ -37,7 +37,6 @@
|
|||||||
android:textColorHint="@color/grey"
|
android:textColorHint="@color/grey"
|
||||||
android:textSize="19sp"
|
android:textSize="19sp"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/btn_search"
|
app:layout_constraintEnd_toStartOf="@+id/btn_search"
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintHorizontal_chainStyle="spread"
|
app:layout_constraintHorizontal_chainStyle="spread"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
@ -54,10 +53,10 @@
|
|||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/linkSearch"
|
app:layout_constraintBottom_toBottomOf="@+id/linkSearch"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/linkSearch"
|
app:layout_constraintStart_toEndOf="@+id/linkSearch"
|
||||||
app:layout_constraintTop_toTopOf="@+id/linkSearch" />
|
app:layout_constraintTop_toTopOf="@+id/linkSearch" />
|
||||||
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/appLogo"
|
android:id="@+id/appLogo"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
@ -73,6 +72,18 @@
|
|||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/linkSearch" />
|
app:layout_constraintTop_toBottomOf="@+id/linkSearch" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btn_history"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:background="@drawable/transparent"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/appLogo"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:srcCompat="@drawable/ic_history" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/appName"
|
android:id="@+id/appName"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -270,5 +281,6 @@
|
|||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/heart" />
|
app:layout_constraintStart_toEndOf="@+id/heart" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</layout>
|
</layout>
|
@ -46,6 +46,11 @@
|
|||||||
app:destination="@id/youtubeFragment"
|
app:destination="@id/youtubeFragment"
|
||||||
app:enterAnim="@android:anim/slide_in_left"
|
app:enterAnim="@android:anim/slide_in_left"
|
||||||
app:exitAnim="@android:anim/slide_out_right" />
|
app:exitAnim="@android:anim/slide_out_right" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_mainFragment_to_downloadRecord"
|
||||||
|
app:destination="@id/downloadRecord"
|
||||||
|
app:enterAnim="@android:anim/slide_in_left"
|
||||||
|
app:exitAnim="@android:anim/slide_out_right" />
|
||||||
</fragment>
|
</fragment>
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/youtubeFragment"
|
android:id="@+id/youtubeFragment"
|
||||||
@ -56,4 +61,20 @@
|
|||||||
android:name="link"
|
android:name="link"
|
||||||
app:argType="string" />
|
app:argType="string" />
|
||||||
</fragment>
|
</fragment>
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/downloadRecord"
|
||||||
|
android:name="com.shabinder.spotiflyer.ui.downloadrecord.DownloadRecordFragment"
|
||||||
|
android:label="DownloadRecord"
|
||||||
|
tools:layout="@layout/download_record_fragment">
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_downloadRecord_to_spotifyFragment"
|
||||||
|
app:destination="@id/spotifyFragment"
|
||||||
|
app:enterAnim="@android:anim/slide_in_left"
|
||||||
|
app:exitAnim="@android:anim/slide_out_right"/>
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_downloadRecord_to_youtubeFragment"
|
||||||
|
app:destination="@id/youtubeFragment"
|
||||||
|
app:enterAnim="@android:anim/slide_in_left"
|
||||||
|
app:exitAnim="@android:anim/slide_out_right"/>
|
||||||
|
</fragment>
|
||||||
</navigation>
|
</navigation>
|
@ -1,9 +1,26 @@
|
|||||||
<?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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
<color name="colorPrimary">#FC5C7D</color>
|
<color name="colorPrimary">#FC5C7D</color>
|
||||||
<color name="colorPrimaryDark">#CE1CFF</color>
|
<color name="colorPrimaryDark">#CE1CFF</color>
|
||||||
<color name="colorAccent">#8497FA</color>
|
<color name="colorAccent">#799BFF</color>
|
||||||
<color name="white">#FFFFFF</color>
|
<color name="white">#FFFFFF</color>
|
||||||
<color name="grey">#99FFFFFF</color>
|
<color name="grey">#99FFFFFF</color>
|
||||||
<color name="black">#000000</color>
|
<color name="black">#000000</color>
|
||||||
|
@ -18,8 +18,8 @@
|
|||||||
|
|
||||||
<AppUpdater>
|
<AppUpdater>
|
||||||
<update>
|
<update>
|
||||||
<latestVersion>1.3</latestVersion>
|
<latestVersion>1.4</latestVersion>
|
||||||
<latestVersionCode>4</latestVersionCode>
|
<latestVersionCode>5</latestVersionCode>
|
||||||
<url>https://github.com/Shabinder/SpotiFlyer/releases</url>
|
<url>https://github.com/Shabinder/SpotiFlyer/releases</url>
|
||||||
</update>
|
</update>
|
||||||
</AppUpdater>
|
</AppUpdater>
|
@ -20,6 +20,7 @@ buildscript {
|
|||||||
ext{
|
ext{
|
||||||
kotlin_version = "1.3.72"
|
kotlin_version = "1.3.72"
|
||||||
navigationVersion = '2.3.0'
|
navigationVersion = '2.3.0'
|
||||||
|
ext.hilt_version = '2.28-alpha'
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
@ -31,6 +32,7 @@ buildscript {
|
|||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
//safe-Args
|
//safe-Args
|
||||||
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
|
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
|
||||||
|
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
|
||||||
// classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
// classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
|
Loading…
Reference in New Issue
Block a user