From 5cffd60e81225222dd3cde09ce0fc554a0712fe1 Mon Sep 17 00:00:00 2001 From: deniscerri <64997243+deniscerri@users.noreply.github.com> Date: Sun, 14 Jun 2026 17:06:40 +0200 Subject: [PATCH] bug fixes --- app/build.gradle | 4 +- app/src/main/AndroidManifest.xml | 1 - .../DownloadMultipleCardViewModel.kt | 7 +++- .../deniscerri/ytdl/ui/adapter/HomeAdapter.kt | 2 +- .../DownloadMultipleBottomSheetDialog.kt | 5 +++ .../crop/CropVideoBottomSheetDialog.kt | 21 +++++++--- .../downloads/CancelledDownloadsFragment.kt | 2 +- .../ui/downloads/ErroredDownloadsFragment.kt | 2 +- .../ytdl/ui/downloads/HistoryFragment.kt | 11 +++-- .../ui/downloads/SavedDownloadsFragment.kt | 2 +- .../downloads/ScheduledDownloadsFragment.kt | 2 +- .../java/com/deniscerri/ytdl/util/FileUtil.kt | 40 +++++++++---------- app/src/main/res/layout/crop_video_sheet.xml | 32 ++++++++++++++- build.gradle | 1 + 14 files changed, 90 insertions(+), 42 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index e1e4b574..94c4bc6f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,7 +12,7 @@ def properties = new Properties() def versionMajor = 1 def versionMinor = 8 def versionPatch = 9 -def versionBuild = 0 // bump for dogfood builds, public betas, etc. +def versionBuild = 1 // bump for dogfood builds, public betas, etc. //WHEN RELEASING BETA KEEP THE SAME VERSION AS LATEST STABLE, SO USERS CAN DOWNGRADE def isBeta = false @@ -237,7 +237,7 @@ dependencies { implementation 'com.google.accompanist:accompanist-webview:0.36.0' implementation 'androidx.compose.material3:material3-android:1.4.0' implementation "io.noties.markwon:core:4.6.2" - implementation("com.github.teamnewpipe:newpipeextractor:v0.26.3") + implementation("com.github.teamnewpipe:newpipeextractor:-SNAPSHOT") implementation("commons-io:commons-io:2.5") implementation("org.apache.commons:commons-compress:1.12") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4207204c..320076b9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -34,7 +34,6 @@ ? = null val processingDownloads: Flow> = Pager( config = PagingConfig( pageSize = 40, enablePlaceholders = false, initialLoadSize = 80 ), - pagingSourceFactory = { dao.getProcessingDownloads() } + pagingSourceFactory = { dao.getProcessingDownloads().also { currentPagingSource = it } } ).flow.cachedIn(viewModelScope) val processingIds: Flow> = dao.getProcessingDownloadsIds() @@ -90,4 +91,8 @@ class DownloadMultipleCardViewModel(application: Application) : AndroidViewModel } }.flowOn(Dispatchers.IO) + fun refreshProcessingDownloads() { + currentPagingSource?.invalidate() + } + } \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/adapter/HomeAdapter.kt b/app/src/main/java/com/deniscerri/ytdl/ui/adapter/HomeAdapter.kt index 28240f33..a19c1bb3 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/adapter/HomeAdapter.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/adapter/HomeAdapter.kt @@ -149,7 +149,7 @@ class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity) true } card.setOnClickListener { - if (checkedItems.isNotEmpty()) { + if (card.isChecked || checkedItems.isNotEmpty()) { checkCard(card, video.id, video) }else{ onItemClickListener.onCardDetailsClick(video) diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt index b095d64c..00c0e67f 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt @@ -31,11 +31,15 @@ import androidx.core.view.WindowInsetsCompat import androidx.core.view.children import androidx.core.view.isVisible import androidx.core.view.setPadding +import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.media3.exoplayer.offline.Download import androidx.navigation.fragment.findNavController +import androidx.paging.CombinedLoadStates +import androidx.paging.LoadState import androidx.preference.PreferenceManager import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager @@ -233,6 +237,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure processingItemsCount = ids.size processingDownloadIDs = ids count.text = "$processingItemsCount ${getString(R.string.selected)}" + downloadMultipleCardViewModel.refreshProcessingDownloads() } } diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/crop/CropVideoBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/crop/CropVideoBottomSheetDialog.kt index 4fc5b7ce..cc8ba112 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/crop/CropVideoBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/crop/CropVideoBottomSheetDialog.kt @@ -74,6 +74,7 @@ class CropVideoBottomSheetDialog( private lateinit var forwardBtn: MaterialButton private lateinit var muteBtn: MaterialButton private lateinit var resetBtn: Button + private lateinit var clearZoom: Button private lateinit var okBtn: Button private lateinit var xEditText: EditText @@ -157,6 +158,7 @@ class CropVideoBottomSheetDialog( forwardBtn = view.findViewById(R.id.forward) muteBtn = view.findViewById(R.id.mute) resetBtn = view.findViewById(R.id.resetButton) + clearZoom = view.findViewById(R.id.clearZoom) okBtn = view.findViewById(R.id.okButton) xEditText = view.findViewById(R.id.crop_x_edittext) @@ -218,9 +220,23 @@ class CropVideoBottomSheetDialog( } resetBtn.isEnabled = item.videoPreferences.cropValues.isNotBlank() + val formatReference = item.allFormats.sortedByDescending { it.height }.firstOrNull { + (it.height ?: 0) > 0 && (it.width ?: 0) > 0 + } + videoWidth = formatReference?.width ?: 0 + videoHeight = formatReference?.height ?: 0 + + clearZoom.setOnClickListener { + listener?.onClearCrop() + ratioChipGroup.findViewById(R.id.chip_free).performClick() + refreshTextInputs(0, 0, videoWidth, videoHeight) + applyVideoCoordsToOverlay(0, 0, videoWidth, videoHeight) + } + okBtn.setOnClickListener { readTextFieldsToVideoCoords()?.let { (x, y, w, h) -> listener?.onChangeCrop(x, y, w, h, videoWidth, videoHeight) + resetBtn.isEnabled = true } player.stop() dismiss() @@ -245,11 +261,6 @@ class CropVideoBottomSheetDialog( } }) - val formatReference = item.allFormats.sortedByDescending { it.height }.firstOrNull { - (it.height ?: 0) > 0 && (it.width ?: 0) > 0 - } - videoWidth = formatReference?.width ?: 0 - videoHeight = formatReference?.height ?: 0 showOverlayAndLoad() videoView.setOnClickListener { diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/CancelledDownloadsFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/CancelledDownloadsFragment.kt index a2645a02..d5a4cf97 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/CancelledDownloadsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/CancelledDownloadsFragment.kt @@ -153,13 +153,13 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic override fun onActionButtonClick(itemID: Long) { lifecycleScope.launch { runCatching { - actionMode?.finish() val item = withContext(Dispatchers.IO){ downloadViewModel.getItemByID(itemID) } withContext(Dispatchers.IO){ downloadViewModel.queueDownloads(listOf(item), true) } + actionMode?.finish() }.onFailure { Toast.makeText(requireContext(), getString(R.string.error_restarting_download), Toast.LENGTH_LONG).show() } diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ErroredDownloadsFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ErroredDownloadsFragment.kt index 7d5ce181..3df6a38d 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ErroredDownloadsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ErroredDownloadsFragment.kt @@ -153,7 +153,6 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL override fun onActionButtonClick(itemID: Long) { lifecycleScope.launch { - actionMode?.finish() val item = withContext(Dispatchers.IO){ downloadViewModel.getItemByID(itemID) } @@ -166,6 +165,7 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL bundle ) } + actionMode?.finish() } } diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/HistoryFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/HistoryFragment.kt index daaf6dc8..fda3c41c 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/HistoryFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/HistoryFragment.kt @@ -590,11 +590,10 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{ deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() } deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int -> lifecycleScope.launch { - actionMode?.finish() - val selectedObjects = getSelectedIDs() historyAdapter.clearCheckedItems() historyViewModel.deleteAllWithIDs(selectedObjects, deleteFile[0]) + actionMode?.finish() } } deleteDialog.show() @@ -602,22 +601,20 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{ } R.id.share -> { lifecycleScope.launch { - actionMode?.finish() - val selectedObjects = getSelectedIDs() val paths = withContext(Dispatchers.IO){ historyViewModel.getDownloadPathsFromIDs(selectedObjects) } FileUtil.shareFileIntent(requireContext(), paths.flatten()) historyAdapter.clearCheckedItems() + actionMode?.finish() + } true } R.id.redownload -> { lifecycleScope.launch { val selectedObjects = getSelectedIDs() - actionMode?.finish() - historyAdapter.clearCheckedItems() if (selectedObjects.size == 1) { val tmp = withContext(Dispatchers.IO) { @@ -633,6 +630,7 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{ Pair("ignore_duplicates", true) )) } + actionMode?.finish() }else { val showDownloadCard = sharedPreferences.getBoolean("download_card", true) downloadViewModel.turnHistoryItemsToProcessingDownloads(selectedObjects, downloadNow = !showDownloadCard) @@ -642,6 +640,7 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{ bundle.putBoolean("ignore_duplicates", true) findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2, bundle) } + actionMode?.finish() } } true diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/SavedDownloadsFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/SavedDownloadsFragment.kt index 396f4309..07bf29de 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/SavedDownloadsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/SavedDownloadsFragment.kt @@ -162,7 +162,6 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis override fun onActionButtonClick(itemID: Long) { lifecycleScope.launch { - actionMode?.finish() val item = withContext(Dispatchers.IO){ downloadViewModel.getItemByID(itemID) @@ -175,6 +174,7 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis }else{ Toast.makeText(requireContext(), getString(R.string.error_restarting_download), Toast.LENGTH_LONG).show() } + actionMode?.finish() } } diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ScheduledDownloadsFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ScheduledDownloadsFragment.kt index b8d429e7..0cb81178 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ScheduledDownloadsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ScheduledDownloadsFragment.kt @@ -159,7 +159,6 @@ class ScheduledDownloadsFragment : Fragment(), ScheduledDownloadAdapter.OnItemCl override fun onActionButtonClick(itemID: Long) { lifecycleScope.launch { - actionMode?.finish() runCatching { withContext(Dispatchers.IO){ downloadViewModel.resetScheduleTimeForItemsAndStartDownload(listOf(itemID)) @@ -167,6 +166,7 @@ class ScheduledDownloadsFragment : Fragment(), ScheduledDownloadAdapter.OnItemCl }.onFailure { Toast.makeText(requireContext(), it.message, Toast.LENGTH_LONG).show() } + actionMode?.finish() } } diff --git a/app/src/main/java/com/deniscerri/ytdl/util/FileUtil.kt b/app/src/main/java/com/deniscerri/ytdl/util/FileUtil.kt index 616d9aaf..2a216fcc 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/FileUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/FileUtil.kt @@ -1,5 +1,6 @@ package com.deniscerri.ytdl.util +import android.content.ContentUris import android.content.Context import android.content.Intent import android.media.MediaScannerConnection @@ -55,30 +56,29 @@ object FileUtil { val file = File(path) val uri = MediaStore.Files.getContentUri("external") - val selection: String - val selectionArgs: Array - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - val parentPath = file.parentFile?.absolutePath.orEmpty() - val primaryRoot = Environment.getExternalStorageDirectory().absolutePath - if (parentPath.startsWith(primaryRoot)) { - val trimmed = parentPath - .removePrefix(primaryRoot) - .removePrefix(File.separator) - val relativePath = if (trimmed.isEmpty()) "" else "$trimmed${File.separator}" - selection = MediaStore.MediaColumns.RELATIVE_PATH + " =? AND " + - MediaStore.MediaColumns.DISPLAY_NAME + " =?" - selectionArgs = arrayOf(relativePath, file.name) - } else { - // Non-primary storage: fall back to DATA query - selection = MediaStore.MediaColumns.DATA + " =?" - selectionArgs = arrayOf(file.absolutePath) + // Query by DATA first to find the exact MediaStore row ID, then delete by ID + val projection = arrayOf(MediaStore.MediaColumns._ID) + val selection = MediaStore.MediaColumns.DATA + " =?" + val selectionArgs = arrayOf(file.absolutePath) + + val rowUri = contentResolver.query(uri, projection, selection, selectionArgs, null) + ?.use { cursor -> + if (cursor.moveToFirst()) { + val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID)) + ContentUris.withAppendedId(uri, id) + } else null + } + + if (rowUri != null) { + contentResolver.delete(rowUri, null, null) } } else { - selection = MediaStore.MediaColumns.DATA + " =?" - selectionArgs = arrayOf(file.absolutePath) + // Pre-Q: DATA column is reliable + val selection = MediaStore.MediaColumns.DATA + " =?" + val selectionArgs = arrayOf(file.absolutePath) + contentResolver.delete(uri, selection, selectionArgs) } - contentResolver.delete(uri, selection, selectionArgs) } fun exists(path: String) : Boolean { diff --git a/app/src/main/res/layout/crop_video_sheet.xml b/app/src/main/res/layout/crop_video_sheet.xml index ff348bf6..a328110a 100644 --- a/app/src/main/res/layout/crop_video_sheet.xml +++ b/app/src/main/res/layout/crop_video_sheet.xml @@ -96,16 +96,36 @@ android:gravity="center_vertical" android:orientation="horizontal"> + + + + + + + + + +