Merge remote-tracking branch 'origin/main'

This commit is contained in:
Hosted Weblate 2023-03-13 22:48:28 +01:00
commit 75ccc5d4e6
No known key found for this signature in database
GPG key ID: A3FAAA06E6569B4C
31 changed files with 383 additions and 273 deletions

View file

@ -320,7 +320,7 @@
<PersistentState>
<option name="values">
<map>
<entry key="url" value="file:/$USER_HOME$/AppData/Local/Android/Sdk/icons/material/materialicons/video_file/baseline_video_file_24.xml" />
<entry key="url" value="file:/$USER_HOME$/AppData/Local/Android/Sdk/icons/material/materialicons/folder_delete/baseline_folder_delete_24.xml" />
</map>
</option>
</PersistentState>
@ -330,8 +330,7 @@
</option>
<option name="values">
<map>
<entry key="assetSourceType" value="FILE" />
<entry key="outputName" value="ic_translate" />
<entry key="outputName" value="ic_folder_delete" />
<entry key="sourceFile" value="C:\Users\denis\Desktop\FREKUENCA\icons8-google-translate.svg" />
</map>
</option>

View file

@ -9,7 +9,13 @@
<div align="center">
![CI](https://github.com/deniscerri/ytdlnis/actions/workflows/android.yml/badge.svg?branch=main&event=pull) [![stable release](https://img.shields.io/github/release/deniscerri/ytdlnis.svg?maxAge=3600&label=download)](https://github.com/deniscerri/ytdlnis/releases/latest) [![downloads](https://img.shields.io/github/downloads/deniscerri/ytdlnis/total?style=flat-square)](https://github.com/deniscerri/ytdlnis/releases) [![Translation status](https://hosted.weblate.org/widgets/ytdlnis/-/svg-badge.svg)](https://hosted.weblate.org/engage/ytdlnis/?utm_source=widget) [![community](https://img.shields.io/badge/Discord-YTDLnis-blueviolet?style=flat-square&logo=discord)](https://discord.gg/WW3KYWxAPm)
![CI](https://github.com/deniscerri/ytdlnis/actions/workflows/android.yml/badge.svg?branch=main&event=pull)
[![stable release](https://img.shields.io/github/release/deniscerri/ytdlnis.svg?maxAge=3600&label=download)](https://github.com/deniscerri/ytdlnis/releases/latest)
[![preview release](https://img.shields.io/github/release/deniscerri/ytdlnis.svg?maxAge=3600&include_prereleases&label=preview)](https://github.com/deniscerri/ytdlnis/releases)
[![downloads](https://img.shields.io/github/downloads/deniscerri/ytdlnis/total?style=flat-square)](https://github.com/deniscerri/ytdlnis/releases)
[![Translation status](https://hosted.weblate.org/widgets/ytdlnis/-/svg-badge.svg)](https://hosted.weblate.org/engage/ytdlnis/?utm_source=widget)
[![community](https://img.shields.io/badge/Discord-YTDLnis-blueviolet?style=flat-square&logo=discord)](https://discord.gg/WW3KYWxAPm)
[![community](https://img.shields.io/badge/Telegram-YTDLnis-blue?style=flat-square&logo=telegram)](https://t.me/ytdlnis)

View file

@ -20,6 +20,7 @@ import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.squareup.picasso.Picasso
import java.util.zip.GZIPOutputStream
class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<DownloadItem?, ActiveDownloadAdapter.ViewHolder>(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) {
private val onItemClickListener: OnItemClickListener
@ -103,6 +104,7 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
val fileSize = card.findViewById<TextView>(R.id.file_size)
fileSize.text = fileUtil.convertFileSize(item.format.filesize)
if (fileSize.text == "?") fileSize.visibility = View.GONE
//OUTPUT
val output = card.findViewById<TextView>(R.id.output)

View file

@ -74,7 +74,9 @@ class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
itemUrl.text = item.url
val formatNote = card.findViewById<Chip>(R.id.format_note)
formatNote!!.text = item.format.format_note.uppercase()
if (item.format.format_note == "?" || item.format.format_note == "") formatNote!!.visibility =
View.GONE
else formatNote!!.text = item.format.format_note.uppercase()
val codec = card.findViewById<Chip>(R.id.codec)
val codecText =

View file

@ -92,22 +92,6 @@ class HistoryAdapter(onItemClickListener: OnItemClickListener, activity: Activit
System.currentTimeMillis(),
DateUtils.MINUTE_IN_MILLIS
)
//
// val time = item.time
// val downloadedTime: String
// if (time == 0L) {
// downloadedTime = activity.getString(R.string.currently_downloading) + " " + item.type
// } else {
// val cal = Calendar.getInstance()
// val date = Date(time * 1000L)
// cal.time = date
// val day = cal[Calendar.DAY_OF_MONTH]
// val month = cal.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault())
// val year = cal[Calendar.YEAR]
// val formatter: DateFormat = SimpleDateFormat("HH:mm", Locale.getDefault())
// val timeString = formatter.format(date)
// downloadedTime = "$day $month $year - $timeString"
// }
datetime.text = relativeTime
// BUTTON ----------------------------------

View file

@ -12,7 +12,7 @@ class HistoryRepository(private val historyDao: HistoryDao) {
}
enum class HistorySortType {
DATE, TITLE, AUTHOR
DATE, TITLE, AUTHOR, FILESIZE
}
suspend fun getItem(id: Int) : HistoryItem {
@ -24,6 +24,13 @@ class HistoryRepository(private val historyDao: HistoryDao) {
HistorySortType.DATE -> historyDao.getHistorySortedByID(query, format, site, sort.toString())
HistorySortType.TITLE -> historyDao.getHistorySortedByTitle(query, format, site, sort.toString())
HistorySortType.AUTHOR -> historyDao.getHistorySortedByAuthor(query, format, site, sort.toString())
HistorySortType.FILESIZE -> {
val items = historyDao.getHistorySortedByID(query, format, site, sort.toString())
when(sort){
HistorySort.DESC -> items.sortedByDescending { it.format.filesize }
HistorySort.ASC -> items.sortedBy { it.format.filesize }
}
}
}
}

View file

@ -22,6 +22,8 @@ import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type
import com.deniscerri.ytdlnis.receiver.ShareActivity
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.UiUtil
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.button.MaterialButton
@ -34,6 +36,7 @@ import com.google.android.material.timepicker.TimeFormat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.util.*
@ -44,11 +47,13 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
private lateinit var downloadViewModel: DownloadViewModel
private lateinit var behavior: BottomSheetBehavior<View>
private lateinit var commandTemplateDao : CommandTemplateDao
private lateinit var uiUtil: UiUtil
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
commandTemplateDao = DBManager.getInstance(requireContext()).commandTemplateDao
uiUtil = UiUtil(FileUtil())
}
@SuppressLint("RestrictedApi")
@ -139,43 +144,12 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
val scheduleBtn = view.findViewById<MaterialButton>(R.id.bottomsheet_schedule_button)
scheduleBtn.setOnClickListener{
val currentDate = Calendar.getInstance()
val date = Calendar.getInstance()
val datepicker = MaterialDatePicker.Builder.datePicker()
.setCalendarConstraints(
CalendarConstraints.Builder()
.setValidator(DateValidatorPointForward.now())
.build()
)
.setSelection(MaterialDatePicker.todayInUtcMilliseconds())
.build()
datepicker.addOnPositiveButtonClickListener{
date.timeInMillis = it
val timepicker = MaterialTimePicker.Builder()
.setTimeFormat(TimeFormat.CLOCK_24H)
.setHour(currentDate.get(Calendar.HOUR_OF_DAY))
.setMinute(currentDate.get(Calendar.MINUTE))
.build()
timepicker.addOnPositiveButtonClickListener{
date[Calendar.HOUR_OF_DAY] = timepicker.hour
date[Calendar.MINUTE] = timepicker.minute
val item: DownloadItem = getDownloadItem();
item.downloadStartTime = date.timeInMillis
downloadViewModel.queueDownloads(listOf(item))
dismiss()
}
timepicker.show(fragmentManager, "timepicker")
uiUtil.showDatePicker(fragmentManager) {
val item: DownloadItem = getDownloadItem();
item.downloadStartTime = it.timeInMillis
downloadViewModel.queueDownloads(listOf(item))
dismiss()
}
datepicker.show(fragmentManager, "datepicker")
}
val download = view.findViewById<Button>(R.id.bottomsheet_download_button)

View file

@ -25,6 +25,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.receiver.ShareActivity
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.UiUtil
import com.google.android.material.bottomappbar.BottomAppBar
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
@ -49,6 +50,7 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
private lateinit var recyclerView: RecyclerView
private lateinit var behavior: BottomSheetBehavior<View>
private lateinit var fileUtil: FileUtil
private lateinit var uiUtil: UiUtil
override fun onCreate(savedInstanceState: Bundle?) {
@ -57,6 +59,7 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java]
fileUtil = FileUtil()
uiUtil = UiUtil(fileUtil)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -70,7 +73,6 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
super.setupDialog(dialog, style)
val view = LayoutInflater.from(context).inflate(R.layout.download_multiple_bottom_sheet, null)
dialog.setContentView(view)
//view.minimumHeight = resources.displayMetrics.heightPixels
dialog.setOnShowListener {
behavior = BottomSheetBehavior.from(view.parent as View)
@ -92,44 +94,13 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
val scheduleBtn = view.findViewById<MaterialButton>(R.id.bottomsheet_schedule_button)
scheduleBtn.setOnClickListener{
val currentDate = Calendar.getInstance()
val date = Calendar.getInstance()
val datepicker = MaterialDatePicker.Builder.datePicker()
.setCalendarConstraints(
CalendarConstraints.Builder()
.setValidator(DateValidatorPointForward.now())
.build()
)
.setSelection(MaterialDatePicker.todayInUtcMilliseconds())
.build()
datepicker.addOnPositiveButtonClickListener{
date.timeInMillis = it
val timepicker = MaterialTimePicker.Builder()
.setTimeFormat(TimeFormat.CLOCK_24H)
.setHour(currentDate.get(Calendar.HOUR_OF_DAY))
.setMinute(currentDate.get(Calendar.MINUTE))
.build()
timepicker.addOnPositiveButtonClickListener{
date[Calendar.HOUR_OF_DAY] = timepicker.hour
date[Calendar.MINUTE] = timepicker.minute
items.forEach { item ->
item.downloadStartTime = date.timeInMillis
}
downloadViewModel.queueDownloads(items)
dismiss()
uiUtil.showDatePicker(parentFragmentManager) {
items.forEach { item ->
item.downloadStartTime = it.timeInMillis
}
timepicker.show(parentFragmentManager, "timepicker")
downloadViewModel.queueDownloads(items)
dismiss()
}
datepicker.show(parentFragmentManager, "datepicker")
}
val download = view.findViewById<Button>(R.id.bottomsheet_download_button)

View file

@ -69,6 +69,7 @@ class FormatSelectionBottomSheetDialog(private val item: DownloadItem, private v
Type.audio -> formats.filter { it.format_note.contains("audio", ignoreCase = true) }
else -> formats.filter { !it.format_note.contains("audio", ignoreCase = true) }
}
if (formats.isEmpty()) throw Exception()
addFormatsToView(linearLayout)
refreshBtn.visibility = View.GONE
}catch (e: Exception){

View file

@ -27,6 +27,7 @@ import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.UiUtil
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.button.MaterialButton
import com.google.android.material.chip.Chip
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -113,9 +114,12 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
btn.icon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_terminal)
}
val formatNote = bottomSheet.findViewById<TextView>(R.id.format_note)
val codec = bottomSheet.findViewById<TextView>(R.id.codec)
val fileSize = bottomSheet.findViewById<TextView>(R.id.file_size)
val time = bottomSheet.findViewById<Chip>(R.id.time)
val formatNote = bottomSheet.findViewById<Chip>(R.id.format_note)
val codec = bottomSheet.findViewById<Chip>(R.id.codec)
val fileSize = bottomSheet.findViewById<Chip>(R.id.file_size)
time!!.visibility = View.GONE
if (item.format.format_note == "?" || item.format.format_note == "") formatNote!!.visibility =
View.GONE

View file

@ -30,6 +30,7 @@ import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.UiUtil
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.button.MaterialButton
import com.google.android.material.chip.Chip
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.io.File
@ -115,10 +116,13 @@ class ErroredDownloadsFragment() : Fragment(), GenericDownloadAdapter.OnItemClic
btn.icon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_terminal)
}
val time = bottomSheet.findViewById<Chip>(R.id.time)
val formatNote = bottomSheet.findViewById<TextView>(R.id.format_note)
val codec = bottomSheet.findViewById<TextView>(R.id.codec)
val fileSize = bottomSheet.findViewById<TextView>(R.id.file_size)
time!!.visibility = View.GONE
if (item.format.format_note == "?" || item.format.format_note == "") formatNote!!.visibility =
View.GONE
else formatNote!!.text = item.format.format_note

View file

@ -8,6 +8,7 @@ import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.InputType
import android.text.format.DateUtils
import android.util.Log
import android.view.*
import android.view.View.*
@ -40,6 +41,10 @@ import com.google.android.material.chip.ChipGroup
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import java.io.File
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.ArrayList
/**
* A fragment representing a list of Items.
@ -239,12 +244,15 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
}
}
private fun changeSortIcon(item: LinearLayout, order: HistorySort){
private fun changeSortIcon(item: TextView, order: HistorySort){
when(order){
HistorySort.DESC -> (item.getChildAt(0) as ImageView).setImageResource(R.drawable.ic_up)
HistorySort.ASC -> (item.getChildAt(0) as ImageView).setImageResource(R.drawable.ic_down)
HistorySort.DESC ->{
item.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_up, 0,0,0)
}
HistorySort.ASC -> {
item.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_down, 0,0,0)
}
}
item.getChildAt(0).visibility = VISIBLE
}
@ -255,33 +263,40 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
sortSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
sortSheet!!.setContentView(R.layout.history_sort_sheet)
val date = sortSheet!!.findViewById<LinearLayout>(R.id.date)
val title = sortSheet!!.findViewById<LinearLayout>(R.id.title)
val author = sortSheet!!.findViewById<LinearLayout>(R.id.author)
val date = sortSheet!!.findViewById<TextView>(R.id.date)
val title = sortSheet!!.findViewById<TextView>(R.id.title)
val author = sortSheet!!.findViewById<TextView>(R.id.author)
val filesize = sortSheet!!.findViewById<TextView>(R.id.filesize)
val sortOptions = listOf(date!!, title!!, author!!)
sortOptions.forEach { it.getChildAt(0).visibility = INVISIBLE }
val sortOptions = listOf(date!!, title!!, author!!, filesize!!)
sortOptions.forEach { it.setCompoundDrawablesWithIntrinsicBounds(R.drawable.empty,0,0,0) }
when(historyViewModel.sortType.value!!) {
HistoryRepository.HistorySortType.DATE -> changeSortIcon(date, historyViewModel.sortOrder.value!!)
HistoryRepository.HistorySortType.TITLE -> changeSortIcon(title, historyViewModel.sortOrder.value!!)
HistoryRepository.HistorySortType.AUTHOR -> changeSortIcon(author, historyViewModel.sortOrder.value!!)
HistoryRepository.HistorySortType.FILESIZE -> changeSortIcon(filesize, historyViewModel.sortOrder.value!!)
}
date.getChildAt(1).setOnClickListener {
sortOptions.forEach { it.getChildAt(0).visibility = INVISIBLE }
date.setOnClickListener {
sortOptions.forEach { it.setCompoundDrawablesWithIntrinsicBounds(R.drawable.empty,0,0,0) }
historyViewModel.setSorting(HistoryRepository.HistorySortType.DATE)
changeSortIcon(date, historyViewModel.sortOrder.value!!)
}
title.getChildAt(1).setOnClickListener {
sortOptions.forEach { it.getChildAt(0).visibility = INVISIBLE }
title.setOnClickListener {
sortOptions.forEach { it.setCompoundDrawablesWithIntrinsicBounds(R.drawable.empty,0,0,0) }
historyViewModel.setSorting(HistoryRepository.HistorySortType.TITLE)
changeSortIcon(title, historyViewModel.sortOrder.value!!)
}
author.getChildAt(1).setOnClickListener {
sortOptions.forEach { it.getChildAt(0).visibility = INVISIBLE }
author.setOnClickListener {
sortOptions.forEach { it.setCompoundDrawablesWithIntrinsicBounds(R.drawable.empty,0,0,0) }
historyViewModel.setSorting(HistoryRepository.HistorySortType.AUTHOR)
changeSortIcon(author, historyViewModel.sortOrder.value!!)
}
filesize.setOnClickListener {
sortOptions.forEach { it.setCompoundDrawablesWithIntrinsicBounds(R.drawable.empty,0,0,0) }
historyViewModel.setSorting(HistoryRepository.HistorySortType.FILESIZE)
changeSortIcon(filesize, historyViewModel.sortOrder.value!!)
}
sortSheet!!.show()
}
@ -400,10 +415,16 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
btn.icon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_terminal)
}
val time = bottomSheet!!.findViewById<TextView>(R.id.time)
val formatNote = bottomSheet!!.findViewById<TextView>(R.id.format_note)
val codec = bottomSheet!!.findViewById<TextView>(R.id.codec)
val fileSize = bottomSheet!!.findViewById<TextView>(R.id.file_size)
val calendar = Calendar.getInstance()
calendar.timeInMillis = item.time * 1000L
time!!.text = SimpleDateFormat(android.text.format.DateFormat.getBestDateTimePattern(Locale.getDefault(), "ddMMMyyyy - HHmm"), Locale.getDefault()).format(calendar.time)
time.isClickable = false
if (item.format.format_note == "?" || item.format.format_note == "") formatNote!!.visibility = GONE
else formatNote!!.text = item.format.format_note

View file

@ -3,6 +3,8 @@ package com.deniscerri.ytdlnis.ui.downloads
import android.app.Activity
import android.content.res.Configuration
import android.os.Bundle
import android.text.format.DateFormat
import android.text.format.DateUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -10,6 +12,7 @@ import android.view.Window
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
@ -28,10 +31,14 @@ import com.deniscerri.ytdlnis.util.NotificationUtil
import com.deniscerri.ytdlnis.util.UiUtil
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.button.MaterialButton
import com.google.android.material.chip.Chip
import com.yausername.youtubedl_android.YoutubeDL
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.w3c.dom.Text
import java.text.SimpleDateFormat
import java.util.*
class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickListener {
@ -119,9 +126,28 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
btn.icon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_terminal)
}
val formatNote = bottomSheet.findViewById<TextView>(R.id.format_note)
val codec = bottomSheet.findViewById<TextView>(R.id.codec)
val fileSize = bottomSheet.findViewById<TextView>(R.id.file_size)
val time = bottomSheet.findViewById<Chip>(R.id.time)
val formatNote = bottomSheet.findViewById<Chip>(R.id.format_note)
val codec = bottomSheet.findViewById<Chip>(R.id.codec)
val fileSize = bottomSheet.findViewById<Chip>(R.id.file_size)
if (item.downloadStartTime <= System.currentTimeMillis() / 1000) time!!.visibility = View.GONE
else {
val calendar = Calendar.getInstance()
calendar.timeInMillis = item.downloadStartTime
time!!.text = SimpleDateFormat(DateFormat.getBestDateTimePattern(Locale.getDefault(), "ddMMMyyyy - HHmm"), Locale.getDefault()).format(calendar.time)
time.setOnClickListener {
uiUtil.showDatePicker(parentFragmentManager) {
bottomSheet.dismiss()
Toast.makeText(context, getString(R.string.download_rescheduled_to) + " " + it.time, Toast.LENGTH_LONG).show()
downloadViewModel.deleteDownload(item)
item.downloadStartTime = it.timeInMillis
WorkManager.getInstance(requireContext()).cancelUniqueWork(item.id.toString())
downloadViewModel.queueDownloads(listOf(item))
}
}
}
if (item.format.format_note == "?" || item.format.format_note == "") formatNote!!.visibility =
View.GONE

View file

@ -12,6 +12,8 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import androidx.preference.*
import androidx.work.WorkInfo
import androidx.work.WorkManager
import com.deniscerri.ytdlnis.BuildConfig
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.util.FileUtil
@ -26,6 +28,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
private var videoPath: Preference? = null
private var commandPath: Preference? = null
private var accessAllFiles : Preference? = null
private var clearCache: Preference? = null
private var incognito: SwitchPreferenceCompat? = null
private var preferredDownloadType : ListPreference? = null
private var downloadCard: SwitchPreferenceCompat? = null
@ -56,12 +59,21 @@ class SettingsFragment : PreferenceFragmentCompat() {
private var updateUtil: UpdateUtil? = null
private var fileUtil: FileUtil? = null
private var activeDownloadCount = 0
private val jsonFormat = Json { prettyPrint = true }
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.root_preferences, rootKey)
updateUtil = UpdateUtil(requireContext())
fileUtil = FileUtil()
WorkManager.getInstance(requireContext()).getWorkInfosByTagLiveData("download").observe(this){
activeDownloadCount = 0
it.forEach {w ->
if (w.state == WorkInfo.State.RUNNING) activeDownloadCount++
}
}
initPreferences()
initListeners()
}
@ -75,6 +87,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
videoPath = findPreference("video_path")
commandPath = findPreference("command_path")
accessAllFiles = findPreference("access_all_files")
clearCache = findPreference("clear_cache")
incognito = findPreference("incognito")
preferredDownloadType = findPreference("preferred_download_type")
downloadCard = findPreference("download_card")
@ -210,6 +223,20 @@ class SettingsFragment : PreferenceFragmentCompat() {
true
}
var cacheSize = requireContext().cacheDir.walkBottomUp().fold(0L) { acc, file -> acc + file.length() }
clearCache!!.summary = "(${fileUtil!!.convertFileSize(cacheSize)}) ${resources.getString(R.string.clear_temporary_files_summary)}"
clearCache!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
if (activeDownloadCount == 0){
requireContext().cacheDir.deleteRecursively()
Toast.makeText(requireContext(), getString(R.string.cache_cleared), Toast.LENGTH_SHORT).show()
cacheSize = requireContext().cacheDir.walkBottomUp().fold(0L) { acc, file -> acc + file.length() }
clearCache!!.summary = "(${fileUtil!!.convertFileSize(cacheSize)}) ${resources.getString(R.string.clear_temporary_files_summary)}"
}else{
Toast.makeText(requireContext(), getString(R.string.downloads_running_try_later), Toast.LENGTH_SHORT).show()
}
true
}
incognito!!.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
val enable = newValue as Boolean

View file

@ -18,17 +18,25 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.content.res.AppCompatResources
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.FileProvider
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LifecycleOwner
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.CommandTemplate
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.Format
import com.deniscerri.ytdlnis.database.models.TemplateShortcut
import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.DateValidatorPointForward
import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.textfield.TextInputLayout
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
import java.io.File
import java.util.*
class UiUtil(private val fileUtil: FileUtil) {
fun populateFormatCard(formatCard : ConstraintLayout, chosenFormat: Format){
@ -214,4 +222,38 @@ class UiUtil(private val fileUtil: FileUtil) {
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
fragmentContext.startActivity(i)
}
fun showDatePicker(fragmentManager: FragmentManager , onSubmit : (chosenDate: Calendar) -> Unit ){
val currentDate = Calendar.getInstance()
val date = Calendar.getInstance()
val datePicker = MaterialDatePicker.Builder.datePicker()
.setCalendarConstraints(
CalendarConstraints.Builder()
.setValidator(DateValidatorPointForward.now())
.build()
)
.setSelection(MaterialDatePicker.todayInUtcMilliseconds())
.build()
datePicker.addOnPositiveButtonClickListener{
date.timeInMillis = it
val timepicker = MaterialTimePicker.Builder()
.setTimeFormat(TimeFormat.CLOCK_24H)
.setHour(currentDate.get(Calendar.HOUR_OF_DAY))
.setMinute(currentDate.get(Calendar.MINUTE))
.build()
timepicker.addOnPositiveButtonClickListener{
date[Calendar.HOUR_OF_DAY] = timepicker.hour
date[Calendar.MINUTE] = timepicker.minute
onSubmit(date)
}
timepicker.show(fragmentManager, "timepicker")
}
datePicker.show(fragmentManager, "datepicker")
}
}

View file

@ -9,7 +9,7 @@
<path
android:name="path"
android:pathData="M 73.977 46.512 C 75.734 48.222 76.781 50.662 76.872 53.315 C 76.945 55.412 77 57.629 77 59.482 C 77 61.335 76.945 63.552 76.872 65.649 C 76.718 70.133 73.835 74.006 69.404 74.78 C 65.706 75.426 60.571 76 54 76 C 47.429 76 42.294 75.426 38.596 74.78 C 34.165 74.006 31.282 70.133 31.128 65.649 C 31.055 63.552 31 61.335 31 59.482 C 31 57.629 31.055 55.412 31.128 53.315 C 31.282 48.831 34.165 44.957 38.596 44.183 C 41.576 43.663 45.489 43.189 50.335 43.025 C 51.232 37.348 56.145 33 62.083 33 C 68.658 33 73.977 38.331 73.977 44.895 L 73.977 46.512 Z M 52.345 42.976 C 52.886 42.968 53.438 42.963 54 42.963 L 54.086 42.963 C 53.495 42.964 52.915 42.969 52.347 42.978 C 52.224 43.601 52.16 44.244 52.16 44.902 L 52.16 51.756 L 48.489 51.76 C 46.26 51.763 44.867 54.174 45.98 56.106 L 51.541 65.764 C 52.656 67.699 55.448 67.701 56.565 65.767 L 62.15 56.095 C 63.267 54.161 61.869 51.744 59.636 51.747 L 55.803 51.752 L 55.803 44.902 L 55.803 44.895 L 55.782 44.895 C 55.782 41.413 58.603 38.59 62.083 38.59 C 65.26 38.59 67.888 40.943 68.322 44.003 C 68.695 44.062 69.056 44.122 69.404 44.183 C 70.345 44.347 71.216 44.651 72.006 45.071 L 72.006 44.895 C 72.006 39.412 67.563 34.967 62.083 34.967 C 57.259 34.967 53.239 38.411 52.345 42.976 Z M 66.248 43.705 C 65.731 41.885 64.058 40.557 62.083 40.557 C 60.356 40.557 58.86 41.571 58.166 43.043 C 61.248 43.162 63.941 43.407 66.248 43.705 Z"
android:fillColor="#d43c3c"
android:fillColor="@color/icon_fg"
android:strokeWidth="1"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:strokeAlpha="0" android:pathData="M0,12h24"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="?android:colorAccent" android:pathData="M22,8v10c0,1.1 -0.9,2 -2,2H4c-1.1,0 -2,-0.9 -2,-2L2.01,6C2.01,4.9 2.9,4 4,4h6l2,2h8C21.1,6 22,6.9 22,8zM16.5,10V9h-2v1H12v1.5h1v4c0,0.83 0.67,1.5 1.5,1.5h2c0.83,0 1.5,-0.67 1.5,-1.5v-4h1V10H16.5zM16.5,15.5h-2v-4h2V15.5z"/>
</vector>

View file

@ -0,0 +1,11 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<gradient
android:angle="90"
android:endColor="#00ffffff"
android:startColor="?attr/colorSurfaceContainer"
android:centerColor="#00ffffff" />
<corners android:radius="0dp" />
</shape>

View file

@ -30,6 +30,7 @@
android:id="@+id/image_view"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:foreground="@drawable/image_gradient_shape"
android:layout_width="match_parent"
android:layout_height="match_parent" />
@ -45,109 +46,97 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/title"
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingStart="10dp"
android:paddingTop="10dp"
android:paddingEnd="10dp"
android:paddingBottom="10dp"
android:shadowColor="@color/black"
android:shadowDx="4"
android:shadowDy="4"
android:shadowRadius="2"
android:textColor="@color/white"
android:textSize="15sp"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/active_download_cancel"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/author"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="bottom"
android:paddingLeft="10dp"
android:paddingTop="10dp"
android:paddingRight="10dp"
android:paddingBottom="10dp"
android:shadowColor="@color/black"
android:shadowDx="4"
android:shadowDy="4"
android:shadowRadius="1.5"
android:textColor="@color/white"
android:textSize="12sp"
android:textStyle="bold"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@+id/linearLayout2"
app:layout_constraintEnd_toStartOf="@+id/active_download_cancel"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title" />
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/format_note"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingVertical="10dp"
android:paddingStart="10dp"
android:paddingEnd="0dp"
android:textSize="15sp"
android:shadowColor="@color/black"
android:shadowDx="4"
android:shadowDy="4"
android:shadowRadius="2"
android:textColor="@color/white"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/output"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="2"
android:paddingStart="10dp"
android:scrollbars="none"
android:paddingTop="10dp"
android:paddingEnd="10dp"
android:shadowColor="@color/black"
android:shadowDx="4"
android:shadowDy="4"
android:shadowRadius="2"
android:textColor="@color/white"
android:textSize="17sp"
android:textStyle="bold" />
<LinearLayout
<TextView
android:id="@+id/author"
android:layout_width="296dp"
android:layout_height="20dp"
android:ellipsize="end"
android:gravity="bottom"
android:maxLines="1"
android:paddingStart="10dp"
android:scrollbars="none"
android:paddingEnd="10dp"
android:paddingBottom="5dp"
android:shadowColor="@color/black"
android:shadowDx="4"
android:shadowDy="4"
android:shadowRadius="1.5"
android:textColor="@color/white"
android:textSize="13sp"
android:textStyle="bold" />
</LinearLayout>
<com.google.android.material.chip.ChipGroup
android:id="@+id/linearLayout2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="10dp"
android:layout_gravity="bottom"
android:orientation="horizontal"
android:padding="10dp"
app:layout_constraintBottom_toTopOf="@+id/output"
app:layout_constraintStart_toEndOf="@+id/format_note">
app:layout_constraintStart_toStartOf="parent">
<com.google.android.material.chip.Chip
android:id="@+id/format_note"
style="@style/Widget.Material3.FloatingActionButton.Large.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
app:layout_constraintBottom_toTopOf="@+id/output"
app:layout_constraintStart_toStartOf="parent" />
<TextView
<com.google.android.material.chip.Chip
android:id="@+id/codec"
style="@style/Widget.Material3.FloatingActionButton.Large.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="?attr/colorSecondary"
android:clickable="false"
android:gravity="center"
android:minWidth="30dp"
android:paddingHorizontal="10dp"
app:cornerRadius="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
<com.google.android.material.chip.Chip
android:id="@+id/file_size"
style="@style/Widget.Material3.FloatingActionButton.Large.Tertiary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:background="?attr/colorSecondary"
android:clickable="false"
android:gravity="center"
android:minWidth="30dp"
android:paddingHorizontal="10dp"
app:cornerRadius="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
</com.google.android.material.chip.ChipGroup>
<TextView
@ -159,12 +148,7 @@
android:paddingRight="10dp"
android:paddingBottom="10dp"
android:textSize="12sp"
android:textStyle="bold"
android:shadowColor="@color/black"
android:shadowDx="4"
android:shadowDy="4"
android:shadowRadius="2"
android:textColor="@color/white"
android:fontFamily="monospace"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />

View file

@ -109,7 +109,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.2" />
app:layout_constraintVertical_bias="0.375" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -83,6 +83,20 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/url">
<com.google.android.material.chip.Chip
android:id="@+id/time"
style="@style/Widget.Material3.FloatingActionButton.Large.Primary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:minWidth="30dp"
app:cornerRadius="10dp"
app:chipIcon="@drawable/ic_clock"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:focusable="true" />
<com.google.android.material.chip.Chip
android:id="@+id/format_note"
style="@style/Widget.Material3.FloatingActionButton.Large.Primary"

View file

@ -21,70 +21,60 @@
android:paddingBottom="20dp"
android:layout_height="wrap_content"/>
<LinearLayout
<TextView
android:id="@+id/date"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:paddingVertical="10dp"
android:drawablePadding="30dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="20dp"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="20dp"
android:paddingEnd="20dp"
android:src="@drawable/ic_up"/>
android:textSize="15sp"
android:text="@string/date_added"
app:drawableLeftCompat="@drawable/ic_arrow_up" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="15sp"
android:text="@string/date_added" />
</LinearLayout>
<LinearLayout
<TextView
android:id="@+id/title"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:paddingVertical="10dp"
android:drawablePadding="30dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="20dp"
android:orientation="horizontal">
<ImageView
android:visibility="invisible"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:paddingEnd="20dp"
android:src="@drawable/ic_up"/>
android:textSize="15sp"
android:text="@string/title"
app:drawableLeftCompat="@drawable/ic_arrow_up" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="15sp"
android:text="@string/title" />
</LinearLayout>
<LinearLayout
<TextView
android:id="@+id/author"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:paddingVertical="10dp"
android:drawablePadding="30dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="20dp"
android:orientation="horizontal">
<ImageView
android:visibility="invisible"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:paddingEnd="20dp"
android:src="@drawable/ic_up"/>
android:textSize="15sp"
android:text="@string/author"
app:drawableLeftCompat="@drawable/ic_arrow_up" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="15sp"
android:text="@string/author" />
</LinearLayout>
<TextView
android:id="@+id/filesize"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:drawablePadding="30dp"
android:paddingVertical="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="15sp"
android:text="@string/file_size"
app:drawableLeftCompat="@drawable/ic_arrow_up" />

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
<androidx.constraintlayout.widget.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/search_suggestions_constraintLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -30,34 +30,32 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageButton
style="?attr/materialIconButtonStyle"
android:layout_width="40dp"
android:scrollbars="none"
android:layout_height="wrap_content"
android:layout_weight="0.01"
android:src="@drawable/ic_search"
/>
<TextView
android:id="@+id/suggestion_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:maxLines="1"
android:background="?attr/selectableItemBackground"
android:focusable="true"
android:clickable="true"
android:ellipsize="end"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:textSize="16sp"
android:textStyle="bold" />
android:textStyle="bold"
android:drawablePadding="20dp"
app:drawableLeftCompat="@drawable/ic_search" />
<ImageButton
style="?attr/materialIconButtonStyle"
android:id="@+id/set_search_query_button"
android:layout_width="40dp"
android:scrollbars="none"
android:background="?attr/selectableItemBackground"
android:focusable="true"
android:clickable="true"
android:layout_height="match_parent"
android:src="@drawable/ic_arrow_up"
android:layout_weight="0.01"

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="white">#FFFFFF</color>
<color name="black">#000000</color>
<color name="grey">#323232</color>
<color name="light_grey">#A6A6A6</color>
<color name="icon_bg">@android:color/system_neutral1_800</color>
<color name="icon_fg">@android:color/system_accent1_100</color>
</resources>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="white">#FFFFFF</color>
<color name="black">#000000</color>
<color name="grey">#323232</color>
<color name="light_grey">#A6A6A6</color>
<color name="icon_bg">@android:color/system_accent1_100</color>
<color name="icon_fg">@android:color/system_neutral1_700</color>
</resources>

View file

@ -5,4 +5,5 @@
<color name="grey">#323232</color>
<color name="light_grey">#A6A6A6</color>
<color name="icon_bg">@android:color/background_dark</color>
<color name="icon_fg">#d43c3c</color>
</resources>

View file

@ -219,4 +219,10 @@
<string name="help_translate_summary">Translate this app on your language using Weblate</string>
<string name="workmanager_updated">You need to restart the app and have no active downloads for this preference to take effect!</string>
<string name="restart">Restart</string>
<string name="download_rescheduled_to">Download rescheduled to:</string>
<string name="file_size">File size</string>
<string name="clear_temporary_files">Clear temporary files</string>
<string name="clear_temporary_files_summary">Delete cached files from crashed/errored/cancelled downloads</string>
<string name="cache_cleared">Temporary files cleared</string>
<string name="downloads_running_try_later">Download worker is running. Try again later</string>
</resources>

View file

@ -36,6 +36,12 @@
android:summary="@string/access_all_directories_summary"
app:title="@string/access_all_directories" />
<Preference
app:icon="@drawable/ic_folder_delete"
app:key="clear_cache"
android:summary="@string/clear_temporary_files_summary"
app:title="@string/clear_temporary_files" />
</PreferenceCategory>
<PreferenceCategory app:title="@string/downloading">

View file

@ -1,17 +0,0 @@
package com.deniscerri.ytdlnis;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}

View file

@ -0,0 +1,16 @@
package com.deniscerri.ytdlnis
import org.junit.Assert
import org.junit.Test
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see [Testing documentation](http://d.android.com/tools/testing)
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
Assert.assertEquals(4, (2 + 2).toLong())
}
}