added swipe gestures in history screen and notifications when downloads are finished and errored with actions open/share/open logs

pull/109/head
deniscerri 3 years ago
parent f3faaeadb7
commit d46fd82f72
No known key found for this signature in database
GPG Key ID: 95C43D517D830350

@ -73,6 +73,10 @@ Join our [Telegram Channel](https://t.me/ytdlnis) or [Discord](https://discord.g
The App's package name is com.deniscerri.ytdl
## 📝 Help Translate on Weblate
<a href="https://hosted.weblate.org/engage/ytdlnis/">
<img src="https://hosted.weblate.org/widgets/ytdlnis/-/strings/open-graph.png" alt="Translation status" />
</a>
<a href="https://hosted.weblate.org/engage/ytdlnis/">
<img src="https://hosted.weblate.org/widgets/ytdlnis/-/multi-auto.svg" alt="Translation status" />

@ -173,6 +173,8 @@
</activity>
<receiver android:name=".receiver.CancelDownloadNotificationReceiver" />
<receiver android:name=".receiver.SharedDownloadNotificationReceiver" />
<receiver android:name=".receiver.OpenDownloadNotificationReceiver" />
<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"

@ -0,0 +1,23 @@
package com.deniscerri.ytdlnis.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import androidx.work.WorkManager
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.NotificationUtil
import com.deniscerri.ytdlnis.util.UiUtil
import com.yausername.youtubedl_android.YoutubeDL
class OpenDownloadNotificationReceiver : BroadcastReceiver() {
override fun onReceive(c: Context, intent: Intent) {
val message = intent.getStringExtra("open")
val path = intent.getStringExtra("path")
val notificationId = intent.getIntExtra("notificationID", 0)
if (message != null && path != null) {
if (notificationId != 0) NotificationUtil(c).cancelDownloadNotification(notificationId)
val uiUtil = UiUtil(FileUtil())
uiUtil.openFileIntent(c, path)
}
}
}

@ -0,0 +1,21 @@
package com.deniscerri.ytdlnis.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.NotificationUtil
import com.deniscerri.ytdlnis.util.UiUtil
class SharedDownloadNotificationReceiver : BroadcastReceiver() {
override fun onReceive(c: Context, intent: Intent) {
val message = intent.getStringExtra("share")
val path = intent.getStringExtra("path")
val notificationId = intent.getIntExtra("notificationID", 0)
if (message != null && path != null) {
if (notificationId != 0) NotificationUtil(c).cancelDownloadNotification(notificationId)
val uiUtil = UiUtil(FileUtil())
uiUtil.shareFileIntent(c, listOf(path))
}
}
}

@ -5,6 +5,8 @@ import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.res.Configuration
import android.graphics.Canvas
import android.graphics.Color
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@ -23,6 +25,7 @@ import androidx.core.view.forEach
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.MainActivity
@ -43,7 +46,10 @@ import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.button.MaterialButton
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
import kotlinx.coroutines.runBlocking
import java.io.File
import java.text.SimpleDateFormat
@ -115,6 +121,8 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
)
recyclerView = view.findViewById(R.id.recyclerviewhistorys)
recyclerView?.adapter = historyAdapter
val itemTouchHelper = ItemTouchHelper(simpleCallback)
itemTouchHelper.attachToRecyclerView(recyclerView)
val landScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
val displayMetrics: DisplayMetrics = requireContext().resources.displayMetrics
@ -386,7 +394,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
}
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
historyViewModel.delete(item, deleteFile[0])
historyViewModel.delete(item, deleteFile[0])
}
deleteDialog.show()
}
@ -491,7 +499,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
redownload.setOnLongClickListener {
bottomSheet!!.cancel()
val sheet = DownloadBottomSheetDialog(downloadViewModel.createResultItemFromHistory(item), item.type, null)
val sheet = DownloadBottomSheetDialog(downloadViewModel.createResultItemFromHistory(item), item.type, downloadViewModel.createDownloadItemFromHistory(item))
sheet.show(parentFragmentManager, "downloadSingleSheet")
true
}
@ -614,6 +622,72 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
selectedObjects?.clear()
}
private var simpleCallback: ItemTouchHelper.SimpleCallback =
object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
override fun onMove(recyclerView: RecyclerView,viewHolder: RecyclerView.ViewHolder,target: RecyclerView.ViewHolder
): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.bindingAdapterPosition
when (direction) {
ItemTouchHelper.LEFT -> {
val deletedItem = historyList!![position]
historyAdapter!!.notifyItemChanged(position)
removeItem(deletedItem!!)
}
ItemTouchHelper.RIGHT -> {
val item = historyList!![position]!!
historyAdapter!!.notifyItemChanged(position)
val sheet = DownloadBottomSheetDialog(downloadViewModel.createResultItemFromHistory(item), item.type, downloadViewModel.createDownloadItemFromHistory(item))
sheet.show(parentFragmentManager, "downloadSingleSheet")
}
}
}
override fun onChildDraw(
c: Canvas,
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
dX: Float,
dY: Float,
actionState: Int,
isCurrentlyActive: Boolean
) {
RecyclerViewSwipeDecorator.Builder(
requireContext(),
c,
recyclerView,
viewHolder,
dX,
dY,
actionState,
isCurrentlyActive
)
.addSwipeLeftBackgroundColor(Color.RED)
.addSwipeLeftActionIcon(R.drawable.baseline_delete_24)
.addSwipeRightBackgroundColor(
MaterialColors.getColor(
requireContext(),
R.attr.colorOnSurfaceInverse, Color.TRANSPARENT
)
)
.addSwipeRightActionIcon(R.drawable.ic_refresh)
.create()
.decorate()
super.onChildDraw(
c,
recyclerView,
viewHolder!!,
dX,
dY,
actionState,
isCurrentlyActive
)
}
}
companion object {
private const val TAG = "historyFragment"
}

@ -11,10 +11,14 @@ import android.os.Build
import androidx.core.app.NotificationCompat
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.receiver.CancelDownloadNotificationReceiver
import com.deniscerri.ytdlnis.receiver.OpenDownloadNotificationReceiver
import com.deniscerri.ytdlnis.receiver.SharedDownloadNotificationReceiver
import com.deniscerri.ytdlnis.ui.more.downloadLogs.DownloadLogActivity
class NotificationUtil(var context: Context) {
private val downloadNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, DOWNLOAD_SERVICE_CHANNEL_ID)
private val commandDownloadNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID)
private val finishedDownloadNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, DOWNLOAD_FINISHED_CHANNEL_ID)
private val notificationManager: NotificationManager = context.getSystemService(NotificationManager::class.java)
fun createNotificationChannel() {
@ -38,6 +42,14 @@ class NotificationUtil(var context: Context) {
channel = NotificationChannel(COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID, name, importance)
channel.description = description
notificationManager.createNotificationChannel(channel)
//finished or errored downloads
name = context.getString(R.string.finished_download_notification_channel_name)
description =
context.getString(R.string.finished_download_notification_channel_description)
channel = NotificationChannel(DOWNLOAD_FINISHED_CHANNEL_ID, name, NotificationManager.IMPORTANCE_HIGH)
channel.description = description
notificationManager.createNotificationChannel(channel)
}
}
@ -45,6 +57,7 @@ class NotificationUtil(var context: Context) {
when(channel) {
DOWNLOAD_SERVICE_CHANNEL_ID -> { return downloadNotificationBuilder}
COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID -> { return commandDownloadNotificationBuilder }
DOWNLOAD_FINISHED_CHANNEL_ID -> { return finishedDownloadNotificationBuilder }
}
return downloadNotificationBuilder
}
@ -89,6 +102,84 @@ class NotificationUtil(var context: Context) {
.build()
}
fun createDownloadFinished(title: String?,
filepath: String?,
channel: String
) {
val notificationBuilder = getBuilder(channel)
val openIntent = Intent(context, OpenDownloadNotificationReceiver::class.java)
openIntent.putExtra("open", "")
openIntent.putExtra("path", filepath)
openIntent.putExtra("notificationID", DOWNLOAD_FINISHED_NOTIFICATION_ID)
val openNotificationPendingIntent = PendingIntent.getBroadcast(
context,
1,
openIntent,
PendingIntent.FLAG_IMMUTABLE
)
val shareIntent = Intent(context, SharedDownloadNotificationReceiver::class.java)
shareIntent.putExtra("share", "")
shareIntent.putExtra("path", filepath)
openIntent.putExtra("notificationID", DOWNLOAD_FINISHED_NOTIFICATION_ID)
val shareNotificationPendingIntent = PendingIntent.getBroadcast(
context,
1,
shareIntent,
PendingIntent.FLAG_IMMUTABLE
)
notificationBuilder
.setContentTitle("${context.getString(R.string.downloaded)} $title")
.setSmallIcon(R.drawable.ic_app_icon)
.setLargeIcon(
BitmapFactory.decodeResource(
context.resources,
R.drawable.ic_app_icon
)
)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.clearActions()
if (filepath != null){
notificationBuilder.addAction(0, context.getString(R.string.Open_File), openNotificationPendingIntent)
notificationBuilder.addAction(0, context.getString(R.string.share), shareNotificationPendingIntent)
}
notificationManager.notify(DOWNLOAD_FINISHED_NOTIFICATION_ID, notificationBuilder.build())
}
fun createDownloadErrored(title: String?,
error: String?,
logFilePath: String?,
channel: String
) {
val notificationBuilder = getBuilder(channel)
val intent = Intent(context, DownloadLogActivity::class.java)
intent.putExtra("path", logFilePath)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
val errorPendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
notificationBuilder
.setContentTitle("${context.getString(R.string.errored)}: $title")
.setContentText(error)
.setSmallIcon(R.drawable.ic_app_icon)
.setLargeIcon(
BitmapFactory.decodeResource(
context.resources,
R.drawable.ic_app_icon
)
)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.clearActions()
if (logFilePath != null){
notificationBuilder.addAction(0, context.getString(R.string.logs), errorPendingIntent)
}
notificationManager.notify(DOWNLOAD_FINISHED_NOTIFICATION_ID, notificationBuilder.build())
}
fun updateDownloadNotification(
id: Int,
desc: String,
@ -118,9 +209,9 @@ class NotificationUtil(var context: Context) {
companion object {
const val DOWNLOAD_SERVICE_CHANNEL_ID = "1"
const val DOWNLOAD_NOTIFICATION_ID = 1
const val COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID = "2"
const val COMMAND_DOWNLOAD_NOTIFICATION_ID = 2
const val DOWNLOAD_FINISHED_CHANNEL_ID = "3"
const val DOWNLOAD_FINISHED_NOTIFICATION_ID = 3
const val FILE_TRANSFER_CHANNEL_ID = "3"
private const val PROGRESS_MAX = 100
private const val PROGRESS_CURR = 0

@ -228,7 +228,12 @@ class UiUtil(private val fileUtil: FileUtil) {
val i = Intent(Intent.ACTION_VIEW)
i.setDataAndType(uri, mime)
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
fragmentContext.startActivity(i)
try {
fragmentContext.startActivity(i)
}catch (e: Exception){
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
fragmentContext.startActivity(i)
}
}
fun shareFileIntent(fragmentContext: Context, paths: List<String>){
@ -251,7 +256,13 @@ class UiUtil(private val fileUtil: FileUtil) {
type = "*/*"
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
}
fragmentContext.startActivity(Intent.createChooser(shareIntent, null))
try {
fragmentContext.startActivity(Intent.createChooser(shareIntent, null))
}catch (e: Exception){
val intent = Intent.createChooser(shareIntent, null)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
fragmentContext.startActivity(intent)
}
}
fun showDatePicker(fragmentManager: FragmentManager , onSubmit : (chosenDate: Calendar) -> Unit ){

@ -16,6 +16,7 @@ import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.HistoryItem
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.ui.more.downloadLogs.DownloadLogActivity
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.NotificationUtil
import com.yausername.youtubedl_android.YoutubeDL
@ -309,6 +310,11 @@ class DownloadWorker(
historyDao.insert(historyItem)
}
}
notificationUtil.createDownloadFinished(
downloadItem.title, if (finalPath.equals(context.getString(R.string.unfound_file))) null else finalPath,
NotificationUtil.DOWNLOAD_FINISHED_CHANNEL_ID
)
runBlocking {
dao.delete(downloadItem.id)
}
@ -342,6 +348,13 @@ class DownloadWorker(
runBlocking {
dao.update(downloadItem)
}
notificationUtil.createDownloadErrored(
downloadItem.title, it.message,
if (logDownloads) logFile.absolutePath else null,
NotificationUtil.DOWNLOAD_FINISHED_CHANNEL_ID
)
return Result.failure(
Data.Builder().putString("output", it.toString()).build()
)

@ -238,4 +238,7 @@
<string name="purple">Purple</string>
<string name="yellow">Yellow</string>
<string name="monochrome">Monochrome</string>
<string name="downloaded">"Downloaded: "</string>
<string name="finished_download_notification_channel_name">Finished Downloads</string>
<string name="finished_download_notification_channel_description">Notification showing when the download has succesfully finished or errored out</string>
</resources>
Loading…
Cancel
Save