added context app bar for cancelled and errored downloads
This commit is contained in:
parent
a6a4754bb2
commit
d24b56835f
16 changed files with 384 additions and 51 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
package com.deniscerri.ytdlnis.adapter
|
package com.deniscerri.ytdlnis.adapter
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
|
@ -15,24 +16,29 @@ import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
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.repository.DownloadRepository
|
||||||
import com.deniscerri.ytdlnis.util.FileUtil
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
|
import com.google.android.material.card.MaterialCardView
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
|
import java.util.ArrayList
|
||||||
|
|
||||||
class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<DownloadItem?, GenericDownloadAdapter.ViewHolder>(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) {
|
class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<DownloadItem?, GenericDownloadAdapter.ViewHolder>(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) {
|
||||||
private val onItemClickListener: OnItemClickListener
|
private val onItemClickListener: OnItemClickListener
|
||||||
private val activity: Activity
|
private val activity: Activity
|
||||||
private var fileUtil: FileUtil
|
private var fileUtil: FileUtil
|
||||||
|
private val checkedItems: ArrayList<Long>
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
checkedItems = ArrayList()
|
||||||
this.onItemClickListener = onItemClickListener
|
this.onItemClickListener = onItemClickListener
|
||||||
this.activity = activity
|
this.activity = activity
|
||||||
fileUtil = FileUtil()
|
fileUtil = FileUtil()
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewHolder(itemView: View, onItemClickListener: OnItemClickListener?) : RecyclerView.ViewHolder(itemView) {
|
class ViewHolder(itemView: View, onItemClickListener: OnItemClickListener?) : RecyclerView.ViewHolder(itemView) {
|
||||||
val cardView: FrameLayout
|
val cardView: MaterialCardView
|
||||||
init {
|
init {
|
||||||
cardView = itemView.findViewById(R.id.download_card_view)
|
cardView = itemView.findViewById(R.id.download_card_view)
|
||||||
}
|
}
|
||||||
|
|
@ -116,15 +122,75 @@ class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
|
||||||
actionButton.setOnClickListener {
|
actionButton.setOnClickListener {
|
||||||
onItemClickListener.onActionButtonClick(item.id)
|
onItemClickListener.onActionButtonClick(item.id)
|
||||||
}
|
}
|
||||||
|
if (checkedItems.contains(item.id)) {
|
||||||
|
card.isChecked = true
|
||||||
|
card.strokeWidth = 5
|
||||||
|
} else {
|
||||||
|
card.isChecked = false
|
||||||
|
card.strokeWidth = 0
|
||||||
|
}
|
||||||
card.setOnClickListener {
|
card.setOnClickListener {
|
||||||
onItemClickListener.onCardClick(item.id)
|
if (checkedItems.size > 0) {
|
||||||
|
checkCard(card, item.id)
|
||||||
|
} else {
|
||||||
|
onItemClickListener.onCardClick(item.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
card.setOnLongClickListener {
|
||||||
|
checkCard(card, item.id)
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun clearCheckeditems() {
|
||||||
|
for (i in 0 until itemCount){
|
||||||
|
val item = getItem(i)
|
||||||
|
if (checkedItems.find { it == item?.id } != null){
|
||||||
|
checkedItems.remove(item?.id)
|
||||||
|
notifyItemChanged(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkedItems.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun checkAll(items: List<DownloadItem?>?){
|
||||||
|
checkedItems.clear()
|
||||||
|
checkedItems.addAll(items!!.map { it!!.id })
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun invertSelected(items: List<DownloadItem?>?){
|
||||||
|
val invertedList = mutableListOf<Long>()
|
||||||
|
items?.forEach {
|
||||||
|
if (!checkedItems.contains(it!!.id)) invertedList.add(it.id)
|
||||||
|
}
|
||||||
|
checkedItems.clear()
|
||||||
|
checkedItems.addAll(invertedList)
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkCard(card: MaterialCardView, itemID: Long) {
|
||||||
|
if (card.isChecked) {
|
||||||
|
card.strokeWidth = 0
|
||||||
|
checkedItems.remove(itemID)
|
||||||
|
} else {
|
||||||
|
card.strokeWidth = 5
|
||||||
|
checkedItems.add(itemID)
|
||||||
|
}
|
||||||
|
card.isChecked = !card.isChecked
|
||||||
|
onItemClickListener.onCardSelect(itemID, card.isChecked)
|
||||||
|
}
|
||||||
|
|
||||||
interface OnItemClickListener {
|
interface OnItemClickListener {
|
||||||
fun onActionButtonClick(itemID: Long)
|
fun onActionButtonClick(itemID: Long)
|
||||||
fun onCardClick(itemID: Long)
|
fun onCardClick(itemID: Long)
|
||||||
|
fun onCardSelect(itemID: Long, isChecked: Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,8 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun parseQuery(inputQuery: String, resetResults: Boolean) : ArrayList<ResultItem?> {
|
suspend fun parseQuery(inputQuery: String, resetResults: Boolean) : ArrayList<ResultItem?> {
|
||||||
if (resetResults) loadingItems.postValue(true)
|
if (resetResults)
|
||||||
|
loadingItems.postValue(true)
|
||||||
val type = getQueryType(inputQuery)
|
val type = getQueryType(inputQuery)
|
||||||
var res = arrayListOf<ResultItem?>()
|
var res = arrayListOf<ResultItem?>()
|
||||||
return withContext(Dispatchers.IO){
|
return withContext(Dispatchers.IO){
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ class CancelDownloadNotificationReceiver : BroadcastReceiver() {
|
||||||
val notificationUtil = NotificationUtil(c)
|
val notificationUtil = NotificationUtil(c)
|
||||||
|
|
||||||
YoutubeDL.getInstance().destroyProcessById(id.toString())
|
YoutubeDL.getInstance().destroyProcessById(id.toString())
|
||||||
WorkManager.getInstance(c).cancelUniqueWork(id.toString())
|
|
||||||
notificationUtil.cancelDownloadNotification(id)
|
notificationUtil.cancelDownloadNotification(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -191,6 +191,13 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
||||||
downloadAllFab.tag = "downloadAll"
|
downloadAllFab.tag = "downloadAll"
|
||||||
downloadAllFab.setOnClickListener(this)
|
downloadAllFab.setOnClickListener(this)
|
||||||
|
|
||||||
|
if (arguments?.getString("url") != null){
|
||||||
|
val url = requireArguments().getString("url")
|
||||||
|
if (inputQueries == null) inputQueries = mutableListOf()
|
||||||
|
searchBar?.text = url
|
||||||
|
inputQueries!!.add(url!!)
|
||||||
|
}
|
||||||
|
|
||||||
if (inputQueries != null) {
|
if (inputQueries != null) {
|
||||||
resultViewModel.deleteAll()
|
resultViewModel.deleteAll()
|
||||||
lifecycleScope.launch(Dispatchers.IO){
|
lifecycleScope.launch(Dispatchers.IO){
|
||||||
|
|
@ -470,7 +477,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
resultViewModel.parseQueries(queryList)
|
resultViewModel.parseQueries(queryList)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -654,6 +660,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
|
||||||
super.onStop()
|
super.onStop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "HomeFragment"
|
private const val TAG = "HomeFragment"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import android.widget.LinearLayout
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.viewpager2.widget.ViewPager2
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
|
|
@ -195,19 +196,23 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
|
||||||
if (quickDownload) {
|
if (quickDownload) {
|
||||||
(updateItem.parent as LinearLayout).visibility = View.VISIBLE
|
(updateItem.parent as LinearLayout).visibility = View.VISIBLE
|
||||||
updateItem.setOnClickListener {
|
updateItem.setOnClickListener {
|
||||||
lifecycleScope.launch(Dispatchers.IO){
|
if (activity is ShareActivity) {
|
||||||
if (activity is ShareActivity) {
|
val intent = Intent(context, ShareActivity::class.java)
|
||||||
val intent = Intent(context, ShareActivity::class.java)
|
intent.action = Intent.ACTION_SEND
|
||||||
intent.action = Intent.ACTION_SEND
|
intent.type = "text/plain"
|
||||||
intent.type = "text/plain"
|
intent.putExtra(Intent.EXTRA_TEXT, resultItem.url)
|
||||||
intent.putExtra(Intent.EXTRA_TEXT, resultItem.url)
|
intent.putExtra("quick_download", false)
|
||||||
intent.putExtra("quick_download", false)
|
startActivity(intent)
|
||||||
startActivity(intent)
|
dismiss()
|
||||||
dismiss()
|
}else{
|
||||||
}else{
|
dismiss()
|
||||||
resultViewModel.parseQueries(listOf(resultItem.url))
|
val bundle = Bundle()
|
||||||
dismiss()
|
bundle.putString("url", resultItem.url)
|
||||||
}
|
findNavController().popBackStack(R.id.homeFragment, true)
|
||||||
|
findNavController().navigate(
|
||||||
|
R.id.homeFragment,
|
||||||
|
bundle
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
|
|
|
||||||
|
|
@ -126,16 +126,16 @@ class ActiveDownloadsFragment() : Fragment(), ActiveDownloadAdapter.OnItemClickL
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cancelDownload(itemID: Long){
|
private fun cancelDownload(itemID: Long){
|
||||||
|
val id = itemID.toInt()
|
||||||
|
YoutubeDL.getInstance().destroyProcessById(id.toString())
|
||||||
|
WorkManager.getInstance(requireContext()).cancelUniqueWork(id.toString())
|
||||||
|
notificationUtil.cancelDownloadNotification(id)
|
||||||
|
|
||||||
list.find { it.id == itemID }?.let {
|
list.find { it.id == itemID }?.let {
|
||||||
it.status = DownloadRepository.Status.Cancelled.toString()
|
it.status = DownloadRepository.Status.Cancelled.toString()
|
||||||
downloadViewModel.updateDownload(it)
|
downloadViewModel.updateDownload(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val id = itemID.toInt()
|
|
||||||
YoutubeDL.getInstance().destroyProcessById(id.toString())
|
|
||||||
WorkManager.getInstance(requireContext()).cancelUniqueWork(id.toString())
|
|
||||||
notificationUtil.cancelDownloadNotification(id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(p0: View?) {
|
override fun onClick(p0: View?) {
|
||||||
|
|
|
||||||
|
|
@ -7,22 +7,25 @@ import android.graphics.Canvas
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import android.view.LayoutInflater
|
import android.view.*
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.view.Window
|
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.appcompat.view.ActionMode
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.view.forEach
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.deniscerri.ytdlnis.MainActivity
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.adapter.GenericDownloadAdapter
|
import com.deniscerri.ytdlnis.adapter.GenericDownloadAdapter
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
|
import com.deniscerri.ytdlnis.database.models.HistoryItem
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding
|
import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding
|
||||||
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog
|
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog
|
||||||
|
|
@ -35,7 +38,11 @@ import com.google.android.material.color.MaterialColors
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
|
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.util.ArrayList
|
||||||
|
|
||||||
|
|
||||||
class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickListener {
|
class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickListener {
|
||||||
|
|
@ -45,6 +52,8 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
|
||||||
private lateinit var downloadViewModel : DownloadViewModel
|
private lateinit var downloadViewModel : DownloadViewModel
|
||||||
private lateinit var cancelledRecyclerView : RecyclerView
|
private lateinit var cancelledRecyclerView : RecyclerView
|
||||||
private lateinit var cancelledDownloads : GenericDownloadAdapter
|
private lateinit var cancelledDownloads : GenericDownloadAdapter
|
||||||
|
private var selectedObjects: ArrayList<DownloadItem>? = null
|
||||||
|
private var actionMode : ActionMode? = null
|
||||||
private lateinit var items : MutableList<DownloadItem>
|
private lateinit var items : MutableList<DownloadItem>
|
||||||
private lateinit var fileUtil: FileUtil
|
private lateinit var fileUtil: FileUtil
|
||||||
private lateinit var uiUtil : UiUtil
|
private lateinit var uiUtil : UiUtil
|
||||||
|
|
@ -57,6 +66,7 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
|
||||||
_binding = FragmentHomeBinding.inflate(inflater, container, false)
|
_binding = FragmentHomeBinding.inflate(inflater, container, false)
|
||||||
fragmentView = inflater.inflate(R.layout.fragment_generic_download_queue, container, false)
|
fragmentView = inflater.inflate(R.layout.fragment_generic_download_queue, container, false)
|
||||||
activity = getActivity()
|
activity = getActivity()
|
||||||
|
selectedObjects = arrayListOf()
|
||||||
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
||||||
items = mutableListOf()
|
items = mutableListOf()
|
||||||
fileUtil = FileUtil()
|
fileUtil = FileUtil()
|
||||||
|
|
@ -213,6 +223,26 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCardSelect(itemID: Long, isChecked: Boolean) {
|
||||||
|
val item = items.find { it.id == itemID }
|
||||||
|
if (isChecked) {
|
||||||
|
selectedObjects!!.add(item!!)
|
||||||
|
if (actionMode == null){
|
||||||
|
actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||||
|
|
||||||
|
}else{
|
||||||
|
actionMode!!.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
selectedObjects!!.remove(item)
|
||||||
|
actionMode?.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
||||||
|
if (selectedObjects!!.isEmpty()){
|
||||||
|
actionMode?.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun removeItem(item: DownloadItem, bottomSheet: BottomSheetDialog?){
|
private fun removeItem(item: DownloadItem, bottomSheet: BottomSheetDialog?){
|
||||||
bottomSheet?.hide()
|
bottomSheet?.hide()
|
||||||
|
|
@ -225,6 +255,80 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
|
||||||
deleteDialog.show()
|
deleteDialog.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val contextualActionBar = object : ActionMode.Callback {
|
||||||
|
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
||||||
|
mode!!.menuInflater.inflate(R.menu.cancelled_downloads_menu_context, menu)
|
||||||
|
mode.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPrepareActionMode(
|
||||||
|
mode: ActionMode?,
|
||||||
|
menu: Menu?
|
||||||
|
): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActionItemClicked(
|
||||||
|
mode: ActionMode?,
|
||||||
|
item: MenuItem?
|
||||||
|
): Boolean {
|
||||||
|
return when (item!!.itemId) {
|
||||||
|
R.id.delete_results -> {
|
||||||
|
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||||
|
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||||
|
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||||
|
for (obj in selectedObjects!!){
|
||||||
|
downloadViewModel.deleteDownload(obj)
|
||||||
|
}
|
||||||
|
clearCheckedItems()
|
||||||
|
actionMode?.finish()
|
||||||
|
}
|
||||||
|
deleteDialog.show()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.redownload -> {
|
||||||
|
runBlocking {
|
||||||
|
downloadViewModel.queueDownloads(selectedObjects!!.toMutableList())
|
||||||
|
actionMode?.finish()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.select_all -> {
|
||||||
|
cancelledDownloads.checkAll(items)
|
||||||
|
selectedObjects?.clear()
|
||||||
|
items.forEach { selectedObjects?.add(it) }
|
||||||
|
mode?.title = getString(R.string.all_items_selected)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.invert_selected -> {
|
||||||
|
cancelledDownloads.invertSelected(items)
|
||||||
|
val invertedList = arrayListOf<DownloadItem>()
|
||||||
|
items.forEach {
|
||||||
|
if (!selectedObjects?.contains(it)!!) invertedList.add(it)
|
||||||
|
}
|
||||||
|
selectedObjects?.clear()
|
||||||
|
selectedObjects?.addAll(invertedList)
|
||||||
|
actionMode!!.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyActionMode(mode: ActionMode?) {
|
||||||
|
actionMode = null
|
||||||
|
clearCheckedItems()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearCheckedItems(){
|
||||||
|
cancelledDownloads.clearCheckeditems()
|
||||||
|
selectedObjects?.clear()
|
||||||
|
}
|
||||||
|
|
||||||
private var simpleCallback: ItemTouchHelper.SimpleCallback =
|
private var simpleCallback: ItemTouchHelper.SimpleCallback =
|
||||||
object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
|
object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
|
||||||
override fun onMove(recyclerView: RecyclerView,viewHolder: RecyclerView.ViewHolder,target: RecyclerView.ViewHolder
|
override fun onMove(recyclerView: RecyclerView,viewHolder: RecyclerView.ViewHolder,target: RecyclerView.ViewHolder
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,13 @@ import android.graphics.Canvas
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import android.view.LayoutInflater
|
import android.view.*
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.view.Window
|
|
||||||
import android.widget.AdapterView
|
import android.widget.AdapterView
|
||||||
import android.widget.AdapterView.OnItemClickListener
|
import android.widget.AdapterView.OnItemClickListener
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.appcompat.view.ActionMode
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
|
@ -40,6 +39,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
|
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import java.util.ArrayList
|
||||||
|
|
||||||
|
|
||||||
class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickListener, OnItemClickListener {
|
class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickListener, OnItemClickListener {
|
||||||
|
|
@ -50,6 +50,8 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
|
||||||
private lateinit var erroredRecyclerView : RecyclerView
|
private lateinit var erroredRecyclerView : RecyclerView
|
||||||
private lateinit var erroredDownloads : GenericDownloadAdapter
|
private lateinit var erroredDownloads : GenericDownloadAdapter
|
||||||
private lateinit var items : MutableList<DownloadItem>
|
private lateinit var items : MutableList<DownloadItem>
|
||||||
|
private var selectedObjects: ArrayList<DownloadItem>? = null
|
||||||
|
private var actionMode : ActionMode? = null
|
||||||
private lateinit var fileUtil: FileUtil
|
private lateinit var fileUtil: FileUtil
|
||||||
private lateinit var uiUtil : UiUtil
|
private lateinit var uiUtil : UiUtil
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
|
|
@ -62,6 +64,7 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
|
||||||
activity = getActivity()
|
activity = getActivity()
|
||||||
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
||||||
items = mutableListOf()
|
items = mutableListOf()
|
||||||
|
selectedObjects = arrayListOf()
|
||||||
fileUtil = FileUtil()
|
fileUtil = FileUtil()
|
||||||
uiUtil = UiUtil(fileUtil)
|
uiUtil = UiUtil(fileUtil)
|
||||||
return fragmentView
|
return fragmentView
|
||||||
|
|
@ -213,6 +216,26 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCardSelect(itemID: Long, isChecked: Boolean) {
|
||||||
|
val item = items.find { it.id == itemID }
|
||||||
|
if (isChecked) {
|
||||||
|
selectedObjects!!.add(item!!)
|
||||||
|
if (actionMode == null){
|
||||||
|
actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||||
|
|
||||||
|
}else{
|
||||||
|
actionMode!!.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
selectedObjects!!.remove(item)
|
||||||
|
actionMode?.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
||||||
|
if (selectedObjects!!.isEmpty()){
|
||||||
|
actionMode?.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun removeItem(item: DownloadItem, bottomSheet: BottomSheetDialog?){
|
private fun removeItem(item: DownloadItem, bottomSheet: BottomSheetDialog?){
|
||||||
bottomSheet?.hide()
|
bottomSheet?.hide()
|
||||||
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
|
@ -228,6 +251,81 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private val contextualActionBar = object : ActionMode.Callback {
|
||||||
|
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
||||||
|
mode!!.menuInflater.inflate(R.menu.cancelled_downloads_menu_context, menu)
|
||||||
|
mode.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPrepareActionMode(
|
||||||
|
mode: ActionMode?,
|
||||||
|
menu: Menu?
|
||||||
|
): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActionItemClicked(
|
||||||
|
mode: ActionMode?,
|
||||||
|
item: MenuItem?
|
||||||
|
): Boolean {
|
||||||
|
return when (item!!.itemId) {
|
||||||
|
R.id.delete_results -> {
|
||||||
|
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||||
|
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||||
|
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||||
|
for (obj in selectedObjects!!){
|
||||||
|
downloadViewModel.deleteDownload(obj)
|
||||||
|
}
|
||||||
|
clearCheckedItems()
|
||||||
|
actionMode?.finish()
|
||||||
|
}
|
||||||
|
deleteDialog.show()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.redownload -> {
|
||||||
|
runBlocking {
|
||||||
|
downloadViewModel.queueDownloads(selectedObjects!!.toMutableList())
|
||||||
|
actionMode?.finish()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.select_all -> {
|
||||||
|
erroredDownloads.checkAll(items)
|
||||||
|
selectedObjects?.clear()
|
||||||
|
items.forEach { selectedObjects?.add(it) }
|
||||||
|
mode?.title = getString(R.string.all_items_selected)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.invert_selected -> {
|
||||||
|
erroredDownloads.invertSelected(items)
|
||||||
|
val invertedList = arrayListOf<DownloadItem>()
|
||||||
|
items.forEach {
|
||||||
|
if (!selectedObjects?.contains(it)!!) invertedList.add(it)
|
||||||
|
}
|
||||||
|
selectedObjects?.clear()
|
||||||
|
selectedObjects?.addAll(invertedList)
|
||||||
|
actionMode!!.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyActionMode(mode: ActionMode?) {
|
||||||
|
actionMode = null
|
||||||
|
clearCheckedItems()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearCheckedItems(){
|
||||||
|
erroredDownloads.clearCheckeditems()
|
||||||
|
selectedObjects?.clear()
|
||||||
|
}
|
||||||
|
|
||||||
private var simpleCallback: ItemTouchHelper.SimpleCallback =
|
private var simpleCallback: ItemTouchHelper.SimpleCallback =
|
||||||
object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
|
object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
|
||||||
override fun onMove(recyclerView: RecyclerView,viewHolder: RecyclerView.ViewHolder,target: RecyclerView.ViewHolder
|
override fun onMove(recyclerView: RecyclerView,viewHolder: RecyclerView.ViewHolder,target: RecyclerView.ViewHolder
|
||||||
|
|
|
||||||
|
|
@ -241,6 +241,10 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCardSelect(itemID: Long, isChecked: Boolean) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
private fun removeItem(itemID: Long){
|
private fun removeItem(itemID: Long){
|
||||||
val item = items.find { it.id == itemID }
|
val item = items.find { it.id == itemID }
|
||||||
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
|
|
||||||
|
|
@ -613,8 +613,10 @@ class InfoUtil(private val context: Context) {
|
||||||
author = jsonObject.getString("uploader")
|
author = jsonObject.getString("uploader")
|
||||||
}
|
}
|
||||||
var duration = ""
|
var duration = ""
|
||||||
if (jsonObject.has("duration")) {
|
runCatching {
|
||||||
duration = formatIntegerDuration(jsonObject.getInt("duration"), Locale.getDefault())
|
if (jsonObject.has("duration")) {
|
||||||
|
duration = formatIntegerDuration(jsonObject.getInt("duration"), Locale.getDefault())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val url = jsonObject.getString("webpage_url")
|
val url = jsonObject.getString("webpage_url")
|
||||||
var thumb: String? = ""
|
var thumb: String? = ""
|
||||||
|
|
|
||||||
|
|
@ -388,6 +388,12 @@ class DownloadWorker(
|
||||||
|
|
||||||
return Result.success()
|
return Result.success()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onStopped() {
|
||||||
|
YoutubeDL.getInstance().destroyProcessById(itemId.toInt().toString())
|
||||||
|
super.onStopped()
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
private fun moveFile(originDir: File, downLocation: String, progress: (progress: Int) -> Unit) : String{
|
private fun moveFile(originDir: File, downLocation: String, progress: (progress: Int) -> Unit) : String{
|
||||||
val fileUtil = FileUtil()
|
val fileUtil = FileUtil()
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,11 @@ class TerminalDownloadWorker(
|
||||||
return Result.success()
|
return Result.success()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
override fun onStopped() {
|
||||||
|
YoutubeDL.getInstance().destroyProcessById(DownloadWorker.itemId.toInt().toString())
|
||||||
|
super.onStopped()
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
private fun moveFile(originDir: File, downLocation: String, progress: (progress: Int) -> Unit) : String{
|
private fun moveFile(originDir: File, downLocation: String, progress: (progress: Int) -> Unit) : String{
|
||||||
val fileUtil = FileUtil()
|
val fileUtil = FileUtil()
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,13 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<FrameLayout
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/download_card_view"
|
android:id="@+id/download_card_view"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar"
|
app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingHorizontal="20dp"
|
android:layout_marginHorizontal="20dp"
|
||||||
android:paddingVertical="10dp"
|
android:layout_marginVertical="10dp"
|
||||||
android:checkable="true"
|
android:checkable="true"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
|
@ -193,6 +193,6 @@
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</FrameLayout>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
<LinearLayout android:layout_width="match_parent"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
|
|
@ -6,28 +7,33 @@
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/materialButton"
|
||||||
style="@style/Widget.Material3.Button.IconButton"
|
style="@style/Widget.Material3.Button.IconButton"
|
||||||
app:iconSize="50dp"
|
android:layout_width="wrap_content"
|
||||||
android:padding="0dp"
|
android:layout_height="wrap_content"
|
||||||
app:iconTint="@null"
|
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:insetBottom="0dp"
|
|
||||||
android:insetLeft="0dp"
|
android:insetLeft="0dp"
|
||||||
android:insetTop="0dp"
|
android:insetTop="0dp"
|
||||||
android:insetRight="0dp"
|
android:insetRight="0dp"
|
||||||
android:layout_width="wrap_content"
|
android:insetBottom="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:padding="0dp"
|
||||||
app:icon="@drawable/ic_app_icon" />
|
app:icon="@drawable/ic_app_icon"
|
||||||
|
app:iconSize="50dp"
|
||||||
|
app:iconTint="@null"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="20dp"
|
android:layout_gravity="center_vertical"
|
||||||
android:layout_marginBottom="20dp"
|
android:text="@string/app_name"
|
||||||
android:layout_marginStart="0dp"
|
|
||||||
android:textAppearance="?attr/textAppearanceHeadlineSmall"
|
android:textAppearance="?attr/textAppearanceHeadlineSmall"
|
||||||
android:textColor="?attr/colorOnSurface"
|
android:textColor="?attr/colorOnSurface"
|
||||||
android:text="@string/app_name" />
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/materialButton"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
29
app/src/main/res/menu/cancelled_downloads_menu_context.xml
Normal file
29
app/src/main/res/menu/cancelled_downloads_menu_context.xml
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
tools:context=".MainActivity" >
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/delete_results"
|
||||||
|
android:title="@string/remove_results"
|
||||||
|
android:icon="@drawable/baseline_delete_24"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/redownload"
|
||||||
|
android:title="@string/redownload"
|
||||||
|
android:icon="@drawable/ic_refresh"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/select_all"
|
||||||
|
android:title="@string/select_all"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/invert_selected"
|
||||||
|
android:title="@string/invert_selected"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
|
</menu>
|
||||||
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<paths>
|
<paths>
|
||||||
<external-path name="external_files" path="."/>
|
<external-path name="external_files" path="."/>
|
||||||
|
<root-path name="external_files" path="/storage/" />
|
||||||
<cache-path
|
<cache-path
|
||||||
name="cache"
|
name="cache"
|
||||||
path="." />
|
path="." />
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue