1.7.3.1
This commit is contained in:
parent
a5bd545f52
commit
b43bf26131
42 changed files with 615 additions and 407 deletions
|
|
@ -11,12 +11,12 @@ def properties = new Properties()
|
|||
def versionMajor = 1
|
||||
def versionMinor = 7
|
||||
def versionPatch = 3
|
||||
def versionBuild = 0 // bump for dogfood builds, public betas, etc.
|
||||
def versionExt = ""
|
||||
def versionBuild = 1 // bump for dogfood builds, public betas, etc.
|
||||
def versionExt = ".${versionBuild}"
|
||||
|
||||
if (versionBuild > 0){
|
||||
versionExt = ".${versionBuild}-beta"
|
||||
}
|
||||
//if (versionBuild > 0){
|
||||
// versionExt = ".${versionBuild}-beta"
|
||||
//}
|
||||
|
||||
android {
|
||||
namespace 'com.deniscerri.ytdlnis'
|
||||
|
|
@ -117,6 +117,13 @@ android {
|
|||
composeOptions {
|
||||
kotlinCompilerExtensionVersion composeVer
|
||||
}
|
||||
|
||||
dependenciesInfo {
|
||||
// Disables dependency metadata when building APKs.
|
||||
includeInApk = false
|
||||
// Disables dependency metadata when building Android App Bundles.
|
||||
includeInBundle = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -216,6 +216,9 @@ interface DownloadDao {
|
|||
@Query("Select url from downloads where status in (:status)")
|
||||
fun getURLsByStatus(status: List<String>) : List<String>
|
||||
|
||||
@Query("Select url from downloads where id in (:ids)")
|
||||
fun getURLsByID(ids: List<Long>) : List<String>
|
||||
|
||||
@Query("UPDATE downloads SET downloadStartTime=0 where id in (:list)")
|
||||
suspend fun resetScheduleTimeForItems(list: List<Long>)
|
||||
|
||||
|
|
@ -243,4 +246,8 @@ interface DownloadDao {
|
|||
|
||||
@Query("Update downloads set id=:newId where id=:id")
|
||||
suspend fun updateDownloadID(id: Long, newId: Long)
|
||||
|
||||
|
||||
@Query("SELECT id from downloads WHERE id > :item1 AND id < :item2 AND status in (:statuses) ORDER BY id DESC")
|
||||
fun getIDsBetweenTwoItems(item1: Long, item2: Long, statuses: List<String>) : List<Long>
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ import com.deniscerri.ytdlnis.R
|
|||
import com.deniscerri.ytdlnis.database.dao.DownloadDao
|
||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||
import com.deniscerri.ytdlnis.database.models.DownloadItemSimple
|
||||
import com.deniscerri.ytdlnis.util.Extensions.toListString
|
||||
import com.deniscerri.ytdlnis.util.FileUtil
|
||||
import com.deniscerri.ytdlnis.work.DownloadWorker
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -63,10 +64,6 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
|||
Active, ActivePaused, PausedReQueued, Queued, QueuedPaused, Error, Cancelled, Saved, Processing
|
||||
}
|
||||
|
||||
private fun List<Status>.toListString() : List<String>{
|
||||
return this.map { it.toString() }
|
||||
}
|
||||
|
||||
suspend fun insert(item: DownloadItem) : Long {
|
||||
return downloadDao.insert(item)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.deniscerri.ytdlnis.database.dao.ObserveSourcesDao
|
|||
import com.deniscerri.ytdlnis.database.models.ObserveSourcesItem
|
||||
import com.deniscerri.ytdlnis.receiver.ObserveAlarmReceiver
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.util.Calendar
|
||||
|
||||
class ObserveSourcesRepository(private val observeSourcesDao: ObserveSourcesDao) {
|
||||
val items : Flow<List<ObserveSourcesItem>> = observeSourcesDao.getAllSourcesFlow()
|
||||
|
|
@ -74,4 +75,47 @@ class ObserveSourcesRepository(private val observeSourcesDao: ObserveSourcesDao)
|
|||
)
|
||||
}
|
||||
|
||||
fun calculateNextTime(item: ObserveSourcesItem) : Long {
|
||||
val c = Calendar.getInstance()
|
||||
|
||||
val hourMin = Calendar.getInstance()
|
||||
hourMin.timeInMillis = item.everyTime
|
||||
|
||||
c.set(Calendar.HOUR_OF_DAY, hourMin.get(Calendar.HOUR_OF_DAY))
|
||||
c.set(Calendar.MINUTE, hourMin.get(Calendar.MINUTE))
|
||||
|
||||
if (item.everyNr == 0) item.everyNr = 1
|
||||
|
||||
when(item.everyCategory){
|
||||
EveryCategory.DAY -> {
|
||||
c.add(Calendar.DAY_OF_MONTH, item.everyNr)
|
||||
}
|
||||
EveryCategory.WEEK -> {
|
||||
if(item.everyWeekDay.isEmpty()){
|
||||
c.add(Calendar.DAY_OF_MONTH, 7 * item.everyNr)
|
||||
}else{
|
||||
val weekDayNr = c.get(Calendar.DAY_OF_WEEK)
|
||||
val followingWeekDay = item.everyWeekDay.firstOrNull { it.toInt() > weekDayNr }
|
||||
if (followingWeekDay == null){
|
||||
c.add(Calendar.DAY_OF_MONTH, item.everyWeekDay.minBy { it.toInt() }.toInt() + (7 - weekDayNr))
|
||||
item.everyNr--
|
||||
}else{
|
||||
c.add(Calendar.DAY_OF_MONTH, followingWeekDay.toInt() - weekDayNr)
|
||||
}
|
||||
|
||||
if (item.everyNr > 1){
|
||||
c.add(Calendar.DAY_OF_MONTH, 7 * item.everyNr)
|
||||
}
|
||||
}
|
||||
}
|
||||
EveryCategory.MONTH -> {
|
||||
c.add(Calendar.MONTH, item.everyNr)
|
||||
c.set(Calendar.DAY_OF_MONTH, item.everyMonthDay)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return c.timeInMillis
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ class CookieViewModel(private val application: Application) : AndroidViewModel(a
|
|||
private val repository: CookieRepository
|
||||
val items: LiveData<List<CookieItem>>
|
||||
|
||||
private val cookieHeader =
|
||||
val cookieHeader =
|
||||
"# Netscape HTTP Cookie File\n" +
|
||||
"# WebView Generated by the YTDLnis app\n" +
|
||||
"# This is a generated file! Do not edit."
|
||||
|
|
|
|||
|
|
@ -940,5 +940,13 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
|||
return dao.getURLsByStatus(list.map { it.toString() })
|
||||
}
|
||||
|
||||
fun getURLsByIds(list: List<Long>) : List<String> {
|
||||
return dao.getURLsByID(list)
|
||||
}
|
||||
|
||||
fun getIDsBetweenTwoItems(item1: Long, item2: Long, statuses: List<String>) : List<Long> {
|
||||
return dao.getIDsBetweenTwoItems(item1, item2, statuses)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -75,68 +75,59 @@ class ObserveSourcesViewModel(private val application: Application) : AndroidVie
|
|||
repository.update(item)
|
||||
}
|
||||
|
||||
fun cancelObservationTaskByID(id : Long){
|
||||
repository.cancelObservationTaskByID(application, id)
|
||||
}
|
||||
|
||||
|
||||
private fun observeTask(it: ObserveSourcesItem){
|
||||
cancelObservationTaskByID(it.id)
|
||||
|
||||
val id = it.id
|
||||
val c = Calendar.getInstance()
|
||||
val date = Calendar.getInstance()
|
||||
date.timeInMillis = it.startsTime
|
||||
val hourMin = Calendar.getInstance()
|
||||
hourMin.timeInMillis = it.everyTime
|
||||
|
||||
val date = Calendar.getInstance()
|
||||
date.timeInMillis = it.startsTime
|
||||
val hourMin = Calendar.getInstance()
|
||||
hourMin.timeInMillis = it.everyTime
|
||||
c.set(Calendar.DAY_OF_MONTH, date.get(Calendar.DAY_OF_MONTH))
|
||||
c.set(Calendar.MONTH, date.get(Calendar.MONTH))
|
||||
c.set(Calendar.YEAR, date.get(Calendar.YEAR))
|
||||
c.set(Calendar.HOUR_OF_DAY, hourMin.get(Calendar.HOUR_OF_DAY))
|
||||
c.set(Calendar.MINUTE, hourMin.get(Calendar.MINUTE))
|
||||
|
||||
|
||||
repository.cancelObservationTaskByID(application, id)
|
||||
|
||||
val intent = Intent(application, ObserveAlarmReceiver::class.java)
|
||||
intent.putExtra("id", id)
|
||||
if (it.everyNr == 0) it.everyNr = 1
|
||||
|
||||
when(it.everyCategory){
|
||||
ObserveSourcesRepository.EveryCategory.DAY -> {
|
||||
alarmManager.setExact(
|
||||
AlarmManager.RTC,
|
||||
c.timeInMillis,
|
||||
PendingIntent.getBroadcast(application, it.id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
||||
)
|
||||
}
|
||||
ObserveSourcesRepository.EveryCategory.DAY -> {}
|
||||
ObserveSourcesRepository.EveryCategory.WEEK -> {
|
||||
if (it.everyWeekDay.isNotEmpty()){
|
||||
val weekDayID = c.get(Calendar.DAY_OF_WEEK).toString()
|
||||
val followingWeekDay = (it.everyWeekDay.firstOrNull { it.toInt() > weekDayID.toInt() } ?: it.everyWeekDay.minBy { it.toInt() }).toInt()
|
||||
c.set(Calendar.DAY_OF_WEEK, followingWeekDay)
|
||||
if(c.timeInMillis < System.currentTimeMillis()){
|
||||
c.add(Calendar.DAY_OF_MONTH, 7)
|
||||
}
|
||||
val weekDayNr = c.get(Calendar.DAY_OF_WEEK)
|
||||
val followingWeekDay = it.everyWeekDay.firstOrNull { it.toInt() >= weekDayNr }
|
||||
if (followingWeekDay == null){
|
||||
c.add(Calendar.DAY_OF_MONTH, it.everyWeekDay.minBy { it.toInt() }.toInt() + (7 - weekDayNr))
|
||||
}else{
|
||||
c.add(Calendar.DAY_OF_MONTH, followingWeekDay.toInt() - weekDayNr)
|
||||
}
|
||||
|
||||
alarmManager.setExact(
|
||||
AlarmManager.RTC,
|
||||
c.timeInMillis,
|
||||
PendingIntent.getBroadcast(application, it.id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
||||
)
|
||||
}
|
||||
ObserveSourcesRepository.EveryCategory.MONTH -> {
|
||||
val theMonthIndex = Month.values().indexOf(it.startsMonth)
|
||||
val currentMonthIndex = c.get(Calendar.MONTH)
|
||||
if (theMonthIndex != currentMonthIndex){
|
||||
c.set(Calendar.MONTH, theMonthIndex)
|
||||
if (c.timeInMillis < System.currentTimeMillis()){
|
||||
if (c.timeInMillis < Calendar.getInstance().timeInMillis){
|
||||
c.add(Calendar.YEAR, 1)
|
||||
}
|
||||
}
|
||||
alarmManager.setExact(
|
||||
AlarmManager.RTC,
|
||||
c.timeInMillis,
|
||||
PendingIntent.getBroadcast(application, it.id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
alarmManager.setExact(
|
||||
AlarmManager.RTC,
|
||||
c.timeInMillis,
|
||||
PendingIntent.getBroadcast(application, it.id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class ObserveAlarmReceiver : BroadcastReceiver() {
|
|||
.setInputData(Data.Builder().putLong("id", sourceID).build())
|
||||
|
||||
WorkManager.getInstance(p0!!).enqueueUniqueWork(
|
||||
sourceID.toString(),
|
||||
"OBSERVE$sourceID",
|
||||
ExistingWorkPolicy.REPLACE,
|
||||
workRequest.build()
|
||||
)
|
||||
|
|
|
|||
|
|
@ -500,7 +500,9 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
|||
|
||||
val url = checkClipboard()
|
||||
url?.apply {
|
||||
combinedList.add(0, SearchSuggestionItem(this.joinToString("\n"), SearchSuggestionType.CLIPBOARD))
|
||||
if (this.isNotEmpty()){
|
||||
combinedList.add(0, SearchSuggestionItem(this.joinToString("\n"), SearchSuggestionType.CLIPBOARD))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -845,7 +847,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
|||
return kotlin.runCatching {
|
||||
val clipboard = requireContext().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = clipboard.primaryClip!!.getItemAt(0).text
|
||||
return clip.split("\r\n").filter { Patterns.WEB_URL.matcher(it).matches() }
|
||||
return clip.split("\r","\n").map { it.trim() }.filter { Patterns.WEB_URL.matcher(it).matches() }
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
|
|
@ -873,27 +875,36 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
|||
|
||||
}
|
||||
|
||||
override fun onSearchSuggestionAdd(text: String) {
|
||||
val present = queriesChipGroup!!.children.firstOrNull { (it as Chip).text.toString() == text }
|
||||
if (present == null) {
|
||||
val chip = layoutinflater!!.inflate(R.layout.input_chip, queriesChipGroup, false) as Chip
|
||||
chip.text = text
|
||||
chip.chipBackgroundColor = ColorStateList.valueOf(MaterialColors.getColor(requireContext(), R.attr.colorSecondaryContainer, Color.BLACK))
|
||||
chip.setOnClickListener {
|
||||
if (queriesChipGroup!!.childCount == 1) queriesConstraint!!.visibility = View.GONE
|
||||
queriesChipGroup!!.removeView(chip)
|
||||
override fun onSearchSuggestionAdd(t: String) {
|
||||
val items = t.split("\n")
|
||||
|
||||
items.forEach {text ->
|
||||
val present = queriesChipGroup!!.children.firstOrNull { (it as Chip).text.toString() == text }
|
||||
if (present == null) {
|
||||
val chip = layoutinflater!!.inflate(R.layout.input_chip, queriesChipGroup, false) as Chip
|
||||
chip.text = text
|
||||
chip.chipBackgroundColor = ColorStateList.valueOf(MaterialColors.getColor(requireContext(), R.attr.colorSecondaryContainer, Color.BLACK))
|
||||
chip.setOnClickListener {
|
||||
if (queriesChipGroup!!.childCount == 1) queriesConstraint!!.visibility = View.GONE
|
||||
queriesChipGroup!!.removeView(chip)
|
||||
}
|
||||
queriesChipGroup!!.addView(chip)
|
||||
}
|
||||
queriesChipGroup!!.addView(chip)
|
||||
|
||||
}
|
||||
|
||||
searchView!!.editText.setText("")
|
||||
if (queriesChipGroup!!.childCount == 0) queriesConstraint!!.visibility = View.GONE
|
||||
else queriesConstraint!!.visibility = View.VISIBLE
|
||||
searchView!!.editText.setText("")
|
||||
|
||||
val clipBoardItem = searchSuggestionsRecyclerView?.layoutManager?.findViewByPosition(0)
|
||||
clipBoardItem?.apply {
|
||||
if ((this as ConstraintLayout).findViewById<TextView>(R.id.suggestion_text).text == getString(R.string.link_you_copied)){
|
||||
searchSuggestionsAdapter?.notifyItemRemoved(0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
override fun onSearchSuggestionLongClick(text: String, position: Int) {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import com.deniscerri.ytdlnis.database.models.DownloadItem
|
|||
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||
import com.deniscerri.ytdlnis.util.Extensions.dp
|
||||
import com.deniscerri.ytdlnis.util.Extensions.loadThumbnail
|
||||
import com.deniscerri.ytdlnis.util.FileUtil
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
|
|
@ -65,17 +66,8 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
|
|||
val thumbnail = card.findViewById<ImageView>(R.id.image_view)
|
||||
|
||||
// THUMBNAIL ----------------------------------
|
||||
if (!sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("queue")){
|
||||
val imageURL = item.thumb
|
||||
if (imageURL.isNotEmpty()) {
|
||||
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
|
||||
} else {
|
||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
||||
}
|
||||
thumbnail.setColorFilter(Color.argb(20, 0, 0, 0))
|
||||
}else{
|
||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
||||
}
|
||||
val hideThumb = sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("queue")
|
||||
uiHandler.post { thumbnail.loadThumbnail(hideThumb, item.thumb) }
|
||||
|
||||
// PROGRESS BAR ----------------------------------------------------
|
||||
val progressBar = card.findViewById<LinearProgressIndicator>(R.id.progress)
|
||||
|
|
@ -168,7 +160,7 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
|
|||
val value = animation.animatedValue as Int
|
||||
pauseButton.cornerRadius = value
|
||||
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_play_arrow_white)
|
||||
pauseButton.contentDescription = activity.getString(R.string.start)
|
||||
pauseButton.contentDescription = activity.getString(R.string.resume)
|
||||
pauseButton.tag = ActiveDownloadAction.Resume
|
||||
pauseButton.isEnabled = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import com.deniscerri.ytdlnis.R
|
||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
||||
import com.deniscerri.ytdlnis.util.Extensions.loadThumbnail
|
||||
import com.deniscerri.ytdlnis.util.Extensions.popup
|
||||
import com.deniscerri.ytdlnis.util.FileUtil
|
||||
import com.google.android.material.button.MaterialButton
|
||||
|
|
@ -62,17 +63,8 @@ class ActiveDownloadMinifiedAdapter(onItemClickListener: OnItemClickListener, ac
|
|||
val thumbnail = card.findViewById<ImageView>(R.id.image_view)
|
||||
|
||||
// THUMBNAIL ----------------------------------
|
||||
if (!sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("queue")){
|
||||
val imageURL = item!!.thumb
|
||||
if (imageURL.isNotEmpty()) {
|
||||
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
|
||||
} else {
|
||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
||||
}
|
||||
thumbnail.setColorFilter(Color.argb(20, 0, 0, 0))
|
||||
}else{
|
||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
||||
}
|
||||
val hideThumb = sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("queue")
|
||||
uiHandler.post { thumbnail.loadThumbnail(hideThumb, item!!.thumb) }
|
||||
|
||||
// PROGRESS BAR ----------------------------------------------------
|
||||
val progressBar = card.findViewById<LinearProgressIndicator>(R.id.progress)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import com.deniscerri.ytdlnis.R
|
||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||
import com.deniscerri.ytdlnis.util.Extensions.loadThumbnail
|
||||
import com.deniscerri.ytdlnis.util.Extensions.popup
|
||||
import com.deniscerri.ytdlnis.util.FileUtil
|
||||
import com.google.android.material.button.MaterialButton
|
||||
|
|
@ -63,17 +64,8 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener
|
|||
val thumbnail = card.findViewById<ImageView>(R.id.downloads_image_view)
|
||||
|
||||
// THUMBNAIL ----------------------------------
|
||||
if (!sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("home")){
|
||||
val imageURL = item.thumb
|
||||
if (!imageURL.isNullOrBlank()) {
|
||||
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
|
||||
} else {
|
||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
||||
}
|
||||
thumbnail.setColorFilter(Color.argb(20, 0, 0, 0))
|
||||
}else{
|
||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
||||
}
|
||||
val hideThumb = sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("home")
|
||||
uiHandler.post { thumbnail.loadThumbnail(hideThumb, item.thumb) }
|
||||
|
||||
val duration = card.findViewById<TextView>(R.id.duration)
|
||||
duration.text = item.duration
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import com.deniscerri.ytdlnis.R
|
||||
import com.deniscerri.ytdlnis.database.models.DownloadItemSimple
|
||||
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
||||
import com.deniscerri.ytdlnis.util.Extensions.loadThumbnail
|
||||
import com.deniscerri.ytdlnis.util.Extensions.popup
|
||||
import com.deniscerri.ytdlnis.util.FileUtil
|
||||
import com.google.android.material.button.MaterialButton
|
||||
|
|
@ -28,12 +29,12 @@ class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
|
|||
) {
|
||||
private val onItemClickListener: OnItemClickListener
|
||||
private val activity: Activity
|
||||
val checkedItems: ArrayList<Long>
|
||||
val checkedItems: MutableSet<Long>
|
||||
var inverted: Boolean
|
||||
private val sharedPreferences: SharedPreferences
|
||||
|
||||
init {
|
||||
checkedItems = ArrayList()
|
||||
checkedItems = mutableSetOf()
|
||||
this.onItemClickListener = onItemClickListener
|
||||
this.activity = activity
|
||||
this.inverted = false
|
||||
|
|
@ -65,16 +66,8 @@ class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
|
|||
val thumbnail = card.findViewById<ImageView>(R.id.downloads_image_view)
|
||||
|
||||
// THUMBNAIL ----------------------------------
|
||||
if (!sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("queue")){
|
||||
val imageURL = item.thumb
|
||||
if (imageURL.isNotEmpty()) {
|
||||
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
|
||||
} else {
|
||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
||||
}
|
||||
}else{
|
||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
||||
}
|
||||
val hideThumb = sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("queue")
|
||||
uiHandler.post { thumbnail.loadThumbnail(hideThumb, item.thumb) }
|
||||
|
||||
val duration = card.findViewById<TextView>(R.id.duration)
|
||||
duration.text = item.duration
|
||||
|
|
@ -180,6 +173,14 @@ class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
|
|||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun checkMultipleItems(list: List<Long>){
|
||||
checkedItems.clear()
|
||||
inverted = false
|
||||
checkedItems.addAll(list)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun invertSelected() {
|
||||
inverted = !inverted
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import com.deniscerri.ytdlnis.R
|
||||
import com.deniscerri.ytdlnis.database.models.HistoryItem
|
||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||
import com.deniscerri.ytdlnis.util.Extensions.loadThumbnail
|
||||
import com.deniscerri.ytdlnis.util.Extensions.popup
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.google.android.material.color.MaterialColors
|
||||
|
|
@ -89,17 +90,8 @@ class HistoryAdapter(onItemClickListener: OnItemClickListener, activity: Activit
|
|||
val thumbnail = card.findViewById<ImageView>(R.id.downloads_image_view)
|
||||
|
||||
// THUMBNAIL ----------------------------------
|
||||
if (!sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("downloads")){
|
||||
val imageURL = item!!.thumb
|
||||
if (imageURL.isNotEmpty()) {
|
||||
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
|
||||
} else {
|
||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
||||
}
|
||||
thumbnail.setColorFilter(Color.argb(20, 0, 0, 0))
|
||||
}else{
|
||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
||||
}
|
||||
val hideThumb = sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("downloads")
|
||||
uiHandler.post { thumbnail.loadThumbnail(hideThumb, item!!.thumb) }
|
||||
|
||||
// TITLE ----------------------------------
|
||||
val itemTitle = card.findViewById<TextView>(R.id.downloads_title)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import com.deniscerri.ytdlnis.R
|
||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||
import com.deniscerri.ytdlnis.util.Extensions.loadThumbnail
|
||||
import com.deniscerri.ytdlnis.util.Extensions.popup
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
|
|
@ -62,16 +63,8 @@ class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity)
|
|||
val thumbnail = card.findViewById<ImageView>(R.id.result_image_view)
|
||||
|
||||
// THUMBNAIL ----------------------------------
|
||||
if (!sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("home")){
|
||||
val imageURL = video!!.thumb
|
||||
if (imageURL.isNotEmpty()) {
|
||||
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
|
||||
} else {
|
||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
||||
}
|
||||
}else{
|
||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
||||
}
|
||||
val hideThumb = sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("home")
|
||||
uiHandler.post { thumbnail.loadThumbnail(hideThumb, video!!.thumb) }
|
||||
|
||||
// TITLE ----------------------------------
|
||||
val videoTitle = card.findViewById<TextView>(R.id.result_title)
|
||||
|
|
|
|||
|
|
@ -57,14 +57,12 @@ class ObserveSourcesAdapter(onItemClickListener: OnItemClickListener, activity:
|
|||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val item = getItem(position)
|
||||
val card = holder.cardView
|
||||
val uiHandler = Handler(Looper.getMainLooper())
|
||||
card.popup()
|
||||
|
||||
if (item == null) return
|
||||
card.tag = item.url
|
||||
holder.itemView.tag = item.url
|
||||
|
||||
val thumbnail = card.findViewById<ImageView>(R.id.result_image_view)
|
||||
|
||||
// TITLE ----------------------------------
|
||||
val itemTitle = card.findViewById<TextView>(R.id.title)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import androidx.recyclerview.widget.ListAdapter
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.deniscerri.ytdlnis.R
|
||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||
import com.deniscerri.ytdlnis.util.Extensions.loadThumbnail
|
||||
import com.deniscerri.ytdlnis.util.Extensions.popup
|
||||
import com.squareup.picasso.Picasso
|
||||
import java.util.*
|
||||
|
|
@ -62,17 +63,8 @@ class PlaylistAdapter(onItemClickListener: OnItemClickListener, activity: Activi
|
|||
val thumbnail = card.findViewById<ImageView>(R.id.downloads_image_view)
|
||||
|
||||
// THUMBNAIL ----------------------------------
|
||||
if (!sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("home")){
|
||||
val imageURL = item!!.thumb
|
||||
if (imageURL.isNotEmpty()) {
|
||||
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
|
||||
} else {
|
||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
||||
}
|
||||
thumbnail.setColorFilter(Color.argb(95, 0, 0, 0))
|
||||
}else{
|
||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
||||
}
|
||||
val hideThumb = sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("home")
|
||||
uiHandler.post { thumbnail.loadThumbnail(hideThumb, item!!.thumb) }
|
||||
|
||||
card.findViewById<TextView>(R.id.title).text = item!!.title
|
||||
card.findViewById<TextView>(R.id.author).text = item.author
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import android.content.SharedPreferences
|
|||
import android.content.res.Configuration
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.format.DateFormat
|
||||
import android.util.DisplayMetrics
|
||||
|
|
@ -32,6 +33,7 @@ import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
|
|||
import com.deniscerri.ytdlnis.R
|
||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||
import com.deniscerri.ytdlnis.database.models.Format
|
||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||
import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel
|
||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||
import com.deniscerri.ytdlnis.database.viewmodel.HistoryViewModel
|
||||
|
|
@ -73,6 +75,8 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
private lateinit var count : TextView
|
||||
private lateinit var sharedPreferences: SharedPreferences
|
||||
|
||||
private lateinit var currentDownloadIDs: List<Long>
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
downloadViewModel = ViewModelProvider(requireActivity())[DownloadViewModel::class.java]
|
||||
|
|
@ -81,6 +85,8 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
commandTemplateViewModel = ViewModelProvider(requireActivity())[CommandTemplateViewModel::class.java]
|
||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
infoUtil = InfoUtil(requireContext())
|
||||
|
||||
currentDownloadIDs = arguments?.getLongArray("currentDownloadIDs")?.toList() ?: listOf()
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi", "NotifyDataSetChanged")
|
||||
|
|
@ -137,6 +143,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.deleteAllWithID(currentDownloadIDs)
|
||||
downloadViewModel.downloadProcessingDownloads(cal.timeInMillis)
|
||||
}
|
||||
|
||||
|
|
@ -153,6 +160,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
download.isEnabled = false
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.deleteAllWithID(currentDownloadIDs)
|
||||
downloadViewModel.downloadProcessingDownloads()
|
||||
}
|
||||
dismiss()
|
||||
|
|
@ -166,6 +174,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
dd.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||
lifecycleScope.launch{
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.deleteAllWithID(currentDownloadIDs)
|
||||
downloadViewModel.moveProcessingToSavedCategory()
|
||||
}
|
||||
dismiss()
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ class ObserveSourcesBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
private lateinit var endsAfterNr: TextInputLayout
|
||||
private lateinit var retryMissingDownloads: MaterialSwitch
|
||||
private lateinit var getOnlyNewUploads: MaterialSwitch
|
||||
private lateinit var resetProcessedLinks: MaterialSwitch
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
|
@ -252,6 +253,7 @@ class ObserveSourcesBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
endsAfterNr = view.findViewById(R.id.after_nr)
|
||||
retryMissingDownloads = view.findViewById(R.id.retry_missing_downloads)
|
||||
getOnlyNewUploads = view.findViewById(R.id.get_new_uploads)
|
||||
resetProcessedLinks = view.findViewById(R.id.reset_processed_links)
|
||||
okButton = view.findViewById(R.id.okButton)
|
||||
|
||||
title.editText!!.setText(currentItem?.name ?: "")
|
||||
|
|
@ -446,6 +448,7 @@ class ObserveSourcesBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
|
||||
retryMissingDownloads.isChecked = currentItem?.retryMissingDownloads ?: false
|
||||
getOnlyNewUploads.isChecked = currentItem?.getOnlyNewUploads ?: false
|
||||
resetProcessedLinks.isVisible = currentItem != null
|
||||
|
||||
if (currentItem != null) okButton.text = getString(R.string.update)
|
||||
okButton.setOnClickListener {
|
||||
|
|
@ -481,7 +484,7 @@ class ObserveSourcesBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
0,
|
||||
getOnlyNewUploads.isChecked,
|
||||
retryMissingDownloads.isChecked,
|
||||
mutableListOf()
|
||||
if (resetProcessedLinks.isChecked) mutableListOf() else currentItem?.alreadyProcessedLinks ?: mutableListOf()
|
||||
)
|
||||
withContext(Dispatchers.IO){
|
||||
observeSourcesViewModel.insert(observeItem)
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickL
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NORMAL, R.style.FullScreenDialogTheme)
|
||||
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
||||
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
|
||||
downloadViewModel = ViewModelProvider(requireActivity())[DownloadViewModel::class.java]
|
||||
resultViewModel = ViewModelProvider(requireActivity())[ResultViewModel::class.java]
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
|
|
@ -173,6 +173,7 @@ class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickL
|
|||
ok = toolbar.menu.getItem(0)
|
||||
ok.isEnabled = false
|
||||
ok.setOnMenuItemClickListener {
|
||||
ok.isEnabled = false
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val checkedItems = listAdapter.getCheckedItems()
|
||||
val checkedResultItems = items.filter { item -> checkedItems.contains(item!!.url) }
|
||||
|
|
@ -196,11 +197,11 @@ class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickL
|
|||
downloadItems.add(i)
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
downloadViewModel.insertToProcessing(downloadItems)
|
||||
}
|
||||
downloadViewModel.insertToProcessing(downloadItems)
|
||||
|
||||
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2)
|
||||
withContext(Dispatchers.Main){
|
||||
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog)
|
||||
}
|
||||
}
|
||||
|
||||
dismiss()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import android.widget.Toast
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.children
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
|
@ -33,6 +34,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
|||
import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter
|
||||
import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
|
||||
import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode
|
||||
import com.deniscerri.ytdlnis.util.Extensions.toListString
|
||||
import com.deniscerri.ytdlnis.util.UiUtil
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.color.MaterialColors
|
||||
|
|
@ -152,6 +154,22 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
|
|||
override fun onCardSelect(isChecked: Boolean, position: Int) {
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = adapter.getSelectedObjectsCount(totalSize)
|
||||
if (actionMode == null) actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||
actionMode?.apply {
|
||||
if (selectedObjects == 0){
|
||||
this.finish()
|
||||
}else{
|
||||
this.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||
if (selectedObjects == 2){
|
||||
val selectedIDs = contextualActionBar.getSelectedIDs().sortedBy { it }
|
||||
val idsInMiddle = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Cancelled).toListString())
|
||||
}
|
||||
this.menu.findItem(R.id.select_between).isVisible = idsInMiddle.isNotEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isChecked) {
|
||||
if (actionMode == null){
|
||||
actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||
|
|
@ -200,21 +218,28 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
|
|||
item: MenuItem?
|
||||
): Boolean {
|
||||
return when (item!!.itemId) {
|
||||
R.id.select_between -> {
|
||||
lifecycleScope.launch {
|
||||
val selectedIDs = getSelectedIDs().sortedBy { it }
|
||||
val idsInMiddle = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Cancelled).toListString())
|
||||
}.toMutableList()
|
||||
idsInMiddle.addAll(selectedIDs)
|
||||
if (idsInMiddle.isNotEmpty()){
|
||||
adapter.checkMultipleItems(idsInMiddle)
|
||||
actionMode?.title = "${idsInMiddle.count()} ${getString(R.string.selected)}"
|
||||
}
|
||||
mode?.menu?.findItem(R.id.select_between)?.isVisible = false
|
||||
}
|
||||
true
|
||||
}
|
||||
R.id.delete_results -> {
|
||||
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
||||
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems, listOf(
|
||||
DownloadRepository.Status.Cancelled))
|
||||
}
|
||||
}else{
|
||||
adapter.checkedItems.toList()
|
||||
}
|
||||
|
||||
val selectedObjects = getSelectedIDs()
|
||||
adapter.clearCheckedItems()
|
||||
downloadViewModel.deleteAllWithID(selectedObjects)
|
||||
actionMode?.finish()
|
||||
|
|
@ -225,21 +250,15 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
|
|||
}
|
||||
R.id.redownload -> {
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems, listOf(
|
||||
DownloadRepository.Status.Cancelled))
|
||||
}
|
||||
}else{
|
||||
adapter.checkedItems.toList()
|
||||
}
|
||||
|
||||
val selectedObjects = getSelectedIDs()
|
||||
if (preferences.getBoolean("download_card", true)){
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.addDownloadsToProcessing(selectedObjects)
|
||||
}
|
||||
withContext(Dispatchers.Main){
|
||||
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2)
|
||||
val bundle = Bundle()
|
||||
bundle.putLongArray("currentDownloadIDs", selectedObjects.toLongArray())
|
||||
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2,bundle)
|
||||
}
|
||||
}else{
|
||||
withContext(Dispatchers.IO) {
|
||||
|
|
@ -266,6 +285,17 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
|
|||
if (selectedObjects == 0) actionMode?.finish()
|
||||
true
|
||||
}
|
||||
R.id.copy_urls -> {
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = getSelectedIDs()
|
||||
val urls = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getURLsByIds(selectedObjects)
|
||||
}
|
||||
UiUtil.copyToClipboard(urls.joinToString("\n"), requireActivity())
|
||||
actionMode?.finish()
|
||||
}
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
|
@ -274,6 +304,17 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
|
|||
actionMode = null
|
||||
adapter.clearCheckedItems()
|
||||
}
|
||||
|
||||
suspend fun getSelectedIDs() : List<Long>{
|
||||
return if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems.toList(), listOf(
|
||||
DownloadRepository.Status.Cancelled))
|
||||
}
|
||||
}else{
|
||||
adapter.checkedItems.toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var simpleCallback: ItemTouchHelper.SimpleCallback =
|
||||
|
|
|
|||
|
|
@ -123,40 +123,36 @@ class DownloadQueueMainFragment : Fragment(){
|
|||
lifecycleScope.launch {
|
||||
downloadViewModel.activeDownloadsCount.collectLatest {
|
||||
tabLayout.getTabAt(0)?.apply {
|
||||
if (it == 0) removeBadge()
|
||||
else createBadge(it)
|
||||
createBadge(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
downloadViewModel.queuedDownloadsCount.collectLatest {
|
||||
tabLayout.getTabAt(1)?.apply {
|
||||
if (it == 0) removeBadge()
|
||||
else createBadge(it)
|
||||
createBadge(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
downloadViewModel.cancelledDownloadsCount.collectLatest {
|
||||
tabLayout.getTabAt(2)?.apply {
|
||||
if (it == 0) removeBadge()
|
||||
else createBadge(it)
|
||||
createBadge(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
downloadViewModel.erroredDownloadsCount.collectLatest {
|
||||
tabLayout.getTabAt(3)?.apply {
|
||||
if (it == 0) removeBadge()
|
||||
else createBadge(it)
|
||||
createBadge(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
downloadViewModel.savedDownloadsCount.collectLatest {
|
||||
tabLayout.getTabAt(4)?.apply {
|
||||
if (it == 0) removeBadge()
|
||||
else createBadge(it)
|
||||
removeBadge()
|
||||
if (it > 0) createBadge(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
|||
import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter
|
||||
import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
|
||||
import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode
|
||||
import com.deniscerri.ytdlnis.util.Extensions.toListString
|
||||
import com.deniscerri.ytdlnis.util.UiUtil
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.color.MaterialColors
|
||||
|
|
@ -154,18 +155,19 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
|
|||
override fun onCardSelect(isChecked: Boolean, position: Int) {
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = adapter.getSelectedObjectsCount(totalSize)
|
||||
if (isChecked) {
|
||||
if (actionMode == null){
|
||||
actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||
|
||||
}else{
|
||||
actionMode!!.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||
}
|
||||
}
|
||||
else {
|
||||
actionMode?.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||
if (actionMode == null) actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||
actionMode?.apply {
|
||||
if (selectedObjects == 0){
|
||||
actionMode?.finish()
|
||||
this.finish()
|
||||
}else{
|
||||
actionMode?.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||
if(selectedObjects == 2){
|
||||
val selectedIDs = contextualActionBar.getSelectedIDs().sortedBy { it }
|
||||
val idsInMiddle = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Error).toListString())
|
||||
}
|
||||
this.menu.findItem(R.id.select_between).isVisible = idsInMiddle.isNotEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -207,20 +209,28 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
|
|||
item: MenuItem?
|
||||
): Boolean {
|
||||
return when (item!!.itemId) {
|
||||
R.id.select_between -> {
|
||||
lifecycleScope.launch {
|
||||
val selectedIDs = getSelectedIDs().sortedBy { it }
|
||||
val idsInMiddle = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Cancelled).toListString())
|
||||
}.toMutableList()
|
||||
idsInMiddle.addAll(selectedIDs)
|
||||
if (idsInMiddle.isNotEmpty()){
|
||||
adapter.checkMultipleItems(idsInMiddle)
|
||||
actionMode?.title = "${idsInMiddle.count()} ${getString(R.string.selected)}"
|
||||
}
|
||||
mode?.menu?.findItem(R.id.select_between)?.isVisible = false
|
||||
}
|
||||
true
|
||||
}
|
||||
R.id.delete_results -> {
|
||||
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
||||
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems, listOf(
|
||||
DownloadRepository.Status.Error))
|
||||
}
|
||||
}else{
|
||||
adapter.checkedItems.toList()
|
||||
}
|
||||
val selectedObjects = getSelectedIDs()
|
||||
adapter.clearCheckedItems()
|
||||
downloadViewModel.deleteAllWithID(selectedObjects)
|
||||
actionMode?.finish()
|
||||
|
|
@ -231,21 +241,16 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
|
|||
}
|
||||
R.id.redownload -> {
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems, listOf(
|
||||
DownloadRepository.Status.Error))
|
||||
}
|
||||
}else{
|
||||
adapter.checkedItems.toList()
|
||||
}
|
||||
val selectedObjects = getSelectedIDs()
|
||||
|
||||
if (preferences.getBoolean("download_card", true)){
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.addDownloadsToProcessing(selectedObjects)
|
||||
}
|
||||
withContext(Dispatchers.Main){
|
||||
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2)
|
||||
val bundle = Bundle()
|
||||
bundle.putLongArray("currentDownloadIDs", selectedObjects.toLongArray())
|
||||
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2,bundle)
|
||||
}
|
||||
}else{
|
||||
withContext(Dispatchers.IO) {
|
||||
|
|
@ -270,6 +275,17 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
|
|||
if (selectedObjects == 0) actionMode?.finish()
|
||||
true
|
||||
}
|
||||
R.id.copy_urls -> {
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = getSelectedIDs()
|
||||
val urls = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getURLsByIds(selectedObjects)
|
||||
}
|
||||
UiUtil.copyToClipboard(urls.joinToString("\n"), requireActivity())
|
||||
actionMode?.finish()
|
||||
}
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
|
@ -278,6 +294,17 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
|
|||
actionMode = null
|
||||
adapter.clearCheckedItems()
|
||||
}
|
||||
|
||||
suspend fun getSelectedIDs() : List<Long>{
|
||||
return if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems.toList(), listOf(
|
||||
DownloadRepository.Status.Error))
|
||||
}
|
||||
}else{
|
||||
adapter.checkedItems.toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var simpleCallback: ItemTouchHelper.SimpleCallback =
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
|||
import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter
|
||||
import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
|
||||
import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode
|
||||
import com.deniscerri.ytdlnis.util.Extensions.toListString
|
||||
import com.deniscerri.ytdlnis.util.FileUtil
|
||||
import com.deniscerri.ytdlnis.util.NotificationUtil
|
||||
import com.deniscerri.ytdlnis.util.UiUtil
|
||||
|
|
@ -180,27 +181,26 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
|||
override fun onCardSelect(isChecked: Boolean, position: Int) {
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = adapter.getSelectedObjectsCount(totalSize)
|
||||
if (actionMode == null) actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||
val now = System.currentTimeMillis()
|
||||
if (isChecked) {
|
||||
if (actionMode == null){
|
||||
actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||
}else{
|
||||
|
||||
actionMode!!.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||
}
|
||||
}
|
||||
else {
|
||||
actionMode?.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||
actionMode?.apply {
|
||||
if (selectedObjects == 0){
|
||||
actionMode?.finish()
|
||||
}
|
||||
}
|
||||
if (actionMode != null){
|
||||
actionMode!!.menu.getItem(2).isVisible = withContext(Dispatchers.IO){
|
||||
downloadViewModel.checkAllQueuedItemsAreScheduledAfterNow(adapter.checkedItems, adapter.inverted, now)
|
||||
}
|
||||
this.finish()
|
||||
}else{
|
||||
this.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||
this.menu.findItem(R.id.download).isVisible = withContext(Dispatchers.IO){
|
||||
downloadViewModel.checkAllQueuedItemsAreScheduledAfterNow(adapter.checkedItems.toList(), adapter.inverted, now)
|
||||
}
|
||||
|
||||
actionMode!!.menu.getItem(1).isVisible = position > 0
|
||||
this.menu.findItem(R.id.up).isVisible = position > 0
|
||||
if(selectedObjects == 2){
|
||||
val selectedIDs = contextualActionBar.getSelectedIDs().sortedBy { it }
|
||||
val idsInMiddle = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Queued, DownloadRepository.Status.QueuedPaused).toListString())
|
||||
}
|
||||
this.menu.findItem(R.id.select_between).isVisible = idsInMiddle.isNotEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -250,20 +250,28 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
|||
item: MenuItem?
|
||||
): Boolean {
|
||||
return when (item!!.itemId) {
|
||||
R.id.select_between -> {
|
||||
lifecycleScope.launch {
|
||||
val selectedIDs = getSelectedIDs().sortedBy { it }
|
||||
val idsInMiddle = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Cancelled).toListString())
|
||||
}.toMutableList()
|
||||
idsInMiddle.addAll(selectedIDs)
|
||||
if (idsInMiddle.isNotEmpty()){
|
||||
adapter.checkMultipleItems(idsInMiddle)
|
||||
actionMode?.title = "${idsInMiddle.count()} ${getString(R.string.selected)}"
|
||||
}
|
||||
mode?.menu?.findItem(R.id.select_between)?.isVisible = false
|
||||
}
|
||||
true
|
||||
}
|
||||
R.id.delete_results -> {
|
||||
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
||||
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems, listOf(
|
||||
DownloadRepository.Status.Queued, DownloadRepository.Status.QueuedPaused))
|
||||
}
|
||||
}else{
|
||||
adapter.checkedItems.toList()
|
||||
}
|
||||
val selectedObjects = getSelectedIDs()
|
||||
adapter.clearCheckedItems()
|
||||
for (id in selectedObjects){
|
||||
YoutubeDL.getInstance().destroyProcessById(id.toInt().toString())
|
||||
|
|
@ -280,14 +288,7 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
|||
}
|
||||
R.id.download -> {
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems, listOf(
|
||||
DownloadRepository.Status.Queued, DownloadRepository.Status.QueuedPaused))
|
||||
}
|
||||
}else{
|
||||
adapter.checkedItems.toList()
|
||||
}
|
||||
val selectedObjects = getSelectedIDs()
|
||||
adapter.clearCheckedItems()
|
||||
for (id in selectedObjects){
|
||||
WorkManager.getInstance(requireContext()).cancelAllWorkByTag(id.toInt().toString())
|
||||
|
|
@ -313,15 +314,7 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
|||
}
|
||||
R.id.up -> {
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems, listOf(
|
||||
DownloadRepository.Status.Queued, DownloadRepository.Status.QueuedPaused))
|
||||
}
|
||||
}else{
|
||||
adapter.checkedItems.toList()
|
||||
}
|
||||
|
||||
val selectedObjects = getSelectedIDs()
|
||||
adapter.clearCheckedItems()
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.putAtTopOfQueue(selectedObjects)
|
||||
|
|
@ -330,6 +323,17 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
|||
}
|
||||
true
|
||||
}
|
||||
R.id.copy_urls -> {
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = getSelectedIDs()
|
||||
val urls = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getURLsByIds(selectedObjects)
|
||||
}
|
||||
UiUtil.copyToClipboard(urls.joinToString("\n"), requireActivity())
|
||||
actionMode?.finish()
|
||||
}
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
|
@ -338,6 +342,17 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
|||
actionMode = null
|
||||
adapter.clearCheckedItems()
|
||||
}
|
||||
|
||||
suspend fun getSelectedIDs() : List<Long>{
|
||||
return if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems.toList(), listOf(
|
||||
DownloadRepository.Status.Queued, DownloadRepository.Status.QueuedPaused))
|
||||
}
|
||||
}else{
|
||||
adapter.checkedItems.toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
|||
import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter
|
||||
import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
|
||||
import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode
|
||||
import com.deniscerri.ytdlnis.util.Extensions.toListString
|
||||
import com.deniscerri.ytdlnis.util.UiUtil
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.color.MaterialColors
|
||||
|
|
@ -152,18 +153,19 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis
|
|||
override fun onCardSelect(isChecked: Boolean, position: Int) {
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = adapter.getSelectedObjectsCount(totalSize)
|
||||
if (isChecked) {
|
||||
if (actionMode == null){
|
||||
actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||
|
||||
}else{
|
||||
actionMode!!.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||
}
|
||||
}
|
||||
else {
|
||||
actionMode?.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||
if (actionMode == null) actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||
actionMode?.apply {
|
||||
if (selectedObjects == 0){
|
||||
actionMode?.finish()
|
||||
this.finish()
|
||||
}else{
|
||||
actionMode?.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||
if(selectedObjects == 2){
|
||||
val selectedIDs = contextualActionBar.getSelectedIDs().sortedBy { it }
|
||||
val idsInMiddle = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Saved).toListString())
|
||||
}
|
||||
this.menu.findItem(R.id.select_between).isVisible = idsInMiddle.isNotEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -201,20 +203,28 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis
|
|||
item: MenuItem?
|
||||
): Boolean {
|
||||
return when (item!!.itemId) {
|
||||
R.id.select_between -> {
|
||||
lifecycleScope.launch {
|
||||
val selectedIDs = getSelectedIDs().sortedBy { it }
|
||||
val idsInMiddle = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Cancelled).toListString())
|
||||
}.toMutableList()
|
||||
idsInMiddle.addAll(selectedIDs)
|
||||
if (idsInMiddle.isNotEmpty()){
|
||||
adapter.checkMultipleItems(idsInMiddle)
|
||||
actionMode?.title = "${idsInMiddle.count()} ${getString(R.string.selected)}"
|
||||
}
|
||||
mode?.menu?.findItem(R.id.select_between)?.isVisible = false
|
||||
}
|
||||
true
|
||||
}
|
||||
R.id.delete_results -> {
|
||||
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
||||
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = if (adapter.inverted){
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems, listOf(
|
||||
DownloadRepository.Status.Saved))
|
||||
}
|
||||
}else{
|
||||
adapter.checkedItems.toList()
|
||||
}
|
||||
val selectedObjects = getSelectedIDs()
|
||||
adapter.clearCheckedItems()
|
||||
for (id in selectedObjects){
|
||||
downloadViewModel.deleteDownload(id)
|
||||
|
|
@ -227,21 +237,16 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis
|
|||
}
|
||||
R.id.download -> {
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems, listOf(
|
||||
DownloadRepository.Status.Saved))
|
||||
}
|
||||
}else{
|
||||
adapter.checkedItems.toList()
|
||||
}
|
||||
val selectedObjects = getSelectedIDs()
|
||||
adapter.clearCheckedItems()
|
||||
if (preferences.getBoolean("download_card", true)){
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.addDownloadsToProcessing(selectedObjects)
|
||||
}
|
||||
withContext(Dispatchers.Main){
|
||||
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2)
|
||||
val bundle = Bundle()
|
||||
bundle.putLongArray("currentDownloadIDs", selectedObjects.toLongArray())
|
||||
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2,bundle)
|
||||
}
|
||||
}else{
|
||||
withContext(Dispatchers.IO) {
|
||||
|
|
@ -265,6 +270,17 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis
|
|||
if (selectedObjects == 0) actionMode?.finish()
|
||||
true
|
||||
}
|
||||
R.id.copy_urls -> {
|
||||
lifecycleScope.launch {
|
||||
val selectedObjects = getSelectedIDs()
|
||||
val urls = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getURLsByIds(selectedObjects)
|
||||
}
|
||||
UiUtil.copyToClipboard(urls.joinToString("\n"), requireActivity())
|
||||
actionMode?.finish()
|
||||
}
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
|
@ -273,6 +289,17 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis
|
|||
actionMode = null
|
||||
adapter.clearCheckedItems()
|
||||
}
|
||||
|
||||
suspend fun getSelectedIDs() : List<Long>{
|
||||
return if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems.toList(), listOf(
|
||||
DownloadRepository.Status.Saved))
|
||||
}
|
||||
}else{
|
||||
adapter.checkedItems.toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ class CookiesFragment : Fragment(), CookieAdapter.OnItemClickListener {
|
|||
builder.setNeutralButton(
|
||||
getString(android.R.string.copy)
|
||||
) { dialog: DialogInterface?, which: Int ->
|
||||
UiUtil.copyToClipboard(item.content, requireActivity())
|
||||
UiUtil.copyToClipboard(cookiesViewModel.cookieHeader + "\n" + item.content, requireActivity())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ class ObserveSourcesFragment : Fragment(), ObserveSourcesAdapter.OnItemClickList
|
|||
}
|
||||
override fun onItemSearch(item: ObserveSourcesItem) {
|
||||
runCatching {
|
||||
observeSourcesViewModel.cancelObservationTaskByID(item.id)
|
||||
val intent = Intent(context, ObserveAlarmReceiver::class.java)
|
||||
intent.putExtra("id", item.id)
|
||||
requireActivity().sendBroadcast(intent)
|
||||
|
|
|
|||
|
|
@ -20,18 +20,22 @@ import android.view.ViewGroup
|
|||
import android.view.ViewOutlineProvider
|
||||
import android.view.animation.Interpolator
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.Px
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.withStarted
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.deniscerri.ytdlnis.R
|
||||
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.neo.highlight.core.Highlight
|
||||
import com.neo.highlight.util.listener.HighlightTextWatcher
|
||||
import com.neo.highlight.util.scheme.ColorScheme
|
||||
import com.squareup.picasso.Picasso
|
||||
import kotlinx.coroutines.launch
|
||||
import me.zhanghai.android.fastscroll.FastScrollerBuilder
|
||||
import org.json.JSONObject
|
||||
|
|
@ -215,10 +219,16 @@ object Extensions {
|
|||
}
|
||||
|
||||
fun TabLayout.Tab.createBadge(nr: Int){
|
||||
this.orCreateBadge.apply {
|
||||
number = nr
|
||||
verticalOffset = 5
|
||||
horizontalOffset = 10
|
||||
removeBadge()
|
||||
if (nr > 0) {
|
||||
orCreateBadge.apply {
|
||||
number = nr
|
||||
verticalOffset = 3
|
||||
horizontalOffset =
|
||||
if (nr < 10) 7
|
||||
else if (nr < 100) 10
|
||||
else 20
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -233,6 +243,26 @@ object Extensions {
|
|||
return lines.dropLastWhile { it.contains("[download") }.joinToString("\n") + "\n${newLine}"
|
||||
}
|
||||
|
||||
fun ImageView.loadThumbnail(hideThumb: Boolean, imageURL: String){
|
||||
if(!hideThumb){
|
||||
if (imageURL.isNotEmpty()) {
|
||||
Picasso.get()
|
||||
.load(imageURL)
|
||||
.resize(1280, 0)
|
||||
.onlyScaleDown()
|
||||
.into(this)
|
||||
|
||||
} else {
|
||||
Picasso.get().load(R.color.black).into(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun List<DownloadRepository.Status>.toListString() : List<String>{
|
||||
return this.map { it.toString() }
|
||||
}
|
||||
|
||||
fun List<String>.closestValue(value: String) = minBy { abs(value.toInt() - it.toInt()) }
|
||||
|
||||
class CustomInterpolator : Interpolator {
|
||||
|
|
|
|||
|
|
@ -761,7 +761,7 @@ class InfoUtil(private val context: Context) {
|
|||
formatProper.format_note = format.getString("format_note")
|
||||
}else{
|
||||
if (!formatProper.format_note.endsWith("audio", true)){
|
||||
formatProper.format_note = "${format.getString("format_note")} audio"
|
||||
formatProper.format_note = format.getString("format_note").uppercase().removeSuffix("AUDIO") + " AUDIO"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -772,8 +772,8 @@ class InfoUtil(private val context: Context) {
|
|||
formatProper.tbr = formatProper.tbr + "k"
|
||||
}
|
||||
|
||||
if(formatProper.vcodec.isEmpty() || formatProper.vcodec == "null"){
|
||||
if(formatProper.acodec.isEmpty() || formatProper.acodec == "null"){
|
||||
if(formatProper.vcodec.isNullOrEmpty() || formatProper.vcodec == "null"){
|
||||
if(formatProper.acodec.isNullOrEmpty() || formatProper.acodec == "null"){
|
||||
formatProper.vcodec = format.getStringByAny("video_ext", "ext").ifEmpty { "unknown" }
|
||||
}
|
||||
}
|
||||
|
|
@ -1032,36 +1032,22 @@ class InfoUtil(private val context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
try {
|
||||
val config = File(context.cacheDir.absolutePath + "/config" + downloadItem.id + "##replaceMetadata.txt")
|
||||
YoutubeDLRequest("").apply {
|
||||
|
||||
if (downloadItem.playlistTitle.isNotBlank()){
|
||||
addCommands(listOf("--replace-in-metadata", "playlist", ".+", downloadItem.playlistTitle))
|
||||
runCatching {
|
||||
if (downloadItem.playlistIndex != null){
|
||||
addOption("--parse-metadata", downloadItem.playlistIndex.toString() + ":%(playlist_index)s")
|
||||
}
|
||||
}
|
||||
if (downloadItem.playlistTitle.isNotBlank()){
|
||||
request.addCommands(listOf("--replace-in-metadata", "video:playlist", ".+", downloadItem.playlistTitle))
|
||||
runCatching {
|
||||
if (downloadItem.playlistIndex != null){
|
||||
request.addOption("--parse-metadata", downloadItem.playlistIndex.toString() + ":%(playlist_index)s")
|
||||
}
|
||||
|
||||
if(downloadItem.title.isNotBlank()){
|
||||
addCommands(listOf("--replace-in-metadata", "title", ".+", downloadItem.title))
|
||||
}
|
||||
|
||||
|
||||
if (downloadItem.author.isNotBlank()){
|
||||
addCommands(listOf("--replace-in-metadata", "uploader", ".+", downloadItem.author))
|
||||
}
|
||||
|
||||
val configData = parseYTDLRequestString(this).replace(" \" \"", "")
|
||||
config.writeText(configData)
|
||||
request.addOption("--config", config.absolutePath)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
if(downloadItem.title.isNotBlank()){
|
||||
request.addCommands(listOf("--replace-in-metadata", "video:title", ".+", downloadItem.title.take(150)))
|
||||
}
|
||||
|
||||
|
||||
if (downloadItem.author.isNotBlank()){
|
||||
request.addCommands(listOf("--replace-in-metadata", "video:uploader", ".+", downloadItem.author.take(30)))
|
||||
}
|
||||
|
||||
request.addOption("--parse-metadata", "uploader:(?P<uploader>.+)(?: - Topic)$")
|
||||
|
|
@ -1111,10 +1097,6 @@ class InfoUtil(private val context: Context) {
|
|||
if (sharedPreferences.getBoolean("write_description", false)){
|
||||
request.addOption("--write-description")
|
||||
}
|
||||
|
||||
filenameTemplate = filenameTemplate
|
||||
.replace("%(uploader)s", "%(uploader,channel).30B")
|
||||
.replace("%(title)s", "%(title).170B")
|
||||
}
|
||||
|
||||
if (sharedPreferences.getBoolean("download_archive", false)){
|
||||
|
|
|
|||
|
|
@ -219,6 +219,7 @@ class NotificationUtil(var context: Context) {
|
|||
}
|
||||
|
||||
fun createDownloadFinished(
|
||||
id: Long,
|
||||
title: String?,
|
||||
filepath: List<String>?,
|
||||
res: Resources
|
||||
|
|
@ -234,64 +235,75 @@ class NotificationUtil(var context: Context) {
|
|||
R.drawable.ic_launcher_foreground_large
|
||||
)
|
||||
)
|
||||
.setGroup(DOWNLOAD_FINISHED_NOTIFICATION_ID.toString())
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
|
||||
.setContentText("")
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.clearActions()
|
||||
if (filepath != null){
|
||||
try{
|
||||
val uri = filepath.first().runCatching {
|
||||
DocumentFile.fromSingleUri(context, Uri.parse(filepath.first())).run{
|
||||
if (this?.exists() == true){
|
||||
this.uri
|
||||
}else if (File(this@runCatching).exists()){
|
||||
FileProvider.getUriForFile(context, context.packageName + ".fileprovider",
|
||||
File(this@runCatching))
|
||||
}else null
|
||||
}
|
||||
}.getOrNull()
|
||||
val uris = filepath.mapNotNull {
|
||||
runCatching {
|
||||
DocumentFile.fromSingleUri(context, Uri.parse(it)).run{
|
||||
if (this?.exists() == true){
|
||||
this.uri
|
||||
}else if (File(it).exists()){
|
||||
FileProvider.getUriForFile(context, context.packageName + ".fileprovider",
|
||||
File(it))
|
||||
}else null
|
||||
}
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
val openFileIntent = Intent()
|
||||
val shareFileIntent = Intent()
|
||||
|
||||
if (uri != null){
|
||||
if (uris.isNotEmpty()){
|
||||
openFileIntent.apply {
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
action = Intent.ACTION_VIEW
|
||||
data = uri
|
||||
data = uris.first()
|
||||
}
|
||||
|
||||
shareFileIntent.apply {
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
action = Intent.ACTION_SEND_MULTIPLE
|
||||
putParcelableArrayListExtra(Intent.EXTRA_STREAM, ArrayList(uris))
|
||||
type = if (uris.size == 1) uris[0].let { context.contentResolver.getType(it) } ?: "media/*" else "*/*"
|
||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
}
|
||||
}
|
||||
|
||||
val openNotificationPendingIntent: PendingIntent = TaskStackBuilder.create(context).run {
|
||||
addNextIntentWithParentStack(openFileIntent)
|
||||
getPendingIntent(0,
|
||||
getPendingIntent(id.toInt(),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
||||
}
|
||||
|
||||
|
||||
|
||||
//share intent
|
||||
val shareIntent = Intent(context, ShareFileActivity::class.java)
|
||||
shareIntent.putExtra("path", filepath.toTypedArray())
|
||||
shareIntent.putExtra("notificationID", DOWNLOAD_FINISHED_NOTIFICATION_ID)
|
||||
val shareNotificationPendingIntent: PendingIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
0,
|
||||
shareIntent,
|
||||
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
|
||||
id.toInt(),
|
||||
Intent.createChooser(shareFileIntent, res.getString(R.string.share)),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
notificationBuilder.addAction(0, res.getString(R.string.Open_File), openNotificationPendingIntent)
|
||||
notificationBuilder.addAction(0, res.getString(R.string.share), shareNotificationPendingIntent)
|
||||
}catch (_: Exception){}
|
||||
}
|
||||
notificationManager.notify(DOWNLOAD_FINISHED_NOTIFICATION_ID, notificationBuilder.build())
|
||||
notificationManager.notify(DOWNLOAD_FINISHED_NOTIFICATION_ID + id.toInt(), notificationBuilder.build())
|
||||
}
|
||||
|
||||
fun createDownloadErrored(title: String?,
|
||||
error: String?,
|
||||
logID: Long?,
|
||||
res: Resources
|
||||
fun createDownloadErrored(
|
||||
id: Long,
|
||||
title: String?,
|
||||
error: String?,
|
||||
logID: Long?,
|
||||
res: Resources
|
||||
) {
|
||||
val notificationBuilder = getBuilder(DOWNLOAD_ERRORED_CHANNEL_ID)
|
||||
|
||||
|
|
@ -333,7 +345,7 @@ class NotificationUtil(var context: Context) {
|
|||
notificationBuilder.setContentIntent(errorPendingIntent)
|
||||
notificationBuilder.addAction(0, res.getString(R.string.logs), errorPendingIntent)
|
||||
}
|
||||
notificationManager.notify(DOWNLOAD_ERRORED_NOTIFICATION_ID, notificationBuilder.build())
|
||||
notificationManager.notify(DOWNLOAD_ERRORED_NOTIFICATION_ID + id.toInt(), notificationBuilder.build())
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -422,7 +434,6 @@ class NotificationUtil(var context: Context) {
|
|||
fun cancelDownloadNotification(id: Int) {
|
||||
notificationManager.cancel(id)
|
||||
}
|
||||
|
||||
fun createMoveCacheFilesNotification(pendingIntent: PendingIntent?, downloadMiscChannelId: String): Notification {
|
||||
val notificationBuilder = getBuilder(downloadMiscChannelId)
|
||||
|
||||
|
|
@ -574,7 +585,7 @@ class NotificationUtil(var context: Context) {
|
|||
.createPendingIntent()
|
||||
|
||||
notificationBuilder
|
||||
.setContentTitle(resources.getString(R.string.finished_download_notification_channel_name))
|
||||
.setContentTitle(resources.getString(R.string.all_queries_finished))
|
||||
.setSmallIcon(R.drawable.ic_launcher_foreground_large)
|
||||
.setLargeIcon(
|
||||
BitmapFactory.decodeResource(
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ class UpdateUtil(var context: Context) {
|
|||
d?.dismiss()
|
||||
}
|
||||
.setNegativeButton(context.resources.getString(R.string.cancel)) { _: DialogInterface?, _: Int -> }
|
||||
.setPositiveButton(context.resources.getString(R.string.update)) { _: DialogInterface?, _: Int ->
|
||||
.setPositiveButton(context.resources.getString(R.string.update)) { d: DialogInterface?, _: Int ->
|
||||
runCatching {
|
||||
val releaseVersion = v.assets.firstOrNull { it.name.contains(Build.SUPPORTED_ABIS[0]) }
|
||||
if (releaseVersion == null){
|
||||
|
|
@ -140,8 +140,8 @@ class UpdateUtil(var context: Context) {
|
|||
}
|
||||
|
||||
context.registerReceiver(onDownloadComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
|
||||
d?.dismiss()
|
||||
}
|
||||
|
||||
}
|
||||
val view = updateDialog.show()
|
||||
val textView = view.findViewById<TextView>(android.R.id.message)
|
||||
|
|
@ -176,7 +176,7 @@ class UpdateUtil(var context: Context) {
|
|||
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
|
||||
layoutParams.setMargins(5, 5, 5, 0)
|
||||
layoutParams.setMargins(10, 10, 10, 0)
|
||||
scrollView.layoutParams = layoutParams
|
||||
scrollView.addView(linearLayout)
|
||||
val releases = getGithubReleases()
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ class DownloadWorker(
|
|||
|
||||
notificationUtil.cancelDownloadNotification(downloadItem.id.toInt())
|
||||
notificationUtil.createDownloadFinished(
|
||||
downloadItem.title, if (finalPaths?.first().equals(context.getString(R.string.unfound_file))) null else finalPaths, resources
|
||||
downloadItem.id, downloadItem.title, if (finalPaths?.first().equals(context.getString(R.string.unfound_file))) null else finalPaths, resources
|
||||
)
|
||||
|
||||
if (wasQuickDownloaded && createResultItem){
|
||||
|
|
@ -326,7 +326,9 @@ class DownloadWorker(
|
|||
}
|
||||
|
||||
notificationUtil.createDownloadErrored(
|
||||
downloadItem.title.ifEmpty { downloadItem.url }, it.message,
|
||||
downloadItem.id,
|
||||
downloadItem.title.ifEmpty { downloadItem.url },
|
||||
it.message,
|
||||
downloadItem.logID,
|
||||
resources
|
||||
)
|
||||
|
|
|
|||
|
|
@ -65,22 +65,20 @@ class ObserveSourceWorker(
|
|||
}
|
||||
}
|
||||
.filter { result ->
|
||||
|
||||
val history = historyRepo.getAllByURL(result.url)
|
||||
if (!item.retryMissingDownloads){
|
||||
//all items that are not present in history
|
||||
history.none { hi -> hi.downloadPath.any { path -> FileUtil.exists(path) } }
|
||||
}else{
|
||||
//all items that are not already processed
|
||||
if(item.alreadyProcessedLinks.isEmpty()){
|
||||
!history.map { it.url }.contains(result.url) && !item.alreadyProcessedLinks.contains(result.url)
|
||||
val history = historyRepo.getAllByURL(result.url)
|
||||
if (item.retryMissingDownloads){
|
||||
//all items that are not present in history
|
||||
history.none { hi -> hi.downloadPath.any { path -> FileUtil.exists(path) } }
|
||||
}else{
|
||||
!item.alreadyProcessedLinks.contains(result.url)
|
||||
//all items that are not already processed
|
||||
if(item.alreadyProcessedLinks.isEmpty()){
|
||||
!history.map { it.url }.contains(result.url)
|
||||
}else{
|
||||
!item.alreadyProcessedLinks.contains(result.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val items = mutableListOf<DownloadItem>()
|
||||
|
||||
res.forEach {
|
||||
|
|
@ -208,47 +206,18 @@ class ObserveSourceWorker(
|
|||
repo.update(item)
|
||||
}
|
||||
|
||||
scheduleForNextTime(item)
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
private fun scheduleForNextTime(item: ObserveSourcesItem){
|
||||
//schedule for next time
|
||||
val alarmManager = context.getSystemService(AlarmManager::class.java)
|
||||
val intent = Intent(context, ObserveAlarmReceiver::class.java)
|
||||
intent.putExtra("id", item.id)
|
||||
|
||||
val c = Calendar.getInstance()
|
||||
val hourMin = Calendar.getInstance()
|
||||
hourMin.timeInMillis = item.everyTime
|
||||
c.set(Calendar.HOUR_OF_DAY, hourMin.get(Calendar.HOUR_OF_DAY))
|
||||
c.set(Calendar.MINUTE, hourMin.get(Calendar.MINUTE))
|
||||
|
||||
when(item.everyCategory){
|
||||
ObserveSourcesRepository.EveryCategory.DAY -> {
|
||||
c.add(Calendar.DAY_OF_MONTH, item.everyNr)
|
||||
}
|
||||
ObserveSourcesRepository.EveryCategory.WEEK -> {
|
||||
if(item.everyWeekDay.isEmpty()){
|
||||
c.add(Calendar.DAY_OF_MONTH, 7 * item.everyNr)
|
||||
}else{
|
||||
val weekDayID = c.get(Calendar.DAY_OF_WEEK).toString()
|
||||
val followingWeekDay = (item.everyWeekDay.firstOrNull { it.toInt() > weekDayID.toInt() } ?: item.everyWeekDay.minBy { it.toInt() }).toInt()
|
||||
c.set(Calendar.DAY_OF_WEEK, followingWeekDay)
|
||||
if (item.everyNr > 1){
|
||||
c.add(Calendar.DAY_OF_MONTH, 7 * item.everyNr)
|
||||
}
|
||||
}
|
||||
}
|
||||
ObserveSourcesRepository.EveryCategory.MONTH -> {
|
||||
c.add(Calendar.MONTH, item.everyNr)
|
||||
c.set(Calendar.DAY_OF_MONTH, item.everyMonthDay)
|
||||
}
|
||||
}
|
||||
alarmManager.setExact(
|
||||
AlarmManager.RTC,
|
||||
c.timeInMillis,
|
||||
AlarmManager.RTC_WAKEUP,
|
||||
repo.calculateNextTime(item),
|
||||
PendingIntent.getBroadcast(context, item.id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
||||
)
|
||||
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
}
|
||||
10
app/src/main/res/drawable/ic_select_between.xml
Normal file
10
app/src/main/res/drawable/ic_select_between.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?android:textColorPrimary">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M440,880L440,712L376,776L320,720L480,560L640,720L584,776L520,712L520,880L440,880ZM160,520L160,440L800,440L800,520L160,520ZM480,400L320,240L376,184L440,248L440,80L520,80L520,248L584,184L640,240L480,400Z"/>
|
||||
</vector>
|
||||
|
|
@ -171,6 +171,7 @@
|
|||
style="?attr/materialIconButtonFilledStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/pause"
|
||||
android:layoutAnimation="@anim/popup_enter"
|
||||
app:backgroundTint="?attr/colorSurface"
|
||||
app:cornerRadius="15dp"
|
||||
|
|
|
|||
|
|
@ -63,35 +63,38 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/format_note"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="10dp"
|
||||
android:textDirection="locale"
|
||||
android:ellipsize="end"
|
||||
android:gravity="start"
|
||||
android:maxLines="2"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/format_id"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="0dp"
|
||||
android:id="@+id/relativeLayout"
|
||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/format_id"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/format_note">
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:layout_marginStart="10dp"
|
||||
android:scrollbars="none">
|
||||
|
||||
<LinearLayout
|
||||
|
|
@ -111,14 +114,14 @@
|
|||
android:clickable="false"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLength="10"
|
||||
android:minWidth="30dp"
|
||||
android:paddingHorizontal="5dp"
|
||||
android:textColor="@color/white"
|
||||
android:textCursorDrawable="@string/audio_format"
|
||||
android:textStyle="bold"
|
||||
app:cornerRadius="10dp"
|
||||
android:maxLength="10"
|
||||
app:drawableStartCompat="@drawable/ic_music_formatcard"
|
||||
android:textCursorDrawable="@string/audio_format"
|
||||
app:drawableTint="@color/white"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
@ -175,16 +178,14 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginBottom="3dp"
|
||||
android:clickable="false"
|
||||
android:ellipsize="end"
|
||||
android:gravity="bottom|end"
|
||||
android:maxLength="10"
|
||||
android:ellipsize="end"
|
||||
app:cornerRadius="10dp"
|
||||
app:layout_constraintVertical_bias="1.0"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/format_note"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/format_note" />
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
|||
|
|
@ -500,6 +500,13 @@
|
|||
android:checked="false"
|
||||
android:text="@string/retry_missing_downloads"/>
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/reset_processed_links"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:text="@string/reset_processed_links"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,13 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".MainActivity" >
|
||||
|
||||
<item
|
||||
android:id="@+id/select_between"
|
||||
android:visible="false"
|
||||
android:title="@string/select_between"
|
||||
android:icon="@drawable/ic_select_between"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/delete_results"
|
||||
android:title="@string/remove_results"
|
||||
|
|
@ -25,5 +32,11 @@
|
|||
android:title="@string/invert_selected"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/copy_urls"
|
||||
android:icon="@drawable/ic_delete_all"
|
||||
android:title="@string/copy_urls"
|
||||
app:showAsAction="never" />
|
||||
|
||||
</menu>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,13 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".MainActivity" >
|
||||
|
||||
<item
|
||||
android:id="@+id/select_between"
|
||||
android:visible="false"
|
||||
android:title="@string/select_between"
|
||||
android:icon="@drawable/ic_select_between"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/delete_results"
|
||||
android:title="@string/remove_results"
|
||||
|
|
@ -32,5 +39,11 @@
|
|||
android:title="@string/invert_selected"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/copy_urls"
|
||||
android:icon="@drawable/ic_delete_all"
|
||||
android:title="@string/copy_urls"
|
||||
app:showAsAction="never" />
|
||||
|
||||
</menu>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,14 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".MainActivity" >
|
||||
|
||||
<item
|
||||
android:id="@+id/select_between"
|
||||
android:visible="false"
|
||||
android:title="@string/select_between"
|
||||
android:icon="@drawable/ic_select_between"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
|
||||
<item
|
||||
android:id="@+id/delete_results"
|
||||
android:title="@string/remove_results"
|
||||
|
|
@ -25,5 +33,11 @@
|
|||
android:title="@string/invert_selected"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/copy_urls"
|
||||
android:icon="@drawable/ic_delete_all"
|
||||
android:title="@string/copy_urls"
|
||||
app:showAsAction="never" />
|
||||
|
||||
</menu>
|
||||
|
||||
|
|
|
|||
|
|
@ -372,4 +372,7 @@
|
|||
<string name="text_size">Text Size</string>
|
||||
<string name="add">Add</string>
|
||||
<string name="back">Back</string>
|
||||
<string name="all_queries_finished">All Queries have been processed</string>
|
||||
<string name="select_between">Select Items Between</string>
|
||||
<string name="reset_processed_links">Reset Already Processed Links</string>
|
||||
</resources>
|
||||
15
fastlane/metadata/android/en-US/changelogs/10731.txt
Normal file
15
fastlane/metadata/android/en-US/changelogs/10731.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
## What's New
|
||||
|
||||
- Added ability to select all items between two selected items in download queue
|
||||
- Fixed app only getting one URL when tapping "Link you copied" in search view even if you copied multiple links
|
||||
- Fixed app crashing on playlist selector when sharing a playlist. Forgor to configure it with the new changes
|
||||
- Fixed app not removing errored, cancelled, saved downloads after you queued them for re-download
|
||||
- Added option to copy URLs of selected downloads in the download queue screen
|
||||
- Made app show all finished and errored notifications, instead of the latest one
|
||||
- Kinda fixed the app not giving you the correct path when sharing a file from the notification
|
||||
- Fixed app crashing when loading soundcloud results. They had stupidly large thumbnails, they are resized now
|
||||
- Added option to reset the recorded links in observe resources when you are trying to update it. From now on they wont get reset like before
|
||||
- Fixed major bug of app adding extra quotes in the yt-dlp config, making it fail on titles with quotes in them
|
||||
|
||||
Had to push this soon because of mostly the last error
|
||||
|
||||
Loading…
Reference in a new issue