add function to modify selected items in multiple download card
This commit is contained in:
parent
ffc4ded5af
commit
ece2cdf631
17 changed files with 751 additions and 131 deletions
|
|
@ -54,11 +54,16 @@ interface DownloadDao {
|
|||
""")
|
||||
fun getProcessingDownloadTypes() : List<String>
|
||||
|
||||
@Query("""
|
||||
SELECT DISTINCT container from downloads where status = 'Processing'
|
||||
""")
|
||||
|
||||
@Query("SELECT DISTINCT type from downloads where status = 'Processing' and id in (:ids)")
|
||||
fun getProcessingDownloadTypesByIDs(ids: List<Long>) : List<String>
|
||||
|
||||
@Query("""SELECT DISTINCT container from downloads where status = 'Processing'""")
|
||||
fun getProcessingDownloadContainers() : List<String>
|
||||
|
||||
@Query("""SELECT DISTINCT container from downloads where id in (:ids)""")
|
||||
fun getDownloadContainersByIDs(ids: List<Long>) : List<String>
|
||||
|
||||
|
||||
@Query("UPDATE downloads set status = 'Processing' WHERE id in (:ids)")
|
||||
suspend fun updateItemsToProcessing(ids: List<Long>)
|
||||
|
|
@ -74,9 +79,15 @@ interface DownloadDao {
|
|||
@Query("UPDATE downloads set downloadPath=:path WHERE status ='Processing'")
|
||||
suspend fun updateProcessingDownloadPath(path: String)
|
||||
|
||||
@Query("UPDATE downloads set downloadPath=:path WHERE id in (:ids)")
|
||||
suspend fun updateDownloadPathByIDs(ids: List<Long>, path: String)
|
||||
|
||||
@Query("UPDATE downloads set container=:cont WHERE status ='Processing'")
|
||||
suspend fun updateProcessingContainer(cont: String)
|
||||
|
||||
@Query("UPDATE downloads set container=:cont WHERE id in (:ids)")
|
||||
suspend fun updateContainerByIds(ids: List<Long>, cont: String)
|
||||
|
||||
@Query("SELECT * FROM downloads WHERE status='Active'")
|
||||
fun getActiveDownloadsList() : List<DownloadItem>
|
||||
|
||||
|
|
@ -356,6 +367,12 @@ interface DownloadDao {
|
|||
@Query("UPDATE downloads set incognito=:incognito WHERE status='Processing'")
|
||||
suspend fun updateProcessingIncognito(incognito: Boolean)
|
||||
|
||||
@Query("UPDATE downloads set incognito=:incognito WHERE id in (:ids)")
|
||||
suspend fun updateIncognitoByIDs(incognito: Boolean, ids: List<Long>)
|
||||
|
||||
@Query("SELECT COUNT(id) FROM downloads WHERE status='Processing' AND incognito='1'")
|
||||
fun getProcessingAsIncognitoCount(): Int
|
||||
|
||||
@Query("SELECT COUNT(id) FROM downloads WHERE status='Processing' AND incognito='1' and id in (:ids)")
|
||||
fun getProcessingAsIncognitoCountByIDs(ids: List<Long>): Int
|
||||
}
|
||||
|
|
@ -140,7 +140,7 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
|||
downloadDao.deleteProcessingByUrl(url)
|
||||
}
|
||||
|
||||
fun getProcessingDownloads() : List<DownloadItem> {
|
||||
fun getAllProcessingDownloads() : List<DownloadItem> {
|
||||
return downloadDao.getProcessingDownloadsList()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -890,7 +890,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
|||
}
|
||||
|
||||
suspend fun queueProcessingDownloads() : QueueDownloadsResult {
|
||||
val processingItems = repository.getProcessingDownloads()
|
||||
val processingItems = repository.getAllProcessingDownloads()
|
||||
return queueDownloads(processingItems)
|
||||
}
|
||||
|
||||
|
|
@ -1087,13 +1087,22 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
|||
return dbManager.downloadDao.getDownloadIDsNotPresentInList(items.ifEmpty { listOf(-1L) }, status.map { it.toString() })
|
||||
}
|
||||
|
||||
suspend fun moveProcessingToSavedCategory(){
|
||||
dao.updateProcessingtoSavedStatus()
|
||||
suspend fun moveProcessingToSavedCategory(selectedItems: List<Long>?){
|
||||
if (selectedItems.isNullOrEmpty()) {
|
||||
dao.updateProcessingtoSavedStatus()
|
||||
}else {
|
||||
repository.setDownloadStatusMultiple(selectedItems, DownloadRepository.Status.Saved)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun updateAllProcessingFormats(formatTuples : List<MultipleItemFormatTuple>) = viewModelScope.launch(Dispatchers.IO) {
|
||||
val items = repository.getProcessingDownloads()
|
||||
fun updateAllProcessingFormats(selectedItems: List<Long>?, formatTuples : List<MultipleItemFormatTuple>) = viewModelScope.launch(Dispatchers.IO) {
|
||||
val items = if (selectedItems.isNullOrEmpty()) {
|
||||
repository.getAllProcessingDownloads()
|
||||
}else {
|
||||
repository.getAllItemsByIDs(selectedItems)
|
||||
}
|
||||
|
||||
items.forEach {
|
||||
val ft = formatTuples.first { ft -> ft.url == it.url }.formatTuple
|
||||
ft.format?.apply {
|
||||
|
|
@ -1112,28 +1121,48 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
|||
|
||||
}
|
||||
|
||||
suspend fun updateProcessingCommandFormat(format: Format){
|
||||
val items = repository.getProcessingDownloads()
|
||||
suspend fun updateProcessingCommandFormat(selectedItems: List<Long>?, format: Format){
|
||||
val items = if (selectedItems.isNullOrEmpty()) {
|
||||
repository.getAllProcessingDownloads()
|
||||
}else {
|
||||
repository.getAllItemsByIDs(selectedItems)
|
||||
}
|
||||
|
||||
items.forEach {
|
||||
it.format = format
|
||||
repository.update(it)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateProcessingContainer(cont: String) {
|
||||
suspend fun updateProcessingContainer(checkedItems: List<Long>?, cont: String) {
|
||||
var container = ""
|
||||
if (cont != resources.getString(R.string.defaultValue)) {
|
||||
container = cont
|
||||
}
|
||||
dao.updateProcessingContainer(container)
|
||||
|
||||
if (checkedItems.isNullOrEmpty()) {
|
||||
dao.updateProcessingContainer(container)
|
||||
}else {
|
||||
dao.updateContainerByIds(checkedItems, container)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
suspend fun updateProcessingDownloadPath(path: String){
|
||||
dao.updateProcessingDownloadPath(path)
|
||||
suspend fun updateProcessingDownloadPath(selectedItems: List<Long>?, path: String){
|
||||
if (selectedItems.isNullOrEmpty()) {
|
||||
dao.updateProcessingDownloadPath(path)
|
||||
}else {
|
||||
dao.updateDownloadPathByIDs(selectedItems, path)
|
||||
}
|
||||
}
|
||||
|
||||
fun getProcessingDownloads() : List<DownloadItem> {
|
||||
return repository.getProcessingDownloads()
|
||||
fun getProcessingDownloads(checkedItems: List<Long>?) : List<DownloadItem> {
|
||||
return if (checkedItems.isNullOrEmpty()) {
|
||||
repository.getAllProcessingDownloads()
|
||||
}else {
|
||||
repository.getAllItemsByIDs(checkedItems)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun updateDownloadItemFormats(id: Long, list: List<Format>) = viewModelScope.launch(Dispatchers.IO) {
|
||||
|
|
@ -1174,9 +1203,14 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
|||
resultRepository.deleteByUrl(url)
|
||||
}
|
||||
|
||||
suspend fun continueUpdatingFormatsOnBackground(){
|
||||
val ids = repository.getProcessingDownloads().map { it.id }
|
||||
dao.updateProcessingtoSavedStatus()
|
||||
suspend fun continueUpdatingFormatsOnBackground(selectedItems: List<Long>?){
|
||||
val ids = if (selectedItems.isNullOrEmpty()) {
|
||||
repository.getAllProcessingDownloads().map { it.id }
|
||||
}else {
|
||||
selectedItems
|
||||
}
|
||||
|
||||
moveProcessingToSavedCategory(selectedItems)
|
||||
|
||||
val id = System.currentTimeMillis().toInt()
|
||||
val workRequest = OneTimeWorkRequestBuilder<UpdateMultipleDownloadsFormatsWorker>()
|
||||
|
|
@ -1213,8 +1247,13 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
|||
|
||||
}
|
||||
|
||||
suspend fun updateProcessingType(newType: Type) {
|
||||
val processing = repository.getProcessingDownloads()
|
||||
suspend fun updateProcessingType(selectedItems: List<Long>?, newType: Type) {
|
||||
val processing = if (selectedItems.isNullOrEmpty()) {
|
||||
repository.getAllProcessingDownloads()
|
||||
}else{
|
||||
repository.getAllItemsByIDs(selectedItems)
|
||||
}
|
||||
|
||||
processing.apply {
|
||||
val new = switchDownloadType(this, newType)
|
||||
new.forEach {
|
||||
|
|
@ -1224,7 +1263,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
|||
}
|
||||
|
||||
suspend fun updateProcessingDownloadTimeAndQueueScheduled(time: Long) : QueueDownloadsResult {
|
||||
val processing = repository.getProcessingDownloads()
|
||||
val processing = repository.getAllProcessingDownloads()
|
||||
processing.forEach {
|
||||
it.downloadStartTime = time
|
||||
it.status = DownloadRepository.Status.Scheduled.toString()
|
||||
|
|
@ -1232,8 +1271,13 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
|||
return queueDownloads(processing)
|
||||
}
|
||||
|
||||
fun checkIfAllProcessingItemsHaveSameType() : Pair<Boolean, Type> {
|
||||
val types = dao.getProcessingDownloadTypes()
|
||||
fun checkIfAllProcessingItemsHaveSameType(selectedItems: List<Long>?) : Pair<Boolean, Type> {
|
||||
val types = if (!selectedItems.isNullOrEmpty()) {
|
||||
dao.getProcessingDownloadTypesByIDs(selectedItems)
|
||||
}else {
|
||||
dao.getProcessingDownloadTypes()
|
||||
}
|
||||
|
||||
if (types.isEmpty()) {
|
||||
return Pair(false, Type.command)
|
||||
}
|
||||
|
|
@ -1241,8 +1285,13 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
|||
return Pair(types.size == 1, Type.valueOf(types.first()))
|
||||
}
|
||||
|
||||
fun checkIfAllProcessingItemsHaveSameContainer() : Pair<Boolean, String> {
|
||||
val containers = dao.getProcessingDownloadContainers()
|
||||
fun checkIfAllProcessingItemsHaveSameContainer(checkedItems: List<Long>?) : Pair<Boolean, String> {
|
||||
val containers = if (checkedItems.isNullOrEmpty()) {
|
||||
dao.getProcessingDownloadContainers()
|
||||
}else {
|
||||
dao.getDownloadContainersByIDs(checkedItems)
|
||||
}
|
||||
|
||||
return Pair(containers.size == 1, containers.first())
|
||||
}
|
||||
|
||||
|
|
@ -1277,12 +1326,20 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
|||
return dao.getScheduledIDsBetweenTwoItems(item1, item2)
|
||||
}
|
||||
|
||||
suspend fun updateProcessingIncognito(incognito: Boolean) {
|
||||
dao.updateProcessingIncognito(incognito)
|
||||
suspend fun updateProcessingIncognito(selectedItems: List<Long>?, incognito: Boolean) {
|
||||
if (selectedItems.isNullOrEmpty()) {
|
||||
dao.updateProcessingIncognito(incognito)
|
||||
}else {
|
||||
dao.updateIncognitoByIDs(incognito, selectedItems)
|
||||
}
|
||||
}
|
||||
|
||||
fun areAllProcessingIncognito() : Boolean {
|
||||
return dao.getProcessingAsIncognitoCount() > 0
|
||||
fun areAllProcessingIncognito(selectedItems: List<Long>?) : Boolean {
|
||||
return if (selectedItems.isNullOrEmpty()) {
|
||||
dao.getProcessingAsIncognitoCount() > 0
|
||||
}else {
|
||||
dao.getProcessingAsIncognitoCountByIDs(selectedItems) > 0
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelDownloadOnly(id : Long) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.deniscerri.ytdl.ui.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Handler
|
||||
|
|
@ -7,6 +8,7 @@ import android.os.Looper
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CheckBox
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
|
|
@ -32,6 +34,10 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener
|
|||
private val onItemClickListener: OnItemClickListener
|
||||
private val activity: Activity
|
||||
private val sharedPreferences : SharedPreferences
|
||||
private var checkedItems: MutableSet<Long> = mutableSetOf()
|
||||
private var currentItems: Set<Long> = setOf()
|
||||
private var _isCheckingItems: Boolean = false
|
||||
private var inverted: Boolean = false
|
||||
|
||||
init {
|
||||
this.onItemClickListener = onItemClickListener
|
||||
|
|
@ -53,6 +59,69 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener
|
|||
return ViewHolder(cardView)
|
||||
}
|
||||
|
||||
fun isCheckingItems() : Boolean {
|
||||
return _isCheckingItems
|
||||
}
|
||||
|
||||
fun getCheckedItemsOrNull(): List<Long>? {
|
||||
val res = if (inverted) {
|
||||
currentItems.filter { !checkedItems.contains(it) }
|
||||
}else {
|
||||
checkedItems
|
||||
}
|
||||
|
||||
return res.toList().ifEmpty { null }
|
||||
}
|
||||
|
||||
fun getCheckedItemsSize() : Int {
|
||||
return if (inverted){
|
||||
currentItems.size - checkedItems.size
|
||||
}else{
|
||||
checkedItems.size
|
||||
}
|
||||
}
|
||||
|
||||
fun removeItemsFromCheckList(ids: List<Long>) {
|
||||
checkedItems.removeAll(ids.toSet())
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun clearCheckedItems() {
|
||||
_isCheckingItems = false
|
||||
inverted = false
|
||||
checkedItems = mutableSetOf()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun selectItems(ids: List<Long>) {
|
||||
checkedItems = mutableSetOf()
|
||||
inverted = false
|
||||
checkedItems.addAll(ids)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun checkAll() {
|
||||
checkedItems = mutableSetOf()
|
||||
inverted = true
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun invertSelected() {
|
||||
inverted = !inverted
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun initCheckingItems(itemIDs: List<Long>) {
|
||||
currentItems = itemIDs.toSet()
|
||||
_isCheckingItems = true
|
||||
checkedItems = mutableSetOf()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val item = getItem(position)
|
||||
val card = holder.cardView
|
||||
|
|
@ -162,18 +231,48 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener
|
|||
}
|
||||
}
|
||||
|
||||
val checkbox = card.findViewById<CheckBox>(R.id.checkBox)
|
||||
checkbox.isVisible = _isCheckingItems
|
||||
checkbox.isChecked = (checkedItems.contains(item.id) && !inverted) || (inverted && !checkedItems.contains(item.id))
|
||||
checkbox.setOnClickListener {
|
||||
if (checkbox.isChecked) {
|
||||
if (inverted) checkedItems.remove(item.id)
|
||||
else checkedItems.add(item.id)
|
||||
onItemClickListener.onCardChecked(item.id)
|
||||
}else {
|
||||
if (inverted) checkedItems.add(item.id)
|
||||
else checkedItems.remove(item.id)
|
||||
onItemClickListener.onCardUnChecked(item.id)
|
||||
}
|
||||
}
|
||||
|
||||
val index = card.findViewById<TextView>(R.id.index)
|
||||
index.isVisible = _isCheckingItems
|
||||
index.text = (position + 1).toString()
|
||||
|
||||
card.setOnClickListener {
|
||||
onItemClickListener.onCardClick(item.id)
|
||||
if (_isCheckingItems) {
|
||||
checkbox.performClick()
|
||||
}else {
|
||||
onItemClickListener.onCardClick(item.id)
|
||||
}
|
||||
}
|
||||
|
||||
card.setOnLongClickListener {
|
||||
onItemClickListener.onDelete(item.id); true
|
||||
if (_isCheckingItems) {
|
||||
checkbox.performClick()
|
||||
}else{
|
||||
onItemClickListener.onDelete(item.id)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
interface OnItemClickListener {
|
||||
fun onButtonClick(id: Long)
|
||||
fun onCardClick(id: Long)
|
||||
fun onCardChecked(id: Long)
|
||||
fun onCardUnChecked(id: Long)
|
||||
fun onDelete(id: Long)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import android.content.SharedPreferences
|
|||
import android.content.res.Configuration
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.LayoutInflater
|
||||
|
|
@ -16,14 +17,18 @@ import android.view.MenuItem
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.Window
|
||||
import android.widget.CheckBox
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.get
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.setPadding
|
||||
import androidx.core.view.updatePadding
|
||||
|
|
@ -59,6 +64,7 @@ import com.google.android.material.button.MaterialButton
|
|||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.elevation.SurfaceColors
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
|
||||
import kotlinx.coroutines.CancellationException
|
||||
|
|
@ -68,6 +74,7 @@ import kotlinx.coroutines.delay
|
|||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.math.abs
|
||||
|
||||
class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), ConfigureMultipleDownloadsAdapter.OnItemClickListener, View.OnClickListener,
|
||||
ConfigureDownloadBottomSheetDialog.OnDownloadItemUpdateListener {
|
||||
|
|
@ -81,6 +88,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
private lateinit var behavior: BottomSheetBehavior<View>
|
||||
private lateinit var bottomAppBar: BottomAppBar
|
||||
private lateinit var filesize : TextView
|
||||
private var itemsFileSize: Long = 0L
|
||||
private lateinit var count : TextView
|
||||
private lateinit var downloadBtn : MaterialButton
|
||||
private lateinit var scheduleBtn : MaterialButton
|
||||
|
|
@ -98,6 +106,10 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
private lateinit var containerBtn : MenuItem
|
||||
private lateinit var containerTextView: TextView
|
||||
|
||||
private lateinit var multipleSelectHeader: ConstraintLayout
|
||||
private lateinit var selectItemsMenuBtn: MaterialButton
|
||||
private lateinit var selectRangeBtn: MaterialButton
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
downloadViewModel = ViewModelProvider(requireActivity())[DownloadViewModel::class.java]
|
||||
|
|
@ -182,28 +194,51 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
lifecycleScope.launch {
|
||||
downloadViewModel.processingDownloads.collectLatest { items ->
|
||||
processingItemsCount = items.size
|
||||
currentDownloadIDs = items.map { it.id }
|
||||
count.text = "$processingItemsCount ${getString(R.string.selected)}"
|
||||
listAdapter.submitList(items)
|
||||
|
||||
updateFileSize(items)
|
||||
|
||||
if (items.isNotEmpty()){
|
||||
if (items.all { it2 -> it2.type == items[0].type }) {
|
||||
formatBtn.icon?.alpha = 255
|
||||
if (items[0].type != DownloadViewModel.Type.command) {
|
||||
moreBtn.icon?.alpha = 255
|
||||
lifecycleScope.launch {
|
||||
val haveSameType = if (listAdapter.isCheckingItems()) {
|
||||
val res = withContext(Dispatchers.IO) {
|
||||
downloadViewModel.checkIfAllProcessingItemsHaveSameType(listAdapter.getCheckedItemsOrNull())
|
||||
}
|
||||
|
||||
res.first
|
||||
}else {
|
||||
items.all { it2 -> it2.type == items[0].type }
|
||||
}
|
||||
|
||||
containerBtn.isVisible = items.first().type != DownloadViewModel.Type.command
|
||||
} else {
|
||||
formatBtn.icon?.alpha = 30
|
||||
moreBtn.icon?.alpha = 30
|
||||
}
|
||||
if (haveSameType) {
|
||||
formatBtn.icon?.alpha = 255
|
||||
if (items[0].type != DownloadViewModel.Type.command) {
|
||||
moreBtn.icon?.alpha = 255
|
||||
}
|
||||
|
||||
if (items.all { it.container == items[0].container }) {
|
||||
setContainerText(items[0].container)
|
||||
}else{
|
||||
setContainerText("")
|
||||
containerBtn.isVisible = items.first().type != DownloadViewModel.Type.command
|
||||
}else {
|
||||
formatBtn.icon?.alpha = 30
|
||||
moreBtn.icon?.alpha = 30
|
||||
}
|
||||
|
||||
val haveSameContainer = if (listAdapter.isCheckingItems()) {
|
||||
val res = withContext(Dispatchers.IO) {
|
||||
downloadViewModel.checkIfAllProcessingItemsHaveSameContainer(listAdapter.getCheckedItemsOrNull())
|
||||
}
|
||||
|
||||
res.first
|
||||
}else {
|
||||
items.all { it.container == items[0].container }
|
||||
}
|
||||
|
||||
if (haveSameContainer) {
|
||||
setContainerText(items[0].container)
|
||||
}else {
|
||||
setContainerText("")
|
||||
}
|
||||
}
|
||||
|
||||
val type = items.first().type
|
||||
|
|
@ -233,7 +268,6 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
toggleLoading(true)
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.deleteAllWithID(currentDownloadIDs)
|
||||
historyViewModel.deleteAllWithIDsCheckFiles(currentHistoryIDs)
|
||||
val result = downloadViewModel.updateProcessingDownloadTimeAndQueueScheduled(cal.timeInMillis)
|
||||
if (result.message.isNotBlank()){
|
||||
|
|
@ -257,7 +291,6 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
toggleLoading(true)
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.deleteAllWithID(currentDownloadIDs)
|
||||
historyViewModel.deleteAllWithIDsCheckFiles(currentHistoryIDs)
|
||||
val result = downloadViewModel.queueProcessingDownloads()
|
||||
if (result.message.isNotBlank()){
|
||||
|
|
@ -283,8 +316,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
dd.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||
lifecycleScope.launch{
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.deleteAllWithID(currentDownloadIDs)
|
||||
downloadViewModel.moveProcessingToSavedCategory()
|
||||
downloadViewModel.moveProcessingToSavedCategory(null)
|
||||
historyViewModel.deleteAllWithIDsCheckFiles(currentHistoryIDs)
|
||||
}
|
||||
|
||||
|
|
@ -300,7 +332,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
|
||||
val formatListener = object : OnMultipleFormatClickListener {
|
||||
override fun onFormatClick(formatTuple: List<MultipleItemFormatTuple>) {
|
||||
downloadViewModel.updateAllProcessingFormats(formatTuple)
|
||||
downloadViewModel.updateAllProcessingFormats(listAdapter.getCheckedItemsOrNull(), formatTuple)
|
||||
}
|
||||
|
||||
override fun onFormatUpdated(url: String, formats: List<Format>) {
|
||||
|
|
@ -314,7 +346,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
override fun onContinueOnBackground() {
|
||||
requireActivity().lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.continueUpdatingFormatsOnBackground()
|
||||
downloadViewModel.continueUpdatingFormatsOnBackground(listAdapter.getCheckedItemsOrNull())
|
||||
}
|
||||
downloadViewModel.processingItemsJob?.cancel(CancellationException())
|
||||
downloadViewModel.processingItemsJob = null
|
||||
|
|
@ -326,7 +358,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
|
||||
lifecycleScope.launch {
|
||||
val allIncognito = withContext(Dispatchers.IO){
|
||||
downloadViewModel.areAllProcessingIncognito()
|
||||
downloadViewModel.areAllProcessingIncognito(listAdapter.getCheckedItemsOrNull())
|
||||
}
|
||||
|
||||
incognitoBtn.icon!!.apply {
|
||||
|
|
@ -359,40 +391,6 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
|
||||
bottomAppBar.setOnMenuItemClickListener { m: MenuItem ->
|
||||
when (m.itemId) {
|
||||
R.id.incognito -> {
|
||||
lifecycleScope.launch {
|
||||
if (m.icon!!.alpha == 255) {
|
||||
incognitoBtn.isEnabled = false
|
||||
withContext(Dispatchers.IO) {
|
||||
downloadViewModel.updateProcessingIncognito(false)
|
||||
withContext(Dispatchers.Main){
|
||||
m.icon!!.alpha = 30
|
||||
m.isEnabled = true
|
||||
}
|
||||
}
|
||||
incognitoBtn.icon?.alpha = 30
|
||||
incognitoBtn.isEnabled = true
|
||||
Toast.makeText(requireContext(), "${getString(R.string.incognito)}: ${getString(R.string.disabled)}", Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
incognitoBtn.isEnabled = false
|
||||
withContext(Dispatchers.IO) {
|
||||
downloadViewModel.updateProcessingIncognito(true)
|
||||
withContext(Dispatchers.Main){
|
||||
}
|
||||
}
|
||||
incognitoBtn.icon?.alpha = 255
|
||||
incognitoBtn.isEnabled = true
|
||||
Toast.makeText(requireContext(), "${getString(R.string.incognito)}: ${getString(R.string.ok)}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
R.id.folder -> {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
pathResultLauncher.launch(intent)
|
||||
}
|
||||
R.id.preferred_download_type -> {
|
||||
lifecycleScope.launch{
|
||||
val bottomSheet = BottomSheetDialog(requireContext())
|
||||
|
|
@ -416,7 +414,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
|
||||
audio!!.setOnClickListener {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
downloadViewModel.updateProcessingType(DownloadViewModel.Type.audio)
|
||||
downloadViewModel.updateProcessingType(listAdapter.getCheckedItemsOrNull(), DownloadViewModel.Type.audio)
|
||||
withContext(Dispatchers.Main){
|
||||
preferredDownloadType.setIcon(R.drawable.baseline_audio_file_24)
|
||||
formatBtn.icon?.alpha = 255
|
||||
|
|
@ -424,7 +422,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
bottomSheet.cancel()
|
||||
}
|
||||
|
||||
val res = downloadViewModel.checkIfAllProcessingItemsHaveSameContainer()
|
||||
val res = downloadViewModel.checkIfAllProcessingItemsHaveSameContainer(listAdapter.getCheckedItemsOrNull())
|
||||
withContext(Dispatchers.Main) {
|
||||
if (!res.first) {
|
||||
setContainerText("")
|
||||
|
|
@ -438,7 +436,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
|
||||
video!!.setOnClickListener {
|
||||
CoroutineScope(Dispatchers.IO).launch{
|
||||
downloadViewModel.updateProcessingType(DownloadViewModel.Type.video)
|
||||
downloadViewModel.updateProcessingType(listAdapter.getCheckedItemsOrNull(), DownloadViewModel.Type.video)
|
||||
withContext(Dispatchers.Main){
|
||||
preferredDownloadType.setIcon(R.drawable.baseline_video_file_24)
|
||||
formatBtn.icon?.alpha = 255
|
||||
|
|
@ -446,7 +444,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
bottomSheet.cancel()
|
||||
}
|
||||
|
||||
val res = downloadViewModel.checkIfAllProcessingItemsHaveSameContainer()
|
||||
val res = downloadViewModel.checkIfAllProcessingItemsHaveSameContainer(listAdapter.getCheckedItemsOrNull())
|
||||
withContext(Dispatchers.Main) {
|
||||
if (!res.first) {
|
||||
setContainerText("")
|
||||
|
|
@ -459,7 +457,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
|
||||
command!!.setOnClickListener {
|
||||
CoroutineScope(Dispatchers.IO).launch{
|
||||
downloadViewModel.updateProcessingType(DownloadViewModel.Type.command)
|
||||
downloadViewModel.updateProcessingType(listAdapter.getCheckedItemsOrNull(), DownloadViewModel.Type.command)
|
||||
withContext(Dispatchers.Main){
|
||||
preferredDownloadType.setIcon(R.drawable.baseline_insert_drive_file_24)
|
||||
formatBtn.icon?.alpha = 255
|
||||
|
|
@ -485,7 +483,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
R.id.format -> {
|
||||
lifecycleScope.launch {
|
||||
val res = withContext(Dispatchers.IO){
|
||||
downloadViewModel.checkIfAllProcessingItemsHaveSameType()
|
||||
downloadViewModel.checkIfAllProcessingItemsHaveSameType(listAdapter.getCheckedItemsOrNull())
|
||||
}
|
||||
if (!res.first){
|
||||
Toast.makeText(requireContext(), getString(R.string.format_filtering_hint), Toast.LENGTH_SHORT).show()
|
||||
|
|
@ -505,13 +503,13 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.updateProcessingCommandFormat(format)
|
||||
downloadViewModel.updateProcessingCommandFormat(listAdapter.getCheckedItemsOrNull(), format)
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
val items = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getProcessingDownloads()
|
||||
downloadViewModel.getProcessingDownloads(listAdapter.getCheckedItemsOrNull())
|
||||
}
|
||||
formatViewModel.setItems(items)
|
||||
val bottomSheet = FormatSelectionBottomSheetDialog( _multipleFormatsListener = formatListener)
|
||||
|
|
@ -520,10 +518,63 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
}
|
||||
}
|
||||
}
|
||||
R.id.folder -> {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
pathResultLauncher.launch(intent)
|
||||
}
|
||||
R.id.select_items -> {
|
||||
if (listAdapter.isCheckingItems()) {
|
||||
multipleSelectHeader.isVisible = false
|
||||
view.findViewById<ConstraintLayout>(R.id.downloadHeader).isVisible = true
|
||||
filesize.isVisible = itemsFileSize > 0L
|
||||
selectRangeBtn.isVisible = false
|
||||
count.text = "${currentDownloadIDs.size} ${getString(R.string.selected)}"
|
||||
listAdapter.clearCheckedItems()
|
||||
}else {
|
||||
multipleSelectHeader.apply {
|
||||
isVisible = true
|
||||
}
|
||||
view.findViewById<ConstraintLayout>(R.id.downloadHeader).isVisible = false
|
||||
filesize.isVisible = false
|
||||
listAdapter.initCheckingItems(currentDownloadIDs)
|
||||
selectRangeBtn.isVisible = true
|
||||
count.text = "0 ${getString(R.string.selected)}"
|
||||
}
|
||||
}
|
||||
R.id.incognito -> {
|
||||
lifecycleScope.launch {
|
||||
if (m.icon!!.alpha == 255) {
|
||||
incognitoBtn.isEnabled = false
|
||||
withContext(Dispatchers.IO) {
|
||||
downloadViewModel.updateProcessingIncognito(listAdapter.getCheckedItemsOrNull(), false)
|
||||
withContext(Dispatchers.Main){
|
||||
m.icon!!.alpha = 30
|
||||
m.isEnabled = true
|
||||
}
|
||||
}
|
||||
incognitoBtn.icon?.alpha = 30
|
||||
incognitoBtn.isEnabled = true
|
||||
Toast.makeText(requireContext(), "${getString(R.string.incognito)}: ${getString(R.string.disabled)}", Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
incognitoBtn.isEnabled = false
|
||||
withContext(Dispatchers.IO) {
|
||||
downloadViewModel.updateProcessingIncognito(listAdapter.getCheckedItemsOrNull(),true)
|
||||
withContext(Dispatchers.Main){
|
||||
}
|
||||
}
|
||||
incognitoBtn.icon?.alpha = 255
|
||||
incognitoBtn.isEnabled = true
|
||||
Toast.makeText(requireContext(), "${getString(R.string.incognito)}: ${getString(R.string.ok)}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
R.id.more -> {
|
||||
lifecycleScope.launch {
|
||||
val res = withContext(Dispatchers.IO){
|
||||
downloadViewModel.checkIfAllProcessingItemsHaveSameType()
|
||||
downloadViewModel.checkIfAllProcessingItemsHaveSameType(listAdapter.getCheckedItemsOrNull())
|
||||
}
|
||||
if (!res.first) {
|
||||
Toast.makeText(
|
||||
|
|
@ -544,7 +595,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
sheetView.findViewById<View>(R.id.adjust).setPadding(padding)
|
||||
|
||||
val items = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getProcessingDownloads()
|
||||
downloadViewModel.getProcessingDownloads(listAdapter.getCheckedItemsOrNull())
|
||||
}
|
||||
|
||||
UiUtil.configureAudio(
|
||||
|
|
@ -626,7 +677,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
sheetView.findViewById<View>(R.id.adjust).setPadding(padding)
|
||||
|
||||
val items = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getProcessingDownloads()
|
||||
downloadViewModel.getProcessingDownloads(listAdapter.getCheckedItemsOrNull())
|
||||
}
|
||||
|
||||
UiUtil.configureVideo(
|
||||
|
|
@ -731,7 +782,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
containerTextView.setOnClickListener {
|
||||
lifecycleScope.launch {
|
||||
val res = withContext(Dispatchers.IO){
|
||||
downloadViewModel.checkIfAllProcessingItemsHaveSameType()
|
||||
downloadViewModel.checkIfAllProcessingItemsHaveSameType(listAdapter.getCheckedItemsOrNull())
|
||||
}
|
||||
if (!res.first){
|
||||
Toast.makeText(requireContext(), getString(R.string.format_filtering_hint), Toast.LENGTH_SHORT).show()
|
||||
|
|
@ -749,7 +800,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
val container = mm.title
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.updateProcessingContainer(container.toString())
|
||||
downloadViewModel.updateProcessingContainer(listAdapter.getCheckedItemsOrNull(), container.toString())
|
||||
}
|
||||
}
|
||||
setContainerText(container.toString())
|
||||
|
|
@ -758,11 +809,92 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
|
||||
popup.show()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
multipleSelectHeader = view.findViewById(R.id.multipleSelectHeader)
|
||||
selectItemsMenuBtn = view.findViewById(R.id.selectItemsMenu)
|
||||
selectItemsMenuBtn.setOnClickListener {
|
||||
val popup = PopupMenu(activity, it)
|
||||
popup.menuInflater.inflate(R.menu.select_multiple_items_menu_context, popup.menu)
|
||||
if (Build.VERSION.SDK_INT > 27) popup.menu.setGroupDividerEnabled(true)
|
||||
|
||||
val selectedItems = listAdapter.getCheckedItemsOrNull() ?: listOf()
|
||||
popup.menu.findItem(R.id.delete).isVisible = selectedItems.isNotEmpty()
|
||||
popup.menu.findItem(R.id.select_between).apply {
|
||||
if (selectedItems.size == 2) {
|
||||
val firstIndex = currentDownloadIDs.indexOf(selectedItems.first())
|
||||
val secondIndex = currentDownloadIDs.indexOf(selectedItems.last())
|
||||
|
||||
isVisible = abs(firstIndex - secondIndex) > 1
|
||||
}else{
|
||||
isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
popup.setOnMenuItemClickListener { m: MenuItem ->
|
||||
when(m.itemId) {
|
||||
R.id.select_between -> {
|
||||
val firstIndex = currentDownloadIDs.indexOf(selectedItems.first())
|
||||
val secondIndex = currentDownloadIDs.indexOf(selectedItems.last())
|
||||
|
||||
val itemsBetween = currentDownloadIDs.filterIndexed { index, _ -> index >= firstIndex && index <= secondIndex }
|
||||
listAdapter.selectItems(itemsBetween)
|
||||
count.text = "${itemsBetween.size} ${getString(R.string.selected)}"
|
||||
}
|
||||
R.id.delete -> {
|
||||
UiUtil.showGenericDeleteAllDialog(requireContext()) {
|
||||
lifecycleScope.launch {
|
||||
val deletedAll = processingItemsCount == listAdapter.getCheckedItemsSize()
|
||||
val toDelete = listAdapter.getCheckedItemsOrNull() ?: listOf()
|
||||
withContext(Dispatchers.IO) {
|
||||
downloadViewModel.deleteAllWithID(toDelete)
|
||||
}
|
||||
listAdapter.removeItemsFromCheckList(toDelete)
|
||||
val checkedSize = listAdapter.getCheckedItemsSize()
|
||||
count.text = "$checkedSize ${getString(R.string.selected)}"
|
||||
if (deletedAll) dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
R.id.select_all -> {
|
||||
listAdapter.checkAll()
|
||||
val checkedSize = listAdapter.getCheckedItemsSize()
|
||||
count.text = "$checkedSize ${getString(R.string.selected)}"
|
||||
}
|
||||
R.id.invert_selected -> {
|
||||
listAdapter.invertSelected()
|
||||
val checkedSize = listAdapter.getCheckedItemsSize()
|
||||
count.text = "$checkedSize ${getString(R.string.selected)}"
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
popup.show()
|
||||
}
|
||||
|
||||
selectRangeBtn = view.findViewById(R.id.selectRangeBtn)
|
||||
selectRangeBtn.setOnClickListener {
|
||||
UiUtil.showSelectRangeDialog(requireActivity(), currentDownloadIDs.size) {
|
||||
val itemsBetween = currentDownloadIDs.filterIndexed { index, _ -> index >= it.first && index <= it.second }
|
||||
listAdapter.selectItems(itemsBetween)
|
||||
selectItemsMenuBtn.isVisible = true
|
||||
count.text = "${itemsBetween.size} ${getString(R.string.selected)}"
|
||||
}
|
||||
}
|
||||
|
||||
val editSelectedOkBtn = view.findViewById<MaterialButton>(R.id.bottomsheet_ok_button)
|
||||
editSelectedOkBtn.setOnClickListener {
|
||||
multipleSelectHeader.isVisible = false
|
||||
view.findViewById<ConstraintLayout>(R.id.downloadHeader).isVisible = true
|
||||
selectItemsMenuBtn.isVisible = false
|
||||
filesize.isVisible = itemsFileSize > 0
|
||||
count.text = "${currentDownloadIDs.size} ${getString(R.string.selected)}"
|
||||
selectRangeBtn.isVisible = false
|
||||
listAdapter.clearCheckedItems()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
@ -819,7 +951,8 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
|
||||
if (fileSizes.all { it > 5L }){
|
||||
val size = FileUtil.convertFileSize(fileSizes.sum())
|
||||
filesize.isVisible = size != "?"
|
||||
itemsFileSize = fileSizes.sum()
|
||||
filesize.isVisible = size != "?" && !listAdapter.isCheckingItems()
|
||||
filesize.text = "${getString(R.string.file_size)}: >~ $size"
|
||||
}else{
|
||||
filesize.visibility = View.GONE
|
||||
|
|
@ -839,7 +972,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
downloadViewModel.updateProcessingDownloadPath(result.data?.data.toString())
|
||||
downloadViewModel.updateProcessingDownloadPath(listAdapter.getCheckedItemsOrNull(), result.data?.data.toString())
|
||||
}
|
||||
|
||||
val path = FileUtil.formatPath(result.data!!.data.toString())
|
||||
|
|
@ -926,6 +1059,20 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
}
|
||||
}
|
||||
|
||||
override fun onCardChecked(id: Long) {
|
||||
selectItemsMenuBtn.isVisible = true
|
||||
val checkedSize = listAdapter.getCheckedItemsSize()
|
||||
count.text = "$checkedSize ${getString(R.string.selected)}"
|
||||
}
|
||||
|
||||
override fun onCardUnChecked(id: Long) {
|
||||
val checkedSize = listAdapter.getCheckedItemsSize()
|
||||
if (checkedSize == 0) {
|
||||
selectItemsMenuBtn.isVisible = false
|
||||
}
|
||||
count.text = "$checkedSize ${getString(R.string.selected)}"
|
||||
}
|
||||
|
||||
override fun onDelete(id: Long) {
|
||||
lifecycleScope.launch {
|
||||
val deletedItem = withContext(Dispatchers.IO){
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import android.os.Environment
|
|||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.text.format.DateFormat
|
||||
import android.text.method.DigitsKeyListener
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.Gravity
|
||||
|
|
@ -2014,6 +2015,73 @@ object UiUtil {
|
|||
dialog.getButton(AlertDialog.BUTTON_NEUTRAL).gravity = Gravity.START
|
||||
}
|
||||
|
||||
fun showSelectRangeDialog(context: Activity, itemCount: Int, rangeSelected: (rangeSelected: Pair<Int, Int>) -> Unit) {
|
||||
val builder = MaterialAlertDialogBuilder(context)
|
||||
builder.setTitle(context.getString(R.string.select_between))
|
||||
builder.setMessage(context.getString(R.string.select_between_desc))
|
||||
builder.setIcon(R.drawable.baseline_format_list_numbered_24)
|
||||
val view = context.layoutInflater.inflate(R.layout.select_range_dialog, null)
|
||||
|
||||
val fromTextInput = view.findViewById<TextInputLayout>(R.id.from_textinput)
|
||||
fromTextInput.editText!!.keyListener = DigitsKeyListener.getInstance("0123456789")
|
||||
val toTextInput = view.findViewById<TextInputLayout>(R.id.to_textinput)
|
||||
toTextInput.editText!!.keyListener = DigitsKeyListener.getInstance("0123456789")
|
||||
|
||||
|
||||
builder.setView(view)
|
||||
builder.setPositiveButton(
|
||||
context.getString(R.string.ok)
|
||||
) { _: DialogInterface?, _: Int ->
|
||||
val firstIndex = fromTextInput.editText!!.text.toString().toInt() - 1
|
||||
val secondIndex = toTextInput.editText!!.text.toString().toInt() - 1
|
||||
|
||||
rangeSelected(Pair(firstIndex,secondIndex))
|
||||
}
|
||||
|
||||
// handle the negative button of the alert dialog
|
||||
builder.setNegativeButton(
|
||||
context.getString(R.string.cancel)
|
||||
) { _: DialogInterface?, _: Int -> }
|
||||
|
||||
val dialog = builder.create()
|
||||
dialog.show()
|
||||
|
||||
|
||||
fun checkRanges(start: String, end: String) : Boolean {
|
||||
fromTextInput.error = ""
|
||||
toTextInput.error = ""
|
||||
|
||||
if (start.isBlank() || end.isBlank()) return false
|
||||
|
||||
val startValid = start.toInt() >= 0
|
||||
val endValid = end.toInt() <= itemCount
|
||||
|
||||
if (!startValid) {
|
||||
fromTextInput.editText?.setText("")
|
||||
fromTextInput.error = "Invalid Number"
|
||||
}
|
||||
if (!endValid) {
|
||||
toTextInput.editText?.setText("")
|
||||
toTextInput.error = "Invalid Number"
|
||||
}
|
||||
|
||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = startValid && endValid
|
||||
return startValid && endValid
|
||||
}
|
||||
|
||||
fromTextInput.editText!!.doAfterTextChanged { editable ->
|
||||
val start = editable.toString()
|
||||
val end = toTextInput.editText!!.text.toString()
|
||||
checkRanges(start, end)
|
||||
}
|
||||
|
||||
toTextInput.editText!!.doAfterTextChanged { editable ->
|
||||
val start = fromTextInput.editText!!.text.toString()
|
||||
val end = editable.toString()
|
||||
checkRanges(start, end)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createPersonalFilenameTemplateChip(context: Activity, text: String, myChipGroup: ChipGroup, onClick: (f: Chip) -> Unit, onLongClick: (f: Chip) -> Unit) : Chip {
|
||||
val tmp = context.layoutInflater.inflate(R.layout.filter_chip, myChipGroup, false) as Chip
|
||||
tmp.text = text
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="?attr/colorAccent" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M2,17h2v0.5L3,17.5v1h1v0.5L2,19v1h3v-4L2,16v1zM3,8h1L4,4L2,4v1h1v3zM2,11h1.8L2,13.1v0.9h3v-1L3.2,13L5,10.9L5,10L2,10v1zM7,5v2h14L21,5L7,5zM7,19h14v-2L7,17v2zM7,13h14v-2L7,11v2z"/>
|
||||
|
||||
</vector>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
<vector android:height="24dp" android:tint="#000000"
|
||||
<vector android:height="24dp" android:tint="?attr/colorAccent"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M3,5h2L5,3c-1.1,0 -2,0.9 -2,2zM3,13h2v-2L3,11v2zM7,21h2v-2L7,19v2zM3,9h2L5,7L3,7v2zM13,3h-2v2h2L13,3zM19,3v2h2c0,-1.1 -0.9,-2 -2,-2zM5,21v-2L3,19c0,1.1 0.9,2 2,2zM3,17h2v-2L3,15v2zM9,3L7,3v2h2L9,3zM11,21h2v-2h-2v2zM19,13h2v-2h-2v2zM19,21c1.1,0 2,-0.9 2,-2h-2v2zM19,9h2L21,7h-2v2zM19,17h2v-2h-2v2zM15,21h2v-2h-2v2zM15,5h2L17,3h-2v2zM7,17h10L17,7L7,7v10zM9,9h6v6L9,15L9,9z"/>
|
||||
|
|
|
|||
14
app/src/main/res/drawable/rounded_corner_index.xml
Normal file
14
app/src/main/res/drawable/rounded_corner_index.xml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<solid android:color="?android:colorBackground" />
|
||||
|
||||
<padding
|
||||
android:left="1dp"
|
||||
android:right="1dp"
|
||||
android:bottom="1dp"
|
||||
android:top="1dp" />
|
||||
|
||||
<corners
|
||||
android:topLeftRadius="5dp"
|
||||
android:bottomRightRadius="5dp" />
|
||||
</shape>
|
||||
|
|
@ -21,25 +21,57 @@
|
|||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:paddingHorizontal="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingVertical="10dp"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/checkBox"
|
||||
android:layout_width="wrap_content"
|
||||
android:minWidth="0dp"
|
||||
android:visibility="gone"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/downloads_image_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:layout_marginStart="10dp"
|
||||
android:background="?attr/colorSurfaceVariant"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="H,16:9"
|
||||
app:layout_constraintEnd_toStartOf="@+id/download_item_data"
|
||||
app:layout_constraintHorizontal_weight="0.3"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/checkBox"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:shapeAppearance="@style/ShapeAppearanceOverlay.Avatar2" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/index"
|
||||
android:visibility="gone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="false"
|
||||
android:ellipsize="end"
|
||||
android:background="@drawable/rounded_corner_index"
|
||||
android:gravity="center"
|
||||
android:textStyle="bold"
|
||||
android:maxLength="17"
|
||||
android:maxLines="1"
|
||||
android:text="1"
|
||||
android:minWidth="10dp"
|
||||
android:paddingHorizontal="5dp"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintTop_toTopOf="@+id/downloads_image_view"
|
||||
app:layout_constraintStart_toStartOf="@+id/downloads_image_view" />
|
||||
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/incognitoLabel"
|
||||
style="?attr/materialIconButtonStyle"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
|
|
@ -34,6 +35,7 @@
|
|||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/downloadHeader"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
|
|
@ -137,39 +139,126 @@
|
|||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/multiple_top_info"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/multipleSelectHeader"
|
||||
android:layout_width="match_parent"
|
||||
android:visibility="gone"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bottom_sheet_edit_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/edit"
|
||||
android:textSize="25sp"
|
||||
android:maxLines="2"
|
||||
android:singleLine="false"
|
||||
app:layout_constraintEnd_toStartOf="@+id/bottomsheet_ok_button"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bottom_sheet_edit_subtitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/edit_selected"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/bottomsheet_ok_button"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/bottom_sheet_edit_title" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/bottomsheet_ok_button"
|
||||
style="@style/Widget.Material3.Button.ElevatedButton.Icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:autoLink="all"
|
||||
android:outlineProvider="none"
|
||||
android:stateListAnimator="@null"
|
||||
android:text="@string/ok"
|
||||
app:icon="@drawable/ic_check"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/count"
|
||||
<LinearLayout
|
||||
android:id="@+id/multiple_top_info"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="20dp"
|
||||
android:paddingTop="10dp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/selectRangeBtn"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/filesize"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
<TextView
|
||||
android:id="@+id/count"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="20dp"
|
||||
android:paddingTop="10dp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/filesize"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:text="@string/file_size"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/selectRangeBtn"
|
||||
android:visibility="gone"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingTop="10dp"
|
||||
android:text="@string/file_size"
|
||||
android:textStyle="bold"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="wrap_content"
|
||||
app:icon="@drawable/baseline_format_list_numbered_24"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/select_between"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/selectItemsMenu"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/selectItemsMenu"
|
||||
android:visibility="gone"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:contentDescription="@string/settings"
|
||||
app:icon="@drawable/baseline_more_vert_24"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/downloadMultipleRecyclerview"
|
||||
|
|
|
|||
|
|
@ -38,8 +38,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:clickable="false"
|
||||
android:ellipsize="end"
|
||||
android:layout_margin="5dp"
|
||||
android:background="#80000000"
|
||||
android:background="@drawable/rounded_corner_index"
|
||||
android:gravity="center"
|
||||
android:textStyle="bold"
|
||||
android:maxLength="17"
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@
|
|||
app:borderWidth="0dp"
|
||||
android:contentDescription="@string/select_all"
|
||||
android:layout_height="55dp"
|
||||
android:backgroundTint="@android:color/transparent"
|
||||
app:layout_anchor="@id/bottomAppBar"
|
||||
android:layout_marginHorizontal="5dp"
|
||||
android:src="@drawable/ic_select_all"
|
||||
|
|
@ -129,6 +130,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:autoLink="all"
|
||||
android:text="@string/ok"
|
||||
android:outlineProvider="none"
|
||||
app:icon="@drawable/ic_check"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
|
|||
54
app/src/main/res/layout/select_range_dialog.xml
Normal file
54
app/src/main/res/layout/select_range_dialog.xml
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="20dp"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:gravity="center"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/from_textinput"
|
||||
style="@style/Widget.Material3.TextInputLayout.FilledBox.Dense"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="45"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:hint="@string/start">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="number"
|
||||
android:maxLength="9" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/to_textinput"
|
||||
style="@style/Widget.Material3.TextInputLayout.FilledBox.Dense"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/end"
|
||||
android:layout_weight="45"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/colon">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text"
|
||||
android:maxLength="9" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -28,13 +28,19 @@
|
|||
app:actionLayout="@layout/bottom_app_bar_text"
|
||||
app:showAsAction="always"/>
|
||||
|
||||
|
||||
<item
|
||||
android:id="@+id/select_items"
|
||||
android:title="@string/select"
|
||||
android:icon="@drawable/ic_select_all"
|
||||
app:showAsAction="always"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/incognito"
|
||||
android:title="@string/incognito"
|
||||
android:icon="@drawable/ic_nightly"
|
||||
app:showAsAction="always"/>
|
||||
|
||||
|
||||
<item
|
||||
android:id="@+id/more"
|
||||
android:title="@string/more"
|
||||
|
|
|
|||
30
app/src/main/res/menu/select_multiple_items_menu_context.xml
Normal file
30
app/src/main/res/menu/select_multiple_items_menu_context.xml
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<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/select_between"
|
||||
android:visible="false"
|
||||
android:title="@string/select_between"
|
||||
android:icon="@drawable/ic_select_between"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/delete"
|
||||
android:title="@string/Remove"
|
||||
android:icon="@drawable/baseline_delete_24"
|
||||
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>
|
||||
|
||||
|
|
@ -477,4 +477,5 @@
|
|||
<string name="generate_potokens_warning">* By enabling this, you need to disable cookies</string>
|
||||
<string name="custom">Custom</string>
|
||||
<string name="cut_unsupported">Cutting is not available for this item</string>
|
||||
<string name="select_between_desc">Specify the range of items to select</string>
|
||||
</resources>
|
||||
|
|
|
|||
Loading…
Reference in a new issue