From d57ab39e82b516d683264e009b282f138844db40 Mon Sep 17 00:00:00 2001 From: zaednasr <75589932+zaednasr@users.noreply.github.com> Date: Sun, 28 Jul 2024 23:33:06 +0200 Subject: [PATCH] 1.7.9 --- app/build.gradle | 2 +- .../ytdl/database/dao/DownloadDao.kt | 8 ++ .../deniscerri/ytdl/database/dao/LogDao.kt | 12 +- .../ytdl/database/dao/TerminalDao.kt | 12 +- .../models/DownloadItemConfigureMultiple.kt | 1 + .../ytdl/database/repository/LogRepository.kt | 5 +- .../database/viewmodel/DownloadViewModel.kt | 13 ++ .../database/viewmodel/ResultViewModel.kt | 8 ++ .../deniscerri/ytdl/receiver/ShareActivity.kt | 12 +- .../com/deniscerri/ytdl/ui/HomeFragment.kt | 4 + .../ConfigureMultipleDownloadsAdapter.kt | 5 + .../ytdl/ui/adapter/PlaylistAdapter.kt | 32 ++--- .../DownloadMultipleBottomSheetDialog.kt | 133 +++++++++++++++--- .../downloadcard/SelectPlaylistItemsDialog.kt | 17 ++- .../ui/downloads/ActiveDownloadsFragment.kt | 13 +- .../deniscerri/ytdl/ui/more/MoreFragment.kt | 50 ++++--- .../more/downloadLogs/DownloadLogFragment.kt | 12 +- .../com/deniscerri/ytdl/util/Extensions.kt | 38 +++-- .../deniscerri/ytdl/work/DownloadWorker.kt | 23 +-- .../ytdl/work/TerminalDownloadWorker.kt | 8 +- .../main/res/layout/bottom_app_bar_text.xml | 12 ++ app/src/main/res/layout/download_card.xml | 21 +++ .../main/res/layout/fragment_download_log.xml | 4 +- .../main/res/menu/download_multiple_menu.xml | 6 + .../main/res/xml/downloading_preferences.xml | 2 +- .../android/en-US/changelogs/10790.txt | 124 ++++++---------- 26 files changed, 378 insertions(+), 199 deletions(-) create mode 100644 app/src/main/res/layout/bottom_app_bar_text.xml diff --git a/app/build.gradle b/app/build.gradle index eecc9d4e..2ee78c91 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,7 +10,7 @@ plugins { def properties = new Properties() def versionMajor = 1 def versionMinor = 7 -def versionPatch = 8 +def versionPatch = 9 def versionBuild = 0 // bump for dogfood builds, public betas, etc. def isBeta = false def versionExt = isBeta ? ".${versionBuild}-beta" : "" diff --git a/app/src/main/java/com/deniscerri/ytdl/database/dao/DownloadDao.kt b/app/src/main/java/com/deniscerri/ytdl/database/dao/DownloadDao.kt index ec647dc7..c532c8b1 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/dao/DownloadDao.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/dao/DownloadDao.kt @@ -45,6 +45,11 @@ interface DownloadDao { """) fun getProcessingDownloadTypes() : List + @Query(""" + SELECT DISTINCT container from downloads where status = 'Processing' + """) + fun getProcessingDownloadContainers() : List + @Query("UPDATE downloads set status = 'Processing' WHERE id in (:ids)") suspend fun updateItemsToProcessing(ids: List) @@ -60,6 +65,9 @@ interface DownloadDao { @Query("UPDATE downloads set downloadPath=:path WHERE status ='Processing'") suspend fun updateProcessingDownloadPath(path: String) + @Query("UPDATE downloads set container=:cont WHERE status ='Processing'") + suspend fun updateProcessingContainer(cont: String) + @Query("SELECT * FROM downloads WHERE status='Active'") fun getActiveDownloadsList() : List diff --git a/app/src/main/java/com/deniscerri/ytdl/database/dao/LogDao.kt b/app/src/main/java/com/deniscerri/ytdl/database/dao/LogDao.kt index aa2915cd..3a99eebe 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/dao/LogDao.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/dao/LogDao.kt @@ -7,7 +7,6 @@ import androidx.room.Query import androidx.room.Transaction import androidx.room.Update import com.deniscerri.ytdl.database.models.LogItem -import com.deniscerri.ytdl.util.Extensions.appendLineToLog import kotlinx.coroutines.flow.Flow @Dao @@ -41,10 +40,15 @@ interface LogDao { suspend fun updateLogContent(l: String, id: Long) @Transaction - suspend fun updateLog(line: String, id: Long) { + suspend fun updateLog(line: String, id: Long, resetLog: Boolean = false) { val l = getByID(id) ?: return - val log = l.content ?: "" - updateLogContent(log.appendLineToLog(line), id) + var log = l.content ?: "" + if (resetLog) { + log = line.lines().joinToString("\n") + }else{ + log += "\n${line}" + } + updateLogContent(log, id) } @Update(onConflict = OnConflictStrategy.REPLACE) diff --git a/app/src/main/java/com/deniscerri/ytdl/database/dao/TerminalDao.kt b/app/src/main/java/com/deniscerri/ytdl/database/dao/TerminalDao.kt index f86cbf7c..a3ee24c7 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/dao/TerminalDao.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/dao/TerminalDao.kt @@ -5,7 +5,6 @@ import androidx.room.Insert import androidx.room.Query import androidx.room.Transaction import com.deniscerri.ytdl.database.models.TerminalItem -import com.deniscerri.ytdl.util.Extensions.appendLineToLog import kotlinx.coroutines.flow.Flow @Dao @@ -29,10 +28,15 @@ interface TerminalDao { @Query("SELECT * FROM terminalDownloads WHERE id=:id LIMIT 1") fun getTerminalById(id: Long) : TerminalItem? @Transaction - suspend fun updateLog(line: String, id: Long){ + suspend fun updateLog(line: String, id: Long, resetLog : Boolean = false){ val t = getTerminalById(id) ?: return - val log = t.log ?: "" - updateTerminalLog(log.appendLineToLog(line), id) + var log = t.log ?: "" + if (resetLog) { + log = line.lines().joinToString("\n") + }else{ + log += "\n${line}" + } + updateTerminalLog(log, id) } @Insert diff --git a/app/src/main/java/com/deniscerri/ytdl/database/models/DownloadItemConfigureMultiple.kt b/app/src/main/java/com/deniscerri/ytdl/database/models/DownloadItemConfigureMultiple.kt index b7c13454..3fb98cc2 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/models/DownloadItemConfigureMultiple.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/models/DownloadItemConfigureMultiple.kt @@ -13,6 +13,7 @@ data class DownloadItemConfigureMultiple( var title: String, var thumb: String, var duration: String, + var container: String, var format: Format, var allFormats: MutableList, var audioPreferences: AudioPreferences, diff --git a/app/src/main/java/com/deniscerri/ytdl/database/repository/LogRepository.kt b/app/src/main/java/com/deniscerri/ytdl/database/repository/LogRepository.kt index 4e5beb75..0a40a872 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/repository/LogRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/repository/LogRepository.kt @@ -2,7 +2,6 @@ package com.deniscerri.ytdl.database.repository import com.deniscerri.ytdl.database.dao.LogDao import com.deniscerri.ytdl.database.models.LogItem -import com.deniscerri.ytdl.util.Extensions.appendLineToLog import kotlinx.coroutines.flow.Flow class LogRepository(private val logDao: LogDao) { @@ -38,9 +37,9 @@ class LogRepository(private val logDao: LogDao) { return logDao.getByID(id) } - suspend fun update(line: String, id: Long){ + suspend fun update(line: String, id: Long, resetLog: Boolean = false){ kotlin.runCatching { - logDao.updateLog(line, id) + logDao.updateLog(line, id, resetLog) } } diff --git a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt index 3834ea6c..d756e7be 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt @@ -1015,6 +1015,14 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } } + suspend fun updateProcessingContainer(cont: String) { + var container = "" + if (cont != resources.getString(R.string.defaultValue)) { + container = cont + } + dao.updateProcessingContainer(container) + } + suspend fun updateProcessingDownloadPath(path: String){ dao.updateProcessingDownloadPath(path) } @@ -1111,6 +1119,11 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel return Pair(types.size == 1, Type.valueOf(types.first())) } + fun checkIfAllProcessingItemsHaveSameContainer() : Pair { + val containers = dao.getProcessingDownloadContainers() + return Pair(containers.size == 1, containers.first()) + } + suspend fun updateItemsWithIdsToProcessingStatus(ids: List) { repository.deleteProcessing() diff --git a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/ResultViewModel.kt b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/ResultViewModel.kt index 56bd8a90..3fbc2b07 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/ResultViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/ResultViewModel.kt @@ -175,6 +175,14 @@ class ResultViewModel(private val application: Application) : AndroidViewModel(a return repository.getItemByURL(url) } + fun getAllByURL(url: String) : List { + return repository.getAllByURL(url) + } + + fun getByID(id: Long) : ResultItem? { + return repository.getItemByID(id) + } + fun addSearchQueryToHistory(query: String) = viewModelScope.launch(Dispatchers.IO) { val allQueries = searchHistoryRepository.getAll() if (allQueries.none { it.query == query }){ diff --git a/app/src/main/java/com/deniscerri/ytdl/receiver/ShareActivity.kt b/app/src/main/java/com/deniscerri/ytdl/receiver/ShareActivity.kt index 75fc7237..83190253 100644 --- a/app/src/main/java/com/deniscerri/ytdl/receiver/ShareActivity.kt +++ b/app/src/main/java/com/deniscerri/ytdl/receiver/ShareActivity.kt @@ -33,6 +33,7 @@ import androidx.navigation.fragment.findNavController import androidx.preference.PreferenceManager import com.deniscerri.ytdl.MainActivity import com.deniscerri.ytdl.R +import com.deniscerri.ytdl.database.models.ResultItem import com.deniscerri.ytdl.database.viewmodel.CookieViewModel import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel import com.deniscerri.ytdl.database.viewmodel.HistoryViewModel @@ -185,13 +186,18 @@ class ShareActivity : BaseActivity() { val command = intent.getStringExtra("COMMAND") ?: "" lifecycleScope.launch { - var result = withContext(Dispatchers.IO){ - resultViewModel.getItemByURL(inputQuery) + val result: ResultItem + val existingResults = withContext(Dispatchers.IO){ + resultViewModel.getAllByURL(inputQuery) } - if (result == null) { + + if (existingResults.isEmpty() || existingResults.size > 1) { resultViewModel.deleteAll() result = downloadViewModel.createEmptyResultItem(inputQuery) + }else{ + result = existingResults.first() } + val downloadType = DownloadViewModel.Type.valueOf(type ?: downloadViewModel.getDownloadType(url = result.url).toString()) if (sharedPreferences.getBoolean("download_card", true) && !background){ val bundle = Bundle() diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/HomeFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/HomeFragment.kt index b08a3db0..64c96b24 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/HomeFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/HomeFragment.kt @@ -45,6 +45,7 @@ import com.deniscerri.ytdl.ui.adapter.HomeAdapter import com.deniscerri.ytdl.ui.adapter.SearchSuggestionsAdapter import com.deniscerri.ytdl.util.Extensions.enableFastScroll import com.deniscerri.ytdl.util.InfoUtil +import com.deniscerri.ytdl.util.NotificationUtil import com.deniscerri.ytdl.util.ThemeUtil import com.deniscerri.ytdl.util.UiUtil import com.facebook.shimmer.ShimmerFrameLayout @@ -84,6 +85,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti private var clipboardFab: ExtendedFloatingActionButton? = null private var homeFabs: LinearLayout? = null private var infoUtil: InfoUtil? = null + private var notificationUtil: NotificationUtil? = null private var downloadQueue: ArrayList? = null private lateinit var resultViewModel : ResultViewModel @@ -121,6 +123,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti mainActivity = activity as MainActivity? quickLaunchSheet = false infoUtil = InfoUtil(requireContext()) + notificationUtil = NotificationUtil(requireContext()) selectedObjects = arrayListOf() return fragmentView } @@ -306,6 +309,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti } if (arguments?.getBoolean("showDownloadsWithUpdatedFormats") == true){ + notificationUtil?.cancelDownloadNotification(NotificationUtil.FORMAT_UPDATING_FINISHED_NOTIFICATION_ID) arguments?.remove("showDownloadsWithUpdatedFormats") CoroutineScope(Dispatchers.IO).launch { val ids = arguments?.getLongArray("downloadIds") ?: return@launch diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/adapter/ConfigureMultipleDownloadsAdapter.kt b/app/src/main/java/com/deniscerri/ytdl/ui/adapter/ConfigureMultipleDownloadsAdapter.kt index 9131d708..83eab791 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/adapter/ConfigureMultipleDownloadsAdapter.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/adapter/ConfigureMultipleDownloadsAdapter.kt @@ -112,6 +112,10 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener codec.text = codecText } + val container = card.findViewById(R.id.container) + container.isVisible = item.container.isNotBlank() + container.text = item.container.uppercase() + val fileSize = card.findViewById(R.id.file_size) val fileSizeReadable = if(item.type != DownloadViewModel.Type.video){ FileUtil.convertFileSize(item.format.filesize) @@ -185,6 +189,7 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener override fun areContentsTheSame(oldItem: DownloadItemConfigureMultiple, newItem: DownloadItemConfigureMultiple): Boolean { return oldItem.title == newItem.title && oldItem.type == newItem.type && + oldItem.container == newItem.container && oldItem.videoPreferences == newItem.videoPreferences && oldItem.format == newItem.format && oldItem.incognito == newItem.incognito diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/adapter/PlaylistAdapter.kt b/app/src/main/java/com/deniscerri/ytdl/ui/adapter/PlaylistAdapter.kt index 1973b6b5..675a1ef2 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/adapter/PlaylistAdapter.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/adapter/PlaylistAdapter.kt @@ -27,7 +27,7 @@ import java.util.* class PlaylistAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter(AsyncDifferConfig.Builder( DIFF_CALLBACK ).build()) { - private val checkedItems: ArrayList + private val checkedItems: ArrayList private val onItemClickListener: OnItemClickListener private val activity: Activity private val sharedPreferences: SharedPreferences @@ -70,9 +70,9 @@ class PlaylistAdapter(onItemClickListener: OnItemClickListener, activity: Activi // CHECKBOX ---------------------------------- val check = card.findViewById(R.id.checkBox) - check.isChecked = checkedItems.contains(item.url) + check.isChecked = checkedItems.contains(item.id) check.setOnClickListener { - checkCard(check.isChecked, item.url) + checkCard(check.isChecked, item.id) } card.setOnClickListener { @@ -80,25 +80,25 @@ class PlaylistAdapter(onItemClickListener: OnItemClickListener, activity: Activi } } - private fun checkCard(isChecked: Boolean, itemURL: String) { + private fun checkCard(isChecked: Boolean, id: Long) { if (isChecked) { - checkedItems.add(itemURL) + checkedItems.add(id) } else { - checkedItems.remove(itemURL) + checkedItems.remove(id) } - onItemClickListener.onCardSelect(itemURL, isChecked, checkedItems) + onItemClickListener.onCardSelect(id, isChecked, checkedItems) } interface OnItemClickListener { - fun onCardSelect(itemURL: String, isChecked: Boolean, checkedItems: List) + fun onCardSelect(itemID: Long, isChecked: Boolean, checkedItems: List) } @SuppressLint("NotifyDataSetChanged") fun clearCheckeditems() { for (i in 0 until itemCount){ val item = getItem(i) - if (checkedItems.find { it == item?.url } != null){ - checkedItems.remove(item?.url) + if (checkedItems.find { it == item?.id } != null){ + checkedItems.remove(item?.id) notifyItemChanged(i) } } @@ -109,15 +109,15 @@ class PlaylistAdapter(onItemClickListener: OnItemClickListener, activity: Activi checkedItems.clear() for (i in 0 until itemCount){ val item = getItem(i) - checkedItems.add(item!!.url) + checkedItems.add(item!!.id) notifyItemChanged(i) } } fun invertSelected(items: List?){ - val invertedList = mutableListOf() + val invertedList = mutableListOf() items?.forEach { - if (!checkedItems.contains(it!!.url)) invertedList.add(it.url) + if (!checkedItems.contains(it!!.id)) invertedList.add(it.id) } checkedItems.clear() checkedItems.addAll(invertedList) @@ -128,19 +128,19 @@ class PlaylistAdapter(onItemClickListener: OnItemClickListener, activity: Activi checkedItems.clear() if (start == end ){ val item = getItem(start) - checkedItems.add(item!!.url) + checkedItems.add(item!!.id) notifyItemChanged(start) }else{ for (i in start..end){ val item = getItem(i) - checkedItems.add(item!!.url) + checkedItems.add(item!!.id) notifyItemChanged(i) } } } - fun getCheckedItems() : List{ + fun getCheckedItems() : List{ return checkedItems } 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 086a9a60..5474307e 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 @@ -16,12 +16,12 @@ import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.view.Window +import android.widget.PopupMenu import android.widget.TextView import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.core.os.bundleOf import androidx.core.view.children -import androidx.core.view.get import androidx.core.view.isVisible import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope @@ -90,6 +90,9 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure private lateinit var currentDownloadIDs: List private var processingItemsCount : Int = 0 + private lateinit var containerBtn : MenuItem + private lateinit var containerTextView: TextView + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) downloadViewModel = ViewModelProvider(requireActivity())[DownloadViewModel::class.java] @@ -145,10 +148,16 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure itemTouchHelper.attachToRecyclerView(recyclerView) } - scheduleBtn = view.findViewById(R.id.bottomsheet_schedule_button) - downloadBtn = view.findViewById(R.id.bottomsheet_download_button) + scheduleBtn = view.findViewById(R.id.bottomsheet_schedule_button) + downloadBtn = view.findViewById(R.id.bottomsheet_download_button) bottomAppBar = view.findViewById(R.id.bottomAppBar) + val preferredDownloadType = bottomAppBar.menu.findItem(R.id.preferred_download_type) + val formatBtn = bottomAppBar.menu.findItem(R.id.format) + val moreBtn = bottomAppBar.menu.findItem(R.id.more) + containerBtn = bottomAppBar.menu.findItem(R.id.container) + containerTextView = containerBtn.actionView as TextView + val incognitoBtn = bottomAppBar.menu.findItem(R.id.incognito) filesize = view.findViewById(R.id.filesize) count = view.findViewById(R.id.count) @@ -167,20 +176,28 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure lifecycleScope.launch { downloadViewModel.processingDownloads.collectLatest { items -> processingItemsCount = items.size - count.text = "${processingItemsCount} ${getString(R.string.selected)}" + count.text = "$processingItemsCount ${getString(R.string.selected)}" listAdapter.submitList(items) updateFileSize(items) if (items.isNotEmpty()){ if (items.all { it2 -> it2.type == items[0].type }) { - bottomAppBar.menu[1].icon?.alpha = 255 + formatBtn.icon?.alpha = 255 if (items[0].type != DownloadViewModel.Type.command) { - bottomAppBar.menu[4].icon?.alpha = 255 + moreBtn.icon?.alpha = 255 } + + containerBtn.isVisible = items.first().type != DownloadViewModel.Type.command } else { - bottomAppBar.menu[1].icon?.alpha = 30 - bottomAppBar.menu[4].icon?.alpha = 30 + formatBtn.icon?.alpha = 30 + moreBtn.icon?.alpha = 30 + } + + if (items.all { it.container == items[0].container }) { + setContainerText(items[0].container) + }else{ + setContainerText("") } val type = items.first().type @@ -191,10 +208,10 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure } DownloadViewModel.Type.video -> { preferredDownloadType.setIcon(R.drawable.baseline_video_file_24) - } DownloadViewModel.Type.command -> { preferredDownloadType.setIcon(R.drawable.baseline_insert_drive_file_24) + setContainerText("") } else -> {} @@ -303,7 +320,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure downloadViewModel.areAllProcessingIncognito() } - bottomAppBar.menu.children.first { it.itemId == R.id.incognito }.icon!!.apply { + incognitoBtn.icon!!.apply { alpha = if (allIncognito){ 255 }else{ @@ -336,7 +353,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure R.id.incognito -> { lifecycleScope.launch { if (m.icon!!.alpha == 255) { - bottomAppBar.menu[3].isEnabled = false + incognitoBtn.isEnabled = false withContext(Dispatchers.IO) { downloadViewModel.updateProcessingIncognito(false) withContext(Dispatchers.Main){ @@ -344,18 +361,18 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure m.isEnabled = true } } - bottomAppBar.menu[3].icon?.alpha = 30 - bottomAppBar.menu[3].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{ - bottomAppBar.menu[3].isEnabled = false + incognitoBtn.isEnabled = false withContext(Dispatchers.IO) { downloadViewModel.updateProcessingIncognito(true) withContext(Dispatchers.Main){ } } - bottomAppBar.menu[3].icon?.alpha = 255 - bottomAppBar.menu[3].isEnabled = true + incognitoBtn.icon?.alpha = 255 + incognitoBtn.isEnabled = true Toast.makeText(requireContext(), "${getString(R.string.incognito)}: ${getString(R.string.ok)}", Toast.LENGTH_SHORT).show() } } @@ -393,10 +410,20 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure downloadViewModel.updateProcessingType(DownloadViewModel.Type.audio) withContext(Dispatchers.Main){ preferredDownloadType.setIcon(R.drawable.baseline_audio_file_24) - bottomAppBar.menu[1].icon?.alpha = 255 - bottomAppBar.menu[4].icon?.alpha = 255 + formatBtn.icon?.alpha = 255 + moreBtn.icon?.alpha = 255 bottomSheet.cancel() } + + val res = downloadViewModel.checkIfAllProcessingItemsHaveSameContainer() + withContext(Dispatchers.Main) { + if (!res.first) { + setContainerText("") + }else{ + setContainerText(res.second) + } + } + } } @@ -405,10 +432,19 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure downloadViewModel.updateProcessingType(DownloadViewModel.Type.video) withContext(Dispatchers.Main){ preferredDownloadType.setIcon(R.drawable.baseline_video_file_24) - bottomAppBar.menu[1].icon?.alpha = 255 - bottomAppBar.menu[4].icon?.alpha = 255 + formatBtn.icon?.alpha = 255 + moreBtn.icon?.alpha = 255 bottomSheet.cancel() } + + val res = downloadViewModel.checkIfAllProcessingItemsHaveSameContainer() + withContext(Dispatchers.Main) { + if (!res.first) { + setContainerText("") + }else{ + setContainerText(res.second) + } + } } } @@ -417,10 +453,12 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure downloadViewModel.updateProcessingType(DownloadViewModel.Type.command) withContext(Dispatchers.Main){ preferredDownloadType.setIcon(R.drawable.baseline_insert_drive_file_24) - bottomAppBar.menu[1].icon?.alpha = 255 - bottomAppBar.menu[4].icon?.alpha = 30 + formatBtn.icon?.alpha = 255 + moreBtn.icon?.alpha = 30 bottomSheet.cancel() + containerBtn.isVisible = false } + } } @@ -676,6 +714,41 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure true } + containerTextView.setOnClickListener { + lifecycleScope.launch { + val res = withContext(Dispatchers.IO){ + downloadViewModel.checkIfAllProcessingItemsHaveSameType() + } + if (!res.first){ + Toast.makeText(requireContext(), getString(R.string.format_filtering_hint), Toast.LENGTH_SHORT).show() + }else{ + val popup = PopupMenu(activity, containerTextView) + when(res.second) { + DownloadViewModel.Type.audio -> resources.getStringArray(R.array.audio_containers) + //video + else -> resources.getStringArray(R.array.video_containers) + }.forEach { + popup.menu.add(it) + } + + popup.setOnMenuItemClickListener { mm -> + val container = mm.title + lifecycleScope.launch { + withContext(Dispatchers.IO){ + downloadViewModel.updateProcessingContainer(container.toString()) + } + } + setContainerText(container.toString()) + true + } + + popup.show() + } + + + } + } + } private fun toggleLoading(loading: Boolean){ @@ -964,5 +1037,21 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure } } + @SuppressLint("SetTextI18n") + private fun setContainerText(cont: String?) { + if (cont == null) { + containerBtn.isVisible = false + return + } + + if (cont == getString(R.string.defaultValue) || cont.isEmpty()) { + containerTextView.text = ".ext" + return + }else{ + containerTextView.text = cont + } + containerBtn.isVisible = true + } + } diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/SelectPlaylistItemsDialog.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/SelectPlaylistItemsDialog.kt index 5d04605c..159d4c9d 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/SelectPlaylistItemsDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/SelectPlaylistItemsDialog.kt @@ -55,7 +55,6 @@ class SelectPlaylistItemsDialog : BottomSheetDialogFragment(), PlaylistAdapter.O private lateinit var selectBetween: MenuItem private lateinit var resultItemIDs: List - private lateinit var itemURLs: List private var items = listOf() override fun onCreate(savedInstanceState: Bundle?) { @@ -179,9 +178,9 @@ class SelectPlaylistItemsDialog : BottomSheetDialogFragment(), PlaylistAdapter.O ok.isEnabled = false lifecycleScope.launch(Dispatchers.IO) { val checkedItems = listAdapter.getCheckedItems() - val checkedResultItems = items.filter { item -> checkedItems.contains(item.url) } + val checkedResultItems = items.filter { item -> checkedItems.contains(item.id) } if (checkedResultItems.size == 1){ - val resultItem = resultViewModel.getItemByURL(checkedResultItems[0].url)!! + val resultItem = resultViewModel.getByID(checkedResultItems[0].id)!! withContext(Dispatchers.Main){ findNavController().navigate(R.id.action_selectPlaylistItemsDialog_to_downloadBottomSheetDialog, bundleOf( Pair("result", resultItem), @@ -223,8 +222,8 @@ class SelectPlaylistItemsDialog : BottomSheetDialogFragment(), PlaylistAdapter.O if(selectedItems.size != 2){ m.isVisible = false }else{ - val item2 = itemURLs.indexOf(selectedItems.last()) - val item1 = itemURLs.indexOf(selectedItems.first()) + val item2 = resultItemIDs.indexOf(selectedItems.last()) + val item1 = resultItemIDs.indexOf(selectedItems.first()) if(item1 > item2) listAdapter.checkRange(item2, item1) else listAdapter.checkRange(item1, item2) count.text = "${listAdapter.getCheckedItems().size} ${resources.getString(R.string.selected)}" @@ -249,7 +248,7 @@ class SelectPlaylistItemsDialog : BottomSheetDialogFragment(), PlaylistAdapter.O toTextInput.isEnabled = !isLoading items = it - itemURLs = items.map { itm -> itm.url } + resultItemIDs = items.map { itm -> itm.id } } } @@ -284,7 +283,7 @@ class SelectPlaylistItemsDialog : BottomSheetDialogFragment(), PlaylistAdapter.O } - override fun onCardSelect(itemURL: String, isChecked: Boolean, checkedItems: List) { + override fun onCardSelect(itemID: Long, isChecked: Boolean, checkedItems: List) { if (checkedItems.size == items.size){ count.text = "(${listAdapter.getCheckedItems().size}) ${resources.getString(R.string.all_items_selected)} " }else{ @@ -302,8 +301,8 @@ class SelectPlaylistItemsDialog : BottomSheetDialogFragment(), PlaylistAdapter.O val size = checkedItems.size if(size != 2) false else { - val item1 = itemURLs.indexOf(checkedItems.first()) - val item2 = itemURLs.indexOf(checkedItems.last()) + val item1 = resultItemIDs.indexOf(checkedItems.first()) + val item2 = resultItemIDs.indexOf(checkedItems.last()) (item1-item2).absoluteValue > 1 } diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ActiveDownloadsFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ActiveDownloadsFragment.kt index ecdfdb7d..03f8a0f8 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ActiveDownloadsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ActiveDownloadsFragment.kt @@ -167,12 +167,17 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis } lifecycleScope.launch { - val activeQueuedDownloadsCount = withContext(Dispatchers.IO) { - downloadViewModel.getActiveQueuedDownloadsCount() + val activeCount = withContext(Dispatchers.IO) { + downloadViewModel.getActiveDownloadsCount() } + + val queuedCount = withContext(Dispatchers.IO) { + downloadViewModel.getQueuedDownloadsCount() + } + val pausedDownloads = preferences.getBoolean("paused_downloads", false) - pause.isVisible = activeQueuedDownloadsCount > 0 && !pausedDownloads - resume.isVisible = activeQueuedDownloadsCount > 0 && pausedDownloads + pause.isVisible = (queuedCount > 0 || activeCount > 0) && !pausedDownloads + resume.isVisible = (queuedCount > 0 || activeCount > 0) && pausedDownloads } lifecycleScope.launch { diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/more/MoreFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/more/MoreFragment.kt index 82e375ae..1d329075 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/more/MoreFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/more/MoreFragment.kt @@ -15,6 +15,7 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.preference.PreferenceManager +import androidx.work.WorkManager import com.deniscerri.ytdl.MainActivity import com.deniscerri.ytdl.R import com.deniscerri.ytdl.database.repository.DownloadRepository @@ -22,11 +23,14 @@ import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel import com.deniscerri.ytdl.ui.more.settings.SettingsActivity import com.deniscerri.ytdl.ui.more.terminal.TerminalActivity import com.deniscerri.ytdl.util.NavbarUtil +import com.deniscerri.ytdl.util.NotificationUtil import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.yausername.youtubedl_android.YoutubeDL import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext import kotlin.system.exitProcess class MoreFragment : Fragment() { @@ -125,28 +129,40 @@ class MoreFragment : Fragment() { doNotShowAgain = compoundButton.isChecked } + val workManager = WorkManager.getInstance(requireContext()) + val notificationUtil = NotificationUtil(requireContext()) + terminateDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() } terminateDialog.setPositiveButton(getString(R.string.ok)) { diag: DialogInterface?, _: Int -> - runBlocking { - val job : Job = lifecycleScope.launch(Dispatchers.IO) { - val activeDownloads = downloadViewModel.getActiveDownloads().toMutableList() - activeDownloads.map { it.status = DownloadRepository.Status.Queued.toString() } - activeDownloads.forEach { - downloadViewModel.updateDownload(it) - } + lifecycleScope.launch { + val activeDownloads = withContext(Dispatchers.IO){ + downloadViewModel.getActiveDownloadsCount() } - runBlocking { - job.join() - if (doNotShowAgain){ - mainSharedPreferencesEditor.putBoolean("ask_terminate_app", false) - mainSharedPreferencesEditor.commit() + if (activeDownloads > 0) { + workManager.cancelAllWorkByTag("download") + val activeDownloadsList = withContext(Dispatchers.IO){ + downloadViewModel.getActiveDownloads() } - mainActivity.finishAndRemoveTask() - mainActivity.finishAffinity() - exitProcess(0) - } - } + activeDownloadsList.forEach { + it.status = DownloadRepository.Status.Queued.toString() + YoutubeDL.getInstance().destroyProcessById(it.id.toString()) + notificationUtil.cancelDownloadNotification(it.id.toInt()) + withContext(Dispatchers.IO) { + downloadViewModel.updateDownload(it) + } + } + mainSharedPreferencesEditor.putBoolean("paused_downloads", true).apply() + } + + if (doNotShowAgain){ + mainSharedPreferencesEditor.putBoolean("ask_terminate_app", false).apply() + } + mainSharedPreferencesEditor.commit() + mainActivity.finishAndRemoveTask() + mainActivity.finishAffinity() + exitProcess(0) + } } terminateDialog.show() }else{ diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogFragment.kt index 7d25eae4..229be4d9 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogFragment.kt @@ -20,10 +20,12 @@ import androidx.core.view.get import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import androidx.preference.PreferenceManager import com.deniscerri.ytdl.MainActivity import com.deniscerri.ytdl.R import com.deniscerri.ytdl.database.viewmodel.LogViewModel +import com.deniscerri.ytdl.util.Extensions.enableFastScroll import com.deniscerri.ytdl.util.Extensions.enableTextHighlight import com.deniscerri.ytdl.util.Extensions.setCustomTextSize import com.google.android.material.appbar.MaterialToolbar @@ -104,8 +106,14 @@ class DownloadLogFragment : Fragment() { } } - content.isFocusable = true - content.enableTextHighlight() + lifecycleScope.launch(Dispatchers.IO){ + content.isFocusable = true + content.enableTextHighlight() + } + + contentScrollView.enableFastScroll() + + val slider = view.findViewById(R.id.textsize_seekbar) bottomAppBar?.setOnMenuItemClickListener { m: MenuItem -> when(m.itemId){ diff --git a/app/src/main/java/com/deniscerri/ytdl/util/Extensions.kt b/app/src/main/java/com/deniscerri/ytdl/util/Extensions.kt index 2be2d9a0..2d655873 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/Extensions.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/Extensions.kt @@ -28,6 +28,7 @@ import android.view.ViewOutlineProvider import android.view.animation.Interpolator import android.widget.EditText import android.widget.ImageView +import android.widget.ScrollView import android.widget.TextView import androidx.annotation.Px import androidx.core.content.ContextCompat @@ -139,6 +140,15 @@ object Extensions { .build() } + fun ScrollView.enableFastScroll() { + val drawable = ShapeDrawable(OvalShape()) + drawable.paint.color = context.getColor(android.R.color.transparent) + + FastScrollerBuilder(this) + .useMd2Style() + .setTrackDrawable(drawable) + .build() + } fun File.getMediaDuration(context: Context): Int { return kotlin.runCatching { if (!exists()) return 0 @@ -251,20 +261,20 @@ object Extensions { } } - fun String.appendLineToLog(line: String): String { - val lines = this.split("\n") - if (!lines.takeLast(3).contains(line)){ - //clean dublicate progress + add newline - var newLine = line - if (newLine.contains("[download")) { - newLine = "[download]" + line.split("[download]").last() - } - - return lines.dropLastWhile { it.contains("[download") }.joinToString("\n") + "\n${newLine}" - } - - return this - } +// fun String.appendLineToLog(line: String): String { +// val lines = this.split("\n") +// if (!lines.takeLast(3).contains(line)){ +// //clean dublicate progress + add newline +// var newLine = line +// if (newLine.contains("[download")) { +// newLine = "[download]" + line.split("[download]").last() +// } +// +// return lines.dropLastWhile { it.contains("[download") }.joinToString("\n") + "\n${newLine}" +// } +// +// return this +// } fun ImageView.loadThumbnail(hideThumb: Boolean, imageURL: String){ if(!hideThumb){ diff --git a/app/src/main/java/com/deniscerri/ytdl/work/DownloadWorker.kt b/app/src/main/java/com/deniscerri/ytdl/work/DownloadWorker.kt index 46215f49..d285036d 100644 --- a/app/src/main/java/com/deniscerri/ytdl/work/DownloadWorker.kt +++ b/app/src/main/java/com/deniscerri/ytdl/work/DownloadWorker.kt @@ -97,11 +97,15 @@ class DownloadWorker( val running = ArrayList(runningYTDLInstances) val useScheduler = sharedPreferences.getBoolean("use_scheduler", false) - if (items.isEmpty() && running.isEmpty()) WorkManager.getInstance(context).cancelWorkById(this@DownloadWorker.id) + if (items.isEmpty() && running.isEmpty()) { + WorkManager.getInstance(context).cancelWorkById(this@DownloadWorker.id) + return@collect + } if (useScheduler){ if (items.none{it.downloadStartTime > 0L} && running.isEmpty() && !alarmScheduler.isDuringTheScheduledTime()) { WorkManager.getInstance(context).cancelWorkById(this@DownloadWorker.id) + return@collect } } val concurrentDownloads = sharedPreferences.getInt("concurrent_downloads", 1) - running.size @@ -138,15 +142,16 @@ class DownloadWorker( val commandString = infoUtil.parseYTDLRequestString(request) - val logString = StringBuilder("\n ${commandString}\n\n") + val initialLogDetails = "Downloading:\n" + + "Title: ${downloadItem.title}\n" + + "URL: ${downloadItem.url}\n" + + "Type: ${downloadItem.type}\n" + + "Command: \n $commandString \n\n" + val logString = StringBuilder(initialLogDetails) val logItem = LogItem( 0, downloadItem.title.ifBlank { downloadItem.url }, - "Downloading:\n" + - "Title: ${downloadItem.title}\n" + - "URL: ${downloadItem.url}\n" + - "Type: ${downloadItem.type}\n" + - "Command: $logString", + logString.toString(), downloadItem.format, downloadItem.type, System.currentTimeMillis(), @@ -182,7 +187,7 @@ class DownloadWorker( resultRepo.updateDownloadItem(downloadItem)?.apply { dao.updateWithoutUpsert(this) } - val wasQuickDownloaded = resultDao.getCountInt() == 0 + //val wasQuickDownloaded = resultDao.getCountInt() == 0 runBlocking { var finalPaths : MutableList? @@ -304,7 +309,7 @@ class DownloadWorker( dao.delete(downloadItem.id) if (logDownloads){ - logRepo.update(it.out, logItem.id) + logRepo.update(initialLogDetails + "\n" + it.out, logItem.id, true) } } diff --git a/app/src/main/java/com/deniscerri/ytdl/work/TerminalDownloadWorker.kt b/app/src/main/java/com/deniscerri/ytdl/work/TerminalDownloadWorker.kt index 902abf23..d90a273b 100644 --- a/app/src/main/java/com/deniscerri/ytdl/work/TerminalDownloadWorker.kt +++ b/app/src/main/java/com/deniscerri/ytdl/work/TerminalDownloadWorker.kt @@ -88,11 +88,12 @@ class TerminalDownloadWorker( val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !sharedPreferences.getBoolean("incognito", false) + val initialLogDetails = "Terminal Task\n" + + "Command: \n ${command}\n\n" val logItem = LogItem( 0, "Terminal Task", - "Terminal Task\n" + - "Command: \n ${command}\n\n", + initialLogDetails, Format(), DownloadViewModel.Type.command, System.currentTimeMillis(), @@ -139,8 +140,7 @@ class TerminalDownloadWorker( } } } - - if (logDownloads) logRepo.update(it.out, logItem.id) + if (logDownloads) logRepo.update(initialLogDetails + "\n" + it.out, logItem.id, true) dao.updateLog(it.out, itemId.toLong()) notificationUtil.cancelDownloadNotification(itemId) delay(1000) diff --git a/app/src/main/res/layout/bottom_app_bar_text.xml b/app/src/main/res/layout/bottom_app_bar_text.xml new file mode 100644 index 00000000..528fd67c --- /dev/null +++ b/app/src/main/res/layout/bottom_app_bar_text.xml @@ -0,0 +1,12 @@ + +