diff --git a/app/src/main/java/com/github/libretube/BackgroundMode.kt b/app/src/main/java/com/github/libretube/BackgroundMode.kt index 12f80aff7..c85e4fd06 100644 --- a/app/src/main/java/com/github/libretube/BackgroundMode.kt +++ b/app/src/main/java/com/github/libretube/BackgroundMode.kt @@ -68,7 +68,7 @@ class BackgroundMode { */ private fun initializePlayerNotification(c: Context) { playerNotification = PlayerNotificationManager - .Builder(c, NOTIFICATION_ID, "background_mode").build() + .Builder(c, 1, "background_mode").build() playerNotification.setPlayer(player) playerNotification.setUsePreviousAction(false) playerNotification.setUseNextAction(false) diff --git a/app/src/main/java/com/github/libretube/DownloadService.kt b/app/src/main/java/com/github/libretube/DownloadService.kt index 23771af3b..216a76092 100644 --- a/app/src/main/java/com/github/libretube/DownloadService.kt +++ b/app/src/main/java/com/github/libretube/DownloadService.kt @@ -1,16 +1,12 @@ package com.github.libretube import android.app.DownloadManager -import android.app.Notification -import android.app.NotificationChannel -import android.app.NotificationManager import android.app.PendingIntent import android.app.Service 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 @@ -33,11 +29,12 @@ class DownloadService : Service() { private lateinit var extension: String private var duration: Int = 0 - // private lateinit var command: String private lateinit var audioDir: File private lateinit var videoDir: File - lateinit var service: NotificationManager - lateinit var notification: NotificationCompat.Builder + private lateinit var notification: NotificationCompat.Builder + private lateinit var downloadType: String + private lateinit var libretubeDir: File + private lateinit var tempDir: File override fun onCreate() { super.onCreate() IS_DOWNLOAD_RUNNING = true @@ -48,44 +45,17 @@ class DownloadService : Service() { videoUrl = intent.getStringExtra("videoUrl")!! audioUrl = intent.getStringExtra("audioUrl")!! extension = intent.getStringExtra("extension")!! - // command = intent.getStringExtra("command")!! duration = intent.getIntExtra("duration", 1) - 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) - "" - } - var pendingIntent: PendingIntent? = null - pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE) + downloadType = if (audioUrl != "" && videoUrl != "") "mux" + else if (audioUrl != "") "audio" + else if (videoUrl != "") "video" + else "none" + if (downloadType != "none") { + downloadNotification(intent) + downloadManager() } else { - PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT) + onDestroy() } - // Creating a notification and setting its various attributes - notification = - NotificationCompat.Builder(this@DownloadService, channelId) - .setSmallIcon(R.drawable.ic_download) - .setContentTitle("LibreTube") - .setContentText("Downloading") - .setPriority(NotificationCompat.PRIORITY_LOW) - .setOngoing(true) - .setOnlyAlertOnce(true) - .setProgress(100, 0, true) - .setContentIntent(pendingIntent) - .setAutoCancel(true) - startForeground(1, notification.build()) - downloadManager() return super.onStartCommand(intent, flags, startId) } @@ -95,58 +65,51 @@ class DownloadService : Service() { } 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() + + // create folder for temporary files + tempDir = File( + applicationContext.getExternalFilesDir(DIRECTORY_DOWNLOADS), + ".tmp" + ) + if (!tempDir.exists()) { + tempDir.mkdirs() Log.e(TAG, "Directory make") } else { - f.deleteRecursively() - f.mkdirs() + tempDir.deleteRecursively() + tempDir.mkdirs() Log.e(TAG, "Directory already have") } - audioDir = File(f, "$videoId-audio") - videoDir = File(f, "$videoId-video") + + // create LibreTube folder in Downloads + libretubeDir = File( + Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS), + "LibreTube" + ) + if (!libretubeDir.exists()) libretubeDir.mkdirs() + + // start download try { - Log.e(TAG, "Directory make") registerReceiver( onDownloadComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE) ) - 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) - if (audioUrl == "") { - downloadId = 0L + when (downloadType) { + "mux" -> { + audioDir = File(tempDir, "$videoId-audio") + videoDir = File(tempDir, "$videoId-video") + downloadId = downloadManagerRequest("Video", "Downloading", videoUrl, videoDir) + } + "video" -> { + videoDir = File(libretubeDir, "$videoId-video") + downloadId = downloadManagerRequest("Video", "Downloading", videoUrl, videoDir) + } + "audio" -> { + audioDir = File(libretubeDir, "$videoId-audio") + downloadId = downloadManagerRequest("Audio", "Downloading", audioUrl, audioDir) + } } } catch (e: IllegalArgumentException) { Log.e(TAG, "download error $e") - try { - downloadId = 0L - 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) - } catch (e: Exception) { - Log.e(TAG, "audio download error $e") - stopService(Intent(this, DownloadService::class.java)) - } } } @@ -156,119 +119,131 @@ class DownloadService : Service() { 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) { - downloadId = 0L - try { - 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) - } catch (e: Exception) { - } - } else if (downloadId == 0L) { - val libreTube = File( - Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS), "LibreTube" - ) - if (!libreTube.exists()) { - libreTube.mkdirs() - Log.e(TAG, "libreTube Directory make") + if (downloadType == "mux") { + downloadManagerRequest("Audio", "Downloading", audioUrl, audioDir) } else { - Log.e(TAG, "libreTube Directory already have") - } - var command: String = when { - videoUrl == "" -> { - "-y -i $audioDir -c copy $libreTube/$videoId-audio$extension" - } - audioUrl == "" -> { - "-y -i $videoDir -c copy $libreTube/$videoId-video$extension" - } - else -> { - "-y -i $videoDir -i $audioDir -c copy $libreTube/${videoId}$extension" - } - } - notification.setContentTitle("Muxing") - FFmpegKit.executeAsync( - command, - { 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(DIRECTORY_DOWNLOADS) - val folder_main = ".tmp" - val f = File(path, folder_main) - f.deleteRecursively() - if (returnCode.toString() != "0") { - var builder = NotificationCompat.Builder(this@DownloadService, "failed") - .setSmallIcon(R.drawable.ic_download) - .setContentTitle(resources.getString(R.string.downloadfailed)) - .setContentText("failure") - .setPriority(NotificationCompat.PRIORITY_HIGH) - createNotificationChannel() - with(NotificationManagerCompat.from(this@DownloadService)) { - // notificationId is a unique int for each notification that you must define - notify(69, builder.build()) - } - } - stopForeground(true) - stopService(Intent(this@DownloadService, DownloadService::class.java)) - }, { - // 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()) - }*/ + downloadSucceededNotification() + onDestroy() } + } else { + muxDownloadedMedia() } } } - private fun createNotificationChannel() { - // Create the NotificationChannel, but only on API 26+ because - // the NotificationChannel class is new and not in the support library - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val name = "failed" - val descriptionText = "Download Failed" - val importance = NotificationManager.IMPORTANCE_DEFAULT - val channel = NotificationChannel("failed", name, importance).apply { - description = descriptionText - } - // Register the channel with the system - val notificationManager: NotificationManager = - getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - notificationManager.createNotificationChannel(channel) + private fun downloadManagerRequest( + title: String, + descriptionText: String, + url: String, + fileDir: File + ): Long { + val request: DownloadManager.Request = + DownloadManager.Request(Uri.parse(url)) + .setTitle(title) // Title of the Download Notification + .setDescription(descriptionText) // Description of the Download Notification + .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE) // Visibility of the download Notification + .setDestinationUri(Uri.fromFile(fileDir)) + .setAllowedOverMetered(true) // Set if download is allowed on Mobile network + .setAllowedOverRoaming(true) // + val downloadManager: DownloadManager = + applicationContext.getSystemService(DOWNLOAD_SERVICE) as DownloadManager + return downloadManager.enqueue(request) + } + + private fun downloadNotification(intent: Intent) { + var pendingIntent: PendingIntent? = null + pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE) + } else { + PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT) + } + // Creating a notification and setting its various attributes + notification = + NotificationCompat.Builder(this@DownloadService, "download_service") + .setSmallIcon(R.drawable.ic_download) + .setContentTitle("LibreTube") + .setContentText("Downloading") + .setPriority(NotificationCompat.PRIORITY_LOW) + .setOngoing(true) + .setOnlyAlertOnce(true) + .setProgress(100, 0, true) + .setContentIntent(pendingIntent) + .setAutoCancel(true) + startForeground(2, notification.build()) + } + + private fun downloadFailedNotification() { + val builder = NotificationCompat.Builder(this@DownloadService, "download_service") + .setSmallIcon(R.drawable.ic_download) + .setContentTitle(resources.getString(R.string.downloadfailed)) + .setContentText("failure") + .setPriority(NotificationCompat.PRIORITY_HIGH) + with(NotificationManagerCompat.from(this@DownloadService)) { + // notificationId is a unique int for each notification that you must define + notify(3, builder.build()) + } + } + + private fun downloadSucceededNotification() { + Log.i(TAG, "Download succeeded") + val builder = NotificationCompat.Builder(this@DownloadService, "download_service") + .setSmallIcon(R.drawable.ic_download) + .setContentTitle(resources.getString(R.string.success)) + .setContentText("success") + .setPriority(NotificationCompat.PRIORITY_HIGH) + with(NotificationManagerCompat.from(this@DownloadService)) { + // notificationId is a unique int for each notification that you must define + notify(4, builder.build()) + } + } + + private fun muxDownloadedMedia() { + val command = "-y -i $videoDir -i $audioDir -c copy $libretubeDir/${videoId}$extension" + notification.setContentTitle("Muxing") + FFmpegKit.executeAsync( + command, + { 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 + ) + ) + tempDir.deleteRecursively() + if (returnCode.toString() != "0") downloadFailedNotification() + else downloadSucceededNotification() + onDestroy() + }, { + // 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()) + }*/ } } override fun onDestroy() { try { unregisterReceiver(onDownloadComplete) - } catch (e: Exception) { - } + } catch (e: Exception) { } + IS_DOWNLOAD_RUNNING = false Log.d(TAG, "dl finished!") + stopForeground(true) + stopService(Intent(this@DownloadService, DownloadService::class.java)) super.onDestroy() } } diff --git a/app/src/main/java/com/github/libretube/MyApp.kt b/app/src/main/java/com/github/libretube/MyApp.kt index 7c37bbee6..2995e496a 100644 --- a/app/src/main/java/com/github/libretube/MyApp.kt +++ b/app/src/main/java/com/github/libretube/MyApp.kt @@ -5,8 +5,6 @@ import android.app.NotificationChannel import android.app.NotificationManager import android.os.Build -const val NOTIFICATION_ID = 1 - class MyApp : Application() { override fun onCreate() { super.onCreate() @@ -15,19 +13,37 @@ class MyApp : Application() { } /** - * Initializes the required [NotificationChannel] for the app. + * Initializes the required [NotificationChannel]s for the app. */ private fun initializeNotificationChannels() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - // Create the NotificationChannel - val name = "Background Mode" - val descriptionText = "Shows a notification with buttons to control the audio player" - val importance = NotificationManager.IMPORTANCE_LOW - val mChannel = NotificationChannel("background_mode", name, importance) - mChannel.description = descriptionText + createNotificationChannel( + "download_service", + "Download Service", + "DownloadService", + NotificationManager.IMPORTANCE_NONE + ) + createNotificationChannel( + "background_mode", + "Background Mode", + "Shows a notification with buttons to control the audio player", + NotificationManager.IMPORTANCE_LOW + ) + } + private fun createNotificationChannel( + id: String, + name: String, + descriptionText: String, + importance: Int + ) { + // Create the NotificationChannel, but only on API 26+ because + // the NotificationChannel class is new and not in the support library + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val channel = NotificationChannel(id, name, importance) + channel.description = descriptionText + // Register the channel in the system val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager - notificationManager.createNotificationChannel(mChannel) + notificationManager.createNotificationChannel(channel) } } diff --git a/app/src/main/java/com/github/libretube/dialogs/DownloadDialog.kt b/app/src/main/java/com/github/libretube/dialogs/DownloadDialog.kt index 1837080a1..769b2e93c 100644 --- a/app/src/main/java/com/github/libretube/dialogs/DownloadDialog.kt +++ b/app/src/main/java/com/github/libretube/dialogs/DownloadDialog.kt @@ -35,7 +35,7 @@ class DownloadDialog : DialogFragment() { var audioUrl = arrayListOf() var selectedVideo = 0 var selectedAudio = 0 - var extension = ".mkv" + var extension = ".mp4" var duration = 0 private lateinit var videoId: String override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { diff --git a/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt index 8a75223aa..48405e6f6 100644 --- a/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt @@ -42,7 +42,6 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.github.libretube.IS_DOWNLOAD_RUNNING import com.github.libretube.MainActivity -import com.github.libretube.NOTIFICATION_ID import com.github.libretube.R import com.github.libretube.SponsorBlockSettings import com.github.libretube.adapters.CommentsAdapter @@ -339,7 +338,7 @@ class PlayerFragment : Fragment() { val notificationManager = context?.getSystemService( Context.NOTIFICATION_SERVICE ) as NotificationManager - notificationManager.cancel(NOTIFICATION_ID) + notificationManager.cancel(1) exoPlayer.release() } catch (e: Exception) { } @@ -812,7 +811,7 @@ class PlayerFragment : Fragment() { mediaSessionConnector.setPlayer(exoPlayer) playerNotification = PlayerNotificationManager - .Builder(c, NOTIFICATION_ID, "background_mode") + .Builder(c, 1, "background_mode") .build() playerNotification.apply { diff --git a/app/src/main/res/layout/dialog_download.xml b/app/src/main/res/layout/dialog_download.xml index 39e99024b..fdbadd384 100644 --- a/app/src/main/res/layout/dialog_download.xml +++ b/app/src/main/res/layout/dialog_download.xml @@ -30,18 +30,21 @@ android:id="@+id/radioGp" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_margin="8dp"> + android:orientation="horizontal" + android:layout_margin="8dp" + android:visibility="gone" >