ffmpeg aar and Build Gradle Files Cleaned.

Targets sdk 30.
Respects Scoped Storage.
This commit is contained in:
Shabinder 2020-11-14 17:53:45 +05:30
parent 04b6f13b22
commit d80821f759
23 changed files with 160 additions and 166 deletions

View File

@ -12,11 +12,9 @@
<set> <set>
<option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" /> <option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/mobile-ffmpeg" />
</set> </set>
</option> </option>
<option name="resolveModulePerSourceSet" value="false" /> <option name="resolveModulePerSourceSet" value="false" />
<option name="useQualifiedModuleNames" value="true" />
</GradleProjectSettings> </GradleProjectSettings>
</option> </option>
</component> </component>

View File

@ -8,7 +8,7 @@
<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>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View File

@ -15,42 +15,33 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
apply plugin: 'com.android.application' plugins {
apply plugin: 'kotlin-android' id 'com.android.application'
apply plugin: 'kotlin-android-extensions' id 'kotlin-android'
apply plugin: 'kotlin-kapt' id 'kotlin-kapt'
apply plugin: "androidx.navigation.safeargs.kotlin" id 'kotlin-android-extensions'
apply plugin: 'dagger.hilt.android.plugin' id 'androidx.navigation.safeargs.kotlin'
apply plugin: 'kotlinx-serialization' id 'dagger.hilt.android.plugin'
id 'kotlinx-serialization'
}
android { android {
compileSdkVersion 29 compileSdkVersion 30
buildToolsVersion "30.0.2" buildToolsVersion "30.0.2"
buildFeatures{ buildFeatures{
//dataBinding = true
viewBinding = true viewBinding = true
} }
defaultConfig { defaultConfig {
applicationId 'com.shabinder.spotiflyer' applicationId 'com.shabinder.spotiflyer'
minSdkVersion 22 minSdkVersion 22
targetSdkVersion 29 targetSdkVersion 30
versionCode 8 versionCode 8
versionName "1.6" versionName "1.6"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/license.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/notice.txt'
exclude 'META-INF/ASL2.0'
exclude("META-INF/*.kotlin_module")
}
buildTypes { buildTypes {
release { release {
minifyEnabled false minifyEnabled false
@ -62,26 +53,42 @@ android {
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
} }
kotlinOptions { kotlinOptions {
jvmTarget = "1.8" jvmTarget = "1.8"
} }
lintOptions { lintOptions {
abortOnError false abortOnError false
} }
kotlinOptions { kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString() jvmTarget = JavaVersion.VERSION_1_8.toString()
} }
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/license.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/notice.txt'
exclude 'META-INF/ASL2.0'
exclude("META-INF/*.kotlin_module")
}
} }
dependencies { dependencies {
implementation fileTree(dir: 'libs', include:['*.jar']) //Android
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.10" implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.10"
implementation 'androidx.core:core-ktx:1.3.2' implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.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.3.0' implementation 'androidx.webkit:webkit:1.3.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
@ -89,46 +96,49 @@ dependencies {
implementation 'androidx.navigation:navigation-ui-ktx:2.3.1' implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'
implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'com.google.android.material:material:1.2.1' implementation 'com.google.android.material:material:1.2.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1" implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1"
//FFmpeg
implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
//Room: Local SQL-lite Database
implementation "androidx.room:room-runtime:2.2.5" implementation "androidx.room:room-runtime:2.2.5"
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0' implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
kapt "androidx.room:room-compiler:2.2.5" kapt "androidx.room:room-compiler:2.2.5"
implementation "androidx.room:room-ktx:2.2.5" implementation "androidx.room:room-ktx:2.2.5"
//Hilt: Dependency Injection
implementation "com.google.dagger:hilt-android:$hilt_version" implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version" kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02' implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02' kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
implementation project(path: ':mobile-ffmpeg') //Glide-Image Loading
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation ("com.github.bumptech.glide:recyclerview-integration:4.11.0") {transitive = true}
implementation ("com.github.bumptech.glide:recyclerview-integration:4.11.0") { kapt ("com.github.bumptech.glide:recyclerview-integration:4.11.0") {transitive = true}
transitive = true
}
kapt ("com.github.bumptech.glide:recyclerview-integration:4.11.0") {
transitive = true
}
//HTTP
implementation 'com.squareup.okhttp3:okhttp:4.9.0' implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:retrofit:2.9.0'
//Json
implementation 'com.squareup.moshi:moshi:1.11.0' implementation 'com.squareup.moshi:moshi:1.11.0'
implementation 'com.squareup.moshi:moshi-kotlin:1.11.0' implementation 'com.squareup.moshi:moshi-kotlin:1.11.0'
implementation "com.squareup.retrofit2:converter-moshi:2.9.0" implementation "com.squareup.retrofit2:converter-moshi:2.9.0"
implementation "com.squareup.retrofit2:converter-scalars:2.9.0" implementation "com.squareup.retrofit2:converter-scalars:2.9.0"
implementation 'com.beust:klaxon:5.4' implementation 'com.beust:klaxon:5.4'
implementation 'me.xdrop:fuzzywuzzy:1.3.1'
//Extras
implementation 'me.xdrop:fuzzywuzzy:1.3.1'
implementation 'com.mpatric:mp3agic:0.9.1' implementation 'com.mpatric:mp3agic:0.9.1'
implementation 'com.shreyaspatil:EasyUpiPayment:3.0.0' implementation 'com.shreyaspatil:EasyUpiPayment:3.0.0'
implementation 'com.github.sealedtx:java-youtube-downloader:2.4.4'
implementation "androidx.tonyodev.fetch2:xfetch2:3.1.5"
implementation 'com.github.javiersantos:AppUpdater:2.7' implementation 'com.github.javiersantos:AppUpdater:2.7'
implementation "androidx.tonyodev.fetch2:xfetch2:3.1.5"
implementation 'com.github.sealedtx:java-youtube-downloader:2.4.4'
implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
testImplementation 'junit:junit:4.13.1' testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

BIN
app/libs/mobile-ffmpeg.aar Executable file

Binary file not shown.

View File

@ -24,8 +24,10 @@
<uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.READ_STORAGE_PERMISSION" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
@ -38,9 +40,11 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:forceDarkAllowed="true" android:forceDarkAllowed="true"
android:requestLegacyExternalStorage="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:requestLegacyExternalStorage="true"
tools:ignore="AllowBackup"
tools:targetApi="q"> tools:targetApi="q">
<!--android:requestLegacyExternalStorage="true" For SDK 28-->
<activity android:name="com.shabinder.spotiflyer.MainActivity" <activity android:name="com.shabinder.spotiflyer.MainActivity"
android:launchMode="singleTask"> android:launchMode="singleTask">

View File

@ -39,7 +39,10 @@ import com.shabinder.spotiflyer.databinding.MainActivityBinding
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper import com.shabinder.spotiflyer.downloadHelper.DownloadHelper
import com.shabinder.spotiflyer.networking.SpotifyService import com.shabinder.spotiflyer.networking.SpotifyService
import com.shabinder.spotiflyer.networking.SpotifyServiceTokenRequest import com.shabinder.spotiflyer.networking.SpotifyServiceTokenRequest
import com.shabinder.spotiflyer.utils.* import com.shabinder.spotiflyer.utils.NetworkInterceptor
import com.shabinder.spotiflyer.utils.createDirectories
import com.shabinder.spotiflyer.utils.showMessage
import com.shabinder.spotiflyer.utils.startService
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -53,7 +56,6 @@ import javax.inject.Inject
/* /*
* This is App's God Activity * This is App's God Activity
* */ * */
@Suppress("DEPRECATION")
@AndroidEntryPoint @AndroidEntryPoint
class MainActivity : AppCompatActivity(){ class MainActivity : AppCompatActivity(){
private var spotifyService : SpotifyService? = null private var spotifyService : SpotifyService? = null
@ -75,15 +77,15 @@ class MainActivity : AppCompatActivity(){
snackBarAnchor = binding.snackBarPosition snackBarAnchor = binding.snackBarPosition
DownloadHelper.youtubeMusicApi = sharedViewModel.youtubeMusicApi DownloadHelper.youtubeMusicApi = sharedViewModel.youtubeMusicApi
//starting Notification and Downloader Service!
startService(this)
authenticateSpotify() authenticateSpotify()
requestPermission() requestPermission()
disableDozeMode() disableDozeMode()
checkIfLatestVersion() checkIfLatestVersion()
createDirectories() createDirectories()
Log.i("Connection Status", isOnline().toString())
//starting Notification and Downloader Service!
startService(this)
handleIntentFromExternalActivity() handleIntentFromExternalActivity()
} }

View File

@ -18,7 +18,6 @@
package com.shabinder.spotiflyer.downloadHelper package com.shabinder.spotiflyer.downloadHelper
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Environment
import android.os.Handler import android.os.Handler
import android.util.Log import android.util.Log
import android.view.View import android.view.View
@ -99,7 +98,6 @@ object DownloadHelper {
if(videoId.isNullOrBlank()) {notFound++ ; updateStatusBar()} if(videoId.isNullOrBlank()) {notFound++ ; updateStatusBar()}
else {//Found Youtube Video ID else {//Found Youtube Video ID
val outputFile: String = val outputFile: String =
Environment.getExternalStorageDirectory().toString() + File.separator +
defaultDir + defaultDir +
removeIllegalChars(type) + File.separator + removeIllegalChars(type) + File.separator +
(if (subFolder == null) { "" } (if (subFolder == null) { "" }

View File

@ -17,7 +17,6 @@
package com.shabinder.spotiflyer.downloadHelper package com.shabinder.spotiflyer.downloadHelper
import android.os.Environment
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import com.shabinder.spotiflyer.models.DownloadObject import com.shabinder.spotiflyer.models.DownloadObject
@ -44,9 +43,7 @@ object YTDownloadHelper {
showNoConnectionAlert() showNoConnectionAlert()
return return
} }
val outputFile: String = val outputFile: String = defaultDir +
Environment.getExternalStorageDirectory().toString() + File.separator +
defaultDir +
removeIllegalChars(type) + File.separator + removeIllegalChars(type) + File.separator +
(if (subFolder == null) { "" } (if (subFolder == null) { "" }
else { removeIllegalChars(subFolder) + File.separator } else { removeIllegalChars(subFolder) + File.separator }

View File

@ -18,7 +18,6 @@
package com.shabinder.spotiflyer.models.gaana package com.shabinder.spotiflyer.models.gaana
data class GaanaPlaylist ( data class GaanaPlaylist (
val tags : String?,
val modified_on : String, val modified_on : String,
val count : Int, val count : Int,
val created_on : String, val created_on : String,

View File

@ -17,7 +17,6 @@
package com.shabinder.spotiflyer.ui.gaana package com.shabinder.spotiflyer.ui.gaana
import android.os.Environment
import androidx.hilt.lifecycle.ViewModelInject import androidx.hilt.lifecycle.ViewModelInject
import com.shabinder.spotiflyer.database.DatabaseDAO import com.shabinder.spotiflyer.database.DatabaseDAO
import com.shabinder.spotiflyer.database.DownloadRecord import com.shabinder.spotiflyer.database.DownloadRecord
@ -165,8 +164,7 @@ class GaanaViewModel @ViewModelInject constructor(
artists = it.artist.map { artist -> artist?.name.toString() }, artists = it.artist.map { artist -> artist?.name.toString() },
durationSec = it.duration, durationSec = it.duration,
albumArt = File( albumArt = File(
Environment.getExternalStorageDirectory(), Provider.imageDir + (it.artworkLink.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg"),
Provider.defaultDir +".Images/" + (it.artworkLink.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg"),
albumName = it.album_title, albumName = it.album_title,
year = it.release_date, year = it.release_date,
comment = "Genres:${it.genre?.map { genre -> genre?.name }?.reduceOrNull { acc, s -> acc + s }}", comment = "Genres:${it.genre?.map { genre -> genre?.name }?.reduceOrNull { acc, s -> acc + s }}",

View File

@ -17,7 +17,6 @@
package com.shabinder.spotiflyer.ui.spotify package com.shabinder.spotiflyer.ui.spotify
import android.os.Environment
import android.util.Log import android.util.Log
import androidx.hilt.lifecycle.ViewModelInject import androidx.hilt.lifecycle.ViewModelInject
import com.shabinder.spotiflyer.database.DatabaseDAO import com.shabinder.spotiflyer.database.DatabaseDAO
@ -29,7 +28,7 @@ import com.shabinder.spotiflyer.models.spotify.Image
import com.shabinder.spotiflyer.models.spotify.Source import com.shabinder.spotiflyer.models.spotify.Source
import com.shabinder.spotiflyer.models.spotify.Track import com.shabinder.spotiflyer.models.spotify.Track
import com.shabinder.spotiflyer.networking.SpotifyService import com.shabinder.spotiflyer.networking.SpotifyService
import com.shabinder.spotiflyer.utils.Provider import com.shabinder.spotiflyer.utils.Provider.imageDir
import com.shabinder.spotiflyer.utils.TrackListViewModel import com.shabinder.spotiflyer.utils.TrackListViewModel
import com.shabinder.spotiflyer.utils.finalOutputDir import com.shabinder.spotiflyer.utils.finalOutputDir
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -200,8 +199,7 @@ class SpotifyViewModel @ViewModelInject constructor(
artists = it.artists?.map { artist -> artist?.name.toString() } ?: listOf(), artists = it.artists?.map { artist -> artist?.name.toString() } ?: listOf(),
durationSec = (it.duration_ms/1000).toInt(), durationSec = (it.duration_ms/1000).toInt(),
albumArt = File( albumArt = File(
Environment.getExternalStorageDirectory(), imageDir + (it.album?.images?.elementAtOrNull(1)?.url ?: it.album?.images?.firstOrNull()?.url.toString()).substringAfterLast('/') + ".jpeg"),
Provider.defaultDir +".Images/" + (it.album?.images?.elementAtOrNull(1)?.url ?: it.album?.images?.firstOrNull()?.url.toString()).substringAfterLast('/') + ".jpeg"),
albumName = it.album?.name, albumName = it.album?.name,
year = it.album?.release_date, year = it.album?.release_date,
comment = "Genres:${it.album?.genres?.joinToString()}", comment = "Genres:${it.album?.genres?.joinToString()}",

View File

@ -68,7 +68,7 @@ class YoutubeFragment : TrackListFragment<YoutubeViewModel,YoutubeFragmentArgs>(
if(link.contains(sampleDomain1,true) ){ if(link.contains(sampleDomain1,true) ){
searchId = link.substringAfterLast("=","error") searchId = link.substringAfterLast("=","error")
} }
if(link.contains(sampleDomain2,true) && !link.contains("playlist",true) ){ if(link.contains(sampleDomain2,true) ){
searchId = link.substringAfterLast("/","error") searchId = link.substringAfterLast("/","error")
} }
if(searchId != "error") { if(searchId != "error") {

View File

@ -18,7 +18,6 @@
package com.shabinder.spotiflyer.ui.youtube package com.shabinder.spotiflyer.ui.youtube
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Environment
import android.util.Log import android.util.Log
import androidx.hilt.lifecycle.ViewModelInject import androidx.hilt.lifecycle.ViewModelInject
import com.github.kiulian.downloader.YoutubeDownloader import com.github.kiulian.downloader.YoutubeDownloader
@ -28,7 +27,7 @@ import com.shabinder.spotiflyer.models.DownloadStatus
import com.shabinder.spotiflyer.models.TrackDetails import com.shabinder.spotiflyer.models.TrackDetails
import com.shabinder.spotiflyer.models.spotify.Source import com.shabinder.spotiflyer.models.spotify.Source
import com.shabinder.spotiflyer.utils.* import com.shabinder.spotiflyer.utils.*
import com.shabinder.spotiflyer.utils.Provider.defaultDir import com.shabinder.spotiflyer.utils.Provider.imageDir
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -36,7 +35,7 @@ import java.io.File
class YoutubeViewModel @ViewModelInject constructor( class YoutubeViewModel @ViewModelInject constructor(
val databaseDAO: DatabaseDAO, val databaseDAO: DatabaseDAO,
val ytDownloader: YoutubeDownloader private val ytDownloader: YoutubeDownloader
) : TrackListViewModel(){ ) : TrackListViewModel(){
/* /*
* YT Album Art Schema * YT Album Art Schema
@ -67,8 +66,7 @@ class YoutubeViewModel @ViewModelInject constructor(
artists = listOf(it.author().toString()), artists = listOf(it.author().toString()),
durationSec = it.lengthSeconds(), durationSec = it.lengthSeconds(),
albumArt = File( albumArt = File(
Environment.getExternalStorageDirectory(), imageDir + it.videoId() + ".jpeg"
defaultDir + ".Images/" + it.videoId() + ".jpeg"
), ),
source = Source.YouTube, source = Source.YouTube,
albumArtURL = "https://i.ytimg.com/vi/${it.videoId()}/hqdefault.jpg", albumArtURL = "https://i.ytimg.com/vi/${it.videoId()}/hqdefault.jpg",
@ -121,10 +119,7 @@ class YoutubeViewModel @ViewModelInject constructor(
title = name, title = name,
artists = listOf(detail?.author().toString()), artists = listOf(detail?.author().toString()),
durationSec = detail?.lengthSeconds()?:0, durationSec = detail?.lengthSeconds()?:0,
albumArt = File( albumArt = File(imageDir,"$searchId.jpeg"),
Environment.getExternalStorageDirectory(),
"$defaultDir.Images/$searchId.jpeg"
),
source = Source.YouTube, source = Source.YouTube,
albumArtURL = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg" albumArtURL = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg"
) )

View File

@ -49,13 +49,21 @@ import javax.inject.Singleton
@Module @Module
object Provider { object Provider {
/*
* mainActivity Instance to use whereEver Needed , as Its God Activity.
* (i.e, Active Through out App' Lifecycle )
* */
// mainActivity Instance to use whereEver Needed , as Its God Activity.
// (i.e, Active Through out App' Lifecycle )
val mainActivity: MainActivity = MainActivity.getInstance() val mainActivity: MainActivity = MainActivity.getInstance()
val defaultDir = Environment.DIRECTORY_MUSIC + File.separator + "SpotiFlyer" + File.separator
//Default Directory to save Media in their Own Categorized Folders
@Suppress("DEPRECATION")// We Do Have Media Access (But Just Media in Media Directory,Not Anything Else)
val defaultDir = Environment.getExternalStorageDirectory().toString() + File.separator +
Environment.DIRECTORY_MUSIC + File.separator +
"SpotiFlyer"+ File.separator
//Default Cache Directory to save Album Art to use them for writing in Media Later
val imageDir:String
get() = mainActivity.externalCacheDir?.absolutePath + File.separator +
".Images" + File.separator
@Provides @Provides

View File

@ -22,6 +22,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -57,6 +58,7 @@ abstract class TrackListFragment<VM : TrackListViewModel , args: NavArgs> : Frag
showNoConnectionAlert() showNoConnectionAlert()
mainActivity.navController.popBackStack() mainActivity.navController.popBackStack()
} }
Handler()
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java) sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
} }

View File

@ -22,7 +22,6 @@ import android.content.Intent
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.os.Build import android.os.Build
import android.os.Environment
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.view.animation.Animation import android.view.animation.Animation
@ -42,6 +41,7 @@ import com.shabinder.spotiflyer.R
import com.shabinder.spotiflyer.models.DownloadObject import com.shabinder.spotiflyer.models.DownloadObject
import com.shabinder.spotiflyer.models.spotify.Source import com.shabinder.spotiflyer.models.spotify.Source
import com.shabinder.spotiflyer.utils.Provider.defaultDir import com.shabinder.spotiflyer.utils.Provider.defaultDir
import com.shabinder.spotiflyer.utils.Provider.imageDir
import com.shabinder.spotiflyer.utils.Provider.mainActivity import com.shabinder.spotiflyer.utils.Provider.mainActivity
import com.shabinder.spotiflyer.worker.ForegroundService import com.shabinder.spotiflyer.worker.ForegroundService
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -63,8 +63,7 @@ fun startService(context:Context?,objects:ArrayList<DownloadObject>? = null ) {
} }
fun finalOutputDir(itemName:String? = null,type:String, subFolder:String?=null,extension:String? = ".mp3"): String{ fun finalOutputDir(itemName:String? = null,type:String, subFolder:String?=null,extension:String? = ".mp3"): String{
return Environment.getExternalStorageDirectory().toString() + File.separator + return defaultDir + removeIllegalChars(type) + File.separator +
defaultDir + removeIllegalChars(type) + File.separator +
(if(subFolder == null){""}else{ removeIllegalChars(subFolder) + File.separator} (if(subFolder == null){""}else{ removeIllegalChars(subFolder) + File.separator}
+ itemName?.let { removeIllegalChars(it) + extension}) + itemName?.let { removeIllegalChars(it) + extension})
} }
@ -121,7 +120,7 @@ fun rotateAnim(view: View){
0F, 360F, 0F, 360F,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f
) )
rotate.duration = 1000 rotate.duration = 2000
rotate.repeatCount = Animation.INFINITE rotate.repeatCount = Animation.INFINITE
rotate.repeatMode = Animation.INFINITE rotate.repeatMode = Animation.INFINITE
rotate.interpolator = LinearInterpolator() rotate.interpolator = LinearInterpolator()
@ -172,26 +171,22 @@ fun bindImage(imgView: ImageView, imgUrl: String?,source: Source?) {
val file = when(source){ val file = when(source){
Source.Spotify->{ Source.Spotify->{
File( File(
Environment.getExternalStorageDirectory(), imageDir + imgUrl.substringAfterLast('/',imgUrl) + ".jpeg"
defaultDir+".Images/" + imgUrl.substringAfterLast('/',imgUrl) + ".jpeg"
) )
} }
Source.YouTube->{ Source.YouTube->{
//Url Format: https://i.ytimg.com/vi/$searchId/maxresdefault.jpg" //Url Format: https://i.ytimg.com/vi/$searchId/maxresdefault.jpg"
// We Are Naming using "$searchId" // We Are Naming using "$searchId"
File( File(
Environment.getExternalStorageDirectory(), imageDir + imgUrl.substringBeforeLast('/',imgUrl).substringAfterLast('/',imgUrl) + ".jpeg"
defaultDir+".Images/" + imgUrl.substringBeforeLast('/',imgUrl).substringAfterLast('/',imgUrl) + ".jpeg"
) )
} }
Source.Gaana -> { Source.Gaana -> {
File( File(
Environment.getExternalStorageDirectory(), imageDir + (imgUrl.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg")
Provider.defaultDir +".Images/" + (imgUrl.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg")
} }
else -> File( else -> File(
Environment.getExternalStorageDirectory(), imageDir + imgUrl.substringAfterLast('/',imgUrl) + ".jpeg"
defaultDir+".Images/" + imgUrl.substringAfterLast('/',imgUrl) + ".jpeg"
) )
} }
// the File to save , append increasing numeric counter to prevent files from getting overwritten. // the File to save , append increasing numeric counter to prevent files from getting overwritten.
@ -221,18 +216,17 @@ fun File.copyTo(file: File) {
} }
} }
fun createDirectory(dir:String){ fun createDirectory(dir:String){
val yourAppDir = File(Environment.getExternalStorageDirectory(), val yourAppDir = File(dir)
dir)
if(!yourAppDir.exists() && !yourAppDir.isDirectory) if(!yourAppDir.exists() && !yourAppDir.isDirectory)
{ // create empty directory { // create empty directory
if (yourAppDir.mkdirs()) if (yourAppDir.mkdirs())
{Log.i("CreateDir","App dir created")} {Log.i("CreateDir","$dir created")}
else else
{Log.w("CreateDir","Unable to create app dir!")} {Log.w("CreateDir","Unable to create Dir: $dir!")}
} }
else else
{Log.i("CreateDir","App dir already exists")} {Log.i("CreateDir","$dir already exists")}
} }
/** /**
* Removing Illegal Chars from File Name * Removing Illegal Chars from File Name
@ -277,7 +271,7 @@ fun removeIllegalChars(fileName: String): String? {
fun createDirectories() { fun createDirectories() {
createDirectory(defaultDir) createDirectory(defaultDir)
createDirectory(defaultDir + ".Images/") createDirectory(imageDir)
createDirectory(defaultDir + "Tracks/") createDirectory(defaultDir + "Tracks/")
createDirectory(defaultDir + "Albums/") createDirectory(defaultDir + "Albums/")
createDirectory(defaultDir + "Playlists/") createDirectory(defaultDir + "Playlists/")
@ -285,20 +279,4 @@ fun createDirectories() {
} }
fun getEmojiByUnicode(unicode: Int): String? { fun getEmojiByUnicode(unicode: Int): String? {
return String(Character.toChars(unicode)) return String(Character.toChars(unicode))
} }
/*
internal val nullOnEmptyConverterFactory = object : Converter.Factory() {
fun converterFactory() = this
override fun responseBodyConverter(
type: Type,
annotations: Array<out Annotation>,
retrofit: Retrofit
) = object : Converter<ResponseBody, Any?> {
val nextResponseBodyConverter =
retrofit.nextResponseBodyConverter<Any?>(converterFactory(), type, annotations)
override fun convert(value: ResponseBody) =
if (value.contentLength() != 0L) nextResponseBodyConverter.convert(value) else null
}
}*/

View File

@ -25,7 +25,10 @@ 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.* import android.os.Build
import android.os.Handler
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
@ -50,6 +53,7 @@ import com.shabinder.spotiflyer.R
import com.shabinder.spotiflyer.models.DownloadObject import com.shabinder.spotiflyer.models.DownloadObject
import com.shabinder.spotiflyer.models.TrackDetails import com.shabinder.spotiflyer.models.TrackDetails
import com.shabinder.spotiflyer.utils.Provider import com.shabinder.spotiflyer.utils.Provider
import com.shabinder.spotiflyer.utils.Provider.imageDir
import com.shabinder.spotiflyer.utils.copyTo import com.shabinder.spotiflyer.utils.copyTo
import com.tonyodev.fetch2.* import com.tonyodev.fetch2.*
import com.tonyodev.fetch2core.DownloadBlock import com.tonyodev.fetch2core.DownloadBlock
@ -59,7 +63,6 @@ import java.io.FileInputStream
import java.io.IOException import java.io.IOException
import java.util.* import java.util.*
@Suppress("DEPRECATION")
class ForegroundService : Service(){ class ForegroundService : Service(){
private val tag = "Foreground Service" private val tag = "Foreground Service"
private val channelId = "ForegroundDownloaderService" private val channelId = "ForegroundDownloaderService"
@ -73,19 +76,13 @@ class ForegroundService : Service(){
private var serviceJob = Job() private var serviceJob = Job()
private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob) private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)
private val requestMap = mutableMapOf<Request, TrackDetails>() private val requestMap = mutableMapOf<Request, TrackDetails>()
private var speed :Long = 0 private var defaultDir = Provider.defaultDir
private var defaultDir = Environment.DIRECTORY_MUSIC + File.separator + "SpotiFlyer" + File.separator
private val parentDirectory = File(Environment.getExternalStorageDirectory(),
defaultDir +File.separator
)
private var wakeLock: PowerManager.WakeLock? = null private var wakeLock: PowerManager.WakeLock? = null
private var isServiceStarted = false private var isServiceStarted = false
var notificationLine = 0 var notificationLine = 0
val messageList = mutableListOf("","","","") val messageList = mutableListOf("","","","")
private var pendingIntent:PendingIntent? = null private var pendingIntent:PendingIntent? = null
override fun onBind(intent: Intent): IBinder? { override fun onBind(intent: Intent): IBinder? {
return null return null
} }
@ -201,7 +198,7 @@ class ForegroundService : Service(){
if(converted == total){ if(converted == total){
Handler().postDelayed({ Handler().postDelayed({
Log.i(tag,"Service destroyed.") Log.i(tag,"Service destroyed.")
deleteFile(parentDirectory) cleanFiles(File(defaultDir))
releaseWakeLock() releaseWakeLock()
stopForeground(true) stopForeground(true)
},2000) },2000)
@ -212,7 +209,7 @@ class ForegroundService : Service(){
super.onTaskRemoved(rootIntent) super.onTaskRemoved(rootIntent)
if(converted == total ){ if(converted == total ){
Log.i(tag,"Service Removed.") Log.i(tag,"Service Removed.")
deleteFile(parentDirectory) cleanFiles(File(defaultDir))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
stopForeground(true) stopForeground(true)
} else { } else {
@ -303,8 +300,6 @@ class ForegroundService : Service(){
requestMap.remove(download.request) requestMap.remove(download.request)
} }
} }
speed = 0
// updateNotification()
} }
override fun onDeleted(download: Download) { override fun onDeleted(download: Download) {
@ -342,7 +337,6 @@ class ForegroundService : Service(){
) { ) {
val track = requestMap[download.request] val track = requestMap[download.request]
Log.i(tag,"${track?.title} ETA: ${etaInMilliSeconds/1000} sec") Log.i(tag,"${track?.title} ETA: ${etaInMilliSeconds/1000} sec")
speed = (downloadedBytesPerSecond/1000)
// updateNotification() // updateNotification()
} }
@ -361,9 +355,7 @@ class ForegroundService : Service(){
.setAllowedOverRoaming(false) .setAllowedOverRoaming(false)
.setTitle(track.title) .setTitle(track.title)
.setDescription("Spotify Downloader Working Up here...") .setDescription("Spotify Downloader Working Up here...")
.setDestinationInExternalPublicDir(Environment.DIRECTORY_MUSIC, outputDir.removePrefix( .setDestinationUri(File(outputDir).toUri())
Environment.getExternalStorageDirectory().toString() + Environment.DIRECTORY_MUSIC + File.separator
))
.setNotificationVisibility(VISIBILITY_VISIBLE_NOTIFY_COMPLETED) .setNotificationVisibility(VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
//Start Download //Start Download
val downloadID = downloadManager.enqueue(request) val downloadID = downloadManager.enqueue(request)
@ -562,18 +554,18 @@ class ForegroundService : Service(){
} }
/** /**
* Deleting All Residual Files except Mp3 Files * Cleaning All Residual Files except Mp3 Files
**/ **/
private fun deleteFile(dir:File) { private fun cleanFiles(dir:File) {
Log.i(tag,"Starting Deletions in ${dir.path} ") Log.i(tag,"Starting Cleaning in ${dir.path} ")
val fList = dir.listFiles() val fList = dir.listFiles()
fList?.let { fList?.let {
for (file in fList) { for (file in fList) {
if (file.isDirectory) { if (file.isDirectory) {
deleteFile(file) cleanFiles(file)
} else if(file.isFile) { } else if(file.isFile) {
if(file.path.toString().substringAfterLast(".") != "mp3"){ if(file.path.toString().substringAfterLast(".") != "mp3"){
Log.i(tag,"deleting ${file.path}") Log.i(tag,"Cleaning ${file.path}")
file.delete() file.delete()
} }
} }
@ -618,25 +610,16 @@ class ForegroundService : Service(){
try { try {
val file = when(source){ val file = when(source){
"spotify" ->{ "spotify" ->{
File( File(imageDir, url.substringAfterLast('/') + ".jpeg")
Environment.getExternalStorageDirectory(),
defaultDir +".Images/" + url.substringAfterLast('/') + ".jpeg"
)
} }
"youtube" ->{ "youtube" ->{
File( File(imageDir, url.substringBeforeLast('/',url).substringAfterLast('/',url) + ".jpeg")
Environment.getExternalStorageDirectory(),
defaultDir +".Images/" + url.substringBeforeLast('/',url).substringAfterLast('/',url) + ".jpeg"
)
} }
"gaana" -> { "gaana" -> {
File( File(imageDir, (url.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg")
Environment.getExternalStorageDirectory(),
Provider.defaultDir +".Images/" + (url.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg")
} }
else -> File(
Environment.getExternalStorageDirectory(), else -> File(imageDir, url.substringAfterLast('/') + ".jpeg")
defaultDir +".Images/" + url.substringAfterLast('/') + ".jpeg")
} }
resource?.copyTo(file) resource?.copyTo(file)
} catch (e: IOException) { } catch (e: IOException) {

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2020 Shabinder Singh
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadiusRatio="2.5"
android:shape="ring"
android:thickness="4dp"
android:useLevel="false">
<solid android:color="@color/colorPrimary"/>
</shape>

View File

@ -15,8 +15,8 @@
~ along with this program. If not, see <https://www.gnu.org/licenses/>. ~ along with this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="26dp" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="36dp"
android:height="22dp" android:viewportWidth="512" android:viewportHeight="512"> android:height="32dp" android:viewportWidth="512" android:viewportHeight="512">
<path android:fillColor="#516AEC" android:pathData="m296,288 l60,-60c7.73,-7.73 17.86,-11.6 28,-11.6 10.055,0 20.101,3.806 27.806,11.407 15.612,15.402 15.207,41.18 -0.3,56.687l-134.293,134.293c-11.716,11.716 -30.711,11.716 -42.426,0l-134.787,-134.787c-7.73,-7.73 -11.6,-17.86 -11.6,-28 0,-10.055 3.806,-20.101 11.407,-27.806 15.402,-15.612 41.18,-15.207 56.687,0.3l59.506,59.506v-232c0,-22.091 17.909,-40 40,-40 22.091,0 40,17.909 40,40z"/> <path android:fillColor="#516AEC" android:pathData="m296,288 l60,-60c7.73,-7.73 17.86,-11.6 28,-11.6 10.055,0 20.101,3.806 27.806,11.407 15.612,15.402 15.207,41.18 -0.3,56.687l-134.293,134.293c-11.716,11.716 -30.711,11.716 -42.426,0l-134.787,-134.787c-7.73,-7.73 -11.6,-17.86 -11.6,-28 0,-10.055 3.806,-20.101 11.407,-27.806 15.402,-15.612 41.18,-15.207 56.687,0.3l59.506,59.506v-232c0,-22.091 17.909,-40 40,-40 22.091,0 40,17.909 40,40z"/>
<path android:fillColor="#EC7EBA" android:pathData="m411.51,284.49 l-134.3,134.3c-11.71,11.71 -30.71,11.71 -42.42,0l-12.74,-12.74c10.69,4.06 23.23,1.77 31.84,-6.84l134.29,-134.29c12.51,-12.51 15.19,-31.7 7.57,-46.74 5.86,1.81 11.39,5.03 16.06,9.63 15.61,15.4 15.2,41.18 -0.3,56.68z"/> <path android:fillColor="#EC7EBA" android:pathData="m411.51,284.49 l-134.3,134.3c-11.71,11.71 -30.71,11.71 -42.42,0l-12.74,-12.74c10.69,4.06 23.23,1.77 31.84,-6.84l134.29,-134.29c12.51,-12.51 15.19,-31.7 7.57,-46.74 5.86,1.81 11.39,5.03 16.06,9.63 15.61,15.4 15.2,41.18 -0.3,56.68z"/>
<path android:fillColor="#EC7EBA" android:pathData="m251.88,27.72c-3.46,-3.46 -7.55,-6.29 -12.08,-8.3 4.95,-2.2 10.43,-3.42 16.2,-3.42 11.04,0 21.04,4.48 28.28,11.72s11.72,17.24 11.72,28.28v232l-15.329,15.329c-6.3,6.3 -17.071,1.838 -17.071,-7.071v-240.258c0,-11.04 -4.48,-21.04 -11.72,-28.28z"/> <path android:fillColor="#EC7EBA" android:pathData="m251.88,27.72c-3.46,-3.46 -7.55,-6.29 -12.08,-8.3 4.95,-2.2 10.43,-3.42 16.2,-3.42 11.04,0 21.04,4.48 28.28,11.72s11.72,17.24 11.72,28.28v232l-15.329,15.329c-6.3,6.3 -17.071,1.838 -17.071,-7.071v-240.258c0,-11.04 -4.48,-21.04 -11.72,-28.28z"/>

View File

@ -41,10 +41,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="48dp" android:layout_marginBottom="48dp"
android:background="@drawable/text_background_accented" android:background="@drawable/text_background_accented"
android:fontFamily="@font/raleway_semibold"
android:foreground="@drawable/rounded_gradient" android:foreground="@drawable/rounded_gradient"
android:padding="7dp" android:padding="7dp"
android:text="Developer: Shabinder Singh" android:text=" Developer: Shabinder Singh "
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="16sp" android:textSize="16sp"
android:visibility="visible" android:visibility="visible"

View File

@ -19,14 +19,14 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="80dp" android:layout_height="70dp"
android:layout_marginBottom="12dp" android:layout_marginBottom="12dp"
android:background="#000000"> android:background="#000000">
<ImageView <ImageView
android:id="@+id/imageUrl" android:id="@+id/imageUrl"
android:layout_width="100dp" android:layout_width="90dp"
android:layout_height="80dp" android:layout_height="70dp"
android:contentDescription="Track Image" android:contentDescription="Track Image"
android:scaleType="centerInside" android:scaleType="centerInside"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
@ -41,6 +41,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="14dp" android:layout_marginTop="14dp"
android:layout_marginEnd="8dp"
android:fontFamily="@font/raleway_semibold" android:fontFamily="@font/raleway_semibold"
android:letterSpacing="0.04" android:letterSpacing="0.04"
android:lines="1" android:lines="1"
@ -50,7 +51,7 @@
android:textColor="#9AB3FF" android:textColor="#9AB3FF"
android:textSize="18sp" android:textSize="18sp"
app:layout_constraintEnd_toStartOf="@+id/btn_download" app:layout_constraintEnd_toStartOf="@+id/btn_download"
app:layout_constraintStart_toStartOf="@+id/artist" app:layout_constraintStart_toEndOf="@+id/imageUrl"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
@ -61,7 +62,6 @@
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:paddingLeft="9dp"
android:text="Alan Walker" android:text="Alan Walker"
android:textSize="12sp" android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
@ -75,8 +75,7 @@
style="@style/TextAppearance.AppCompat.Body2" style="@style/TextAppearance.AppCompat.Body2"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="3dp" android:layout_marginEnd="8dp"
android:paddingLeft="9dp"
android:text="4 minutes, 20 sec" android:text="4 minutes, 20 sec"
android:textSize="12sp" android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="@+id/artist" app:layout_constraintBottom_toBottomOf="@+id/artist"
@ -88,8 +87,9 @@
android:id="@+id/btn_download" android:id="@+id/btn_download"
android:layout_width="60dp" android:layout_width="60dp"
android:layout_height="0dp" android:layout_height="0dp"
android:background="@drawable/circular_background"
android:backgroundTint="@color/black" android:backgroundTint="@color/black"
android:scaleType="fitCenter" android:scaleType="centerInside"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"

View File

@ -28,11 +28,13 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.1.0' classpath 'com.android.tools.build:gradle:4.2.0-alpha16'
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"
//Hilt
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
//Kotlinx-Serialization
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
@ -45,9 +47,6 @@ allprojects {
jcenter() jcenter()
mavenCentral() mavenCentral()
maven { url "https://jitpack.io" } maven { url "https://jitpack.io" }
flatDir {
dirs 'libs'
}
} }
} }

View File

@ -14,7 +14,5 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
include ':mobile-ffmpeg'
include ':app' include ':app'
rootProject.name = "spotiflyer" rootProject.name = "spotiflyer"