1.7.7
This commit is contained in:
parent
f724b14dfa
commit
3c5167f6eb
15 changed files with 193 additions and 477 deletions
|
|
@ -10,7 +10,7 @@ plugins {
|
||||||
def properties = new Properties()
|
def properties = new Properties()
|
||||||
def versionMajor = 1
|
def versionMajor = 1
|
||||||
def versionMinor = 7
|
def versionMinor = 7
|
||||||
def versionPatch = 6
|
def versionPatch = 7
|
||||||
def versionBuild = 0 // bump for dogfood builds, public betas, etc.
|
def versionBuild = 0 // bump for dogfood builds, public betas, etc.
|
||||||
def isBeta = false
|
def isBeta = false
|
||||||
def versionExt = isBeta ? ".${versionBuild}-beta" : ""
|
def versionExt = isBeta ? ".${versionBuild}-beta" : ""
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,6 @@
|
||||||
<uses-permission android:name="android.permission.ACTION_OPEN_DOCUMENT" />
|
<uses-permission android:name="android.permission.ACTION_OPEN_DOCUMENT" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
|
||||||
<!-- for display over apps-->
|
|
||||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
|
||||||
|
|
||||||
<!-- alarm scheduler-->
|
<!-- alarm scheduler-->
|
||||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
|
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
|
||||||
|
|
||||||
|
|
@ -434,8 +430,6 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<service android:name=".services.ProcessDownloadsInBackgroundService" android:exported="false" />
|
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
||||||
android:enabled="false"
|
android:enabled="false"
|
||||||
|
|
|
||||||
|
|
@ -38,9 +38,9 @@ interface DownloadDao {
|
||||||
|
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT DISTINCT type from downloads where status = 'Processing' and id in (:ids)
|
SELECT DISTINCT type from downloads where status = 'Processing'
|
||||||
""")
|
""")
|
||||||
fun getProcessingDownloadTypes(ids: List<Long>) : List<String>
|
fun getProcessingDownloadTypes() : List<String>
|
||||||
|
|
||||||
|
|
||||||
@Query("UPDATE downloads set status = 'Processing' WHERE id in (:ids)")
|
@Query("UPDATE downloads set status = 'Processing' WHERE id in (:ids)")
|
||||||
|
|
@ -56,6 +56,12 @@ interface DownloadDao {
|
||||||
@Query("SELECT * FROM downloads WHERE status = 'Processing'")
|
@Query("SELECT * FROM downloads WHERE status = 'Processing'")
|
||||||
fun getProcessingDownloadsList() : List<DownloadItem>
|
fun getProcessingDownloadsList() : List<DownloadItem>
|
||||||
|
|
||||||
|
@Query("UPDATE downloads set downloadStartTime=:time, status='Scheduled' WHERE status ='Processing'")
|
||||||
|
suspend fun updateProcessingDownloadTime(time: Long)
|
||||||
|
|
||||||
|
@Query("UPDATE downloads set downloadPath=:path WHERE status ='Processing'")
|
||||||
|
suspend fun updateProcessingDownloadPath(path: String)
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Active'")
|
@Query("SELECT * FROM downloads WHERE status='Active'")
|
||||||
suspend fun getActiveDownloadsList() : List<DownloadItem>
|
suspend fun getActiveDownloadsList() : List<DownloadItem>
|
||||||
|
|
||||||
|
|
@ -126,12 +132,6 @@ interface DownloadDao {
|
||||||
@Query("SELECT * FROM downloads WHERE id IN (:ids)")
|
@Query("SELECT * FROM downloads WHERE id IN (:ids)")
|
||||||
fun getDownloadsByIds(ids: List<Long>) : List<DownloadItem>
|
fun getDownloadsByIds(ids: List<Long>) : List<DownloadItem>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Processing' AND id >=:id")
|
|
||||||
fun getProcessingItemsFromID(id: Long) : Flow<List<DownloadItem>>
|
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Processing' AND id >=:id and id<=:id2")
|
|
||||||
fun getProcessingItemsBetweenIDs(id: Long, id2: Long) : List<DownloadItem>
|
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE id IN (:ids)")
|
@Query("SELECT * FROM downloads WHERE id IN (:ids)")
|
||||||
fun getDownloadsByIdsFlow(ids: List<Long>) : Flow<List<DownloadItem>>
|
fun getDownloadsByIdsFlow(ids: List<Long>) : Flow<List<DownloadItem>>
|
||||||
|
|
||||||
|
|
@ -231,8 +231,8 @@ interface DownloadDao {
|
||||||
@Query("Update downloads SET status='Queued', downloadStartTime = 0 WHERE id in (:list)")
|
@Query("Update downloads SET status='Queued', downloadStartTime = 0 WHERE id in (:list)")
|
||||||
suspend fun reQueueDownloadItems(list: List<Long>)
|
suspend fun reQueueDownloadItems(list: List<Long>)
|
||||||
|
|
||||||
@Query("Update downloads SET status='Saved' WHERE status='Processing' and id in (:ids)")
|
@Query("Update downloads SET status='Saved' WHERE status='Processing'")
|
||||||
fun updateProcessingtoSavedStatus(ids: List<Long>)
|
suspend fun updateProcessingtoSavedStatus()
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
suspend fun putAtTopOfTheQueue(existingIDs: List<Long>){
|
suspend fun putAtTopOfTheQueue(existingIDs: List<Long>){
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,11 @@ package com.deniscerri.ytdl.database.repository
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.text.format.DateFormat
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.core.os.postDelayed
|
||||||
import androidx.paging.Pager
|
import androidx.paging.Pager
|
||||||
import androidx.paging.PagingConfig
|
import androidx.paging.PagingConfig
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
|
@ -28,6 +31,8 @@ import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -109,14 +114,6 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
return downloadDao.getDownloadById(id)
|
return downloadDao.getDownloadById(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getProcessingItemsFromID(id: Long) : Flow<List<DownloadItem>> {
|
|
||||||
return downloadDao.getProcessingItemsFromID(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getProcessingItemsBetweenIDs(first: Long, last:Long) : List<DownloadItem> {
|
|
||||||
return downloadDao.getProcessingItemsBetweenIDs(first, last)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getAllItemsByIDs(ids : List<Long>) : List<DownloadItem>{
|
fun getAllItemsByIDs(ids : List<Long>) : List<DownloadItem>{
|
||||||
return downloadDao.getDownloadsByIds(ids)
|
return downloadDao.getDownloadsByIds(ids)
|
||||||
}
|
}
|
||||||
|
|
@ -137,6 +134,10 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
return downloadDao.getActiveAndQueuedDownloadsList()
|
return downloadDao.getActiveAndQueuedDownloadsList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun updateProcessingDownloadTime(time: Long) {
|
||||||
|
downloadDao.updateProcessingDownloadTime(time)
|
||||||
|
}
|
||||||
|
|
||||||
fun getActiveAndQueuedDownloadIDs() : List<Long> {
|
fun getActiveAndQueuedDownloadIDs() : List<Long> {
|
||||||
return downloadDao.getActiveAndQueuedDownloadIDs()
|
return downloadDao.getActiveAndQueuedDownloadIDs()
|
||||||
}
|
}
|
||||||
|
|
@ -258,12 +259,23 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val handler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
val isCurrentNetworkMetered = (context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).isActiveNetworkMetered
|
val isCurrentNetworkMetered = (context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).isActiveNetworkMetered
|
||||||
if (!allowMeteredNetworks && isCurrentNetworkMetered){
|
if (!allowMeteredNetworks && isCurrentNetworkMetered){
|
||||||
Looper.prepare().run {
|
handler.postDelayed({
|
||||||
Toast.makeText(context, context.getString(R.string.metered_network_download_start_info), Toast.LENGTH_LONG).show()
|
Toast.makeText(context, context.getString(R.string.metered_network_download_start_info), Toast.LENGTH_LONG).show()
|
||||||
}
|
}, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val first = queuedItems.first()
|
||||||
|
if (first.downloadStartTime > 0L) {
|
||||||
|
val date = SimpleDateFormat(DateFormat.getBestDateTimePattern(Locale.getDefault(), "ddMMMyyyy - HHmm"), Locale.getDefault()).format(queuedItems.first().downloadStartTime)
|
||||||
|
handler.postDelayed({
|
||||||
|
Toast.makeText(context, context.getString(R.string.download_rescheduled_to) + " " + date, Toast.LENGTH_LONG).show()
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -9,11 +9,8 @@ import android.util.DisplayMetrics
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MediatorLiveData
|
import androidx.lifecycle.MediatorLiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.asFlow
|
|
||||||
import androidx.lifecycle.asLiveData
|
import androidx.lifecycle.asLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.media3.exoplayer.offline.Download
|
|
||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.work.Data
|
import androidx.work.Data
|
||||||
|
|
@ -46,21 +43,9 @@ import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.flow.asFlow
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
import kotlinx.coroutines.flow.combine
|
|
||||||
import kotlinx.coroutines.flow.filter
|
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
|
||||||
import kotlinx.coroutines.flow.flowOf
|
|
||||||
import kotlinx.coroutines.flow.last
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.flow.mapLatest
|
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
|
|
@ -75,8 +60,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
val allDownloads : Flow<PagingData<DownloadItem>>
|
val allDownloads : Flow<PagingData<DownloadItem>>
|
||||||
val queuedDownloads : Flow<PagingData<DownloadItemSimple>>
|
val queuedDownloads : Flow<PagingData<DownloadItemSimple>>
|
||||||
val activeDownloads : Flow<List<DownloadItem>>
|
val activeDownloads : Flow<List<DownloadItem>>
|
||||||
val allProcessingDownloads : Flow<List<DownloadItem>>
|
val processingDownloads : Flow<List<DownloadItem>>
|
||||||
var mostRecentProcessingDownloads: Flow<List<DownloadItem>>
|
|
||||||
val cancelledDownloads : Flow<PagingData<DownloadItemSimple>>
|
val cancelledDownloads : Flow<PagingData<DownloadItemSimple>>
|
||||||
val erroredDownloads : Flow<PagingData<DownloadItemSimple>>
|
val erroredDownloads : Flow<PagingData<DownloadItemSimple>>
|
||||||
val savedDownloads : Flow<PagingData<DownloadItemSimple>>
|
val savedDownloads : Flow<PagingData<DownloadItemSimple>>
|
||||||
|
|
@ -115,8 +99,6 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
auto, audio, video, command
|
auto, audio, video, command
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val processingDownloads = MediatorLiveData<List<DownloadItem>>(listOf())
|
|
||||||
var processingItemsFlow : ProcessingItemsJob? = null
|
var processingItemsFlow : ProcessingItemsJob? = null
|
||||||
var processingItems = MutableStateFlow(false)
|
var processingItems = MutableStateFlow(false)
|
||||||
|
|
||||||
|
|
@ -143,8 +125,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
allDownloads = repository.allDownloads.flow
|
allDownloads = repository.allDownloads.flow
|
||||||
queuedDownloads = repository.queuedDownloads.flow
|
queuedDownloads = repository.queuedDownloads.flow
|
||||||
activeDownloads = repository.activeDownloads
|
activeDownloads = repository.activeDownloads
|
||||||
allProcessingDownloads = repository.processingDownloads
|
processingDownloads = repository.processingDownloads
|
||||||
mostRecentProcessingDownloads = repository.getProcessingItemsFromID(0)
|
|
||||||
savedDownloads = repository.savedDownloads.flow
|
savedDownloads = repository.savedDownloads.flow
|
||||||
scheduledDownloads = repository.scheduledDownloads.flow
|
scheduledDownloads = repository.scheduledDownloads.flow
|
||||||
cancelledDownloads = repository.cancelledDownloads.flow
|
cancelledDownloads = repository.cancelledDownloads.flow
|
||||||
|
|
@ -385,6 +366,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
fun turnDownloadItemsToProcessingDownloads(itemIDs: List<Long>) = viewModelScope.launch(Dispatchers.IO){
|
fun turnDownloadItemsToProcessingDownloads(itemIDs: List<Long>) = viewModelScope.launch(Dispatchers.IO){
|
||||||
updateProcessingJobData(ProcessingItemsJob(null, DownloadItem::class.java.toString(), itemIDs))
|
updateProcessingJobData(ProcessingItemsJob(null, DownloadItem::class.java.toString(), itemIDs))
|
||||||
val job = viewModelScope.launch(Dispatchers.IO) {
|
val job = viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
repository.deleteProcessing()
|
||||||
processingItems.emit(true)
|
processingItems.emit(true)
|
||||||
try {
|
try {
|
||||||
itemIDs.forEachIndexed { idx, it ->
|
itemIDs.forEachIndexed { idx, it ->
|
||||||
|
|
@ -395,7 +377,6 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
item.status = DownloadRepository.Status.Processing.toString()
|
item.status = DownloadRepository.Status.Processing.toString()
|
||||||
processingItemsFlow?.apply {
|
processingItemsFlow?.apply {
|
||||||
val id = repository.insert(item)
|
val id = repository.insert(item)
|
||||||
if (idx == 0) mostRecentProcessingDownloads = repository.getProcessingItemsFromID(id)
|
|
||||||
processingDownloadItemIDs.add(id)
|
processingDownloadItemIDs.add(id)
|
||||||
updateProcessingJobData(this)
|
updateProcessingJobData(this)
|
||||||
}
|
}
|
||||||
|
|
@ -499,7 +480,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
repository.deleteProcessing()
|
repository.deleteProcessing()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteAllWithID(ids: List<Long>) = viewModelScope.launch(Dispatchers.IO){
|
suspend fun deleteAllWithID(ids: List<Long>) {
|
||||||
repository.deleteAllWithIDs(ids)
|
repository.deleteAllWithIDs(ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -618,6 +599,11 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
repository.startDownloadWorker(emptyList(), application)
|
repository.startDownloadWorker(emptyList(), application)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun queueProcessingDownloads(){
|
||||||
|
val processingItems = repository.getProcessingDownloads()
|
||||||
|
queueDownloads(processingItems)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun queueDownloads(items: List<DownloadItem>, ign : Boolean = false) : List<SharedDownloadViewModel.AlreadyExistsIDs> {
|
suspend fun queueDownloads(items: List<DownloadItem>, ign : Boolean = false) : List<SharedDownloadViewModel.AlreadyExistsIDs> {
|
||||||
val ids = sharedDownloadViewModel.queueDownloads(items, ign)
|
val ids = sharedDownloadViewModel.queueDownloads(items, ign)
|
||||||
return ids
|
return ids
|
||||||
|
|
@ -639,43 +625,33 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
return dbManager.downloadDao.getDownloadIDsNotPresentInList(items.ifEmpty { listOf(-1L) }, status.map { it.toString() })
|
return dbManager.downloadDao.getDownloadIDsNotPresentInList(items.ifEmpty { listOf(-1L) }, status.map { it.toString() })
|
||||||
}
|
}
|
||||||
|
|
||||||
fun moveProcessingToSavedCategory(ids: List<Long>){
|
suspend fun moveProcessingToSavedCategory(){
|
||||||
ids.chunked(100).forEach {
|
dao.updateProcessingtoSavedStatus()
|
||||||
dao.updateProcessingtoSavedStatus(it)
|
}
|
||||||
|
|
||||||
|
suspend fun updateProcessingFormat(selectedFormats: List<FormatTuple>): List<Long> {
|
||||||
|
val items = repository.getProcessingDownloads()
|
||||||
|
items.forEachIndexed { index, i ->
|
||||||
|
selectedFormats[index].format?.apply {
|
||||||
|
i.format = this
|
||||||
|
}
|
||||||
|
if (i.type == Type.video) selectedFormats[index].audioFormats?.map { it.format_id }?.let { i.videoPreferences.audioFormatIDs.addAll(it) }
|
||||||
|
repository.update(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return items.map { itm -> itm.format.filesize }
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun updateProcessingCommandFormat(format: Format){
|
||||||
|
val items = repository.getProcessingDownloads()
|
||||||
|
items.forEach {
|
||||||
|
it.format = format
|
||||||
|
repository.update(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateProcessingFormat(ids: List<Long>, selectedFormats: List<FormatTuple>): List<Long> {
|
suspend fun updateProcessingDownloadPath(path: String){
|
||||||
return ids.chunked(100).map {
|
dao.updateProcessingDownloadPath(path)
|
||||||
val items = repository.getAllItemsByIDs(it)
|
|
||||||
items.forEachIndexed { index, i ->
|
|
||||||
selectedFormats[index].format?.apply {
|
|
||||||
i.format = this
|
|
||||||
}
|
|
||||||
if (i.type == Type.video) selectedFormats[index].audioFormats?.map { it.format_id }?.let { i.videoPreferences.audioFormatIDs.addAll(it) }
|
|
||||||
repository.update(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
items.map { itm -> itm.format.filesize }
|
|
||||||
}.flatten()
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun updateProcessingCommandFormat(ids: List<Long>, format: Format){
|
|
||||||
ids.chunked(100).forEach {
|
|
||||||
repository.getAllItemsByIDs(it).forEach { i ->
|
|
||||||
i.format = format
|
|
||||||
repository.update(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun updateProcessingDownloadPath(ids: List<Long>, path: String){
|
|
||||||
ids.chunked(100).forEach {
|
|
||||||
repository.getAllItemsByIDs(it).forEach { i ->
|
|
||||||
i.downloadPath = path
|
|
||||||
repository.update(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getProcessingDownloadsCount() : Int {
|
fun getProcessingDownloadsCount() : Int {
|
||||||
|
|
@ -687,42 +663,35 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
suspend fun updateProcessingAllFormats(ids: List<Long>, formatCollection: List<List<Format>>) {
|
suspend fun updateProcessingAllFormats(formatCollection: List<List<Format>>) {
|
||||||
ids.chunked(100).forEach {
|
val items = repository.getProcessingDownloads()
|
||||||
val items = repository.getAllItemsByIDs(it)
|
items.forEachIndexed { index, i ->
|
||||||
items.forEachIndexed { index, i ->
|
i.allFormats.clear()
|
||||||
i.allFormats.clear()
|
if (formatCollection.size == items.size && formatCollection[index].isNotEmpty()) {
|
||||||
if (formatCollection.size == items.size && formatCollection[index].isNotEmpty()) {
|
runCatching {
|
||||||
runCatching {
|
i.allFormats.addAll(formatCollection[index])
|
||||||
i.allFormats.addAll(formatCollection[index])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
i.format = getFormat(i.allFormats, i.type)
|
|
||||||
kotlin.runCatching {
|
|
||||||
dbManager.resultDao.getResultByURL(i.url)?.apply {
|
|
||||||
this.formats = formatCollection[index].toMutableList()
|
|
||||||
dbManager.resultDao.update(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
repository.update(i)
|
|
||||||
}
|
}
|
||||||
|
i.format = getFormat(i.allFormats, i.type)
|
||||||
|
kotlin.runCatching {
|
||||||
|
dbManager.resultDao.getResultByURL(i.url)?.apply {
|
||||||
|
this.formats = formatCollection[index].toMutableList()
|
||||||
|
dbManager.resultDao.update(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repository.update(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun continueUpdatingFormatsOnBackground(itemIDs: List<Long>){
|
suspend fun continueUpdatingFormatsOnBackground(){
|
||||||
itemIDs.chunked(100).forEach {list ->
|
val ids = dao.getProcessingDownloadsList().map { it.id }
|
||||||
repository.getAllItemsByIDs(list).onEach {
|
dao.updateProcessingtoSavedStatus()
|
||||||
it.status = DownloadRepository.Status.Saved.toString()
|
|
||||||
repository.update(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val id = System.currentTimeMillis().toInt()
|
val id = System.currentTimeMillis().toInt()
|
||||||
val workRequest = OneTimeWorkRequestBuilder<UpdatePlaylistFormatsWorker>()
|
val workRequest = OneTimeWorkRequestBuilder<UpdatePlaylistFormatsWorker>()
|
||||||
.setInputData(
|
.setInputData(
|
||||||
Data.Builder()
|
Data.Builder()
|
||||||
.putLongArray("ids", itemIDs.toLongArray())
|
.putLongArray("ids", ids.toLongArray())
|
||||||
.putInt("id", id)
|
.putInt("id", id)
|
||||||
.build())
|
.build())
|
||||||
.addTag("updateFormats")
|
.addTag("updateFormats")
|
||||||
|
|
@ -736,23 +705,22 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateProcessingType(ids: List<Long>, newType: Type) {
|
suspend fun updateProcessingType(newType: Type) {
|
||||||
ids.chunked(100).forEach { _ ->
|
val processing = repository.getProcessingDownloads()
|
||||||
repository.getAllItemsByIDs(ids).apply {
|
processing.apply {
|
||||||
val new = switchDownloadType(this, newType)
|
val new = switchDownloadType(this, newType)
|
||||||
new.forEach {
|
new.forEach {
|
||||||
repository.update(it)
|
repository.update(it)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkIfAllProcessingItemsHaveSameType(ids: List<Long>) : Pair<Boolean, Type> {
|
suspend fun updateProcessingDownloadTime(time: Long) {
|
||||||
val types = mutableSetOf<String>()
|
repository.updateProcessingDownloadTime(time)
|
||||||
ids.chunked(100).forEach {
|
}
|
||||||
types.addAll(dao.getProcessingDownloadTypes(it))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
fun checkIfAllProcessingItemsHaveSameType() : Pair<Boolean, Type> {
|
||||||
|
val types = dao.getProcessingDownloadTypes()
|
||||||
return Pair(types.size == 1, Type.valueOf(types.first()))
|
return Pair(types.size == 1, Type.valueOf(types.first()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -321,7 +321,7 @@ class SharedDownloadViewModel(private val context: Context) {
|
||||||
val prefVideo = sharedPreferences.getString("format_importance_video", itemValues.joinToString(","))!!
|
val prefVideo = sharedPreferences.getString("format_importance_video", itemValues.joinToString(","))!!
|
||||||
|
|
||||||
prefVideo.split(",").forEachIndexed { idx, s ->
|
prefVideo.split(",").forEachIndexed { idx, s ->
|
||||||
val importance = (itemValues.size - idx) * 10
|
var importance = (itemValues.size - idx) * 10
|
||||||
|
|
||||||
when(s) {
|
when(s) {
|
||||||
"id" -> {
|
"id" -> {
|
||||||
|
|
@ -345,8 +345,9 @@ class SharedDownloadViewModel(private val context: Context) {
|
||||||
for(i in 0..preferenceIndex){
|
for(i in 0..preferenceIndex){
|
||||||
removeAt(0)
|
removeAt(0)
|
||||||
}
|
}
|
||||||
add(0, preference)
|
requirements.add { it: Format -> if (it.format_note.contains(preference, ignoreCase = true)) importance else 0 }
|
||||||
forEachIndexed { index, res ->
|
forEachIndexed { index, res ->
|
||||||
|
if (index >= 2) importance = 0
|
||||||
requirements.add { it: Format -> if (it.format_note.contains(res, ignoreCase = true)) (importance - index - 1) else 0 }
|
requirements.add { it: Format -> if (it.format_note.contains(res, ignoreCase = true)) (importance - index - 1) else 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,165 +0,0 @@
|
||||||
package com.deniscerri.ytdl.services
|
|
||||||
|
|
||||||
import android.app.Service
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Binder
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.IBinder
|
|
||||||
import androidx.core.content.IntentCompat
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import com.deniscerri.ytdl.database.DBManager
|
|
||||||
import com.deniscerri.ytdl.database.models.DownloadItem
|
|
||||||
import com.deniscerri.ytdl.database.models.ResultItem
|
|
||||||
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
|
||||||
import com.deniscerri.ytdl.database.repository.ResultRepository
|
|
||||||
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
|
||||||
import com.deniscerri.ytdl.database.viewmodel.SharedDownloadViewModel
|
|
||||||
import com.deniscerri.ytdl.util.NotificationUtil
|
|
||||||
import kotlinx.coroutines.CancellationException
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.SupervisorJob
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.isActive
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
|
|
||||||
|
|
||||||
class ProcessDownloadsInBackgroundService : Service() {
|
|
||||||
|
|
||||||
private val binder: IBinder = LocalBinder()
|
|
||||||
private val queueProcessingDownloadsJobList = mutableListOf<Job>()
|
|
||||||
private lateinit var repository: DownloadRepository
|
|
||||||
private lateinit var resultRepository: ResultRepository
|
|
||||||
private lateinit var downloadViewModel: SharedDownloadViewModel
|
|
||||||
inner class LocalBinder: Binder() {
|
|
||||||
val service: ProcessDownloadsInBackgroundService
|
|
||||||
get() = this@ProcessDownloadsInBackgroundService
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate() {
|
|
||||||
super.onCreate()
|
|
||||||
val dbManager = DBManager.getInstance(this)
|
|
||||||
repository = DownloadRepository(dbManager.downloadDao)
|
|
||||||
resultRepository = ResultRepository(dbManager.resultDao, this)
|
|
||||||
downloadViewModel = SharedDownloadViewModel(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
|
||||||
|
|
||||||
val binding = intent.getBooleanExtra("binding", false)
|
|
||||||
if (binding) {
|
|
||||||
val cancel = intent.getBooleanExtra("cancel", false)
|
|
||||||
if (cancel) {
|
|
||||||
cancelAllProcessingJobs()
|
|
||||||
CoroutineScope(SupervisorJob()).launch(Dispatchers.IO){
|
|
||||||
repository.deleteProcessing()
|
|
||||||
withContext(Dispatchers.Main){
|
|
||||||
if (Build.VERSION.SDK_INT > 23){
|
|
||||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
|
||||||
}else{
|
|
||||||
stopForeground(true)
|
|
||||||
}
|
|
||||||
stopSelf()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
return super.onStartCommand(intent, flags, startId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val notificationUtil = NotificationUtil(this)
|
|
||||||
startForeground(System.currentTimeMillis().toInt(), notificationUtil.createProcessingDownloads())
|
|
||||||
|
|
||||||
|
|
||||||
val itemType = intent.getStringExtra("itemType")!!
|
|
||||||
val itemIDs = intent.getLongArrayExtra("itemIDs")!!.toList()
|
|
||||||
val processingItemIDs = intent.getLongArrayExtra("processingItemIDs")!!.toList()
|
|
||||||
val timeInMillis = intent.getLongExtra("timeInMillis", 0)
|
|
||||||
val processingFinished = intent.getBooleanExtra("processingFinished", true)
|
|
||||||
|
|
||||||
val job = CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) {
|
|
||||||
if (!processingFinished) {
|
|
||||||
when(itemType) {
|
|
||||||
ResultItem::class.java.toString() -> {
|
|
||||||
itemIDs.chunked(100).map { ids ->
|
|
||||||
resultRepository.getAllByIDs(ids).map {
|
|
||||||
downloadViewModel.createDownloadItemFromResult(
|
|
||||||
result = it, givenType = DownloadViewModel.Type.valueOf(
|
|
||||||
downloadViewModel.getDownloadType(url = it.url).toString()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}.apply {
|
|
||||||
if (timeInMillis > 0) {
|
|
||||||
this.forEach {
|
|
||||||
it.setAsScheduling(timeInMillis)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isActive){
|
|
||||||
downloadViewModel.queueDownloads(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DownloadItem::class.java.toString() -> {
|
|
||||||
itemIDs.chunked(100).map { ids ->
|
|
||||||
repository.getAllItemsByIDs(ids).apply {
|
|
||||||
if (timeInMillis > 0) {
|
|
||||||
this.forEach {
|
|
||||||
it.setAsScheduling(timeInMillis)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isActive){
|
|
||||||
downloadViewModel.queueDownloads(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else {
|
|
||||||
repository.getProcessingItemsBetweenIDs(processingItemIDs.first(), processingItemIDs.last()).apply {
|
|
||||||
this.chunked(100).map {
|
|
||||||
if (timeInMillis > 0){
|
|
||||||
this.forEach { d ->
|
|
||||||
d.setAsScheduling(timeInMillis)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isActive){
|
|
||||||
downloadViewModel.queueDownloads(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
job.invokeOnCompletion {
|
|
||||||
queueProcessingDownloadsJobList.remove(job)
|
|
||||||
if (queueProcessingDownloadsJobList.isEmpty()){
|
|
||||||
stopForeground(true)
|
|
||||||
stopSelf()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
queueProcessingDownloadsJobList.add(job)
|
|
||||||
|
|
||||||
return super.onStartCommand(intent, flags, startId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBind(intent: Intent): IBinder {
|
|
||||||
return binder
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun DownloadItem.setAsScheduling(timeInMillis: Long) {
|
|
||||||
status = DownloadRepository.Status.Scheduled.toString()
|
|
||||||
downloadStartTime = timeInMillis
|
|
||||||
}
|
|
||||||
|
|
||||||
fun cancelAllProcessingJobs(){
|
|
||||||
queueProcessingDownloadsJobList.onEach { it.cancel(CancellationException()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -3,32 +3,23 @@ package com.deniscerri.ytdl.ui.downloadcard
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.ComponentName
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.ServiceConnection
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.IBinder
|
|
||||||
import android.text.format.DateFormat
|
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import android.util.Log
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.Window
|
import android.view.Window
|
||||||
import android.widget.Button
|
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.view.children
|
import androidx.core.view.children
|
||||||
import androidx.core.view.forEach
|
|
||||||
import androidx.core.view.get
|
import androidx.core.view.get
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
|
@ -45,13 +36,11 @@ import com.deniscerri.ytdl.database.viewmodel.CommandTemplateViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.HistoryViewModel
|
import com.deniscerri.ytdl.database.viewmodel.HistoryViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
|
||||||
import com.deniscerri.ytdl.services.ProcessDownloadsInBackgroundService
|
|
||||||
import com.deniscerri.ytdl.ui.adapter.ConfigureMultipleDownloadsAdapter
|
import com.deniscerri.ytdl.ui.adapter.ConfigureMultipleDownloadsAdapter
|
||||||
import com.deniscerri.ytdl.util.Extensions.enableFastScroll
|
import com.deniscerri.ytdl.util.Extensions.enableFastScroll
|
||||||
import com.deniscerri.ytdl.util.FileUtil
|
import com.deniscerri.ytdl.util.FileUtil
|
||||||
import com.deniscerri.ytdl.util.InfoUtil
|
import com.deniscerri.ytdl.util.InfoUtil
|
||||||
import com.deniscerri.ytdl.util.UiUtil
|
import com.deniscerri.ytdl.util.UiUtil
|
||||||
import com.facebook.shimmer.Shimmer
|
|
||||||
import com.facebook.shimmer.ShimmerFrameLayout
|
import com.facebook.shimmer.ShimmerFrameLayout
|
||||||
import com.google.android.material.bottomappbar.BottomAppBar
|
import com.google.android.material.bottomappbar.BottomAppBar
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
|
|
@ -61,22 +50,14 @@ import com.google.android.material.button.MaterialButton
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.elevation.SurfaceColors
|
import com.google.android.material.elevation.SurfaceColors
|
||||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
|
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.filter
|
|
||||||
import kotlinx.coroutines.flow.firstOrNull
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.Locale
|
|
||||||
import java.util.function.Predicate
|
|
||||||
|
|
||||||
class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), ConfigureMultipleDownloadsAdapter.OnItemClickListener, View.OnClickListener,
|
class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), ConfigureMultipleDownloadsAdapter.OnItemClickListener, View.OnClickListener,
|
||||||
ConfigureDownloadBottomSheetDialog.OnDownloadItemUpdateListener {
|
ConfigureDownloadBottomSheetDialog.OnDownloadItemUpdateListener {
|
||||||
|
|
@ -91,6 +72,8 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
private lateinit var bottomAppBar: BottomAppBar
|
private lateinit var bottomAppBar: BottomAppBar
|
||||||
private lateinit var filesize : TextView
|
private lateinit var filesize : TextView
|
||||||
private lateinit var count : TextView
|
private lateinit var count : TextView
|
||||||
|
private lateinit var downloadBtn : MaterialButton
|
||||||
|
private lateinit var scheduleBtn : MaterialButton
|
||||||
private lateinit var title: TextView
|
private lateinit var title: TextView
|
||||||
private lateinit var subtitle: TextView
|
private lateinit var subtitle: TextView
|
||||||
private lateinit var shimmerTitle: ShimmerFrameLayout
|
private lateinit var shimmerTitle: ShimmerFrameLayout
|
||||||
|
|
@ -154,19 +137,14 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
itemTouchHelper.attachToRecyclerView(recyclerView)
|
itemTouchHelper.attachToRecyclerView(recyclerView)
|
||||||
}
|
}
|
||||||
|
|
||||||
val scheduleBtn = view.findViewById<MaterialButton>(R.id.bottomsheet_schedule_button)
|
scheduleBtn = view.findViewById<MaterialButton>(R.id.bottomsheet_schedule_button)
|
||||||
val download = view.findViewById<Button>(R.id.bottomsheet_download_button)
|
downloadBtn = view.findViewById<MaterialButton>(R.id.bottomsheet_download_button)
|
||||||
bottomAppBar = view.findViewById(R.id.bottomAppBar)
|
bottomAppBar = view.findViewById(R.id.bottomAppBar)
|
||||||
val preferredDownloadType = bottomAppBar.menu.findItem(R.id.preferred_download_type)
|
val preferredDownloadType = bottomAppBar.menu.findItem(R.id.preferred_download_type)
|
||||||
|
|
||||||
filesize = view.findViewById(R.id.filesize)
|
filesize = view.findViewById(R.id.filesize)
|
||||||
count = view.findViewById(R.id.count)
|
count = view.findViewById(R.id.count)
|
||||||
|
|
||||||
val continueInBackgroundSnackBar = Snackbar.make(recyclerView, R.string.process_downloads_background, Snackbar.LENGTH_LONG)
|
|
||||||
val snackbarView: View = continueInBackgroundSnackBar.view
|
|
||||||
val snackTextView = snackbarView.findViewById<View>(com.google.android.material.R.id.snackbar_text) as TextView
|
|
||||||
snackTextView.maxLines = 9999999
|
|
||||||
|
|
||||||
title = view.findViewById(R.id.bottom_sheet_title)
|
title = view.findViewById(R.id.bottom_sheet_title)
|
||||||
subtitle = view.findViewById(R.id.bottom_sheet_subtitle)
|
subtitle = view.findViewById(R.id.bottom_sheet_subtitle)
|
||||||
shimmerTitle = view.findViewById(R.id.shimmer_loading_title)
|
shimmerTitle = view.findViewById(R.id.shimmer_loading_title)
|
||||||
|
|
@ -174,17 +152,12 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
downloadViewModel.processingItems.collectLatest {
|
downloadViewModel.processingItems.collectLatest {
|
||||||
val loading = it
|
toggleLoading(it)
|
||||||
toggleLoadingShimmerTitle(loading)
|
|
||||||
bottomAppBar.menu.children.forEach { m -> m.isEnabled = !loading }
|
|
||||||
if (!loading){
|
|
||||||
continueInBackgroundSnackBar.dismiss()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
downloadViewModel.mostRecentProcessingDownloads.collectLatest { items ->
|
downloadViewModel.processingDownloads.collectLatest { items ->
|
||||||
processingItemsCount = items.size
|
processingItemsCount = items.size
|
||||||
count.text = "${processingItemsCount} ${getString(R.string.selected)}"
|
count.text = "${processingItemsCount} ${getString(R.string.selected)}"
|
||||||
listAdapter.submitList(items)
|
listAdapter.submitList(items)
|
||||||
|
|
@ -224,71 +197,31 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
|
|
||||||
|
|
||||||
scheduleBtn.setOnClickListener{
|
scheduleBtn.setOnClickListener{
|
||||||
fun initSchedule(processingFinished: Boolean) {
|
UiUtil.showDatePicker(parentFragmentManager) { cal ->
|
||||||
UiUtil.showDatePicker(parentFragmentManager) { cal ->
|
toggleLoading(true)
|
||||||
scheduleBtn.isEnabled = false
|
|
||||||
download.isEnabled = false
|
|
||||||
|
|
||||||
lifecycleScope.launch {
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.deleteAllWithID(currentDownloadIDs)
|
|
||||||
}
|
|
||||||
|
|
||||||
getProcessingItemsData()?.apply {
|
|
||||||
this.job?.cancel(CancellationException())
|
|
||||||
this.job = null
|
|
||||||
downloadProcessDownloadsInBackground(this, processingFinished)
|
|
||||||
}
|
|
||||||
|
|
||||||
val date = SimpleDateFormat(DateFormat.getBestDateTimePattern(Locale.getDefault(), "ddMMMyyyy - HHmm"), Locale.getDefault()).format(cal.timeInMillis)
|
|
||||||
Toast.makeText(context, getString(R.string.download_rescheduled_to) + " " + date, Toast.LENGTH_LONG).show()
|
|
||||||
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shimmerTitle.isVisible){
|
|
||||||
continueInBackgroundSnackBar.setAction(R.string.ok) {
|
|
||||||
initSchedule(false)
|
|
||||||
}
|
|
||||||
continueInBackgroundSnackBar.show()
|
|
||||||
}else{
|
|
||||||
initSchedule(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
download!!.setOnClickListener {
|
|
||||||
fun initDownload(processingFinished: Boolean) {
|
|
||||||
scheduleBtn.isEnabled = false
|
|
||||||
download.isEnabled = false
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.updateProcessingDownloadTime(cal.timeInMillis)
|
||||||
downloadViewModel.deleteAllWithID(currentDownloadIDs)
|
downloadViewModel.deleteAllWithID(currentDownloadIDs)
|
||||||
}
|
downloadViewModel.queueProcessingDownloads()
|
||||||
|
|
||||||
getProcessingItemsData()?.apply {
|
|
||||||
this.job?.cancel(CancellationException())
|
|
||||||
this.job = null
|
|
||||||
downloadProcessDownloadsInBackground(this, processingFinished)
|
|
||||||
}
|
}
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shimmerTitle.isVisible){
|
|
||||||
continueInBackgroundSnackBar.setAction(R.string.ok) {
|
|
||||||
initDownload(false)
|
|
||||||
}
|
|
||||||
continueInBackgroundSnackBar.show()
|
|
||||||
}else{
|
|
||||||
initDownload(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
download.setOnLongClickListener {
|
downloadBtn.setOnClickListener {
|
||||||
|
toggleLoading(true)
|
||||||
|
lifecycleScope.launch {
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.deleteAllWithID(currentDownloadIDs)
|
||||||
|
downloadViewModel.queueProcessingDownloads()
|
||||||
|
}
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadBtn.setOnLongClickListener {
|
||||||
val dd = MaterialAlertDialogBuilder(requireContext())
|
val dd = MaterialAlertDialogBuilder(requireContext())
|
||||||
dd.setTitle(getString(R.string.save_for_later))
|
dd.setTitle(getString(R.string.save_for_later))
|
||||||
dd.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
dd.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||||
|
|
@ -296,7 +229,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
lifecycleScope.launch{
|
lifecycleScope.launch{
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
downloadViewModel.deleteAllWithID(currentDownloadIDs)
|
downloadViewModel.deleteAllWithID(currentDownloadIDs)
|
||||||
downloadViewModel.moveProcessingToSavedCategory(getProcessingDownloadItemIDs())
|
downloadViewModel.moveProcessingToSavedCategory()
|
||||||
}
|
}
|
||||||
getProcessingItemsData()?.apply {
|
getProcessingItemsData()?.apply {
|
||||||
this.job?.cancel(CancellationException())
|
this.job?.cancel(CancellationException())
|
||||||
|
|
@ -312,13 +245,13 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
val formatListener = object : OnFormatClickListener {
|
val formatListener = object : OnFormatClickListener {
|
||||||
override fun onFormatClick(selectedFormats: List<FormatTuple>) {
|
override fun onFormatClick(selectedFormats: List<FormatTuple>) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
downloadViewModel.updateProcessingFormat(getProcessingDownloadItemIDs(), selectedFormats)
|
downloadViewModel.updateProcessingFormat(selectedFormats)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFormatsUpdated(allFormats: List<List<Format>>) {
|
override fun onFormatsUpdated(allFormats: List<List<Format>>) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
downloadViewModel.updateProcessingAllFormats(getProcessingDownloadItemIDs(), allFormats)
|
downloadViewModel.updateProcessingAllFormats(allFormats)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -326,7 +259,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
override fun onContinueOnBackground() {
|
override fun onContinueOnBackground() {
|
||||||
requireActivity().lifecycleScope.launch {
|
requireActivity().lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
downloadViewModel.continueUpdatingFormatsOnBackground(getProcessingDownloadItemIDs())
|
downloadViewModel.continueUpdatingFormatsOnBackground()
|
||||||
}
|
}
|
||||||
getProcessingItemsData()?.apply {
|
getProcessingItemsData()?.apply {
|
||||||
this.job?.cancel(CancellationException())
|
this.job?.cancel(CancellationException())
|
||||||
|
|
@ -369,7 +302,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
|
|
||||||
audio!!.setOnClickListener {
|
audio!!.setOnClickListener {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
downloadViewModel.updateProcessingType(getProcessingDownloadItemIDs(), DownloadViewModel.Type.audio)
|
downloadViewModel.updateProcessingType(DownloadViewModel.Type.audio)
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
preferredDownloadType.setIcon(R.drawable.baseline_audio_file_24)
|
preferredDownloadType.setIcon(R.drawable.baseline_audio_file_24)
|
||||||
bottomAppBar.menu[1].icon?.alpha = 255
|
bottomAppBar.menu[1].icon?.alpha = 255
|
||||||
|
|
@ -381,7 +314,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
|
|
||||||
video!!.setOnClickListener {
|
video!!.setOnClickListener {
|
||||||
CoroutineScope(Dispatchers.IO).launch{
|
CoroutineScope(Dispatchers.IO).launch{
|
||||||
downloadViewModel.updateProcessingType(getProcessingDownloadItemIDs(), DownloadViewModel.Type.video)
|
downloadViewModel.updateProcessingType(DownloadViewModel.Type.video)
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
preferredDownloadType.setIcon(R.drawable.baseline_video_file_24)
|
preferredDownloadType.setIcon(R.drawable.baseline_video_file_24)
|
||||||
bottomAppBar.menu[1].icon?.alpha = 255
|
bottomAppBar.menu[1].icon?.alpha = 255
|
||||||
|
|
@ -393,7 +326,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
|
|
||||||
command!!.setOnClickListener {
|
command!!.setOnClickListener {
|
||||||
CoroutineScope(Dispatchers.IO).launch{
|
CoroutineScope(Dispatchers.IO).launch{
|
||||||
downloadViewModel.updateProcessingType(getProcessingDownloadItemIDs(), DownloadViewModel.Type.command)
|
downloadViewModel.updateProcessingType(DownloadViewModel.Type.command)
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
preferredDownloadType.setIcon(R.drawable.baseline_insert_drive_file_24)
|
preferredDownloadType.setIcon(R.drawable.baseline_insert_drive_file_24)
|
||||||
bottomAppBar.menu[1].icon?.alpha = 255
|
bottomAppBar.menu[1].icon?.alpha = 255
|
||||||
|
|
@ -417,7 +350,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
R.id.format -> {
|
R.id.format -> {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val res = withContext(Dispatchers.IO){
|
val res = withContext(Dispatchers.IO){
|
||||||
downloadViewModel.checkIfAllProcessingItemsHaveSameType(getProcessingDownloadItemIDs())
|
downloadViewModel.checkIfAllProcessingItemsHaveSameType()
|
||||||
}
|
}
|
||||||
if (!res.first){
|
if (!res.first){
|
||||||
Toast.makeText(requireContext(), getString(R.string.format_filtering_hint), Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireContext(), getString(R.string.format_filtering_hint), Toast.LENGTH_SHORT).show()
|
||||||
|
|
@ -435,13 +368,15 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
it.joinToString(" ") { c -> c.content }
|
it.joinToString(" ") { c -> c.content }
|
||||||
)
|
)
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
lifecycleScope.launch {
|
||||||
downloadViewModel.updateProcessingCommandFormat(getProcessingDownloadItemIDs(), format)
|
withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.updateProcessingCommandFormat(format)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
val items = withContext(Dispatchers.IO){
|
val items = withContext(Dispatchers.IO){
|
||||||
downloadViewModel.getAllByIDs(getProcessingDownloadItemIDs())
|
downloadViewModel.getProcessingDownloads()
|
||||||
}
|
}
|
||||||
val flatFormatCollection = items.map { it.allFormats }.flatten()
|
val flatFormatCollection = items.map { it.allFormats }.flatten()
|
||||||
val commonFormats = withContext(Dispatchers.IO){
|
val commonFormats = withContext(Dispatchers.IO){
|
||||||
|
|
@ -465,7 +400,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
R.id.more -> {
|
R.id.more -> {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val res = withContext(Dispatchers.IO){
|
val res = withContext(Dispatchers.IO){
|
||||||
downloadViewModel.checkIfAllProcessingItemsHaveSameType(getProcessingDownloadItemIDs())
|
downloadViewModel.checkIfAllProcessingItemsHaveSameType()
|
||||||
}
|
}
|
||||||
if (!res.first) {
|
if (!res.first) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
|
|
@ -486,7 +421,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
sheetView.findViewById<View>(R.id.adjust).setPadding(padding,padding,padding,padding)
|
sheetView.findViewById<View>(R.id.adjust).setPadding(padding,padding,padding,padding)
|
||||||
|
|
||||||
val items = withContext(Dispatchers.IO){
|
val items = withContext(Dispatchers.IO){
|
||||||
downloadViewModel.getAllByIDs(getProcessingDownloadItemIDs())
|
downloadViewModel.getProcessingDownloads()
|
||||||
}
|
}
|
||||||
|
|
||||||
UiUtil.configureAudio(
|
UiUtil.configureAudio(
|
||||||
|
|
@ -567,7 +502,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
sheetView.findViewById<View>(R.id.adjust).setPadding(padding,padding,padding,padding)
|
sheetView.findViewById<View>(R.id.adjust).setPadding(padding,padding,padding,padding)
|
||||||
|
|
||||||
val items = withContext(Dispatchers.IO){
|
val items = withContext(Dispatchers.IO){
|
||||||
downloadViewModel.getAllByIDs(getProcessingDownloadItemIDs())
|
downloadViewModel.getProcessingDownloads()
|
||||||
}
|
}
|
||||||
|
|
||||||
UiUtil.configureVideo(
|
UiUtil.configureVideo(
|
||||||
|
|
@ -664,19 +599,27 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toggleLoadingShimmerTitle(show: Boolean) {
|
private fun toggleLoading(loading: Boolean){
|
||||||
shimmerTitle.isVisible = show
|
shimmerTitle.isVisible = loading
|
||||||
title.isVisible = !show
|
title.isVisible = !loading
|
||||||
shimmerSubtitle.isVisible = show
|
shimmerSubtitle.isVisible = loading
|
||||||
subtitle.isVisible = !show
|
subtitle.isVisible = !loading
|
||||||
|
|
||||||
if (show){
|
if (loading){
|
||||||
shimmerTitle.startShimmer()
|
shimmerTitle.startShimmer()
|
||||||
shimmerSubtitle.startShimmer()
|
shimmerSubtitle.startShimmer()
|
||||||
}else{
|
}else{
|
||||||
shimmerTitle.stopShimmer()
|
shimmerTitle.stopShimmer()
|
||||||
shimmerSubtitle.stopShimmer()
|
shimmerSubtitle.stopShimmer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scheduleBtn.isEnabled = !loading
|
||||||
|
downloadBtn.isEnabled = !loading
|
||||||
|
bottomAppBar.menu.children.forEach { m -> m.isEnabled = !loading }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toggleLoadingShimmerTitle(show: Boolean) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateFileSize(items: List<Long>){
|
private fun updateFileSize(items: List<Long>){
|
||||||
|
|
@ -705,7 +648,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
}
|
}
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
downloadViewModel.updateProcessingDownloadPath(getProcessingDownloadItemIDs(), result.data?.data.toString())
|
downloadViewModel.updateProcessingDownloadPath(result.data?.data.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
val path = FileUtil.formatPath(result.data!!.data.toString())
|
val path = FileUtil.formatPath(result.data!!.data.toString())
|
||||||
|
|
@ -837,12 +780,18 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDismiss(dialog: DialogInterface) {
|
override fun onDismiss(dialog: DialogInterface) {
|
||||||
getProcessingItemsData()?.apply {
|
lifecycleScope.launch {
|
||||||
if (this.job != null){
|
withContext(Dispatchers.IO){
|
||||||
this.job?.cancel(CancellationException())
|
getProcessingItemsData()?.apply {
|
||||||
downloadViewModel.deleteAllWithID(this.processingDownloadItemIDs)
|
if (this.job?.isActive == true){
|
||||||
|
this.job?.cancel(CancellationException())
|
||||||
|
downloadViewModel.deleteAllWithID(this.processingDownloadItemIDs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
downloadViewModel.deleteProcessing()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onDismiss(dialog)
|
super.onDismiss(dialog)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -923,24 +872,8 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getProcessingDownloadItemIDs() : List<Long> {
|
|
||||||
return downloadViewModel.processingItemsFlow?.processingDownloadItemIDs ?: listOf()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getProcessingItemsData() : DownloadViewModel.ProcessingItemsJob? {
|
private fun getProcessingItemsData() : DownloadViewModel.ProcessingItemsJob? {
|
||||||
return downloadViewModel.processingItemsFlow
|
return downloadViewModel.processingItemsFlow
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun downloadProcessDownloadsInBackground(jobData: DownloadViewModel.ProcessingItemsJob, processingFinished: Boolean, timeInMillis: Long = 0){
|
|
||||||
Intent(requireActivity(), ProcessDownloadsInBackgroundService::class.java).also { intent ->
|
|
||||||
intent.putExtra("itemType", jobData.originItemType)
|
|
||||||
intent.putExtra("itemIDs", jobData.originItemIDs.toLongArray())
|
|
||||||
intent.putExtra("processingItemIDs", jobData.processingDownloadItemIDs.toLongArray())
|
|
||||||
intent.putExtra("timeInMillis", timeInMillis)
|
|
||||||
intent.putExtra("processingFinished", processingFinished)
|
|
||||||
requireActivity().startService(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -257,7 +257,24 @@ class SelectPlaylistItemsDialog : BottomSheetDialogFragment(), PlaylistAdapter.O
|
||||||
|
|
||||||
|
|
||||||
private fun checkRanges(start: String, end: String) : Boolean {
|
private fun checkRanges(start: String, end: String) : Boolean {
|
||||||
return start.isNotBlank() && end.isNotBlank()
|
fromTextInput.error = ""
|
||||||
|
toTextInput.error = ""
|
||||||
|
|
||||||
|
if (start.isBlank() || end.isBlank()) return false
|
||||||
|
|
||||||
|
val startValid = start.toInt() >= 0
|
||||||
|
val endValid = end.toInt() <= resultItemIDs.size
|
||||||
|
|
||||||
|
if (!startValid) {
|
||||||
|
fromTextInput.editText?.setText("")
|
||||||
|
fromTextInput.error = "Invalid Number"
|
||||||
|
}
|
||||||
|
if (!endValid) {
|
||||||
|
toTextInput.editText?.setText("")
|
||||||
|
toTextInput.error = "Invalid Number"
|
||||||
|
}
|
||||||
|
|
||||||
|
return startValid && endValid
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reset(){
|
private fun reset(){
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ import com.deniscerri.ytdl.MainActivity
|
||||||
import com.deniscerri.ytdl.R
|
import com.deniscerri.ytdl.R
|
||||||
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
||||||
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdl.services.ProcessDownloadsInBackgroundService
|
|
||||||
import com.deniscerri.ytdl.util.Extensions.createBadge
|
import com.deniscerri.ytdl.util.Extensions.createBadge
|
||||||
import com.deniscerri.ytdl.util.NotificationUtil
|
import com.deniscerri.ytdl.util.NotificationUtil
|
||||||
import com.deniscerri.ytdl.util.UiUtil
|
import com.deniscerri.ytdl.util.UiUtil
|
||||||
|
|
@ -266,7 +265,6 @@ class DownloadQueueMainFragment : Fragment(){
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cancelAllDownloads() {
|
private fun cancelAllDownloads() {
|
||||||
cancelBackgroundProcessingDownloads()
|
|
||||||
workManager.cancelAllWorkByTag("download")
|
workManager.cancelAllWorkByTag("download")
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val notificationUtil = NotificationUtil(requireContext())
|
val notificationUtil = NotificationUtil(requireContext())
|
||||||
|
|
@ -280,26 +278,4 @@ class DownloadQueueMainFragment : Fragment(){
|
||||||
downloadViewModel.cancelActiveQueued()
|
downloadViewModel.cancelActiveQueued()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cancelBackgroundProcessingDownloads(){
|
|
||||||
val connection = object : ServiceConnection {
|
|
||||||
private lateinit var mService: ProcessDownloadsInBackgroundService
|
|
||||||
private var mBound: Boolean = false
|
|
||||||
override fun onServiceConnected(className: ComponentName, service: IBinder) {
|
|
||||||
val binder = service as ProcessDownloadsInBackgroundService.LocalBinder
|
|
||||||
mService = binder.service
|
|
||||||
mBound = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onServiceDisconnected(arg0: ComponentName) {
|
|
||||||
mBound = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Intent(requireActivity(), ProcessDownloadsInBackgroundService::class.java).also { intent ->
|
|
||||||
intent.putExtra("binding", true)
|
|
||||||
intent.putExtra("cancel", true)
|
|
||||||
requireActivity().bindService(intent, connection, Context.BIND_AUTO_CREATE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -26,7 +26,6 @@ import com.deniscerri.ytdl.receiver.CancelDownloadNotificationReceiver
|
||||||
import com.deniscerri.ytdl.receiver.CancelWorkReceiver
|
import com.deniscerri.ytdl.receiver.CancelWorkReceiver
|
||||||
import com.deniscerri.ytdl.receiver.PauseDownloadNotificationReceiver
|
import com.deniscerri.ytdl.receiver.PauseDownloadNotificationReceiver
|
||||||
import com.deniscerri.ytdl.receiver.ResumeActivity
|
import com.deniscerri.ytdl.receiver.ResumeActivity
|
||||||
import com.deniscerri.ytdl.services.ProcessDownloadsInBackgroundService
|
|
||||||
import com.deniscerri.ytdl.util.Extensions.toBitmap
|
import com.deniscerri.ytdl.util.Extensions.toBitmap
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
|
@ -643,36 +642,6 @@ class NotificationUtil(var context: Context) {
|
||||||
notificationManager.notify(QUERY_PROCESS_FINISHED_NOTIFICATION_ID, notificationBuilder.build())
|
notificationManager.notify(QUERY_PROCESS_FINISHED_NOTIFICATION_ID, notificationBuilder.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createProcessingDownloads() : Notification{
|
|
||||||
val notificationBuilder = getBuilder(DOWNLOAD_MISC_CHANNEL_ID)
|
|
||||||
|
|
||||||
val intent = Intent(context, ProcessDownloadsInBackgroundService::class.java)
|
|
||||||
intent.putExtra("binding", true)
|
|
||||||
intent.putExtra("cancel", true)
|
|
||||||
val cancelNotificationPendingIntent = PendingIntent.getService(
|
|
||||||
context,
|
|
||||||
System.currentTimeMillis().toInt(),
|
|
||||||
intent,
|
|
||||||
PendingIntent.FLAG_IMMUTABLE
|
|
||||||
)
|
|
||||||
|
|
||||||
notificationBuilder
|
|
||||||
.setContentTitle(resources.getString(R.string.processing))
|
|
||||||
.setSmallIcon(R.drawable.ic_app_icon)
|
|
||||||
.setLargeIcon(
|
|
||||||
BitmapFactory.decodeResource(
|
|
||||||
resources,
|
|
||||||
R.drawable.ic_app_icon
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
|
||||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
|
||||||
.clearActions()
|
|
||||||
.addAction(0, resources.getString(R.string.cancel), cancelNotificationPendingIntent)
|
|
||||||
|
|
||||||
return notificationBuilder.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val DOWNLOAD_SERVICE_CHANNEL_ID = "1"
|
const val DOWNLOAD_SERVICE_CHANNEL_ID = "1"
|
||||||
const val COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID = "2"
|
const val COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID = "2"
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_marginStart="50dp"
|
android:layout_marginStart="50dp"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
|
@ -32,6 +33,7 @@
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox.Dense"
|
style="@style/Widget.Material3.TextInputLayout.FilledBox.Dense"
|
||||||
android:layout_width="70dp"
|
android:layout_width="70dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
app:errorEnabled="true"
|
||||||
android:layout_marginEnd="10dp"
|
android:layout_marginEnd="10dp"
|
||||||
android:hint="@string/start">
|
android:hint="@string/start">
|
||||||
|
|
||||||
|
|
@ -48,6 +50,7 @@
|
||||||
android:layout_width="70dp"
|
android:layout_width="70dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/end"
|
android:hint="@string/end"
|
||||||
|
app:errorEnabled="true"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/colon">
|
app:layout_constraintStart_toEndOf="@+id/colon">
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -391,8 +391,7 @@
|
||||||
<string name="minute">Phút</string>
|
<string name="minute">Phút</string>
|
||||||
<string name="second">Giây</string>
|
<string name="second">Giây</string>
|
||||||
<string name="milliseconds">Mili-giây</string>
|
<string name="milliseconds">Mili-giây</string>
|
||||||
<string name="format_importance_video">Định dạng theo thứ tự quan trọng (Video)</string>
|
<string name="format_importance">Định dạng theo thứ tự quan trọng</string>
|
||||||
<string name="format_importance_audio">Định dạng theo thứ tự quan trọng (Âm thanh)</string>
|
|
||||||
<string name="format_importance_note">Điều chỉnh định dạng theo tầm quan trọng của phần tử. Thứ tự này sẽ được sử dụng khi ứng dụng tìm nạp các định dạng trong thẻ tải xuống và tự động chọn định dạng!</string>
|
<string name="format_importance_note">Điều chỉnh định dạng theo tầm quan trọng của phần tử. Thứ tự này sẽ được sử dụng khi ứng dụng tìm nạp các định dạng trong thẻ tải xuống và tự động chọn định dạng!</string>
|
||||||
<string name="no_audio">Video không âm thanh</string>
|
<string name="no_audio">Video không âm thanh</string>
|
||||||
<string name="enable_alarm_permission">Bạn cần cho phép quyền SCHEDULE_EXACT_ALARM trong cài đặt ứng dụng.</string>
|
<string name="enable_alarm_permission">Bạn cần cho phép quyền SCHEDULE_EXACT_ALARM trong cài đặt ứng dụng.</string>
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@
|
||||||
app:defaultValue="false"
|
app:defaultValue="false"
|
||||||
android:icon="@drawable/baseline_layers_24"
|
android:icon="@drawable/baseline_layers_24"
|
||||||
android:key="display_over_apps"
|
android:key="display_over_apps"
|
||||||
|
app:isPreferenceVisible="false"
|
||||||
app:summary="@string/display_over_apps_summary"
|
app:summary="@string/display_over_apps_summary"
|
||||||
app:title="@string/display_over_apps" />
|
app:title="@string/display_over_apps" />
|
||||||
|
|
||||||
|
|
|
||||||
8
fastlane/metadata/android/en-US/changelogs/10770.txt
Normal file
8
fastlane/metadata/android/en-US/changelogs/10770.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
## Shortest changelog ever
|
||||||
|
|
||||||
|
- Fixed multiple download card issues. Processing them in the background was not a good idea for most users as it was confusing and users thought it wasnt doing anything. It led to duplicate downloads
|
||||||
|
Usually playlist downloads dont really have 4000 items in them so background. If you have one, just enable quick download and consider the whole playlist as a single item. :)
|
||||||
|
|
||||||
|
- Some users had issues with the display over apps perm. Since that perm ended up not doing anything to the revanced issue, i removed it
|
||||||
|
|
||||||
|
Had to push this out since multiple download card was almost broken
|
||||||
Loading…
Reference in a new issue