diff --git a/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.kt b/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.kt index 0490d26f..14ec2706 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.kt @@ -19,6 +19,7 @@ import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat import androidx.core.view.WindowInsetsCompat +import androidx.core.view.forEach import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.NavHostFragment @@ -110,6 +111,14 @@ class MainActivity : AppCompatActivity() { bottomNav.visibility = View.VISIBLE } + fun disableBottomNavigation(){ + bottomNav.menu.forEach { it.isEnabled = false } + } + + fun enableBottomNavigation(){ + bottomNav.menu.forEach { it.isEnabled = true } + } + override fun onResume() { super.onResume() val incognitoHeader = findViewById(R.id.incognito_header) diff --git a/app/src/main/java/com/deniscerri/ytdlnis/adapter/HomeAdapter.kt b/app/src/main/java/com/deniscerri/ytdlnis/adapter/HomeAdapter.kt index d9f6e0ae..11205185 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/adapter/HomeAdapter.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/adapter/HomeAdapter.kt @@ -167,13 +167,14 @@ class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity) fun onCardClick(videoURL: String, add: Boolean) } - fun clearCheckedVideos() { -// val size = checkedVideos.size -// for (i in 0 until size) { -// val position = checkedVideos[i] -// notifyItemChanged(position) -// } -// checkedVideos.clear() + fun checkAll(items: List?){ + checkedVideos.clear() + checkedVideos.addAll(items!!.map { it!!.url }) + notifyDataSetChanged() + } + + fun clearCheckedItems(){ + checkedVideos.clear() } companion object { diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ResultDao.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ResultDao.kt index 414ac66e..22df2c74 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ResultDao.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ResultDao.kt @@ -27,6 +27,9 @@ interface ResultDao { @Query("DELETE FROM results") suspend fun deleteAll() + @Query("DELETE FROM results WHERE id=:id") + suspend fun delete(id: Long) + @Query("SELECT * FROM results WHERE url=:url LIMIT 1") fun getResultByURL(url: String) : ResultItem diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt index 92e180be..3bd82a56 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt @@ -108,6 +108,9 @@ class ResultRepository(private val resultDao: ResultDao, private val commandTemp return arrayListOf() } + suspend fun delete(item: ResultItem){ + resultDao.delete(item.id) + } suspend fun deleteAll(){ itemCount.postValue(0) diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/HistoryViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/HistoryViewModel.kt index 41344ab6..1c85d674 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/HistoryViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/HistoryViewModel.kt @@ -4,6 +4,7 @@ import android.app.Application import androidx.lifecycle.* import com.deniscerri.ytdlnis.database.DBManager 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.HistorySort import com.deniscerri.ytdlnis.database.repository.HistoryRepository.HistorySortType @@ -103,4 +104,5 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application) fun clearDeleted() = viewModelScope.launch(Dispatchers.IO) { repository.clearDeletedHistory() } + } \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt index d0f6c3da..25ef0b47 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt @@ -150,4 +150,10 @@ class ResultViewModel(application: Application) : AndroidViewModel(application) fun getSearchHistory() : List { return searchHistoryRepository.getAll() } + + fun deleteSelected(selectedItems : List) = viewModelScope.launch(Dispatchers.IO) { + selectedItems.forEach { + repository.delete(it) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt index 4348e8e8..034a22e1 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt @@ -10,16 +10,14 @@ import android.graphics.Color import android.os.Bundle import android.os.Handler import android.os.Looper -import android.text.InputType import android.util.Log import android.view.* import android.view.View.* import android.widget.* +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.view.ActionMode import androidx.constraintlayout.widget.ConstraintLayout 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.forEach import androidx.core.widget.addTextChangedListener @@ -29,7 +27,6 @@ import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import androidx.work.WorkManager import com.deniscerri.ytdlnis.MainActivity import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.adapter.HomeAdapter @@ -58,9 +55,7 @@ import java.util.* class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickListener { - private var inputQuery: String? = null private var inputQueries: MutableList? = null - private var inputQueriesLength = 0 private var homeAdapter: HomeAdapter? = 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 downloadViewModel : DownloadViewModel - private var downloading = false private var fragmentView: View? = null private var activity: Activity? = null private var mainActivity: MainActivity? = null @@ -94,9 +88,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi private var quickLaunchSheet = false private var sharedPreferences: SharedPreferences? = null private var _binding : FragmentHomeBinding? = null - - private var workManager: WorkManager? = null - + private var actionMode: ActionMode? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -106,7 +98,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi activity = getActivity() mainActivity = activity as MainActivity? quickLaunchSheet = false - return fragmentView } @@ -233,7 +224,24 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi textView.text = getString(R.string.link_you_copied) textView.setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_language, 0, 0, 0) val mb = linkYouCopied.findViewById(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 { searchView.setText(clip.toString()) @@ -485,20 +493,23 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi override fun onCardClick(videoURL: String, add: Boolean) { val item = resultsList?.find { it -> it?.url == videoURL } - if (add) selectedObjects!!.add(item!!) else selectedObjects!!.remove(item) - if (selectedObjects!!.size > 1) { - downloadAllFabCoordinator!!.visibility = GONE - downloadFabs!!.visibility = VISIBLE + if (add) { + selectedObjects!!.add(item!!) + if (actionMode == null){ + actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar) + + }else{ + actionMode!!.title = "${selectedObjects!!.size} ${getString(R.string.selected)}" + } } else { - downloadFabs!!.visibility = GONE - if(resultsList!!.size > 1){ - if (resultsList!![1]!!.playlistTitle.isNotEmpty() && resultsList!![1]!!.playlistTitle != getString(R.string.trendingPlaylist)){ - downloadAllFabCoordinator!!.visibility = VISIBLE - }else{ - downloadAllFabCoordinator!!.visibility = GONE - } + selectedObjects!!.remove(item) + actionMode?.title = "${selectedObjects!!.size} ${getString(R.string.selected)}" + if (selectedObjects!!.isEmpty()){ + actionMode?.finish() } } + + } override fun onClick(v: View) { @@ -506,19 +517,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi v.tag.toString() } catch (e: Exception) {""} 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") { lifecycleScope.launch { 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 { private const val TAG = "HomeFragment" } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/CutVideoBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/CutVideoBottomSheetDialog.kt index e5ba7044..7721f659 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/CutVideoBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/CutVideoBottomSheetDialog.kt @@ -399,6 +399,10 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list chipGroup.removeView(chip) selectedCuts.remove(chip.text.toString()) listener.onChangeCut(selectedCuts) + if (selectedCuts.isEmpty()){ + player.stop() + dismiss() + } } deleteDialog.show() true @@ -451,6 +455,10 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list chipGroup.removeView(chip) selectedCuts.remove(chip.text.toString()) listener.onChangeCut(selectedCuts) + if (selectedCuts.isEmpty()){ + player.stop() + dismiss() + } } deleteDialog.show() true diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt index 33354367..39e7af02 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt @@ -155,11 +155,10 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr } } formatCard.setOnClickListener{_ -> - formats.forEach { - Log.e("aa", it.toString()) + if (parentFragmentManager.findFragmentByTag("formatSheet") == null){ + 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 { - val bottomSheet = CutVideoBottomSheetDialog(downloadItem, cutVideoListener) - bottomSheet.show(parentFragmentManager, "cutVideoSheet") + if (parentFragmentManager.findFragmentByTag("cutVideoSheet") == null){ + val bottomSheet = CutVideoBottomSheetDialog(downloadItem, cutVideoListener) + bottomSheet.show(parentFragmentManager, "cutVideoSheet") + } } }catch (e : Exception){ diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt index 4e3d7f3f..2ed0765e 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt @@ -161,8 +161,10 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr } } formatCard.setOnClickListener{ - val bottomSheet = FormatSelectionBottomSheetDialog(downloadItem, formats, listener) - bottomSheet.show(parentFragmentManager, "formatSheet") + if (parentFragmentManager.findFragmentByTag("formatSheet") == null){ + val bottomSheet = FormatSelectionBottomSheetDialog(downloadItem, formats, listener) + bottomSheet.show(parentFragmentManager, "formatSheet") + } } container?.isEnabled = true @@ -295,8 +297,10 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr } } cut.setOnClickListener { - val bottomSheet = CutVideoBottomSheetDialog(downloadItem, cutVideoListener) - bottomSheet.show(parentFragmentManager, "cutVideoSheet") + if (parentFragmentManager.findFragmentByTag("cutVideoSheet") == null){ + val bottomSheet = CutVideoBottomSheetDialog(downloadItem, cutVideoListener) + bottomSheet.show(parentFragmentManager, "cutVideoSheet") + } } } catch (e: Exception) { diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/HistoryFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/HistoryFragment.kt index 0f8db793..9d1962a1 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/HistoryFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/HistoryFragment.kt @@ -13,10 +13,15 @@ import android.view.* import android.view.View.GONE import android.view.View.VISIBLE import android.widget.* +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.view.ActionMode import androidx.appcompat.widget.SearchView import androidx.core.content.ContextCompat +import androidx.core.view.children +import androidx.core.view.forEach import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.navigation.ui.AppBarConfiguration 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.HistoryViewModel import com.deniscerri.ytdlnis.databinding.FragmentHistoryBinding +import com.deniscerri.ytdlnis.ui.downloadcard.DownloadMultipleBottomSheetDialog import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.UiUtil 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.dialog.MaterialAlertDialogBuilder import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext import java.io.File import java.text.SimpleDateFormat import java.util.* @@ -72,10 +81,10 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{ private var historyList: List? = null private var allhistoryList: List? = null private var selectedObjects: ArrayList? = null - private var deleteFab: ExtendedFloatingActionButton? = null private var fileUtil: FileUtil? = null private var uiUtil: UiUtil? = null private var _binding : FragmentHistoryBinding? = null + private var actionMode : ActionMode? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -98,13 +107,8 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{ noResults = view.findViewById(R.id.no_results) selectionChips = view.findViewById(R.id.history_selection_chips) websiteGroup = view.findViewById(R.id.website_chip_group) - deleteFab = view.findViewById(R.id.delete_selected_fab) fileUtil = FileUtil() uiUtil = UiUtil(FileUtil()) - deleteFab?.tag = "deleteSelected" - deleteFab?.setOnClickListener{ - removeSelectedItems() - } uiHandler = Handler(Looper.getMainLooper()) 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) { if (bottomSheet != null) bottomSheet!!.hide() val deleteFile = booleanArrayOf(false) @@ -429,7 +412,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{ if (isPresent){ 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) { val item = findItem(itemID) - if (isChecked) selectedObjects!!.add(item!!) - else selectedObjects!!.remove(item) - if (selectedObjects!!.size > 1) { - deleteFab!!.visibility = VISIBLE - } else { - deleteFab!!.visibility = GONE + 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() + } } } @@ -527,6 +519,69 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{ 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 { private const val TAG = "historyFragment" } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt b/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt index 54cf3810..6d18728c 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt @@ -644,7 +644,7 @@ class InfoUtil(context: Context) { } catch (e: Exception) { arrayOf(youtubeDLResponse.out) } - return results.toMutableList() + return results.filter { it!!.isNotEmpty() }.toMutableList() } catch (e: Exception) { return mutableListOf() } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/util/UiUtil.kt b/app/src/main/java/com/deniscerri/ytdlnis/util/UiUtil.kt index e201f6a4..cff71034 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/util/UiUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/util/UiUtil.kt @@ -226,19 +226,25 @@ class UiUtil(private val fileUtil: FileUtil) { fragmentContext.startActivity(i) } - fun shareFileIntent(fragmentContext: Context, downloadPath: String){ - val file = File(downloadPath) - val uri = FileProvider.getUriForFile( - fragmentContext, - "com.deniscerri.ytdl.fileprovider", - file - ) - val mime = fragmentContext.contentResolver.getType(uri) + fun shareFileIntent(fragmentContext: Context, paths: List){ + val uris : ArrayList = arrayListOf() + paths.forEach { + val file = File(it) + if (! file.exists()) return@forEach + val uri = FileProvider.getUriForFile( + fragmentContext, + "com.deniscerri.ytdl.fileprovider", + file + ) + uris.add(uri) + } + val shareIntent: Intent = Intent().apply { - action = Intent.ACTION_SEND - putExtra(Intent.EXTRA_STREAM, uri) - type = mime + action = Intent.ACTION_SEND_MULTIPLE + putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris) + type = "*/*" + putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) } fragmentContext.startActivity(Intent.createChooser(shareIntent, null)) } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt b/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt index 357c9091..d285eefd 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt @@ -135,7 +135,7 @@ class DownloadWorker( when(type){ DownloadViewModel.Type.audio -> { 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" if (audioQualityId.isNotBlank()){ @@ -157,7 +157,7 @@ class DownloadWorker( request.addOption("-x") }else{ request.addOption("--remux-video", ext) - request.addOption("--ppa", "VideoRemuxer:-c:a $codec") + request.addOption("--ppa", "VideoRemuxer:-vn -c:a $codec") } } diff --git a/app/src/main/res/drawable/baseline_delete_24.xml b/app/src/main/res/drawable/baseline_delete_24.xml new file mode 100644 index 00000000..1a93522f --- /dev/null +++ b/app/src/main/res/drawable/baseline_delete_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/baseline_download_24.xml b/app/src/main/res/drawable/baseline_download_24.xml new file mode 100644 index 00000000..e8d7c46d --- /dev/null +++ b/app/src/main/res/drawable/baseline_download_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/baseline_share_24.xml b/app/src/main/res/drawable/baseline_share_24.xml new file mode 100644 index 00000000..66aa3204 --- /dev/null +++ b/app/src/main/res/drawable/baseline_share_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/configure_download_bottom_sheet.xml b/app/src/main/res/layout/configure_download_bottom_sheet.xml index 6fc891fd..cee1f511 100644 --- a/app/src/main/res/layout/configure_download_bottom_sheet.xml +++ b/app/src/main/res/layout/configure_download_bottom_sheet.xml @@ -92,7 +92,7 @@ style="@style/Widget.Material3.Button.TextButton.Icon" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginHorizontal="10dp" + android:layout_margin="10dp" android:singleLine="true" android:text="@string/app_name" android:textSize="15sp" diff --git a/app/src/main/res/layout/download_bottom_sheet.xml b/app/src/main/res/layout/download_bottom_sheet.xml index 3cd47261..6ae34a10 100644 --- a/app/src/main/res/layout/download_bottom_sheet.xml +++ b/app/src/main/res/layout/download_bottom_sheet.xml @@ -111,7 +111,7 @@ style="@style/Widget.Material3.Button.TextButton.Icon" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginHorizontal="10dp" + android:layout_margin="10dp" android:singleLine="true" android:text="@string/app_name" android:textSize="15sp" diff --git a/app/src/main/res/layout/fragment_history.xml b/app/src/main/res/layout/fragment_history.xml index 09cb3da1..0a2b278b 100644 --- a/app/src/main/res/layout/fragment_history.xml +++ b/app/src/main/res/layout/fragment_history.xml @@ -125,25 +125,6 @@ - - - - - - - - diff --git a/app/src/main/res/menu/history_menu_context.xml b/app/src/main/res/menu/history_menu_context.xml new file mode 100644 index 00000000..c7505de2 --- /dev/null +++ b/app/src/main/res/menu/history_menu_context.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/app/src/main/res/menu/main_menu_context.xml b/app/src/main/res/menu/main_menu_context.xml new file mode 100644 index 00000000..3b227bea --- /dev/null +++ b/app/src/main/res/menu/main_menu_context.xml @@ -0,0 +1,24 @@ + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c1d248da..42596584 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -195,4 +195,6 @@ You need to have at least one command template created! Format ID copied to clipboard Suggested + Select All + Share \ No newline at end of file