added contextual menus for main and history fragments

This commit is contained in:
deniscerri 2023-04-09 11:05:29 +02:00
parent 9b2d24725f
commit f5ad2aee1d
No known key found for this signature in database
GPG key ID: 95C43D517D830350
23 changed files with 348 additions and 123 deletions

View file

@ -19,6 +19,7 @@ import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.forEach
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
@ -110,6 +111,14 @@ class MainActivity : AppCompatActivity() {
bottomNav.visibility = View.VISIBLE bottomNav.visibility = View.VISIBLE
} }
fun disableBottomNavigation(){
bottomNav.menu.forEach { it.isEnabled = false }
}
fun enableBottomNavigation(){
bottomNav.menu.forEach { it.isEnabled = true }
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
val incognitoHeader = findViewById<TextView>(R.id.incognito_header) val incognitoHeader = findViewById<TextView>(R.id.incognito_header)

View file

@ -167,13 +167,14 @@ class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity)
fun onCardClick(videoURL: String, add: Boolean) fun onCardClick(videoURL: String, add: Boolean)
} }
fun clearCheckedVideos() { fun checkAll(items: List<ResultItem?>?){
// val size = checkedVideos.size checkedVideos.clear()
// for (i in 0 until size) { checkedVideos.addAll(items!!.map { it!!.url })
// val position = checkedVideos[i] notifyDataSetChanged()
// notifyItemChanged(position) }
// }
// checkedVideos.clear() fun clearCheckedItems(){
checkedVideos.clear()
} }
companion object { companion object {

View file

@ -27,6 +27,9 @@ interface ResultDao {
@Query("DELETE FROM results") @Query("DELETE FROM results")
suspend fun deleteAll() suspend fun deleteAll()
@Query("DELETE FROM results WHERE id=:id")
suspend fun delete(id: Long)
@Query("SELECT * FROM results WHERE url=:url LIMIT 1") @Query("SELECT * FROM results WHERE url=:url LIMIT 1")
fun getResultByURL(url: String) : ResultItem fun getResultByURL(url: String) : ResultItem

View file

@ -108,6 +108,9 @@ class ResultRepository(private val resultDao: ResultDao, private val commandTemp
return arrayListOf() return arrayListOf()
} }
suspend fun delete(item: ResultItem){
resultDao.delete(item.id)
}
suspend fun deleteAll(){ suspend fun deleteAll(){
itemCount.postValue(0) itemCount.postValue(0)

View file

@ -4,6 +4,7 @@ import android.app.Application
import androidx.lifecycle.* import androidx.lifecycle.*
import com.deniscerri.ytdlnis.database.DBManager import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.models.HistoryItem import com.deniscerri.ytdlnis.database.models.HistoryItem
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.repository.HistoryRepository import com.deniscerri.ytdlnis.database.repository.HistoryRepository
import com.deniscerri.ytdlnis.database.repository.HistoryRepository.HistorySort import com.deniscerri.ytdlnis.database.repository.HistoryRepository.HistorySort
import com.deniscerri.ytdlnis.database.repository.HistoryRepository.HistorySortType import com.deniscerri.ytdlnis.database.repository.HistoryRepository.HistorySortType
@ -103,4 +104,5 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
fun clearDeleted() = viewModelScope.launch(Dispatchers.IO) { fun clearDeleted() = viewModelScope.launch(Dispatchers.IO) {
repository.clearDeletedHistory() repository.clearDeletedHistory()
} }
} }

View file

@ -150,4 +150,10 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
fun getSearchHistory() : List<SearchHistoryItem> { fun getSearchHistory() : List<SearchHistoryItem> {
return searchHistoryRepository.getAll() return searchHistoryRepository.getAll()
} }
fun deleteSelected(selectedItems : List<ResultItem>) = viewModelScope.launch(Dispatchers.IO) {
selectedItems.forEach {
repository.delete(it)
}
}
} }

View file

@ -10,16 +10,14 @@ import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.text.InputType
import android.util.Log import android.util.Log
import android.view.* import android.view.*
import android.view.View.* import android.view.View.*
import android.widget.* import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.ContextCompat
import androidx.core.text.TextUtilsCompat
import androidx.core.view.ViewCompat
import androidx.core.view.children import androidx.core.view.children
import androidx.core.view.forEach import androidx.core.view.forEach
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
@ -29,7 +27,6 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.work.WorkManager
import com.deniscerri.ytdlnis.MainActivity import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.adapter.HomeAdapter import com.deniscerri.ytdlnis.adapter.HomeAdapter
@ -58,9 +55,7 @@ import java.util.*
class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickListener { class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickListener {
private var inputQuery: String? = null
private var inputQueries: MutableList<String>? = null private var inputQueries: MutableList<String>? = null
private var inputQueriesLength = 0
private var homeAdapter: HomeAdapter? = null private var homeAdapter: HomeAdapter? = null
private var searchSuggestions: ScrollView? = null private var searchSuggestions: ScrollView? = null
@ -77,7 +72,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
private lateinit var resultViewModel : ResultViewModel private lateinit var resultViewModel : ResultViewModel
private lateinit var downloadViewModel : DownloadViewModel private lateinit var downloadViewModel : DownloadViewModel
private var downloading = false
private var fragmentView: View? = null private var fragmentView: View? = null
private var activity: Activity? = null private var activity: Activity? = null
private var mainActivity: MainActivity? = null private var mainActivity: MainActivity? = null
@ -94,9 +88,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
private var quickLaunchSheet = false private var quickLaunchSheet = false
private var sharedPreferences: SharedPreferences? = null private var sharedPreferences: SharedPreferences? = null
private var _binding : FragmentHomeBinding? = null private var _binding : FragmentHomeBinding? = null
private var actionMode: ActionMode? = null
private var workManager: WorkManager? = null
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
@ -106,7 +98,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
activity = getActivity() activity = getActivity()
mainActivity = activity as MainActivity? mainActivity = activity as MainActivity?
quickLaunchSheet = false quickLaunchSheet = false
return fragmentView return fragmentView
} }
@ -233,7 +224,24 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
textView.text = getString(R.string.link_you_copied) textView.text = getString(R.string.link_you_copied)
textView.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_language, 0, 0, 0) textView.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_language, 0, 0, 0)
val mb = linkYouCopied.findViewById<ImageButton>(R.id.set_search_query_button) val mb = linkYouCopied.findViewById<ImageButton>(R.id.set_search_query_button)
mb.visibility = INVISIBLE mb.setImageResource(R.drawable.ic_plus)
mb.setOnClickListener {
val present = queriesChipGroup!!.children.firstOrNull { (it as Chip).text.toString() == clip.toString() }
if (present == null) {
val chip = layoutinflater!!.inflate(R.layout.input_chip, queriesChipGroup, false) as Chip
chip.text = clip.toString()
chip.chipBackgroundColor = ColorStateList.valueOf(MaterialColors.getColor(requireContext(), R.attr.colorSecondaryContainer, Color.BLACK))
chip.setOnClickListener {
queriesChipGroup!!.removeView(chip)
}
queriesChipGroup!!.addView(chip)
}
if (queriesChipGroup!!.childCount == 0) queriesConstraint.visibility = GONE
else queriesConstraint.visibility = VISIBLE
searchView.editText.setText("")
linkYouCopied.visibility = GONE
}
textView.setOnClickListener { textView.setOnClickListener {
searchView.setText(clip.toString()) searchView.setText(clip.toString())
@ -485,20 +493,23 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
override fun onCardClick(videoURL: String, add: Boolean) { override fun onCardClick(videoURL: String, add: Boolean) {
val item = resultsList?.find { it -> it?.url == videoURL } val item = resultsList?.find { it -> it?.url == videoURL }
if (add) selectedObjects!!.add(item!!) else selectedObjects!!.remove(item) if (add) {
if (selectedObjects!!.size > 1) { selectedObjects!!.add(item!!)
downloadAllFabCoordinator!!.visibility = GONE if (actionMode == null){
downloadFabs!!.visibility = VISIBLE actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
}else{
actionMode!!.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
}
} else { } else {
downloadFabs!!.visibility = GONE selectedObjects!!.remove(item)
if(resultsList!!.size > 1){ actionMode?.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
if (resultsList!![1]!!.playlistTitle.isNotEmpty() && resultsList!![1]!!.playlistTitle != getString(R.string.trendingPlaylist)){ if (selectedObjects!!.isEmpty()){
downloadAllFabCoordinator!!.visibility = VISIBLE actionMode?.finish()
}else{
downloadAllFabCoordinator!!.visibility = GONE
}
} }
} }
} }
override fun onClick(v: View) { override fun onClick(v: View) {
@ -506,19 +517,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
v.tag.toString() v.tag.toString()
} catch (e: Exception) {""} } catch (e: Exception) {""}
if (viewIdName.isNotEmpty()) { if (viewIdName.isNotEmpty()) {
if (viewIdName == "downloadSelected") {
lifecycleScope.launch {
val downloadList = withContext(Dispatchers.IO){
downloadViewModel.turnResultItemsToDownloadItems(selectedObjects!!)
}
if (sharedPreferences!!.getBoolean("download_card", true)) {
val bottomSheet = DownloadMultipleBottomSheetDialog(downloadList.toMutableList())
bottomSheet.show(parentFragmentManager, "downloadMultipleSheet")
} else {
downloadViewModel.queueDownloads(downloadList)
}
}
}
if (viewIdName == "downloadAll") { if (viewIdName == "downloadAll") {
lifecycleScope.launch { lifecycleScope.launch {
val downloadList = withContext(Dispatchers.IO){ val downloadList = withContext(Dispatchers.IO){
@ -535,6 +533,94 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
} }
} }
private val contextualActionBar = object : ActionMode.Callback {
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
mode!!.menuInflater.inflate(R.menu.main_menu_context, menu)
mode.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
(activity as MainActivity).disableBottomNavigation()
searchBar?.isEnabled = false
searchBar!!.menu.forEach { it.isEnabled = false }
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(fragmentContext!!)
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 ->
if (selectedObjects?.size == resultsList?.size){
resultViewModel.deleteAll()
}else{
resultViewModel.deleteSelected(selectedObjects!!.toList())
}
clearCheckedItems()
actionMode?.finish()
}
deleteDialog.show()
true
}
R.id.download -> {
lifecycleScope.launch {
val downloadList = withContext(Dispatchers.IO){
downloadViewModel.turnResultItemsToDownloadItems(selectedObjects!!)
}
clearCheckedItems()
actionMode?.finish()
if (sharedPreferences!!.getBoolean("download_card", true)) {
val bottomSheet = DownloadMultipleBottomSheetDialog(downloadList.toMutableList())
bottomSheet.show(parentFragmentManager, "downloadMultipleSheet")
} else {
downloadViewModel.queueDownloads(downloadList)
}
}
true
}
R.id.select_all -> {
homeAdapter?.checkAll(resultsList)
selectedObjects?.clear()
resultsList?.forEach { selectedObjects?.add(it!!) }
mode?.title = getString(R.string.all_items_selected)
true
}
else -> false
}
}
override fun onDestroyActionMode(mode: ActionMode?) {
actionMode = null
(activity as MainActivity).enableBottomNavigation()
clearCheckedItems()
searchBar?.isEnabled = true
searchBar!!.menu.forEach { it.isEnabled = true }
}
}
private fun clearCheckedItems(){
homeAdapter?.clearCheckedItems()
selectedObjects?.forEach {
homeAdapter?.notifyItemChanged(resultsList!!.indexOf(it))
}
selectedObjects?.clear()
}
override fun onStop() {
actionMode?.finish()
super.onStop()
}
companion object { companion object {
private const val TAG = "HomeFragment" private const val TAG = "HomeFragment"
} }

View file

@ -399,6 +399,10 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
chipGroup.removeView(chip) chipGroup.removeView(chip)
selectedCuts.remove(chip.text.toString()) selectedCuts.remove(chip.text.toString())
listener.onChangeCut(selectedCuts) listener.onChangeCut(selectedCuts)
if (selectedCuts.isEmpty()){
player.stop()
dismiss()
}
} }
deleteDialog.show() deleteDialog.show()
true true
@ -451,6 +455,10 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
chipGroup.removeView(chip) chipGroup.removeView(chip)
selectedCuts.remove(chip.text.toString()) selectedCuts.remove(chip.text.toString())
listener.onChangeCut(selectedCuts) listener.onChangeCut(selectedCuts)
if (selectedCuts.isEmpty()){
player.stop()
dismiss()
}
} }
deleteDialog.show() deleteDialog.show()
true true

View file

@ -155,11 +155,10 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
} }
} }
formatCard.setOnClickListener{_ -> formatCard.setOnClickListener{_ ->
formats.forEach { if (parentFragmentManager.findFragmentByTag("formatSheet") == null){
Log.e("aa", it.toString()) val bottomSheet = FormatSelectionBottomSheetDialog(downloadItem, formats, listener)
bottomSheet.show(parentFragmentManager, "formatSheet")
} }
val bottomSheet = FormatSelectionBottomSheetDialog(downloadItem, formats, listener)
bottomSheet.show(parentFragmentManager, "formatSheet")
} }
@ -265,8 +264,10 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
} }
} }
cut.setOnClickListener { cut.setOnClickListener {
val bottomSheet = CutVideoBottomSheetDialog(downloadItem, cutVideoListener) if (parentFragmentManager.findFragmentByTag("cutVideoSheet") == null){
bottomSheet.show(parentFragmentManager, "cutVideoSheet") val bottomSheet = CutVideoBottomSheetDialog(downloadItem, cutVideoListener)
bottomSheet.show(parentFragmentManager, "cutVideoSheet")
}
} }
}catch (e : Exception){ }catch (e : Exception){

View file

@ -161,8 +161,10 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
} }
} }
formatCard.setOnClickListener{ formatCard.setOnClickListener{
val bottomSheet = FormatSelectionBottomSheetDialog(downloadItem, formats, listener) if (parentFragmentManager.findFragmentByTag("formatSheet") == null){
bottomSheet.show(parentFragmentManager, "formatSheet") val bottomSheet = FormatSelectionBottomSheetDialog(downloadItem, formats, listener)
bottomSheet.show(parentFragmentManager, "formatSheet")
}
} }
container?.isEnabled = true container?.isEnabled = true
@ -295,8 +297,10 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
} }
} }
cut.setOnClickListener { cut.setOnClickListener {
val bottomSheet = CutVideoBottomSheetDialog(downloadItem, cutVideoListener) if (parentFragmentManager.findFragmentByTag("cutVideoSheet") == null){
bottomSheet.show(parentFragmentManager, "cutVideoSheet") val bottomSheet = CutVideoBottomSheetDialog(downloadItem, cutVideoListener)
bottomSheet.show(parentFragmentManager, "cutVideoSheet")
}
} }
} catch (e: Exception) { } catch (e: Exception) {

View file

@ -13,10 +13,15 @@ import android.view.*
import android.view.View.GONE import android.view.View.GONE
import android.view.View.VISIBLE import android.view.View.VISIBLE
import android.widget.* import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.children
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.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
@ -32,6 +37,7 @@ import com.deniscerri.ytdlnis.database.repository.HistoryRepository.HistorySort
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.HistoryViewModel import com.deniscerri.ytdlnis.database.viewmodel.HistoryViewModel
import com.deniscerri.ytdlnis.databinding.FragmentHistoryBinding import com.deniscerri.ytdlnis.databinding.FragmentHistoryBinding
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadMultipleBottomSheetDialog
import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.UiUtil import com.deniscerri.ytdlnis.util.UiUtil
import com.facebook.shimmer.ShimmerFrameLayout import com.facebook.shimmer.ShimmerFrameLayout
@ -43,7 +49,10 @@ import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup import com.google.android.material.chip.ChipGroup
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -72,10 +81,10 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
private var historyList: List<HistoryItem?>? = null private var historyList: List<HistoryItem?>? = null
private var allhistoryList: List<HistoryItem?>? = null private var allhistoryList: List<HistoryItem?>? = null
private var selectedObjects: ArrayList<HistoryItem>? = null private var selectedObjects: ArrayList<HistoryItem>? = null
private var deleteFab: ExtendedFloatingActionButton? = null
private var fileUtil: FileUtil? = null private var fileUtil: FileUtil? = null
private var uiUtil: UiUtil? = null private var uiUtil: UiUtil? = null
private var _binding : FragmentHistoryBinding? = null private var _binding : FragmentHistoryBinding? = null
private var actionMode : ActionMode? = null
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
@ -98,13 +107,8 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
noResults = view.findViewById(R.id.no_results) noResults = view.findViewById(R.id.no_results)
selectionChips = view.findViewById(R.id.history_selection_chips) selectionChips = view.findViewById(R.id.history_selection_chips)
websiteGroup = view.findViewById(R.id.website_chip_group) websiteGroup = view.findViewById(R.id.website_chip_group)
deleteFab = view.findViewById(R.id.delete_selected_fab)
fileUtil = FileUtil() fileUtil = FileUtil()
uiUtil = UiUtil(FileUtil()) uiUtil = UiUtil(FileUtil())
deleteFab?.tag = "deleteSelected"
deleteFab?.setOnClickListener{
removeSelectedItems()
}
uiHandler = Handler(Looper.getMainLooper()) uiHandler = Handler(Looper.getMainLooper())
selectedObjects = ArrayList() selectedObjects = ArrayList()
@ -363,27 +367,6 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
} }
private fun removeSelectedItems() {
if (bottomSheet != null) bottomSheet!!.hide()
val deleteFile = booleanArrayOf(false)
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
deleteDialog.setMultiChoiceItems(
arrayOf(getString(R.string.delete_files_too)),
booleanArrayOf(false)
) { _: DialogInterface?, _: Int, b: Boolean -> deleteFile[0] = b }
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
for (item in selectedObjects!!){
historyViewModel.delete(item, deleteFile[0])
}
selectedObjects = ArrayList()
historyAdapter!!.clearCheckeditems()
deleteFab!!.visibility = GONE
}
deleteDialog.show()
}
private fun removeItem(item: HistoryItem) { private fun removeItem(item: HistoryItem) {
if (bottomSheet != null) bottomSheet!!.hide() if (bottomSheet != null) bottomSheet!!.hide()
val deleteFile = booleanArrayOf(false) val deleteFile = booleanArrayOf(false)
@ -429,7 +412,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
if (isPresent){ if (isPresent){
btn.setOnClickListener { btn.setOnClickListener {
uiUtil!!.shareFileIntent(requireContext(),item.downloadPath) uiUtil!!.shareFileIntent(requireContext(), listOf(item.downloadPath))
} }
} }
@ -514,12 +497,21 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
override fun onCardSelect(itemID: Long, isChecked: Boolean) { override fun onCardSelect(itemID: Long, isChecked: Boolean) {
val item = findItem(itemID) val item = findItem(itemID)
if (isChecked) selectedObjects!!.add(item!!) if (isChecked) {
else selectedObjects!!.remove(item) selectedObjects!!.add(item!!)
if (selectedObjects!!.size > 1) { if (actionMode == null){
deleteFab!!.visibility = VISIBLE actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
} else {
deleteFab!!.visibility = GONE }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()
}
} }
} }
@ -527,6 +519,69 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
return historyList?.find { it?.id == id } return historyList?.find { it?.id == id }
} }
private val contextualActionBar = object : ActionMode.Callback {
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
mode!!.menuInflater.inflate(R.menu.history_menu_context, menu)
mode.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
(activity as MainActivity).disableBottomNavigation()
topAppBar!!.menu.forEach { it.isEnabled = false }
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 deleteFile = booleanArrayOf(false)
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
deleteDialog.setMultiChoiceItems(
arrayOf(getString(R.string.delete_files_too)),
booleanArrayOf(false)
) { _: DialogInterface?, _: Int, b: Boolean -> deleteFile[0] = b }
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
for (obj in selectedObjects!!){
historyViewModel.delete(obj, deleteFile[0])
}
clearCheckedItems()
actionMode?.finish()
}
deleteDialog.show()
true
}
R.id.share -> {
uiUtil?.shareFileIntent(requireContext(), selectedObjects!!.map { it.downloadPath })
clearCheckedItems()
actionMode?.finish()
true
}
else -> false
}
}
override fun onDestroyActionMode(mode: ActionMode?) {
actionMode = null
(activity as MainActivity).enableBottomNavigation()
clearCheckedItems()
topAppBar!!.menu.forEach { it.isEnabled = true }
}
}
private fun clearCheckedItems(){
historyAdapter?.clearCheckeditems()
selectedObjects?.clear()
}
companion object { companion object {
private const val TAG = "historyFragment" private const val TAG = "historyFragment"
} }

View file

@ -644,7 +644,7 @@ class InfoUtil(context: Context) {
} catch (e: Exception) { } catch (e: Exception) {
arrayOf(youtubeDLResponse.out) arrayOf(youtubeDLResponse.out)
} }
return results.toMutableList() return results.filter { it!!.isNotEmpty() }.toMutableList()
} catch (e: Exception) { } catch (e: Exception) {
return mutableListOf() return mutableListOf()
} }

View file

@ -226,19 +226,25 @@ class UiUtil(private val fileUtil: FileUtil) {
fragmentContext.startActivity(i) fragmentContext.startActivity(i)
} }
fun shareFileIntent(fragmentContext: Context, downloadPath: String){ fun shareFileIntent(fragmentContext: Context, paths: List<String>){
val file = File(downloadPath) val uris : ArrayList<Uri> = arrayListOf()
val uri = FileProvider.getUriForFile( paths.forEach {
fragmentContext, val file = File(it)
"com.deniscerri.ytdl.fileprovider", if (! file.exists()) return@forEach
file val uri = FileProvider.getUriForFile(
) fragmentContext,
val mime = fragmentContext.contentResolver.getType(uri) "com.deniscerri.ytdl.fileprovider",
file
)
uris.add(uri)
}
val shareIntent: Intent = Intent().apply { val shareIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND action = Intent.ACTION_SEND_MULTIPLE
putExtra(Intent.EXTRA_STREAM, uri) putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris)
type = mime type = "*/*"
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
} }
fragmentContext.startActivity(Intent.createChooser(shareIntent, null)) fragmentContext.startActivity(Intent.createChooser(shareIntent, null))
} }

View file

@ -135,7 +135,7 @@ class DownloadWorker(
when(type){ when(type){
DownloadViewModel.Type.audio -> { DownloadViewModel.Type.audio -> {
var audioQualityId : String = downloadItem.format.format_id var audioQualityId : String = downloadItem.format.format_id
if (audioQualityId.isBlank() || audioQualityId == "0" || audioQualityId == context.getString(R.string.best_quality)) audioQualityId = "bestaudio" if (audioQualityId.isBlank() || audioQualityId == "0" || audioQualityId == context.getString(R.string.best_quality)) audioQualityId = ""
else if (audioQualityId == context.getString(R.string.worst_quality)) audioQualityId = "worstaudio" else if (audioQualityId == context.getString(R.string.worst_quality)) audioQualityId = "worstaudio"
if (audioQualityId.isNotBlank()){ if (audioQualityId.isNotBlank()){
@ -157,7 +157,7 @@ class DownloadWorker(
request.addOption("-x") request.addOption("-x")
}else{ }else{
request.addOption("--remux-video", ext) request.addOption("--remux-video", ext)
request.addOption("--ppa", "VideoRemuxer:-c:a $codec") request.addOption("--ppa", "VideoRemuxer:-vn -c:a $codec")
} }
} }

View file

@ -0,0 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="?android:colorAccent" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="?android:colorAccent" android:pathData="M5,20h14v-2H5V20zM19,9h-4V3H9v6H5l7,7L19,9z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="?android:colorAccent" android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
</vector>

View file

@ -92,7 +92,7 @@
style="@style/Widget.Material3.Button.TextButton.Icon" style="@style/Widget.Material3.Button.TextButton.Icon"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp" android:layout_margin="10dp"
android:singleLine="true" android:singleLine="true"
android:text="@string/app_name" android:text="@string/app_name"
android:textSize="15sp" android:textSize="15sp"

View file

@ -111,7 +111,7 @@
style="@style/Widget.Material3.Button.TextButton.Icon" style="@style/Widget.Material3.Button.TextButton.Icon"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp" android:layout_margin="10dp"
android:singleLine="true" android:singleLine="true"
android:text="@string/app_name" android:text="@string/app_name"
android:textSize="15sp" android:textSize="15sp"

View file

@ -125,25 +125,6 @@
</androidx.recyclerview.widget.RecyclerView> </androidx.recyclerview.widget.RecyclerView>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/delete_selected_coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/delete_selected_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:visibility="gone"
android:layout_gravity="bottom|end"
android:text="@string/delete_selected"
app:icon="@drawable/ic_delete_all"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<include layout="@layout/history_no_results" <include layout="@layout/history_no_results"
android:visibility="gone" /> android:visibility="gone" />

View file

@ -0,0 +1,19 @@
<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/share"
android:icon="@drawable/baseline_share_24"
app:showAsAction="ifRoom"
android:title="@string/share" />
</menu>

View file

@ -0,0 +1,24 @@
<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/download"
android:title="@string/download"
android:icon="@drawable/baseline_download_24"
app:showAsAction="ifRoom" />
<item
android:id="@+id/select_all"
android:title="@string/select_all"
app:showAsAction="never" />
</menu>

View file

@ -195,4 +195,6 @@
<string name="add_template_first">You need to have at least one command template created!</string> <string name="add_template_first">You need to have at least one command template created!</string>
<string name="formatid_copied_to_clipboard">Format ID copied to clipboard</string> <string name="formatid_copied_to_clipboard">Format ID copied to clipboard</string>
<string name="suggested">Suggested</string> <string name="suggested">Suggested</string>
<string name="select_all">Select All</string>
<string name="share">Share</string>
</resources> </resources>