mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-24 18:04:33 +01:00
ffmpeg aar and Build Gradle Files Cleaned.
Targets sdk 30. Respects Scoped Storage.
This commit is contained in:
parent
04b6f13b22
commit
d80821f759
@ -12,11 +12,9 @@
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
<option value="$PROJECT_DIR$/mobile-ffmpeg" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveModulePerSourceSet" value="false" />
|
||||
<option name="useQualifiedModuleNames" value="true" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<component name="ProjectPlainTextFileTypeManager">
|
||||
<file url="file://$PROJECT_DIR$/app/src/main/java/com/shabinder/spotiflyer/testing/YoutubeInterface.kt.backup" />
|
||||
</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" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
@ -15,42 +15,33 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: "androidx.navigation.safeargs.kotlin"
|
||||
apply plugin: 'dagger.hilt.android.plugin'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'kotlin-kapt'
|
||||
id 'kotlin-android-extensions'
|
||||
id 'androidx.navigation.safeargs.kotlin'
|
||||
id 'dagger.hilt.android.plugin'
|
||||
id 'kotlinx-serialization'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion "30.0.2"
|
||||
|
||||
buildFeatures{
|
||||
//dataBinding = true
|
||||
viewBinding = true
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId 'com.shabinder.spotiflyer'
|
||||
minSdkVersion 22
|
||||
targetSdkVersion 29
|
||||
targetSdkVersion 30
|
||||
versionCode 8
|
||||
versionName "1.6"
|
||||
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 {
|
||||
release {
|
||||
minifyEnabled false
|
||||
@ -62,26 +53,42 @@ android {
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
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 {
|
||||
implementation fileTree(dir: 'libs', include:['*.jar'])
|
||||
//Android
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.10"
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.browser:browser:1.2.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.lifecycle:lifecycle-extensions: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.recyclerview:recyclerview:1.1.0'
|
||||
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-android:1.4.0'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.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.lifecycle:lifecycle-livedata-ktx:2.2.0'
|
||||
kapt "androidx.room:room-compiler:2.2.5"
|
||||
implementation "androidx.room:room-ktx:2.2.5"
|
||||
|
||||
//Hilt: Dependency Injection
|
||||
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 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation ("com.github.bumptech.glide:recyclerview-integration:4.11.0") {
|
||||
transitive = true
|
||||
}
|
||||
kapt ("com.github.bumptech.glide:recyclerview-integration:4.11.0") {
|
||||
transitive = true
|
||||
}
|
||||
//Glide-Image Loading
|
||||
implementation ("com.github.bumptech.glide:recyclerview-integration:4.11.0") {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.retrofit2:retrofit:2.9.0'
|
||||
|
||||
//Json
|
||||
implementation 'com.squareup.moshi:moshi: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-scalars:2.9.0"
|
||||
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.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 "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'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||
|
BIN
app/libs/mobile-ffmpeg.aar
Executable file
BIN
app/libs/mobile-ffmpeg.aar
Executable file
Binary file not shown.
@ -24,8 +24,10 @@
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_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.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.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
@ -38,9 +40,11 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:forceDarkAllowed="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:theme="@style/AppTheme"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
tools:ignore="AllowBackup"
|
||||
tools:targetApi="q">
|
||||
<!--android:requestLegacyExternalStorage="true" For SDK 28-->
|
||||
|
||||
<activity android:name="com.shabinder.spotiflyer.MainActivity"
|
||||
android:launchMode="singleTask">
|
||||
|
@ -39,7 +39,10 @@ import com.shabinder.spotiflyer.databinding.MainActivityBinding
|
||||
import com.shabinder.spotiflyer.downloadHelper.DownloadHelper
|
||||
import com.shabinder.spotiflyer.networking.SpotifyService
|
||||
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 dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
@ -53,7 +56,6 @@ import javax.inject.Inject
|
||||
/*
|
||||
* This is App's God Activity
|
||||
* */
|
||||
@Suppress("DEPRECATION")
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : AppCompatActivity(){
|
||||
private var spotifyService : SpotifyService? = null
|
||||
@ -75,15 +77,15 @@ class MainActivity : AppCompatActivity(){
|
||||
snackBarAnchor = binding.snackBarPosition
|
||||
DownloadHelper.youtubeMusicApi = sharedViewModel.youtubeMusicApi
|
||||
|
||||
//starting Notification and Downloader Service!
|
||||
startService(this)
|
||||
|
||||
authenticateSpotify()
|
||||
|
||||
requestPermission()
|
||||
disableDozeMode()
|
||||
checkIfLatestVersion()
|
||||
createDirectories()
|
||||
Log.i("Connection Status", isOnline().toString())
|
||||
//starting Notification and Downloader Service!
|
||||
startService(this)
|
||||
|
||||
handleIntentFromExternalActivity()
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
package com.shabinder.spotiflyer.downloadHelper
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Environment
|
||||
import android.os.Handler
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
@ -99,7 +98,6 @@ object DownloadHelper {
|
||||
if(videoId.isNullOrBlank()) {notFound++ ; updateStatusBar()}
|
||||
else {//Found Youtube Video ID
|
||||
val outputFile: String =
|
||||
Environment.getExternalStorageDirectory().toString() + File.separator +
|
||||
defaultDir +
|
||||
removeIllegalChars(type) + File.separator +
|
||||
(if (subFolder == null) { "" }
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
package com.shabinder.spotiflyer.downloadHelper
|
||||
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import com.shabinder.spotiflyer.models.DownloadObject
|
||||
@ -44,9 +43,7 @@ object YTDownloadHelper {
|
||||
showNoConnectionAlert()
|
||||
return
|
||||
}
|
||||
val outputFile: String =
|
||||
Environment.getExternalStorageDirectory().toString() + File.separator +
|
||||
defaultDir +
|
||||
val outputFile: String = defaultDir +
|
||||
removeIllegalChars(type) + File.separator +
|
||||
(if (subFolder == null) { "" }
|
||||
else { removeIllegalChars(subFolder) + File.separator }
|
||||
|
@ -18,7 +18,6 @@
|
||||
package com.shabinder.spotiflyer.models.gaana
|
||||
|
||||
data class GaanaPlaylist (
|
||||
val tags : String?,
|
||||
val modified_on : String,
|
||||
val count : Int,
|
||||
val created_on : String,
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
package com.shabinder.spotiflyer.ui.gaana
|
||||
|
||||
import android.os.Environment
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
import com.shabinder.spotiflyer.database.DatabaseDAO
|
||||
import com.shabinder.spotiflyer.database.DownloadRecord
|
||||
@ -165,8 +164,7 @@ class GaanaViewModel @ViewModelInject constructor(
|
||||
artists = it.artist.map { artist -> artist?.name.toString() },
|
||||
durationSec = it.duration,
|
||||
albumArt = File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
Provider.defaultDir +".Images/" + (it.artworkLink.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg"),
|
||||
Provider.imageDir + (it.artworkLink.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg"),
|
||||
albumName = it.album_title,
|
||||
year = it.release_date,
|
||||
comment = "Genres:${it.genre?.map { genre -> genre?.name }?.reduceOrNull { acc, s -> acc + s }}",
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
package com.shabinder.spotiflyer.ui.spotify
|
||||
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
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.Track
|
||||
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.finalOutputDir
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@ -200,8 +199,7 @@ class SpotifyViewModel @ViewModelInject constructor(
|
||||
artists = it.artists?.map { artist -> artist?.name.toString() } ?: listOf(),
|
||||
durationSec = (it.duration_ms/1000).toInt(),
|
||||
albumArt = File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
Provider.defaultDir +".Images/" + (it.album?.images?.elementAtOrNull(1)?.url ?: it.album?.images?.firstOrNull()?.url.toString()).substringAfterLast('/') + ".jpeg"),
|
||||
imageDir + (it.album?.images?.elementAtOrNull(1)?.url ?: it.album?.images?.firstOrNull()?.url.toString()).substringAfterLast('/') + ".jpeg"),
|
||||
albumName = it.album?.name,
|
||||
year = it.album?.release_date,
|
||||
comment = "Genres:${it.album?.genres?.joinToString()}",
|
||||
|
@ -68,7 +68,7 @@ class YoutubeFragment : TrackListFragment<YoutubeViewModel,YoutubeFragmentArgs>(
|
||||
if(link.contains(sampleDomain1,true) ){
|
||||
searchId = link.substringAfterLast("=","error")
|
||||
}
|
||||
if(link.contains(sampleDomain2,true) && !link.contains("playlist",true) ){
|
||||
if(link.contains(sampleDomain2,true) ){
|
||||
searchId = link.substringAfterLast("/","error")
|
||||
}
|
||||
if(searchId != "error") {
|
||||
|
@ -18,7 +18,6 @@
|
||||
package com.shabinder.spotiflyer.ui.youtube
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import androidx.hilt.lifecycle.ViewModelInject
|
||||
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.spotify.Source
|
||||
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.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
@ -36,7 +35,7 @@ import java.io.File
|
||||
|
||||
class YoutubeViewModel @ViewModelInject constructor(
|
||||
val databaseDAO: DatabaseDAO,
|
||||
val ytDownloader: YoutubeDownloader
|
||||
private val ytDownloader: YoutubeDownloader
|
||||
) : TrackListViewModel(){
|
||||
/*
|
||||
* YT Album Art Schema
|
||||
@ -67,8 +66,7 @@ class YoutubeViewModel @ViewModelInject constructor(
|
||||
artists = listOf(it.author().toString()),
|
||||
durationSec = it.lengthSeconds(),
|
||||
albumArt = File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
defaultDir + ".Images/" + it.videoId() + ".jpeg"
|
||||
imageDir + it.videoId() + ".jpeg"
|
||||
),
|
||||
source = Source.YouTube,
|
||||
albumArtURL = "https://i.ytimg.com/vi/${it.videoId()}/hqdefault.jpg",
|
||||
@ -121,10 +119,7 @@ class YoutubeViewModel @ViewModelInject constructor(
|
||||
title = name,
|
||||
artists = listOf(detail?.author().toString()),
|
||||
durationSec = detail?.lengthSeconds()?:0,
|
||||
albumArt = File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
"$defaultDir.Images/$searchId.jpeg"
|
||||
),
|
||||
albumArt = File(imageDir,"$searchId.jpeg"),
|
||||
source = Source.YouTube,
|
||||
albumArtURL = "https://i.ytimg.com/vi/$searchId/hqdefault.jpg"
|
||||
)
|
||||
|
@ -49,13 +49,21 @@ import javax.inject.Singleton
|
||||
@Module
|
||||
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 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
|
||||
|
@ -22,6 +22,7 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
@ -57,6 +58,7 @@ abstract class TrackListFragment<VM : TrackListViewModel , args: NavArgs> : Frag
|
||||
showNoConnectionAlert()
|
||||
mainActivity.navController.popBackStack()
|
||||
}
|
||||
Handler()
|
||||
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,6 @@ import android.content.Intent
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
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.spotify.Source
|
||||
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.worker.ForegroundService
|
||||
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{
|
||||
return Environment.getExternalStorageDirectory().toString() + File.separator +
|
||||
defaultDir + removeIllegalChars(type) + File.separator +
|
||||
return defaultDir + removeIllegalChars(type) + File.separator +
|
||||
(if(subFolder == null){""}else{ removeIllegalChars(subFolder) + File.separator}
|
||||
+ itemName?.let { removeIllegalChars(it) + extension})
|
||||
}
|
||||
@ -121,7 +120,7 @@ fun rotateAnim(view: View){
|
||||
0F, 360F,
|
||||
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f
|
||||
)
|
||||
rotate.duration = 1000
|
||||
rotate.duration = 2000
|
||||
rotate.repeatCount = Animation.INFINITE
|
||||
rotate.repeatMode = Animation.INFINITE
|
||||
rotate.interpolator = LinearInterpolator()
|
||||
@ -172,26 +171,22 @@ fun bindImage(imgView: ImageView, imgUrl: String?,source: Source?) {
|
||||
val file = when(source){
|
||||
Source.Spotify->{
|
||||
File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
defaultDir+".Images/" + imgUrl.substringAfterLast('/',imgUrl) + ".jpeg"
|
||||
imageDir + imgUrl.substringAfterLast('/',imgUrl) + ".jpeg"
|
||||
)
|
||||
}
|
||||
Source.YouTube->{
|
||||
//Url Format: https://i.ytimg.com/vi/$searchId/maxresdefault.jpg"
|
||||
// We Are Naming using "$searchId"
|
||||
File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
defaultDir+".Images/" + imgUrl.substringBeforeLast('/',imgUrl).substringAfterLast('/',imgUrl) + ".jpeg"
|
||||
imageDir + imgUrl.substringBeforeLast('/',imgUrl).substringAfterLast('/',imgUrl) + ".jpeg"
|
||||
)
|
||||
}
|
||||
Source.Gaana -> {
|
||||
File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
Provider.defaultDir +".Images/" + (imgUrl.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg")
|
||||
imageDir + (imgUrl.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg")
|
||||
}
|
||||
else -> File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
defaultDir+".Images/" + imgUrl.substringAfterLast('/',imgUrl) + ".jpeg"
|
||||
imageDir + imgUrl.substringAfterLast('/',imgUrl) + ".jpeg"
|
||||
)
|
||||
}
|
||||
// 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){
|
||||
val yourAppDir = File(Environment.getExternalStorageDirectory(),
|
||||
dir)
|
||||
val yourAppDir = File(dir)
|
||||
|
||||
if(!yourAppDir.exists() && !yourAppDir.isDirectory)
|
||||
{ // create empty directory
|
||||
if (yourAppDir.mkdirs())
|
||||
{Log.i("CreateDir","App dir created")}
|
||||
{Log.i("CreateDir","$dir created")}
|
||||
else
|
||||
{Log.w("CreateDir","Unable to create app dir!")}
|
||||
{Log.w("CreateDir","Unable to create Dir: $dir!")}
|
||||
}
|
||||
else
|
||||
{Log.i("CreateDir","App dir already exists")}
|
||||
{Log.i("CreateDir","$dir already exists")}
|
||||
}
|
||||
/**
|
||||
* Removing Illegal Chars from File Name
|
||||
@ -277,7 +271,7 @@ fun removeIllegalChars(fileName: String): String? {
|
||||
|
||||
fun createDirectories() {
|
||||
createDirectory(defaultDir)
|
||||
createDirectory(defaultDir + ".Images/")
|
||||
createDirectory(imageDir)
|
||||
createDirectory(defaultDir + "Tracks/")
|
||||
createDirectory(defaultDir + "Albums/")
|
||||
createDirectory(defaultDir + "Playlists/")
|
||||
@ -286,19 +280,3 @@ fun createDirectories() {
|
||||
fun getEmojiByUnicode(unicode: Int): String? {
|
||||
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
|
||||
}
|
||||
}*/
|
||||
|
@ -25,7 +25,10 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
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 androidx.annotation.RequiresApi
|
||||
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.TrackDetails
|
||||
import com.shabinder.spotiflyer.utils.Provider
|
||||
import com.shabinder.spotiflyer.utils.Provider.imageDir
|
||||
import com.shabinder.spotiflyer.utils.copyTo
|
||||
import com.tonyodev.fetch2.*
|
||||
import com.tonyodev.fetch2core.DownloadBlock
|
||||
@ -59,7 +63,6 @@ import java.io.FileInputStream
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
class ForegroundService : Service(){
|
||||
private val tag = "Foreground Service"
|
||||
private val channelId = "ForegroundDownloaderService"
|
||||
@ -73,19 +76,13 @@ class ForegroundService : Service(){
|
||||
private var serviceJob = Job()
|
||||
private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)
|
||||
private val requestMap = mutableMapOf<Request, TrackDetails>()
|
||||
private var speed :Long = 0
|
||||
private var defaultDir = Environment.DIRECTORY_MUSIC + File.separator + "SpotiFlyer" + File.separator
|
||||
private val parentDirectory = File(Environment.getExternalStorageDirectory(),
|
||||
defaultDir +File.separator
|
||||
)
|
||||
private var defaultDir = Provider.defaultDir
|
||||
private var wakeLock: PowerManager.WakeLock? = null
|
||||
private var isServiceStarted = false
|
||||
var notificationLine = 0
|
||||
val messageList = mutableListOf("","","","")
|
||||
private var pendingIntent:PendingIntent? = null
|
||||
|
||||
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
return null
|
||||
}
|
||||
@ -201,7 +198,7 @@ class ForegroundService : Service(){
|
||||
if(converted == total){
|
||||
Handler().postDelayed({
|
||||
Log.i(tag,"Service destroyed.")
|
||||
deleteFile(parentDirectory)
|
||||
cleanFiles(File(defaultDir))
|
||||
releaseWakeLock()
|
||||
stopForeground(true)
|
||||
},2000)
|
||||
@ -212,7 +209,7 @@ class ForegroundService : Service(){
|
||||
super.onTaskRemoved(rootIntent)
|
||||
if(converted == total ){
|
||||
Log.i(tag,"Service Removed.")
|
||||
deleteFile(parentDirectory)
|
||||
cleanFiles(File(defaultDir))
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
stopForeground(true)
|
||||
} else {
|
||||
@ -303,8 +300,6 @@ class ForegroundService : Service(){
|
||||
requestMap.remove(download.request)
|
||||
}
|
||||
}
|
||||
speed = 0
|
||||
// updateNotification()
|
||||
}
|
||||
|
||||
override fun onDeleted(download: Download) {
|
||||
@ -342,7 +337,6 @@ class ForegroundService : Service(){
|
||||
) {
|
||||
val track = requestMap[download.request]
|
||||
Log.i(tag,"${track?.title} ETA: ${etaInMilliSeconds/1000} sec")
|
||||
speed = (downloadedBytesPerSecond/1000)
|
||||
// updateNotification()
|
||||
}
|
||||
|
||||
@ -361,9 +355,7 @@ class ForegroundService : Service(){
|
||||
.setAllowedOverRoaming(false)
|
||||
.setTitle(track.title)
|
||||
.setDescription("Spotify Downloader Working Up here...")
|
||||
.setDestinationInExternalPublicDir(Environment.DIRECTORY_MUSIC, outputDir.removePrefix(
|
||||
Environment.getExternalStorageDirectory().toString() + Environment.DIRECTORY_MUSIC + File.separator
|
||||
))
|
||||
.setDestinationUri(File(outputDir).toUri())
|
||||
.setNotificationVisibility(VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
||||
//Start Download
|
||||
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) {
|
||||
Log.i(tag,"Starting Deletions in ${dir.path} ")
|
||||
private fun cleanFiles(dir:File) {
|
||||
Log.i(tag,"Starting Cleaning in ${dir.path} ")
|
||||
val fList = dir.listFiles()
|
||||
fList?.let {
|
||||
for (file in fList) {
|
||||
if (file.isDirectory) {
|
||||
deleteFile(file)
|
||||
cleanFiles(file)
|
||||
} else if(file.isFile) {
|
||||
if(file.path.toString().substringAfterLast(".") != "mp3"){
|
||||
Log.i(tag,"deleting ${file.path}")
|
||||
Log.i(tag,"Cleaning ${file.path}")
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
@ -618,25 +610,16 @@ class ForegroundService : Service(){
|
||||
try {
|
||||
val file = when(source){
|
||||
"spotify" ->{
|
||||
File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
defaultDir +".Images/" + url.substringAfterLast('/') + ".jpeg"
|
||||
)
|
||||
File(imageDir, url.substringAfterLast('/') + ".jpeg")
|
||||
}
|
||||
"youtube" ->{
|
||||
File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
defaultDir +".Images/" + url.substringBeforeLast('/',url).substringAfterLast('/',url) + ".jpeg"
|
||||
)
|
||||
File(imageDir, url.substringBeforeLast('/',url).substringAfterLast('/',url) + ".jpeg")
|
||||
}
|
||||
"gaana" -> {
|
||||
File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
Provider.defaultDir +".Images/" + (url.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg")
|
||||
File(imageDir, (url.substringBeforeLast('/').substringAfterLast('/')) + ".jpeg")
|
||||
}
|
||||
else -> File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
defaultDir +".Images/" + url.substringAfterLast('/') + ".jpeg")
|
||||
|
||||
else -> File(imageDir, url.substringAfterLast('/') + ".jpeg")
|
||||
}
|
||||
resource?.copyTo(file)
|
||||
} catch (e: IOException) {
|
||||
|
28
app/src/main/res/drawable/circular_background.xml
Normal file
28
app/src/main/res/drawable/circular_background.xml
Normal 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>
|
||||
|
||||
|
@ -15,8 +15,8 @@
|
||||
~ 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"
|
||||
android:height="22dp" android:viewportWidth="512" android:viewportHeight="512">
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="36dp"
|
||||
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="#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"/>
|
||||
|
@ -41,7 +41,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="48dp"
|
||||
android:background="@drawable/text_background_accented"
|
||||
android:fontFamily="@font/raleway_semibold"
|
||||
android:foreground="@drawable/rounded_gradient"
|
||||
android:padding="7dp"
|
||||
android:text=" Developer: Shabinder Singh "
|
||||
|
@ -19,14 +19,14 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="80dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:background="#000000">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageUrl"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="70dp"
|
||||
android:contentDescription="Track Image"
|
||||
android:scaleType="centerInside"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
@ -41,6 +41,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="14dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:fontFamily="@font/raleway_semibold"
|
||||
android:letterSpacing="0.04"
|
||||
android:lines="1"
|
||||
@ -50,7 +51,7 @@
|
||||
android:textColor="#9AB3FF"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/btn_download"
|
||||
app:layout_constraintStart_toStartOf="@+id/artist"
|
||||
app:layout_constraintStart_toEndOf="@+id/imageUrl"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
@ -61,7 +62,6 @@
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:paddingLeft="9dp"
|
||||
android:text="Alan Walker"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
@ -75,8 +75,7 @@
|
||||
style="@style/TextAppearance.AppCompat.Body2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="3dp"
|
||||
android:paddingLeft="9dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="4 minutes, 20 sec"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/artist"
|
||||
@ -88,8 +87,9 @@
|
||||
android:id="@+id/btn_download"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="@drawable/circular_background"
|
||||
android:backgroundTint="@color/black"
|
||||
android:scaleType="fitCenter"
|
||||
android:scaleType="centerInside"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
|
@ -28,11 +28,13 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
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"
|
||||
//safe-Args
|
||||
//Safe-Args
|
||||
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
|
||||
//Hilt
|
||||
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
|
||||
//Kotlinx-Serialization
|
||||
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
@ -45,9 +47,6 @@ allprojects {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
maven { url "https://jitpack.io" }
|
||||
flatDir {
|
||||
dirs 'libs'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,5 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
include ':mobile-ffmpeg'
|
||||
|
||||
include ':app'
|
||||
rootProject.name = "spotiflyer"
|
||||
|
Loading…
Reference in New Issue
Block a user