use download manager

This commit is contained in:
rimthekid 2022-03-03 10:38:36 +04:00
parent 6879c91dd2
commit 7159eb0472
6 changed files with 242 additions and 41 deletions

View File

@ -51,7 +51,7 @@ android {
}
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
//debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
@ -74,5 +74,5 @@ dependencies {
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.1'
implementation 'com.squareup.retrofit2:converter-scalars:2.1.0'
implementation 'com.arthenica:ffmpeg-kit-https:4.5.1.LTS'
implementation 'com.arthenica:ffmpeg-kit-min:4.5.1.LTS'
}

View File

@ -8,12 +8,30 @@
"variantName": "release",
"elements": [
{
"type": "UNIVERSAL",
"filters": [],
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86_64"
}
],
"attributes": [],
"versionCode": 4,
"versionName": "0.2.2",
"outputFile": "app-universal-release.apk"
"outputFile": "app-x86_64-release.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86"
}
],
"attributes": [],
"versionCode": 4,
"versionName": "0.2.2",
"outputFile": "app-x86-release.apk"
},
{
"type": "ONE_OF_MANY",
@ -40,32 +58,6 @@
"versionCode": 4,
"versionName": "0.2.2",
"outputFile": "app-armeabi-v7a-release.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86_64"
}
],
"attributes": [],
"versionCode": 4,
"versionName": "0.2.2",
"outputFile": "app-x86_64-release.apk"
},
{
"type": "ONE_OF_MANY",
"filters": [
{
"filterType": "ABI",
"value": "x86"
}
],
"attributes": [],
"versionCode": 4,
"versionName": "0.2.2",
"outputFile": "app-x86-release.apk"
}
],
"elementType": "File"

View File

@ -7,6 +7,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_libretube"
@ -17,6 +18,7 @@
android:name=".myApp"
android:networkSecurityConfig="@xml/network_security_config"
android:largeHeap="true"
android:requestLegacyExternalStorage="true"
>
<activity
android:name=".Player"
@ -37,6 +39,10 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".DownloadService"
android:enabled="true"
android:exported="false" />
</application>
</manifest>

View File

@ -0,0 +1,173 @@
package com.github.libretube
import android.app.*
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.os.IBinder
import android.util.Log
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import com.arthenica.ffmpegkit.FFmpegKit
import java.io.File
class DownloadService : Service(){
val TAG = "DownloadService"
private var downloadId: Long =0
private lateinit var videoId: String
private lateinit var videoUrl: String
private lateinit var audioUrl: String
private var duration: Int = 0
//private lateinit var command: String
private lateinit var audioDir: File
private lateinit var videoDir: File
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
videoId = intent?.getStringExtra("videoId")!!
videoUrl = intent.getStringExtra("videoUrl")!!
audioUrl = intent.getStringExtra("audioUrl")!!
//command = intent.getStringExtra("command")!!
duration = intent.getIntExtra("duration",1)
downloadManager()
return super.onStartCommand(intent, flags, startId)
}
override fun onBind(intent: Intent?): IBinder? {
TODO("Not yet implemented")
}
private fun downloadManager() {
val path = applicationContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
val folder_main = ".tmp"
val f = File(path, folder_main)
if (!f.exists()) {
f.mkdirs()
Log.e(TAG, "Directory make")
} else {
Log.e(TAG, "Directory already have")
}
audioDir = File(f, "$videoId-audio")
videoDir = File(f, "$videoId-video")
try {
Log.e(TAG, "Directory make")
val request: DownloadManager.Request =
DownloadManager.Request(Uri.parse(videoUrl))
.setTitle("Video") // Title of the Download Notification
.setDescription("Downloading") // Description of the Download Notification
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE) // Visibility of the download Notification
.setDestinationUri(Uri.fromFile(videoDir))
.setAllowedOverMetered(true) // Set if download is allowed on Mobile network
.setAllowedOverRoaming(true) //
val downloadManager: DownloadManager =
applicationContext.getSystemService(DOWNLOAD_SERVICE) as DownloadManager
downloadId = downloadManager.enqueue(request)
registerReceiver(onDownloadComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
} catch (e: IllegalArgumentException) {
Log.e(TAG, "download error $e")
}
}
private val onDownloadComplete: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
//Fetching the download id received with the broadcast
val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
//Checking if the received broadcast is for our enqueued download by matching download id
if (downloadId == id) {
Toast.makeText(this@DownloadService, "Download Completed", Toast.LENGTH_SHORT)
.show()
downloadId=0
val request: DownloadManager.Request =
DownloadManager.Request(Uri.parse(audioUrl))
.setTitle("Audio") // Title of the Download Notification
.setDescription("Downloading") // Description of the Download Notification
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE) // Visibility of the download Notification
.setDestinationUri(Uri.fromFile(audioDir))
.setAllowedOverMetered(true) // Set if download is allowed on Mobile network
.setAllowedOverRoaming(true) //
val downloadManager: DownloadManager =
applicationContext.getSystemService(DOWNLOAD_SERVICE) as DownloadManager
downloadManager.enqueue(request)
}else if (downloadId == 0L){
val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val chan = NotificationChannel("service",
"DownloadService", NotificationManager.IMPORTANCE_NONE)
chan.lightColor = Color.BLUE
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
service.createNotificationChannel(chan)
"service"
} else {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
""
}
//Toast.makeText(this,command, Toast.LENGTH_SHORT).show()
val pendingIntent: PendingIntent = PendingIntent.getActivity(
this@DownloadService, 0, intent, 0)
//Sets the maximum progress as 100
val progressMax = 100
//Creating a notification and setting its various attributes
val notification =
NotificationCompat.Builder(this@DownloadService, channelId)
.setSmallIcon(R.drawable.ic_download)
.setContentTitle("LibreTube")
.setContentText("Downloading")
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(true)
.setOnlyAlertOnce(true)
.setProgress(progressMax, 0, true)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
FFmpegKit.executeAsync("-y -i $videoDir -i $audioDir -c copy ${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)}/${videoId}.mkv",
{ session ->
val state = session.state
val returnCode = session.returnCode
// CALLED WHEN SESSION IS EXECUTED
Log.d(
TAG,
String.format(
"FFmpeg process exited with state %s and rc %s.%s",
state,
returnCode,
session.failStackTrace
)
)
val path = applicationContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
val folder_main = ".tmp"
val f = File(path, folder_main)
f.deleteRecursively()
stopForeground(true)
}, {
// CALLED WHEN SESSION PRINTS LOGS
Log.e(TAG,it.message.toString())
}) {
// CALLED WHEN SESSION GENERATES STATISTICS
Log.e(TAG+"stat",it.time.toString())
val progress = it.time/(10*duration!!)
if (progress<1){
notification
.setProgress(progressMax, progress.toInt(), false)
service.notify(1,notification.build())
}
}
startForeground(1,notification.build())
}
}
}
override fun onDestroy() {
unregisterReceiver(onDownloadComplete)
super.onDestroy()
}
}

View File

@ -3,15 +3,16 @@ package com.github.libretube
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.app.ActivityManager
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.os.Build
import android.os.Build.VERSION.SDK_INT
import android.os.Bundle
import android.os.Environment
import android.os.Environment.DIRECTORY_DOWNLOADS
import android.text.Html
import android.util.Log
import android.util.TypedValue
@ -23,13 +24,13 @@ import androidx.appcompat.app.AlertDialog
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat.getSystemService
import androidx.core.net.toUri
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.arthenica.ffmpegkit.FFmpegKit
import com.github.libretube.adapters.TrendingAdapter
import com.github.libretube.obj.PipedStream
import com.github.libretube.obj.Subscribe
@ -48,7 +49,6 @@ import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
import com.google.android.material.button.MaterialButton
import com.squareup.picasso.Picasso
import retrofit2.HttpException
import java.io.File
import java.io.IOException
import kotlin.math.abs
@ -364,6 +364,9 @@ class PlayerFragment : Fragment() {
val subButton = view.findViewById<MaterialButton>(R.id.player_subscribe)
isSubscribed(subButton, channelId!!)
}
//check if livestream
if (response.duration!!>0){
//download clicked
relDownloadVideo.setOnClickListener {
val mainActivity = activity as MainActivity
Log.e(TAG,"download button clicked!")
@ -396,11 +399,24 @@ class PlayerFragment : Fragment() {
)
}
}
FFmpegKit.executeAsync("-i ${response.videoStreams[0].url} -i ${response.audioStreams!![0].url} -c copy ${context?.getExternalFilesDir(DIRECTORY_DOWNLOADS)}${File.separator}output2.mkv",
val builderr: AlertDialog.Builder? = activity?.let {
AlertDialog.Builder(it)
}
var videos = videosNameArray.drop(1).toTypedArray()
builderr!!.setTitle(R.string.choose_quality_dialog)
.setItems(videos,
DialogInterface.OnClickListener { _, which ->
val intent = Intent(context,DownloadService::class.java)
intent.putExtra("videoId",videoId)
intent.putExtra("videoUrl",response.videoStreams[which].url)
intent.putExtra("audioUrl",response.audioStreams!![0].url)
intent.putExtra("duration",response.duration)
//intent.putExtra("command","-y -i ${response.videoStreams[which].url} -i ${response.audioStreams!![0].url} -c copy ${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)}/${videoId}.mkv")
context?.startService(intent)
/*FFmpegKit.executeAsync("-y -i ${response.videoStreams[which].url} -i ${response.audioStreams!![0].url} -c copy ${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)}/${videoId}.mkv",
{ session ->
val state = session.state
val returnCode = session.returnCode
// CALLED WHEN SESSION IS EXECUTED
Log.d(
TAG,
@ -413,11 +429,17 @@ class PlayerFragment : Fragment() {
)
}, {
// CALLED WHEN SESSION PRINTS LOGS
Log.e(TAG,it.toString())
Log.e(TAG,it.message.toString())
}) {
// CALLED WHEN SESSION GENERATES STATISTICS
}
Log.e(TAG,it.time.toString())
}*/
})
val dialog: AlertDialog? = builderr?.create()
dialog?.show()
}
}else{
Toast.makeText(context,R.string.cannotDownload, Toast.LENGTH_SHORT).show()
}
}
}
@ -427,8 +449,15 @@ class PlayerFragment : Fragment() {
}
/* private fun isMyServiceRunning(serviceClass: Class<*>): Boolean {
val manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager?
for (service in manager!!.getRunningServices(Int.MAX_VALUE)) {
if (serviceClass.name == service.service.className) {
return true
}
}
return false
}*/
private fun isSubscribed(button: MaterialButton, channel_id: String){
@SuppressLint("ResourceAsColor")
fun run() {

View File

@ -23,4 +23,5 @@
<string name="login_register">Login/Register</string>
<string name="please_login">Please Login or Register from the settings to show your Subscriptions!</string>
<string name="subscribeIsEmpty">Subscribe to some channels first!</string>
<string name="cannotDownload">Can\'t Download this stream!</string>
</resources>