This commit is contained in:
zaednasr 2023-11-04 20:35:19 +01:00
parent 1b55784839
commit c30285133b
No known key found for this signature in database
GPG key ID: 92B1DE23AD3D0E9E
52 changed files with 533 additions and 365 deletions

View file

@ -4,13 +4,14 @@ plugins {
id 'org.jetbrains.kotlin.android' id 'org.jetbrains.kotlin.android'
id 'com.google.devtools.ksp' id 'com.google.devtools.ksp'
id "org.jetbrains.kotlin.plugin.serialization" version "1.8.10" id "org.jetbrains.kotlin.plugin.serialization" version "1.8.10"
id "org.jetbrains.kotlin.plugin.parcelize" version "1.8.10"
} }
def properties = new Properties() def properties = new Properties()
def versionMajor = 1 def versionMajor = 1
def versionMinor = 6 def versionMinor = 6
def versionPatch = 8 def versionPatch = 8
def versionBuild = 0 // bump for dogfood builds, public betas, etc. def versionBuild = 1 // bump for dogfood builds, public betas, etc.
def versionExt = "" def versionExt = ""
if (versionBuild > 0){ if (versionBuild > 0){

View file

@ -45,6 +45,7 @@
android:name=".MainActivity" android:name=".MainActivity"
android:configChanges="locale" android:configChanges="locale"
android:exported="true" android:exported="true"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustPan"> android:windowSoftInputMode="adjustPan">
</activity> </activity>
@ -53,6 +54,7 @@
android:configChanges="smallestScreenSize|layoutDirection|locale|orientation|screenSize" android:configChanges="smallestScreenSize|layoutDirection|locale|orientation|screenSize"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:exported="true" android:exported="true"
android:launchMode="singleInstance"
android:theme="@style/Theme.BottomSheet"> android:theme="@style/Theme.BottomSheet">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.SEND" /> <action android:name="android.intent.action.SEND" />
@ -125,6 +127,7 @@
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:configChanges="locale" android:configChanges="locale"
android:launchMode="singleTask"
android:targetActivity=".MainActivity" android:targetActivity=".MainActivity"
android:windowSoftInputMode="adjustPan" android:windowSoftInputMode="adjustPan"
android:exported="true"> android:exported="true">
@ -147,6 +150,7 @@
android:icon="@mipmap/ic_launcher_dark" android:icon="@mipmap/ic_launcher_dark"
android:roundIcon="@mipmap/ic_launcher_round_dark" android:roundIcon="@mipmap/ic_launcher_round_dark"
android:configChanges="locale" android:configChanges="locale"
android:launchMode="singleTask"
android:targetActivity=".MainActivity" android:targetActivity=".MainActivity"
android:windowSoftInputMode="adjustPan"> android:windowSoftInputMode="adjustPan">
<intent-filter> <intent-filter>
@ -167,6 +171,7 @@
android:icon="@mipmap/ic_launcher_light" android:icon="@mipmap/ic_launcher_light"
android:roundIcon="@mipmap/ic_launcher_round_light" android:roundIcon="@mipmap/ic_launcher_round_light"
android:configChanges="locale" android:configChanges="locale"
android:launchMode="singleTask"
android:targetActivity=".MainActivity" android:targetActivity=".MainActivity"
android:windowSoftInputMode="adjustPan"> android:windowSoftInputMode="adjustPan">
<intent-filter> <intent-filter>
@ -184,6 +189,7 @@
android:name=".ui.more.WebViewActivity" android:name=".ui.more.WebViewActivity"
android:configChanges="locale" android:configChanges="locale"
android:exported="true" android:exported="true"
android:launchMode="singleTask"
android:parentActivityName=".MainActivity" android:parentActivityName=".MainActivity"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<intent-filter> <intent-filter>

View file

@ -30,6 +30,7 @@ import androidx.fragment.app.FragmentContainerView
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
@ -48,6 +49,7 @@ import com.deniscerri.ytdlnis.util.ThemeUtil
import com.deniscerri.ytdlnis.util.UpdateUtil import com.deniscerri.ytdlnis.util.UpdateUtil
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
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.navigation.NavigationBarView import com.google.android.material.navigation.NavigationBarView
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
import com.google.android.material.navigationrail.NavigationRailView import com.google.android.material.navigationrail.NavigationRailView
@ -82,6 +84,7 @@ class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
ThemeUtil.updateTheme(this) ThemeUtil.updateTheme(this)
window.navigationBarColor = SurfaceColors.SURFACE_2.getColor(this)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
context = baseContext context = baseContext
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java] resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
@ -362,8 +365,10 @@ class MainActivity : BaseActivity() {
Toast.makeText(context, "Couldn't read file", Toast.LENGTH_LONG).show() Toast.makeText(context, "Couldn't read file", Toast.LENGTH_LONG).show()
return return
} }
val bottomSheet = DownloadBottomSheetDialog(type = DownloadViewModel.Type.command, result = downloadViewModel.createEmptyResultItem(f.absolutePath)) val bundle = Bundle()
bottomSheet.show(supportFragmentManager, "downloadSingleSheet") bundle.putParcelable("result", downloadViewModel.createEmptyResultItem(f.absolutePath))
bundle.putSerializable("type", DownloadViewModel.Type.command)
navController.navigate(R.id.downloadBottomSheetDialog, bundle)
}else{ }else{
val `is` = contentResolver.openInputStream(uri!!) val `is` = contentResolver.openInputStream(uri!!)
val textBuilder = StringBuilder() val textBuilder = StringBuilder()

View file

@ -13,10 +13,10 @@ interface DownloadDao {
@Query("SELECT * FROM downloads ORDER BY status") @Query("SELECT * FROM downloads ORDER BY status")
fun getAllDownloads() : PagingSource<Int, DownloadItem> fun getAllDownloads() : PagingSource<Int, DownloadItem>
@Query("SELECT * FROM downloads WHERE status='Active' or status='Paused'") @Query("SELECT * FROM downloads WHERE status in ('Active', 'ActivePaused', 'PausedReQueued')")
fun getActiveDownloads() : Flow<List<DownloadItem>> fun getActiveDownloads() : Flow<List<DownloadItem>>
@Query("SELECT COUNT(*) FROM downloads WHERE status='Active' or status='Paused'") @Query("SELECT COUNT(*) FROM downloads WHERE status in ('Active', 'ActivePaused', 'PausedReQueued')")
fun getActiveDownloadsCountFlow() : Flow<Int> fun getActiveDownloadsCountFlow() : Flow<Int>
@Query("SELECT COUNT(*) FROM downloads WHERE status in (:status)") @Query("SELECT COUNT(*) FROM downloads WHERE status in (:status)")
@ -25,34 +25,34 @@ interface DownloadDao {
@Query("SELECT COUNT(*) FROM downloads WHERE status in (:status)") @Query("SELECT COUNT(*) FROM downloads WHERE status in (:status)")
fun getDownloadsCountByStatus(status : List<String>) : Int fun getDownloadsCountByStatus(status : List<String>) : Int
@Query("SELECT * FROM downloads WHERE status='Active' or status='Paused'") @Query("SELECT * FROM downloads WHERE status in('Active','ActivePaused','PausedReQueued')")
fun getActiveAndPausedDownloadsList() : List<DownloadItem> fun getActiveAndPausedDownloadsList() : List<DownloadItem>
@Query("SELECT * FROM downloads WHERE status='Active'") @Query("SELECT * FROM downloads WHERE status='Active'")
fun getActiveDownloadsList() : List<DownloadItem> fun getActiveDownloadsList() : List<DownloadItem>
@Query("SELECT * FROM downloads WHERE status='Active' or status='Queued' or status='QueuedPaused' or status='Paused'") @Query("SELECT * FROM downloads WHERE status in('Active','Queued','QueuedPaused','ActivePaused','PausedReQueued')")
fun getActiveAndQueuedDownloadsList() : List<DownloadItem> fun getActiveAndQueuedDownloadsList() : List<DownloadItem>
@Query("SELECT id FROM downloads WHERE status='Active' or status='Queued' or status='QueuedPaused' or status='Paused'") @Query("SELECT id FROM downloads WHERE status in('Active','Queued','QueuedPaused','ActivePaused','PausedReQueued')")
fun getActiveAndQueuedDownloadIDs() : List<Long> fun getActiveAndQueuedDownloadIDs() : List<Long>
@Query("SELECT * FROM downloads WHERE status='Active' or status='Queued' or status='QueuedPaused' or status='Paused'") @Query("SELECT * FROM downloads WHERE status in('Active','Queued','QueuedPaused','ActivePaused','PausedReQueued')")
fun getActiveAndQueuedDownloads() : Flow<List<DownloadItem>> fun getActiveAndQueuedDownloads() : Flow<List<DownloadItem>>
@Query("SELECT * FROM downloads WHERE status='Queued' or status='QueuedPaused' ORDER BY downloadStartTime, id") @Query("SELECT * FROM downloads WHERE status in('Queued','QueuedPaused') ORDER BY downloadStartTime, id")
fun getQueuedDownloads() : PagingSource<Int, DownloadItemSimple> fun getQueuedDownloads() : PagingSource<Int, DownloadItemSimple>
@Query("SELECT format FROM downloads WHERE status='Queued' or status='QueuedPaused'") @Query("SELECT format FROM downloads WHERE status in('Queued','QueuedPaused')")
fun getSelectedFormatFromQueued() : List<Format> fun getSelectedFormatFromQueued() : List<Format>
@Query("SELECT * FROM downloads WHERE downloadStartTime <= :currentTime and status='Queued' ORDER BY downloadStartTime, id LIMIT 20") @Query("SELECT * FROM downloads WHERE downloadStartTime <= :currentTime and status in ('Queued', 'PausedReQueued') ORDER BY downloadStartTime, id LIMIT 20")
fun getQueuedDownloadsThatAreNotScheduledChunked(currentTime: Long) : Flow<List<DownloadItem>> fun getQueuedDownloadsThatAreNotScheduledChunked(currentTime: Long) : Flow<List<DownloadItem>>
@Query("SELECT * FROM downloads WHERE status='Queued' or status='QueuedPaused' or status='Paused' ORDER BY downloadStartTime, id") @Query("SELECT * FROM downloads WHERE status in ('Queued','QueuedPaused','ActivePaused','PausedReQueued') ORDER BY downloadStartTime, id")
fun getQueuedAndPausedDownloads() : Flow<List<DownloadItem>> fun getQueuedAndPausedDownloads() : Flow<List<DownloadItem>>
@Query("SELECT * FROM downloads WHERE status='Queued' or status='QueuedPaused' ORDER BY downloadStartTime, id") @Query("SELECT * FROM downloads WHERE status in('Queued','QueuedPaused') ORDER BY downloadStartTime, id")
fun getQueuedDownloadsList() : List<DownloadItem> fun getQueuedDownloadsList() : List<DownloadItem>
@Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC") @Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC")
@ -87,7 +87,7 @@ interface DownloadDao {
@Query("UPDATE downloads " + @Query("UPDATE downloads " +
"SET status = CASE " + "SET status = CASE " +
" WHEN status = 'Active' THEN 'Paused' " + " WHEN status = 'Active' THEN 'ActivePaused' " +
" WHEN status = 'Queued' THEN 'QueuedPaused' " + " WHEN status = 'Queued' THEN 'QueuedPaused' " +
" ELSE status " + " ELSE status " +
" END;") " END;")
@ -95,7 +95,7 @@ interface DownloadDao {
@Query("UPDATE downloads " + @Query("UPDATE downloads " +
"SET status = CASE " + "SET status = CASE " +
" WHEN status = 'Paused' THEN 'Active' " + " WHEN status = 'ActivePaused' THEN 'Active' " +
" WHEN status = 'QueuedPaused' THEN 'Queued' " + " WHEN status = 'QueuedPaused' THEN 'Queued' " +
" ELSE status " + " ELSE status " +
" END;") " END;")
@ -125,7 +125,7 @@ interface DownloadDao {
@Query("DELETE FROM downloads WHERE id in (:list)") @Query("DELETE FROM downloads WHERE id in (:list)")
suspend fun deleteAllWithIDs(list: List<Long>) suspend fun deleteAllWithIDs(list: List<Long>)
@Query("UPDATE downloads SET status='Cancelled' WHERE status='Queued' or status='QueuedPaused' or status='Active' or status='Paused'") @Query("UPDATE downloads SET status='Cancelled' WHERE status in('Queued','QueuedPaused','Active','ActivePaused','PausedReQueued')")
suspend fun cancelActiveQueued() suspend fun cancelActiveQueued()
@Query("DELETE FROM downloads WHERE status='Processing' AND id=:id") @Query("DELETE FROM downloads WHERE status='Processing' AND id=:id")

View file

@ -1,7 +1,11 @@
package com.deniscerri.ytdlnis.database.models package com.deniscerri.ytdlnis.database.models
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class AudioPreferences( data class AudioPreferences(
var embedThumb: Boolean = true, var embedThumb: Boolean = true,
var splitByChapters: Boolean = false, var splitByChapters: Boolean = false,
var sponsorBlockFilters: ArrayList<String> = arrayListOf() var sponsorBlockFilters: ArrayList<String> = arrayListOf()
) ) : Parcelable

View file

@ -1,7 +1,10 @@
package com.deniscerri.ytdlnis.database.models package com.deniscerri.ytdlnis.database.models
import android.os.Parcelable
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class ChapterItem( data class ChapterItem(
@SerializedName(value = "start_time") @SerializedName(value = "start_time")
var start_time: Long, var start_time: Long,
@ -9,4 +12,4 @@ data class ChapterItem(
var end_time: Long, var end_time: Long,
@SerializedName(value = "title") @SerializedName(value = "title")
var title: String, var title: String,
) ) : Parcelable

View file

@ -1,11 +1,14 @@
package com.deniscerri.ytdlnis.database.models package com.deniscerri.ytdlnis.database.models
import android.os.Parcelable
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import kotlinx.parcelize.Parcelize
@Entity(tableName = "downloads") @Entity(tableName = "downloads")
@Parcelize
data class DownloadItem( data class DownloadItem(
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
var id: Long, var id: Long,
@ -24,7 +27,7 @@ data class DownloadItem(
var downloadPath: String, var downloadPath: String,
var website: String, var website: String,
val downloadSize: String, val downloadSize: String,
val playlistTitle: String, var playlistTitle: String,
val audioPreferences : AudioPreferences, val audioPreferences : AudioPreferences,
val videoPreferences: VideoPreferences, val videoPreferences: VideoPreferences,
@ColumnInfo(defaultValue = "") @ColumnInfo(defaultValue = "")
@ -36,4 +39,4 @@ data class DownloadItem(
@ColumnInfo(defaultValue = "0") @ColumnInfo(defaultValue = "0")
var downloadStartTime: Long, var downloadStartTime: Long,
var logID: Long? var logID: Long?
) ) : Parcelable

View file

@ -1,7 +1,10 @@
package com.deniscerri.ytdlnis.database.models package com.deniscerri.ytdlnis.database.models
import android.os.Parcelable
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class Format( data class Format(
@SerializedName(value = "format_id", alternate = ["itag"]) @SerializedName(value = "format_id", alternate = ["itag"])
var format_id: String = "", var format_id: String = "",
@ -23,4 +26,4 @@ data class Format(
var asr: String? = "", var asr: String? = "",
@SerializedName(value = "url") @SerializedName(value = "url")
var url: String? = "" var url: String? = ""
) ) : Parcelable

View file

@ -1,10 +1,13 @@
package com.deniscerri.ytdlnis.database.models package com.deniscerri.ytdlnis.database.models
import android.os.Parcelable
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import kotlinx.parcelize.Parcelize
@Entity(tableName = "results") @Entity(tableName = "results")
@Parcelize
data class ResultItem( data class ResultItem(
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
var id: Long, var id: Long,
@ -20,4 +23,4 @@ data class ResultItem(
var urls: String, var urls: String,
var chapters: MutableList<ChapterItem>?, var chapters: MutableList<ChapterItem>?,
var creationTime: Long = System.currentTimeMillis() / 1000, var creationTime: Long = System.currentTimeMillis() / 1000,
) ) : Parcelable

View file

@ -1,5 +1,9 @@
package com.deniscerri.ytdlnis.database.models package com.deniscerri.ytdlnis.database.models
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class VideoPreferences ( data class VideoPreferences (
var embedSubs: Boolean = true, var embedSubs: Boolean = true,
var addChapters: Boolean = true, var addChapters: Boolean = true,
@ -9,4 +13,4 @@ data class VideoPreferences (
var subsLanguages: String = "en.*,.*-orig", var subsLanguages: String = "en.*,.*-orig",
var audioFormatIDs : ArrayList<String> = arrayListOf(), var audioFormatIDs : ArrayList<String> = arrayListOf(),
var removeAudio: Boolean = false var removeAudio: Boolean = false
) ) : Parcelable

View file

@ -37,7 +37,7 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
val activeDownloadsCount : Flow<Int> = downloadDao.getActiveDownloadsCountFlow() val activeDownloadsCount : Flow<Int> = downloadDao.getActiveDownloadsCountFlow()
enum class Status { enum class Status {
Active, Paused, Queued, QueuedPaused, Error, Cancelled, Saved Active, ActivePaused, PausedReQueued, Queued, QueuedPaused, Error, Cancelled, Saved
} }
suspend fun insert(item: DownloadItem) : Long { suspend fun insert(item: DownloadItem) : Long {

View file

@ -612,7 +612,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
} }
items.forEach { items.forEach {
if (it.status != DownloadRepository.Status.Paused.toString()) it.status = DownloadRepository.Status.Queued.toString() if (it.status != DownloadRepository.Status.ActivePaused.toString()) it.status = DownloadRepository.Status.Queued.toString()
val currentCommand = infoUtil.buildYoutubeDLRequest(it) val currentCommand = infoUtil.buildYoutubeDLRequest(it)
val parsedCurrentCommand = infoUtil.parseYTDLRequestString(currentCommand) val parsedCurrentCommand = infoUtil.parseYTDLRequestString(currentCommand)
val existingDownload = activeAndQueuedDownloads.firstOrNull{d -> val existingDownload = activeAndQueuedDownloads.firstOrNull{d ->
@ -640,7 +640,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
existing.add(it) existing.add(it)
}else{ }else{
if (it.id == 0L){ if (it.id == 0L){
val id = withContext(Dispatchers.IO){ val id = runBlocking {
repository.insert(it) repository.insert(it)
} }
it.id = id it.id = id
@ -665,6 +665,13 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
} }
} }
if (items.any { it.playlistTitle.isEmpty() }){
items.forEachIndexed { index, it -> it.playlistTitle = "Various[${index+1}]" }
withContext(Dispatchers.IO){
dao.updateMultiple(items)
}
}
if (!useScheduler || alarmScheduler.isDuringTheScheduledTime() || items.any { it.downloadStartTime > 0L } ){ if (!useScheduler || alarmScheduler.isDuringTheScheduledTime() || items.any { it.downloadStartTime > 0L } ){
startDownloadWorker(queuedItems) startDownloadWorker(queuedItems)

View file

@ -25,7 +25,7 @@ class PauseDownloadNotificationReceiver : BroadcastReceiver() {
CoroutineScope(Dispatchers.IO).launch{ CoroutineScope(Dispatchers.IO).launch{
runCatching { runCatching {
val item = dbManager.downloadDao.getDownloadById(id.toLong()) val item = dbManager.downloadDao.getDownloadById(id.toLong())
item.status = DownloadRepository.Status.Paused.toString() item.status = DownloadRepository.Status.ActivePaused.toString()
dbManager.downloadDao.update(item) dbManager.downloadDao.update(item)
} }
} }

View file

@ -1,6 +1,7 @@
package com.deniscerri.ytdlnis.receiver package com.deniscerri.ytdlnis.receiver
import android.Manifest import android.Manifest
import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent import android.content.Intent
@ -16,10 +17,16 @@ import android.util.Log
import android.util.Patterns import android.util.Patterns
import android.view.WindowManager import android.view.WindowManager
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.content.IntentSanitizer
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.NavDestination
import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.deniscerri.ytdlnis.MainActivity import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
@ -34,6 +41,7 @@ import com.deniscerri.ytdlnis.ui.downloadcard.SelectPlaylistItemsDialog
import com.deniscerri.ytdlnis.util.ThemeUtil import com.deniscerri.ytdlnis.util.ThemeUtil
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlin.properties.Delegates import kotlin.properties.Delegates
@ -46,6 +54,7 @@ class ShareActivity : BaseActivity() {
private lateinit var downloadViewModel: DownloadViewModel private lateinit var downloadViewModel: DownloadViewModel
private lateinit var cookieViewModel: CookieViewModel private lateinit var cookieViewModel: CookieViewModel
private lateinit var sharedPreferences: SharedPreferences private lateinit var sharedPreferences: SharedPreferences
private lateinit var navController: NavController
private var quickDownload by Delegates.notNull<Boolean>() private var quickDownload by Delegates.notNull<Boolean>()
@ -83,6 +92,28 @@ class ShareActivity : BaseActivity() {
handleIntents(intent) handleIntents(intent)
} }
override fun onResume() {
super.onResume()
navController.addOnDestinationChangedListener(object: NavController.OnDestinationChangedListener{
override fun onDestinationChanged(
controller: NavController,
destination: NavDestination,
arguments: Bundle?
) {
if (navController.currentBackStack.value.isEmpty()) return
navController.removeOnDestinationChangedListener(this)
lifecycleScope.launch {
navController.currentBackStack.collectLatest {
if (it.isEmpty()){
this@ShareActivity.finish()
}
}
}
}
})
}
override fun onNewIntent(intent: Intent) { override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent) super.onNewIntent(intent)
handleIntents(intent) handleIntents(intent)
@ -91,6 +122,9 @@ class ShareActivity : BaseActivity() {
private fun handleIntents(intent: Intent) { private fun handleIntents(intent: Intent) {
askPermissions() askPermissions()
val navHostFragment = supportFragmentManager.findFragmentById(R.id.frame_layout) as NavHostFragment
navController = navHostFragment.findNavController()
val action = intent.action val action = intent.action
Log.e("aa", intent.toString()) Log.e("aa", intent.toString())
if (Intent.ACTION_SEND == action || Intent.ACTION_VIEW == action) { if (Intent.ACTION_SEND == action || Intent.ACTION_VIEW == action) {
@ -129,10 +163,10 @@ class ShareActivity : BaseActivity() {
} }
val downloadType = DownloadViewModel.Type.valueOf(type ?: downloadViewModel.getDownloadType(url = result.url).toString()) val downloadType = DownloadViewModel.Type.valueOf(type ?: downloadViewModel.getDownloadType(url = result.url).toString())
if (sharedPreferences.getBoolean("download_card", true) && !background){ if (sharedPreferences.getBoolean("download_card", true) && !background){
val bottomSheet = DownloadBottomSheetDialog( val bundle = Bundle()
result = result, bundle.putParcelable("result", result)
type = downloadType) bundle.putSerializable("type", downloadType)
bottomSheet.show(supportFragmentManager, "downloadSingleSheet") navController.setGraph(R.navigation.share_nav_graph, bundle)
}else{ }else{
lifecycleScope.launch(Dispatchers.IO){ lifecycleScope.launch(Dispatchers.IO){
val downloadItem = downloadViewModel.createDownloadItemFromResult( val downloadItem = downloadViewModel.createDownloadItemFromResult(
@ -152,32 +186,6 @@ class ShareActivity : BaseActivity() {
} }
} }
private fun showDownloadSheet(it: ResultItem){
}
private fun showSelectPlaylistItems(it: List<ResultItem?>){
if (sharedPreferences.getBoolean("download_card", true)){
val bottomSheet = SelectPlaylistItemsDialog(it, DownloadViewModel.Type.valueOf(sharedPreferences.getString("preferred_download_type", "video")!!))
bottomSheet.show(supportFragmentManager, "downloadPlaylistSheet")
}else{
lifecycleScope.launch(Dispatchers.IO){
val downloadItems = mutableListOf<DownloadItem>()
lifecycleScope.launch(Dispatchers.IO){
it.forEach { res ->
val i = downloadViewModel.createDownloadItemFromResult(
result = res!!,
givenType = DownloadViewModel.Type.valueOf(sharedPreferences.getString("preferred_download_type", "video")!!))
i.format = downloadViewModel.getLatestCommandTemplateAsFormat()
downloadItems.add(i)
}
downloadViewModel.queueDownloads(downloadItems)
}
}
this.finish()
}
}
private fun askPermissions() { private fun askPermissions() {
val permissions = arrayListOf<String>() val permissions = arrayListOf<String>()
if (!checkFilePermission()) { if (!checkFilePermission()) {

View file

@ -20,12 +20,14 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.os.bundleOf
import androidx.core.view.children import androidx.core.view.children
import androidx.core.view.forEach import androidx.core.view.forEach
import androidx.core.widget.doAfterTextChanged import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -158,28 +160,30 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
recyclerView?.enableFastScroll() recyclerView?.enableFastScroll()
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java] resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
resultViewModel.items.observe(viewLifecycleOwner) { resultViewModel.items.observe(requireActivity()) {
homeAdapter!!.submitList(it) kotlin.runCatching {
resultsList = it homeAdapter!!.submitList(it)
if(resultViewModel.repository.itemCount.value > 1 || resultViewModel.repository.itemCount.value == -1){ resultsList = it
if (it.size > 1 && it[0].playlistTitle.isNotEmpty() && !loadingItems){ if(resultViewModel.repository.itemCount.value > 1 || resultViewModel.repository.itemCount.value == -1){
downloadAllFabCoordinator!!.visibility = VISIBLE if (it.size > 1 && it[0].playlistTitle.isNotEmpty() && !loadingItems){
downloadAllFabCoordinator!!.visibility = VISIBLE
}else{
downloadAllFabCoordinator!!.visibility = GONE
}
}else if (resultViewModel.repository.itemCount.value == 1){
if (sharedPreferences!!.getBoolean("download_card", true)){
if(it.size == 1 && quickLaunchSheet && parentFragmentManager.findFragmentByTag("downloadSingleSheet") == null){
showSingleDownloadSheet(
it[0],
DownloadViewModel.Type.valueOf(sharedPreferences!!.getString("preferred_download_type", "video")!!)
)
}
}
}else{ }else{
downloadAllFabCoordinator!!.visibility = GONE downloadAllFabCoordinator!!.visibility = GONE
} }
}else if (resultViewModel.repository.itemCount.value == 1){ quickLaunchSheet = true
if (sharedPreferences!!.getBoolean("download_card", true)){
if(it.size == 1 && quickLaunchSheet && parentFragmentManager.findFragmentByTag("downloadSingleSheet") == null){
showSingleDownloadSheet(
it[0],
DownloadViewModel.Type.valueOf(sharedPreferences!!.getString("preferred_download_type", "video")!!)
)
}
}
}else{
downloadAllFabCoordinator!!.visibility = GONE
} }
quickLaunchSheet = true
} }
initMenu() initMenu()
@ -271,8 +275,9 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
ids?.forEach { ids?.forEach {
items.add(downloadViewModel.getItemByID(it)) items.add(downloadViewModel.getItemByID(it))
} }
val bottomSheet = DownloadMultipleBottomSheetDialog(items.toMutableList()) findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2, bundleOf(
bottomSheet.show(parentFragmentManager, "downloadMultipleSheet") Pair("downloads", items)
))
} }
} }
@ -560,20 +565,18 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
if(sharedPreferences!!.getBoolean("quick_download", false) || sharedPreferences!!.getString("preferred_download_type", "video") == "command"){ if(sharedPreferences!!.getBoolean("quick_download", false) || sharedPreferences!!.getString("preferred_download_type", "video") == "command"){
if (queryList.size == 1 && Patterns.WEB_URL.matcher(queryList.first()).matches()){ if (queryList.size == 1 && Patterns.WEB_URL.matcher(queryList.first()).matches()){
if (sharedPreferences!!.getBoolean("download_card", true)) { if (sharedPreferences!!.getBoolean("download_card", true)) {
showSingleDownloadSheet( withContext(Dispatchers.Main){
resultItem = downloadViewModel.createEmptyResultItem(queryList.first()), showSingleDownloadSheet(
type = DownloadViewModel.Type.valueOf(sharedPreferences!!.getString("preferred_download_type", "video")!!) resultItem = downloadViewModel.createEmptyResultItem(queryList.first()),
) type = DownloadViewModel.Type.valueOf(sharedPreferences!!.getString("preferred_download_type", "video")!!)
} else { )
lifecycleScope.launch{
val downloadItem = withContext(Dispatchers.IO){
downloadViewModel.createDownloadItemFromResult(
result = downloadViewModel.createEmptyResultItem(queryList.first()),
givenType = DownloadViewModel.Type.valueOf(sharedPreferences!!.getString("preferred_download_type", "video")!!)
)
}
downloadViewModel.queueDownloads(listOf(downloadItem))
} }
} else {
val downloadItem = downloadViewModel.createDownloadItemFromResult(
result = downloadViewModel.createEmptyResultItem(queryList.first()),
givenType = DownloadViewModel.Type.valueOf(sharedPreferences!!.getString("preferred_download_type", "video")!!)
)
downloadViewModel.queueDownloads(listOf(downloadItem))
} }
}else{ }else{
@ -620,8 +623,13 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
resultItem: ResultItem, resultItem: ResultItem,
type: DownloadViewModel.Type type: DownloadViewModel.Type
){ ){
val bottomSheet = DownloadBottomSheetDialog(resultItem, downloadViewModel.getDownloadType(type, resultItem.url)) if(findNavController().currentBackStack.value.firstOrNull {it.destination.id == R.id.downloadBottomSheetDialog} == null){
bottomSheet.show(parentFragmentManager, "downloadSingleSheet") //show the fragment if its not in the backstack
val bundle = Bundle()
bundle.putParcelable("result", resultItem)
bundle.putSerializable("type", downloadViewModel.getDownloadType(type, resultItem.url))
findNavController().navigate(R.id.downloadBottomSheetDialog, bundle)
}
} }
override fun onCardClick(videoURL: String, add: Boolean) { override fun onCardClick(videoURL: String, add: Boolean) {
@ -646,8 +654,9 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
override fun onCardDetailsClick(videoURL: String) { override fun onCardDetailsClick(videoURL: String) {
if (parentFragmentManager.findFragmentByTag("resultDetails") == null && resultsList != null && resultsList!!.isNotEmpty()){ if (parentFragmentManager.findFragmentByTag("resultDetails") == null && resultsList != null && resultsList!!.isNotEmpty()){
val bottomSheet = ResultCardDetailsDialog(resultsList!!.first{it!!.url == videoURL}!!) val bundle = Bundle()
bottomSheet.show(parentFragmentManager, "cutVideoSheet") bundle.putParcelable("result", resultsList!!.first{it!!.url == videoURL}!!)
findNavController().navigate(R.id.resultCardDetailsDialog, bundle)
} }
} }
@ -662,8 +671,9 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
downloadViewModel.turnResultItemsToDownloadItems(resultsList!!) downloadViewModel.turnResultItemsToDownloadItems(resultsList!!)
} }
if (sharedPreferences!!.getBoolean("download_card", true)) { if (sharedPreferences!!.getBoolean("download_card", true)) {
val bottomSheet = DownloadMultipleBottomSheetDialog(downloadList.toMutableList()) findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2, bundleOf(
bottomSheet.show(parentFragmentManager, "downloadMultipleSheet") Pair("downloads", downloadList)
))
} else { } else {
downloadViewModel.queueDownloads(downloadList) downloadViewModel.queueDownloads(downloadList)
} }
@ -723,8 +733,9 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
} }
if (sharedPreferences!!.getBoolean("download_card", true)) { if (sharedPreferences!!.getBoolean("download_card", true)) {
val bottomSheet = DownloadMultipleBottomSheetDialog(downloadList.toMutableList()) findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2, bundleOf(
bottomSheet.show(parentFragmentManager, "downloadMultipleSheet") Pair("downloads", downloadList)
))
} else { } else {
downloadViewModel.queueDownloads(downloadList) downloadViewModel.queueDownloads(downloadList)
} }

View file

@ -133,25 +133,37 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
if (cancelButton.hasOnClickListeners()) cancelButton.setOnClickListener(null) if (cancelButton.hasOnClickListeners()) cancelButton.setOnClickListener(null)
cancelButton.setOnClickListener {onItemClickListener.onCancelClick(item.id)} cancelButton.setOnClickListener {onItemClickListener.onCancelClick(item.id)}
if (item.status == DownloadRepository.Status.Paused.toString()){
progressBar.isIndeterminate = false when(DownloadRepository.Status.valueOf(item.status)){
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_play_arrow_white) DownloadRepository.Status.Active -> {
pauseButton.tag = ActiveDownloadAction.Resume progressBar.isIndeterminate = true
cancelButton.visibility = View.VISIBLE pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_pause_white)
}else{ cancelButton.visibility = View.GONE
progressBar.isIndeterminate = true pauseButton.isEnabled = true
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_pause_white) pauseButton.tag = ActiveDownloadAction.Pause
cancelButton.visibility = View.GONE }
pauseButton.tag = ActiveDownloadAction.Pause DownloadRepository.Status.ActivePaused -> {
progressBar.isIndeterminate = false
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_play_arrow_white)
pauseButton.tag = ActiveDownloadAction.Resume
pauseButton.isEnabled = true
cancelButton.visibility = View.VISIBLE
}
DownloadRepository.Status.PausedReQueued -> {
progressBar.isIndeterminate = true
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.ic_refresh)
pauseButton.tag = null
pauseButton.isEnabled = false
cancelButton.visibility = View.GONE
}
else -> {}
} }
pauseButton.setOnClickListener { pauseButton.setOnClickListener {
if (pauseButton.tag == ActiveDownloadAction.Pause){ if (pauseButton.tag == ActiveDownloadAction.Pause){
onItemClickListener.onPauseClick(item.id, ActiveDownloadAction.Pause, position) onItemClickListener.onPauseClick(item.id, ActiveDownloadAction.Pause, position)
}else{ }else{
onItemClickListener.onPauseClick(item.id, ActiveDownloadAction.Resume, position) onItemClickListener.onPauseClick(item.id, ActiveDownloadAction.Resume, position)
} }
} }

View file

@ -116,7 +116,7 @@ class ActiveDownloadMinifiedAdapter(onItemClickListener: OnItemClickListener, ac
if (cancelButton.hasOnClickListeners()) cancelButton.setOnClickListener(null) if (cancelButton.hasOnClickListeners()) cancelButton.setOnClickListener(null)
cancelButton.setOnClickListener {onItemClickListener.onCancelClick(item.id)} cancelButton.setOnClickListener {onItemClickListener.onCancelClick(item.id)}
if (item.status == DownloadRepository.Status.Paused.toString()){ if (item.status == DownloadRepository.Status.ActivePaused.toString()){
progressBar.isIndeterminate = false progressBar.isIndeterminate = false
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_play_arrow_white) pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_play_arrow_white)
pauseButton.tag = ActiveDownloadAdapter.ActiveDownloadAction.Resume pauseButton.tag = ActiveDownloadAdapter.ActiveDownloadAction.Resume

View file

@ -5,8 +5,8 @@ import android.app.Dialog
import android.content.DialogInterface import android.content.DialogInterface
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Looper
import android.text.format.DateFormat import android.text.format.DateFormat
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.util.Patterns import android.util.Patterns
@ -17,13 +17,16 @@ import android.widget.Button
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.Toast import android.widget.Toast
import androidx.core.content.edit import androidx.core.content.edit
import androidx.core.os.bundleOf
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.NavDestination
import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.DBManager import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao
@ -44,14 +47,20 @@ import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.progressindicator.LinearProgressIndicator import com.google.android.material.progressindicator.LinearProgressIndicator
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.Locale
class DownloadBottomSheetDialog(private var result: ResultItem, private val type: Type, private var currentDownloadItem: DownloadItem? = null) : BottomSheetDialogFragment() { class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
private lateinit var tabLayout: TabLayout private lateinit var tabLayout: TabLayout
private lateinit var viewPager2: ViewPager2 private lateinit var viewPager2: ViewPager2
private lateinit var fragmentAdapter : DownloadFragmentAdapter private lateinit var fragmentAdapter : DownloadFragmentAdapter
@ -64,6 +73,18 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
private lateinit var sharedPreferences : SharedPreferences private lateinit var sharedPreferences : SharedPreferences
private lateinit var updateItem : Button private lateinit var updateItem : Button
private lateinit var view: View private lateinit var view: View
private lateinit var shimmerLoading :ShimmerFrameLayout
private lateinit var title : View
private lateinit var shimmerLoadingSubtitle : ShimmerFrameLayout
private lateinit var subtitle : View
private var updateJob : Job? = null
private var updateFormatsJob : Job? = null
private lateinit var result: ResultItem
private lateinit var type: Type
private var currentDownloadItem: DownloadItem? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -81,6 +102,26 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
view = LayoutInflater.from(context).inflate(R.layout.download_bottom_sheet, null) view = LayoutInflater.from(context).inflate(R.layout.download_bottom_sheet, null)
dialog.setContentView(view) dialog.setContentView(view)
if (Build.VERSION.SDK_INT >= 33){
arguments?.getParcelable("result", ResultItem::class.java)
}else{
arguments?.getParcelable<ResultItem>("result")
}.apply {
if (this == null){
dismiss()
return
}else{
result = this
}
}
type = arguments?.getSerializable("type") as Type
currentDownloadItem = if (Build.VERSION.SDK_INT >= 33){
arguments?.getParcelable("downloadItem", DownloadItem::class.java)
}else{
arguments?.getParcelable<DownloadItem>("downloadItem")
}
dialog.setOnShowListener { dialog.setOnShowListener {
behavior = BottomSheetBehavior.from(view.parent as View) behavior = BottomSheetBehavior.from(view.parent as View)
val displayMetrics = DisplayMetrics() val displayMetrics = DisplayMetrics()
@ -95,6 +136,13 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
viewPager2 = view.findViewById(R.id.download_viewpager) viewPager2 = view.findViewById(R.id.download_viewpager)
updateItem = view.findViewById(R.id.update_item) updateItem = view.findViewById(R.id.update_item)
//loading shimmers
shimmerLoading = view.findViewById(R.id.shimmer_loading_title)
title = view.findViewById(R.id.bottom_sheet_title)
shimmerLoadingSubtitle = view.findViewById(R.id.shimmer_loading_subtitle)
subtitle = view.findViewById(R.id.bottom_sheet_subtitle)
(viewPager2.getChildAt(0) as? RecyclerView)?.apply { (viewPager2.getChildAt(0) as? RecyclerView)?.apply {
isNestedScrollingEnabled = false isNestedScrollingEnabled = false
overScrollMode = View.OVER_SCROLL_NEVER overScrollMode = View.OVER_SCROLL_NEVER
@ -224,6 +272,8 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
scheduleBtn.setOnClickListener{ scheduleBtn.setOnClickListener{
UiUtil.showDatePicker(fragmentManager) { UiUtil.showDatePicker(fragmentManager) {
updateJob?.cancel()
updateFormatsJob?.cancel()
scheduleBtn.isEnabled = false scheduleBtn.isEnabled = false
download.isEnabled = false download.isEnabled = false
val item: DownloadItem = getDownloadItem() val item: DownloadItem = getDownloadItem()
@ -237,6 +287,8 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
} }
} }
download!!.setOnClickListener { download!!.setOnClickListener {
updateJob?.cancel()
updateFormatsJob?.cancel()
scheduleBtn.isEnabled = false scheduleBtn.isEnabled = false
download.isEnabled = false download.isEnabled = false
val item: DownloadItem = getDownloadItem() val item: DownloadItem = getDownloadItem()
@ -285,7 +337,7 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
(updateItem.parent as LinearLayout).visibility = View.VISIBLE (updateItem.parent as LinearLayout).visibility = View.VISIBLE
updateItem.setOnClickListener { updateItem.setOnClickListener {
(updateItem.parent as LinearLayout).visibility = View.GONE (updateItem.parent as LinearLayout).visibility = View.GONE
initUpdateData(view) initUpdateData()
} }
} }
}else{ }else{
@ -300,7 +352,7 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
//update in the background if there is no data //update in the background if there is no data
if(result.title.isEmpty() && currentDownloadItem == null && !sharedPreferences.getBoolean("quick_download", false) && type != Type.command){ if(result.title.isEmpty() && currentDownloadItem == null && !sharedPreferences.getBoolean("quick_download", false) && type != Type.command){
initUpdateData(view) initUpdateData()
}else { }else {
val usingGenericFormatsOrEmpty = result.formats.isEmpty() || result.formats.any { it.format_note.contains("ytdlnisgeneric") } val usingGenericFormatsOrEmpty = result.formats.isEmpty() || result.formats.any { it.format_note.contains("ytdlnisgeneric") }
if (usingGenericFormatsOrEmpty && sharedPreferences.getBoolean("update_formats", false) && !sharedPreferences.getBoolean("quick_download", false)){ if (usingGenericFormatsOrEmpty && sharedPreferences.getBoolean("update_formats", false) && !sharedPreferences.getBoolean("quick_download", false)){
@ -377,48 +429,83 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
} }
} }
private fun initUpdateData(v: View){ private fun initUpdateData() {
val shimmerLoading = v.findViewById<ShimmerFrameLayout>(R.id.shimmer_loading_title) updateJob = CoroutineScope(SupervisorJob()).launch(Dispatchers.IO){
val title = v.findViewById<View>(R.id.bottom_sheet_title) findNavController().saveState()
val shimmerLoadingSubtitle = v.findViewById<ShimmerFrameLayout>(R.id.shimmer_loading_subtitle) kotlin.runCatching {
val subtitle = v.findViewById<View>(R.id.bottom_sheet_subtitle)
val updateJob = CoroutineScope(SupervisorJob()).launch(Dispatchers.IO){
withContext(Dispatchers.Main){
title.visibility = View.GONE
subtitle.visibility = View.GONE
shimmerLoading.visibility = View.VISIBLE
shimmerLoadingSubtitle.visibility = View.VISIBLE
shimmerLoading.startShimmer()
shimmerLoadingSubtitle.startShimmer()
}
if (result.url.isBlank()){
withContext(Dispatchers.Main){dismiss()}
return@launch
}
val result = resultViewModel.parseQueries(listOf(result.url))
if (result.isEmpty()){
return@launch
}
if (result.size == 1 && result[0] != null){
fragmentAdapter.setResultItem(result[0]!!)
withContext(Dispatchers.Main){ withContext(Dispatchers.Main){
runCatching { title.visibility = View.GONE
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment subtitle.visibility = View.GONE
f.updateUI(result[0]) shimmerLoading.visibility = View.VISIBLE
} shimmerLoadingSubtitle.visibility = View.VISIBLE
shimmerLoading.startShimmer()
runCatching { shimmerLoadingSubtitle.startShimmer()
val f1 = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
f1.updateUI(result[0])
}
} }
withContext(Dispatchers.Main){ if (result.url.isBlank()){
withContext(Dispatchers.Main){dismiss()}
return@launch
}
val result = resultViewModel.parseQueries(listOf(result.url))
if (result.isEmpty()){
return@launch
}
if (result.size == 1 && result[0] != null){
fragmentAdapter.setResultItem(result[0]!!)
withContext(Dispatchers.Main){
runCatching {
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
f.updateUI(result[0])
}
runCatching {
val f1 = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
f1.updateUI(result[0])
}
}
withContext(Dispatchers.Main){
title.visibility = View.VISIBLE
subtitle.visibility = View.VISIBLE
shimmerLoading.visibility = View.GONE
shimmerLoadingSubtitle.visibility = View.GONE
shimmerLoading.stopShimmer()
shimmerLoadingSubtitle.stopShimmer()
}
val usingGenericFormatsOrEmpty = result[0]!!.formats.isEmpty() || result[0]!!.formats.any { it.format_note.contains("ytdlnisgeneric") }
if (usingGenericFormatsOrEmpty && sharedPreferences.getBoolean("update_formats", false)){
initUpdateFormats(result[0]!!.url)
} else {
}
}else{
//open multi download card instead
if (activity is ShareActivity){
val preferredType = DownloadViewModel.Type.valueOf(sharedPreferences.getString("preferred_download_type", "video")!!)
withContext(Dispatchers.Main){
findNavController().navigate(R.id.action_downloadBottomSheetDialog_to_selectPlaylistItemsDialog, bundleOf(
Pair("results", result),
Pair("type", preferredType),
))
}
}else{
dismiss()
}
}
}
}
shimmerLoading.setOnClickListener {
updateJob?.cancel()
(updateItem.parent as LinearLayout).visibility = View.VISIBLE
}
updateJob?.invokeOnCompletion {
kotlin.runCatching {
requireActivity().runOnUiThread {
title.visibility = View.VISIBLE title.visibility = View.VISIBLE
subtitle.visibility = View.VISIBLE subtitle.visibility = View.VISIBLE
shimmerLoading.visibility = View.GONE shimmerLoading.visibility = View.GONE
@ -426,48 +513,13 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
shimmerLoading.stopShimmer() shimmerLoading.stopShimmer()
shimmerLoadingSubtitle.stopShimmer() shimmerLoadingSubtitle.stopShimmer()
} }
val usingGenericFormatsOrEmpty = result[0]!!.formats.isEmpty() || result[0]!!.formats.any { it.format_note.contains("ytdlnisgeneric") }
if (usingGenericFormatsOrEmpty && sharedPreferences.getBoolean("update_formats", false)){
initUpdateFormats(result[0]!!.url)
}
}else{
//open multi download card instead
if (activity is ShareActivity){
val preferredType = DownloadViewModel.Type.valueOf(sharedPreferences.getString("preferred_download_type", "video")!!)
withContext(Dispatchers.Main){
val playlistSelect = SelectPlaylistItemsDialog(items = result, type = preferredType)
parentFragmentManager.addFragmentOnAttachListener { fragmentManager, fragment ->
dismiss()
}
playlistSelect.show(parentFragmentManager, "downloadPlaylistSheet")
}
}else{
dismiss()
}
} }
} }
shimmerLoading.setOnClickListener { updateJob?.start()
updateJob.cancel()
(updateItem.parent as LinearLayout).visibility = View.VISIBLE
}
updateJob.invokeOnCompletion {
requireActivity().runOnUiThread {
title.visibility = View.VISIBLE
subtitle.visibility = View.VISIBLE
shimmerLoading.visibility = View.GONE
shimmerLoadingSubtitle.visibility = View.GONE
shimmerLoading.stopShimmer()
shimmerLoadingSubtitle.stopShimmer()
}
}
updateJob.start()
} }
private fun initUpdateFormats(url: String){ private fun initUpdateFormats(url: String){
CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) { updateFormatsJob = CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) {
withContext(Dispatchers.Main){ withContext(Dispatchers.Main){
runCatching { runCatching {
val f1 = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment val f1 = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
@ -510,25 +562,7 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
} }
} }
} updateFormatsJob?.start()
override fun onCancel(dialog: DialogInterface) {
super.onCancel(dialog)
cleanUp()
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
cleanUp()
}
private fun cleanUp(){
kotlin.runCatching {
if (activity is ShareActivity && !parentFragmentManager.fragments.map { it.tag }.contains("downloadPlaylistSheet")){
(activity as ShareActivity).finish()
}
}
} }
} }

View file

@ -9,6 +9,7 @@ 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.Build
import android.os.Bundle import android.os.Bundle
import android.text.format.DateFormat import android.text.format.DateFormat
import android.util.DisplayMetrics import android.util.DisplayMetrics
@ -37,6 +38,7 @@ import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.ui.adapter.ConfigureMultipleDownloadsAdapter import com.deniscerri.ytdlnis.ui.adapter.ConfigureMultipleDownloadsAdapter
import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.Format import com.deniscerri.ytdlnis.database.models.Format
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.repository.DownloadRepository import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
@ -66,7 +68,7 @@ import kotlinx.coroutines.withContext
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadItem>) : BottomSheetDialogFragment(), ConfigureMultipleDownloadsAdapter.OnItemClickListener, View.OnClickListener, class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), ConfigureMultipleDownloadsAdapter.OnItemClickListener, View.OnClickListener,
ConfigureDownloadBottomSheetDialog.OnDownloadItemUpdateListener { ConfigureDownloadBottomSheetDialog.OnDownloadItemUpdateListener {
private lateinit var downloadViewModel: DownloadViewModel private lateinit var downloadViewModel: DownloadViewModel
private lateinit var historyViewModel: HistoryViewModel private lateinit var historyViewModel: HistoryViewModel
@ -80,6 +82,8 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
private lateinit var filesize : TextView private lateinit var filesize : TextView
private lateinit var sharedPreferences: SharedPreferences private lateinit var sharedPreferences: SharedPreferences
private lateinit var items: MutableList<DownloadItem>
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -97,6 +101,19 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
val view = LayoutInflater.from(context).inflate(R.layout.download_multiple_bottom_sheet, null) val view = LayoutInflater.from(context).inflate(R.layout.download_multiple_bottom_sheet, null)
dialog.setContentView(view) dialog.setContentView(view)
if (Build.VERSION.SDK_INT >= 33){
arguments?.getParcelableArrayList("downloads", DownloadItem::class.java)
}else{
arguments?.getParcelableArrayList<DownloadItem>("downloads")
}.apply {
if (this == null){
dismiss()
return
}else{
items = this
}
}
if (items.isEmpty()) dismiss() if (items.isEmpty()) dismiss()
dialog.setOnShowListener { dialog.setOnShowListener {
@ -138,10 +155,7 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
item.downloadStartTime = it.timeInMillis item.downloadStartTime = it.timeInMillis
} }
runBlocking { runBlocking {
val chunks = items.chunked(10) downloadViewModel.queueDownloads(items)
for (c in chunks) {
downloadViewModel.queueDownloads(c)
}
val first = items.first() val first = items.first()
val date = SimpleDateFormat(DateFormat.getBestDateTimePattern(Locale.getDefault(), "ddMMMyyyy - HHmm"), Locale.getDefault()).format(first.downloadStartTime) val date = SimpleDateFormat(DateFormat.getBestDateTimePattern(Locale.getDefault(), "ddMMMyyyy - HHmm"), Locale.getDefault()).format(first.downloadStartTime)
Toast.makeText(context, getString(R.string.download_rescheduled_to) + " " + date, Toast.LENGTH_LONG).show() Toast.makeText(context, getString(R.string.download_rescheduled_to) + " " + date, Toast.LENGTH_LONG).show()
@ -154,11 +168,7 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
scheduleBtn.isEnabled = false scheduleBtn.isEnabled = false
download.isEnabled = false download.isEnabled = false
runBlocking { runBlocking {
val chunks = items.chunked(10) downloadViewModel.queueDownloads(items)
for (c in chunks) {
downloadViewModel.queueDownloads(c)
}
} }
dismiss() dismiss()
} }
@ -559,24 +569,6 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
} }
} }
override fun onCancel(dialog: DialogInterface) {
super.onCancel(dialog)
cleanup()
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
cleanup()
}
private fun cleanup(){
kotlin.runCatching {
if (activity is ShareActivity){
(activity as ShareActivity).finish()
}
}
}
override fun onButtonClick(itemURL: String) { override fun onButtonClick(itemURL: String) {
lifecycleScope.launch { lifecycleScope.launch {
val item = items.find { it.url == itemURL } ?: return@launch val item = items.find { it.url == itemURL } ?: return@launch

View file

@ -10,6 +10,7 @@ import android.content.res.Configuration
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Color import android.graphics.Color
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Environment import android.os.Environment
import android.text.format.DateFormat import android.text.format.DateFormat
@ -70,7 +71,7 @@ import java.text.SimpleDateFormat
import java.util.* import java.util.*
class ResultCardDetailsDialog(private val item: ResultItem) : BottomSheetDialogFragment(), GenericDownloadAdapter.OnItemClickListener, ActiveDownloadMinifiedAdapter.OnItemClickListener { class ResultCardDetailsDialog : BottomSheetDialogFragment(), GenericDownloadAdapter.OnItemClickListener, ActiveDownloadMinifiedAdapter.OnItemClickListener {
private lateinit var infoUtil: InfoUtil private lateinit var infoUtil: InfoUtil
private lateinit var notificationUtil: NotificationUtil private lateinit var notificationUtil: NotificationUtil
private lateinit var videoView: PlayerView private lateinit var videoView: PlayerView
@ -85,6 +86,7 @@ class ResultCardDetailsDialog(private val item: ResultItem) : BottomSheetDialogF
private lateinit var sharedPreferences: SharedPreferences private lateinit var sharedPreferences: SharedPreferences
private lateinit var dialogView : View private lateinit var dialogView : View
private lateinit var item: ResultItem
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -136,6 +138,19 @@ class ResultCardDetailsDialog(private val item: ResultItem) : BottomSheetDialogF
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val i = if (Build.VERSION.SDK_INT >= 33){
arguments?.getParcelable("result", ResultItem::class.java)
}else{
arguments?.getParcelable<ResultItem>("result")
}
if (i == null) {
dismiss()
return
}
item = i
//remove outdated player url of 1hr so it can refetch it in the player //remove outdated player url of 1hr so it can refetch it in the player
if (item.creationTime > System.currentTimeMillis() - 3600000) item.urls = "" if (item.creationTime > System.currentTimeMillis() - 3600000) item.urls = ""
@ -293,10 +308,12 @@ class ResultCardDetailsDialog(private val item: ResultItem) : BottomSheetDialogF
} }
private fun onButtonClick(type: DownloadViewModel.Type){ private fun onButtonClick(type: DownloadViewModel.Type){
this.dismiss()
if (sharedPreferences.getBoolean("download_card", true)) { if (sharedPreferences.getBoolean("download_card", true)) {
val bottomSheet = DownloadBottomSheetDialog(item, type) val bundle = Bundle()
bottomSheet.show(parentFragmentManager, "downloadSingleSheet") bundle.putParcelable("result", item)
bundle.putSerializable("type", type)
findNavController().navigateUp()
findNavController().navigate(R.id.downloadBottomSheetDialog, bundle)
} else { } else {
lifecycleScope.launch{ lifecycleScope.launch{
val downloadItem = withContext(Dispatchers.IO){ val downloadItem = withContext(Dispatchers.IO){
@ -305,6 +322,7 @@ class ResultCardDetailsDialog(private val item: ResultItem) : BottomSheetDialogF
givenType = type) givenType = type)
} }
downloadViewModel.queueDownloads(listOf(downloadItem)) downloadViewModel.queueDownloads(listOf(downloadItem))
findNavController().navigateUp()
} }
} }
} }
@ -324,7 +342,6 @@ class ResultCardDetailsDialog(private val item: ResultItem) : BottomSheetDialogF
kotlin.runCatching { kotlin.runCatching {
videoView.player?.stop() videoView.player?.stop()
videoView.player?.release() videoView.player?.release()
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("resultDetails")!!).commit()
} }
} }
@ -582,7 +599,7 @@ class ResultCardDetailsDialog(private val item: ResultItem) : BottomSheetDialogF
ActiveDownloadAdapter.ActiveDownloadAction.Pause -> { ActiveDownloadAdapter.ActiveDownloadAction.Pause -> {
lifecycleScope.launch { lifecycleScope.launch {
cancelItem(itemID.toInt()) cancelItem(itemID.toInt())
item.status = DownloadRepository.Status.Paused.toString() item.status = DownloadRepository.Status.ActivePaused.toString()
withContext(Dispatchers.IO){ withContext(Dispatchers.IO){
downloadViewModel.updateDownload(item) downloadViewModel.updateDownload(item)
} }
@ -591,7 +608,7 @@ class ResultCardDetailsDialog(private val item: ResultItem) : BottomSheetDialogF
} }
ActiveDownloadAdapter.ActiveDownloadAction.Resume -> { ActiveDownloadAdapter.ActiveDownloadAction.Resume -> {
lifecycleScope.launch { lifecycleScope.launch {
item.status = DownloadRepository.Status.Active.toString() item.status = DownloadRepository.Status.PausedReQueued.toString()
withContext(Dispatchers.IO){ withContext(Dispatchers.IO){
downloadViewModel.updateDownload(item) downloadViewModel.updateDownload(item)
} }

View file

@ -3,16 +3,19 @@ package com.deniscerri.ytdlnis.ui.downloadcard
import android.app.ActionBar.LayoutParams import android.app.ActionBar.LayoutParams
import android.app.Dialog import android.app.Dialog
import android.content.DialogInterface import android.content.DialogInterface
import android.os.Build
import android.os.Bundle import android.os.Bundle
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 androidx.core.os.bundleOf
import androidx.core.widget.doAfterTextChanged import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
@ -30,8 +33,9 @@ import com.google.android.material.floatingactionbutton.ExtendedFloatingActionBu
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class SelectPlaylistItemsDialog(private val items: List<ResultItem?>, private val type: DownloadViewModel.Type) : DialogFragment(), PlaylistAdapter.OnItemClickListener { class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickListener {
private lateinit var downloadViewModel: DownloadViewModel private lateinit var downloadViewModel: DownloadViewModel
private lateinit var resultViewModel: ResultViewModel private lateinit var resultViewModel: ResultViewModel
private lateinit var listAdapter : PlaylistAdapter private lateinit var listAdapter : PlaylistAdapter
@ -41,6 +45,10 @@ class SelectPlaylistItemsDialog(private val items: List<ResultItem?>, private va
private lateinit var fromTextInput: TextInputLayout private lateinit var fromTextInput: TextInputLayout
private lateinit var toTextInput: TextInputLayout private lateinit var toTextInput: TextInputLayout
private lateinit var items: List<ResultItem?>
private lateinit var type: DownloadViewModel.Type
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setStyle(STYLE_NORMAL, R.style.FullScreenDialogTheme) setStyle(STYLE_NORMAL, R.style.FullScreenDialogTheme)
@ -65,6 +73,21 @@ class SelectPlaylistItemsDialog(private val items: List<ResultItem?>, private va
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (Build.VERSION.SDK_INT >= 33){
arguments?.getParcelableArrayList("results", ResultItem::class.java)
}else{
arguments?.getParcelableArrayList<ResultItem>("results")
}.apply {
if (this == null){
dismiss()
return
}else{
items = this
}
}
type = arguments?.getSerializable("type") as DownloadViewModel.Type
dialog?.window?.setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) dialog?.window?.setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
toolbar = view.findViewById<MaterialToolbar>(R.id.toolbar) toolbar = view.findViewById<MaterialToolbar>(R.id.toolbar)
@ -156,11 +179,12 @@ class SelectPlaylistItemsDialog(private val items: List<ResultItem?>, private va
val checkedResultItems = items.filter { item -> checkedItems.contains(item!!.url) } val checkedResultItems = items.filter { item -> checkedItems.contains(item!!.url) }
if (checkedResultItems.size == 1){ if (checkedResultItems.size == 1){
val resultItem = resultViewModel.getItemByURL(checkedResultItems[0]!!.url) val resultItem = resultViewModel.getItemByURL(checkedResultItems[0]!!.url)
val bottomSheet = DownloadBottomSheetDialog(resultItem, type) withContext(Dispatchers.Main){
parentFragmentManager.addFragmentOnAttachListener { fragmentManager, fragment -> findNavController().navigate(R.id.action_selectPlaylistItemsDialog_to_downloadBottomSheetDialog, bundleOf(
dismiss() Pair("result", resultItem),
Pair("type", downloadViewModel.getDownloadType(type, resultItem.url)),
))
} }
bottomSheet.show(parentFragmentManager, "downloadSingleSheet")
}else{ }else{
val downloadItems = mutableListOf<DownloadItem>() val downloadItems = mutableListOf<DownloadItem>()
checkedResultItems.forEach { c -> checkedResultItems.forEach { c ->
@ -173,11 +197,11 @@ class SelectPlaylistItemsDialog(private val items: List<ResultItem?>, private va
downloadItems.add(i) downloadItems.add(i)
} }
val bottomSheet = DownloadMultipleBottomSheetDialog(downloadItems) withContext(Dispatchers.Main){
parentFragmentManager.addFragmentOnAttachListener { fragmentManager, fragment -> findNavController().navigate(R.id.action_selectPlaylistItemsDialog_to_downloadMultipleBottomSheetDialog, bundleOf(
dismiss() Pair("downloads", downloadItems)
))
} }
bottomSheet.show(parentFragmentManager, "downloadMultipleSheet")
} }
dismiss() dismiss()
@ -210,25 +234,6 @@ class SelectPlaylistItemsDialog(private val items: List<ResultItem?>, private va
} }
} }
override fun onCancel(dialog: DialogInterface) {
super.onCancel(dialog)
cleanup()
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
cleanup()
}
private fun cleanup(){
kotlin.runCatching {
val movedToMultipleOrSingleSheet = parentFragmentManager.fragments.map { it.tag }.contains("downloadMultipleSheet") || parentFragmentManager.fragments.map { it.tag }.contains("downloadSingleSheet")
if (activity is ShareActivity && !movedToMultipleOrSingleSheet){
(activity as ShareActivity).finish()
}
}
}
private fun checkRanges(start: String, end: String) : Boolean { private fun checkRanges(start: String, end: String) : Boolean {
return start.isNotBlank() && end.isNotBlank() return start.isNotBlank() && end.isNotBlank()
} }

View file

@ -94,7 +94,7 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
downloadViewModel.getActiveDownloads() downloadViewModel.getActiveDownloads()
}.forEach { }.forEach {
cancelItem(it.id.toInt()) cancelItem(it.id.toInt())
it.status = DownloadRepository.Status.Paused.toString() it.status = DownloadRepository.Status.ActivePaused.toString()
downloadViewModel.updateDownload(it) downloadViewModel.updateDownload(it)
} }
@ -111,7 +111,7 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
downloadViewModel.getActiveDownloads() downloadViewModel.getActiveDownloads()
} }
val toQueue = active.filter { it.status == DownloadRepository.Status.Paused.toString() }.toMutableList() val toQueue = active.filter { it.status == DownloadRepository.Status.ActivePaused.toString() }.toMutableList()
toQueue.forEach { toQueue.forEach {
it.status = DownloadRepository.Status.Queued.toString() it.status = DownloadRepository.Status.Queued.toString()
downloadViewModel.updateDownload(it) downloadViewModel.updateDownload(it)
@ -162,7 +162,7 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
if (it.size > 1){ if (it.size > 1){
pauseResume.visibility = View.VISIBLE pauseResume.visibility = View.VISIBLE
if (it.all { l -> l.status == DownloadRepository.Status.Paused.toString() }){ if (it.all { l -> l.status == DownloadRepository.Status.ActivePaused.toString() }){
pauseResume.text = requireContext().getString(R.string.resume) pauseResume.text = requireContext().getString(R.string.resume)
}else{ }else{
pauseResume.text = requireContext().getString(R.string.pause) pauseResume.text = requireContext().getString(R.string.pause)
@ -208,7 +208,7 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
ActiveDownloadAdapter.ActiveDownloadAction.Pause -> { ActiveDownloadAdapter.ActiveDownloadAction.Pause -> {
lifecycleScope.launch { lifecycleScope.launch {
cancelItem(itemID.toInt()) cancelItem(itemID.toInt())
item.status = DownloadRepository.Status.Paused.toString() item.status = DownloadRepository.Status.ActivePaused.toString()
withContext(Dispatchers.IO){ withContext(Dispatchers.IO){
downloadViewModel.updateDownload(item) downloadViewModel.updateDownload(item)
} }
@ -217,7 +217,7 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
} }
ActiveDownloadAdapter.ActiveDownloadAction.Resume -> { ActiveDownloadAdapter.ActiveDownloadAction.Resume -> {
lifecycleScope.launch { lifecycleScope.launch {
item.status = DownloadRepository.Status.Active.toString() item.status = DownloadRepository.Status.PausedReQueued.toString()
withContext(Dispatchers.IO){ withContext(Dispatchers.IO){
downloadViewModel.updateDownload(item) downloadViewModel.updateDownload(item)
} }

View file

@ -14,9 +14,11 @@ import android.widget.RelativeLayout
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
@ -131,12 +133,12 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
} }
}, },
longClickDownloadButton = { longClickDownloadButton = {
val sheet = DownloadBottomSheetDialog( findNavController().navigate(R.id.downloadBottomSheetDialog, bundleOf(
currentDownloadItem = it, Pair("downloadItem", it),
result = downloadViewModel.createResultItemFromDownload(it), Pair("result", downloadViewModel.createResultItemFromDownload(it)),
type = it.type Pair("type", it.type)
)
) )
sheet.show(parentFragmentManager, "downloadSingleSheet")
}, },
scheduleButtonClick = {} scheduleButtonClick = {}
) )

View file

@ -10,6 +10,8 @@ import android.widget.Toast
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import androidx.work.WorkManager import androidx.work.WorkManager

View file

@ -15,6 +15,7 @@ import android.widget.AdapterView.OnItemClickListener
import android.widget.RelativeLayout import android.widget.RelativeLayout
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -134,12 +135,11 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
} }
}, },
longClickDownloadButton = { longClickDownloadButton = {
val sheet = DownloadBottomSheetDialog( findNavController().navigate(R.id.downloadBottomSheetDialog, bundleOf(
currentDownloadItem = it, Pair("downloadItem", it),
result = downloadViewModel.createResultItemFromDownload(it), Pair("result", downloadViewModel.createResultItemFromDownload(it)),
type = it.type Pair("type", it.type)
) ))
sheet.show(parentFragmentManager, "downloadSingleSheet")
}, },
scheduleButtonClick = {} scheduleButtonClick = {}
) )

View file

@ -27,6 +27,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
import androidx.core.view.forEach import androidx.core.view.forEach
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
@ -411,11 +412,10 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
historyViewModel.delete(it, false) historyViewModel.delete(it, false)
}, },
redownloadShowDownloadCard = { redownloadShowDownloadCard = {
val sheet = DownloadBottomSheetDialog( findNavController().navigate(R.id.downloadBottomSheetDialog, bundleOf(
result = downloadViewModel.createResultItemFromHistory(it), Pair("result", downloadViewModel.createResultItemFromHistory(it)),
type = it.type Pair("type", it.type),
) ))
sheet.show(parentFragmentManager, "downloadSingleSheet")
} }
) )
} }
@ -547,10 +547,10 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
ItemTouchHelper.RIGHT -> { ItemTouchHelper.RIGHT -> {
val item = historyList!![position]!! val item = historyList!![position]!!
historyAdapter!!.notifyItemChanged(position) historyAdapter!!.notifyItemChanged(position)
val sheet = DownloadBottomSheetDialog(type = item.type, findNavController().navigate(R.id.downloadBottomSheetDialog, bundleOf(
result = downloadViewModel.createResultItemFromHistory(item) Pair("result", downloadViewModel.createResultItemFromHistory(item)),
) Pair("type", item.type)
sheet.show(parentFragmentManager, "downloadSingleSheet") ))
} }
} }
} }

View file

@ -16,9 +16,11 @@ import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
@ -154,12 +156,11 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
} }
}, },
longClickDownloadButton = { longClickDownloadButton = {
val sheet = DownloadBottomSheetDialog( findNavController().navigate(R.id.downloadBottomSheetDialog, bundleOf(
currentDownloadItem = it, Pair("downloadItem", it),
result = downloadViewModel.createResultItemFromDownload(it), Pair("result", downloadViewModel.createResultItemFromDownload(it)),
type = it.type Pair("type", it.type)
) ))
sheet.show(parentFragmentManager, "downloadSingleSheet")
}, },
scheduleButtonClick = {downloadItem -> scheduleButtonClick = {downloadItem ->
UiUtil.showDatePicker(parentFragmentManager) { UiUtil.showDatePicker(parentFragmentManager) {

View file

@ -14,9 +14,11 @@ import android.widget.RelativeLayout
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
@ -131,12 +133,11 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis
} }
}, },
longClickDownloadButton = { it: DownloadItem -> longClickDownloadButton = { it: DownloadItem ->
val sheet = DownloadBottomSheetDialog( findNavController().navigate(R.id.downloadBottomSheetDialog, bundleOf(
currentDownloadItem = it, Pair("downloadItem", it),
result = downloadViewModel.createResultItemFromDownload(it), Pair("result", downloadViewModel.createResultItemFromDownload(it)),
type = it.type Pair("type", it.type),
) ))
sheet.show(parentFragmentManager, "downloadSingleSheet")
}, },
scheduleButtonClick = {} scheduleButtonClick = {}
) )

View file

@ -69,7 +69,6 @@ class DownloadLogFragment : Fragment() {
} }
content = view.findViewById(R.id.content) content = view.findViewById(R.id.content)
content.movementMethod = ScrollingMovementMethod()
content.setTextIsSelectable(true) content.setTextIsSelectable(true)
contentScrollView = view.findViewById(R.id.content_scrollview) contentScrollView = view.findViewById(R.id.content_scrollview)
val bottomAppBar = view.findViewById<BottomAppBar>(R.id.bottomAppBar) val bottomAppBar = view.findViewById<BottomAppBar>(R.id.bottomAppBar)
@ -89,7 +88,7 @@ class DownloadLogFragment : Fragment() {
} }
val id = arguments?.getLong("logID") val id = arguments?.getLong("logID")
if (id == null) { if (id == null || id == 0L) {
mainActivity.onBackPressedDispatcher.onBackPressed() mainActivity.onBackPressedDispatcher.onBackPressed()
} }
@ -155,19 +154,11 @@ class DownloadLogFragment : Fragment() {
true true
} }
// contentScrollView.setOnScrollChangeListener { view, sx, sy, osx, osy -> contentScrollView.setOnScrollChangeListener { view, sx, sy, osx, osy ->
// if (sy < osy){ if (sy < osy){
// if (contentScrollView.canScrollVertically(1)){ bottomAppBar?.menu?.get(1)?.isVisible = contentScrollView.canScrollVertically(1)
// shouldScroll = false }
// content.movementMethod = LinkMovementMethod.getInstance() }
// bottomAppBar?.menu?.get(1)?.isVisible = true
// }else{
// shouldScroll = true
// content.movementMethod = ScrollingMovementMethod()
// bottomAppBar?.menu?.get(1)?.isVisible = false
// }
// }
// }
logViewModel.getLogFlowByID(id!!).observe(viewLifecycleOwner){logItem -> logViewModel.getLogFlowByID(id!!).observe(viewLifecycleOwner){logItem ->

View file

@ -57,6 +57,7 @@ class NotificationUtil(var context: Context) {
val importance = NotificationManager.IMPORTANCE_LOW val importance = NotificationManager.IMPORTANCE_LOW
var channel = NotificationChannel(DOWNLOAD_WORKER_CHANNEL_ID, name, importance) var channel = NotificationChannel(DOWNLOAD_WORKER_CHANNEL_ID, name, importance)
channel.description = description channel.description = description
channel.setSound(null, null)
notificationManager.createNotificationChannel(channel) notificationManager.createNotificationChannel(channel)
//gui downloads //gui downloads

View file

@ -611,7 +611,7 @@ object UiUtil {
if (item.format.format_note == "?" || item.format.format_note == "") formatNote!!.visibility = if (item.format.format_note == "?" || item.format.format_note == "") formatNote!!.visibility =
View.GONE View.GONE
else formatNote!!.text = item.format.format_note.uppercase() else formatNote!!.text = item.format.format_note
if (item.format.container != "") { if (item.format.container != "") {
container!!.text = if (file.exists()) file.extension.uppercase() else item.format.container.uppercase() container!!.text = if (file.exists()) file.extension.uppercase() else item.format.container.uppercase()

View file

@ -30,8 +30,6 @@ import com.yausername.youtubedl_android.YoutubeDL.UpdateStatus
import io.noties.markwon.AbstractMarkwonPlugin import io.noties.markwon.AbstractMarkwonPlugin
import io.noties.markwon.Markwon import io.noties.markwon.Markwon
import io.noties.markwon.MarkwonConfiguration import io.noties.markwon.MarkwonConfiguration
import io.noties.markwon.MarkwonSpansFactory
import io.noties.markwon.core.MarkwonTheme
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File
@ -94,7 +92,7 @@ class UpdateUtil(var context: Context) {
.setTitle(v.tag_name) .setTitle(v.tag_name)
.setMessage(v.body) .setMessage(v.body)
.setIcon(R.drawable.ic_update_app) .setIcon(R.drawable.ic_update_app)
.setNeutralButton(R.string.skip){ d: DialogInterface?, _:Int -> .setNeutralButton(R.string.ignore){ d: DialogInterface?, _:Int ->
skippedVersions.add(v.tag_name) skippedVersions.add(v.tag_name)
sharedPreferences.edit().putString("skip_updates", skippedVersions.joinToString(",")).apply() sharedPreferences.edit().putString("skip_updates", skippedVersions.joinToString(",")).apply()
d?.dismiss() d?.dismiss()

View file

@ -259,7 +259,10 @@ class DownloadWorker(
}else{ }else{
logString.append("${it.message}\n") logString.append("${it.message}\n")
logItem.content = logString.toString() logItem.content = logString.toString()
logRepo.insert(logItem) val logID = withContext(Dispatchers.IO){
logRepo.insert(logItem)
}
downloadItem.logID = logID
} }
} }
@ -277,8 +280,8 @@ class DownloadWorker(
} }
notificationUtil.createDownloadErrored( notificationUtil.createDownloadErrored(
downloadItem.title, it.message, downloadItem.title.ifEmpty { downloadItem.url }, it.message,
if (logDownloads) logItem.id else null, downloadItem.logID,
NotificationUtil.DOWNLOAD_FINISHED_CHANNEL_ID NotificationUtil.DOWNLOAD_FINISHED_CHANNEL_ID
) )

View file

@ -77,7 +77,15 @@ class TerminalDownloadWorker(
} }
} }
request.addOption("-P", FileUtil.getDefaultCommandPath() + "/" + itemId) val commandPath = sharedPreferences.getString("command_path", FileUtil.getDefaultCommandPath())!!
val noCache = !sharedPreferences.getBoolean("cache_downloads", true) && File(FileUtil.formatPath(commandPath)).canWrite()
if (!noCache){
request.addOption("-P", FileUtil.getCachePath(context) + "/TERMINAL/" + itemId)
}else if (!request.hasOption("-P")){
request.addOption("-P", FileUtil.formatPath(commandPath))
}
val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !sharedPreferences.getBoolean("incognito", false) val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !sharedPreferences.getBoolean("incognito", false)
@ -120,16 +128,18 @@ class TerminalDownloadWorker(
} }
}.onSuccess { }.onSuccess {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
//move file from internal to set download directory if(!noCache){
try { //move file from internal to set download directory
FileUtil.moveFile(File(FileUtil.getDefaultCommandPath() + "/" + itemId),context, downloadLocation!!, false){ p -> try {
setProgressAsync(workDataOf("progress" to p)) FileUtil.moveFile(File(FileUtil.getCachePath(context) + "/TERMINAL/" + itemId),context, downloadLocation!!, false){ p ->
setProgressAsync(workDataOf("progress" to p))
}
}catch (e: Exception){
e.printStackTrace()
handler.postDelayed({
Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
}, 1000)
} }
}catch (e: Exception){
e.printStackTrace()
handler.postDelayed({
Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
}, 1000)
} }
} }

View file

@ -1,8 +1,21 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:context=".receiver.ShareActivity" tools:context=".receiver.ShareActivity"
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/frame_layout"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -27,6 +27,12 @@
<action <action
android:id="@+id/action_homeFragment_to_resultCardDetailsDialog" android:id="@+id/action_homeFragment_to_resultCardDetailsDialog"
app:destination="@id/resultCardDetailsDialog" /> app:destination="@id/resultCardDetailsDialog" />
<action
android:id="@+id/action_homeFragment_to_downloadBottomSheetDialog"
app:destination="@id/downloadBottomSheetDialog" />
<action
android:id="@+id/action_homeFragment_to_downloadMultipleBottomSheetDialog2"
app:destination="@id/downloadMultipleBottomSheetDialog2" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/historyFragment" android:id="@+id/historyFragment"
@ -96,7 +102,7 @@
android:id="@+id/downloadLogFragment" android:id="@+id/downloadLogFragment"
android:name="com.deniscerri.ytdlnis.ui.more.downloadLogs.DownloadLogFragment" android:name="com.deniscerri.ytdlnis.ui.more.downloadLogs.DownloadLogFragment"
android:label="DownloadLogFragment" /> android:label="DownloadLogFragment" />
<fragment <dialog
android:id="@+id/resultCardDetailsDialog" android:id="@+id/resultCardDetailsDialog"
android:name="com.deniscerri.ytdlnis.ui.downloadcard.ResultCardDetailsDialog" android:name="com.deniscerri.ytdlnis.ui.downloadcard.ResultCardDetailsDialog"
android:label="ResultCardDetailsDialog" /> android:label="ResultCardDetailsDialog" />
@ -142,4 +148,12 @@
android:name="com.deniscerri.ytdlnis.ui.more.settings.GeneralSettingsFragment" android:name="com.deniscerri.ytdlnis.ui.more.settings.GeneralSettingsFragment"
android:label="AppearanceSettingsFragment" /> android:label="AppearanceSettingsFragment" />
</navigation> </navigation>
<dialog
android:id="@+id/downloadBottomSheetDialog"
android:name="com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog"
android:label="DownloadBottomSheetDialog" />
<dialog
android:id="@+id/downloadMultipleBottomSheetDialog2"
android:name="com.deniscerri.ytdlnis.ui.downloadcard.DownloadMultipleBottomSheetDialog"
android:label="DownloadMultipleBottomSheetDialog" />
</navigation> </navigation>

View file

@ -8,14 +8,27 @@
android:label="DownloadBottomSheetDialog" > android:label="DownloadBottomSheetDialog" >
<action <action
android:id="@+id/action_downloadBottomSheetDialog_to_selectPlaylistItemsDialog" android:id="@+id/action_downloadBottomSheetDialog_to_selectPlaylistItemsDialog"
app:destination="@id/selectPlaylistItemsDialog" /> app:destination="@id/selectPlaylistItemsDialog"
app:launchSingleTop="true"
app:popUpTo="@id/downloadBottomSheetDialog"
app:popUpToInclusive="true" />
</dialog> </dialog>
<dialog <dialog
android:id="@+id/selectPlaylistItemsDialog"
android:name="com.deniscerri.ytdlnis.ui.downloadcard.SelectPlaylistItemsDialog" android:name="com.deniscerri.ytdlnis.ui.downloadcard.SelectPlaylistItemsDialog"
android:label="SelectPlaylistItemsDialog"> android:label="SelectPlaylistItemsDialog">
<action <action
android:id="@+id/action_selectPlaylistItemsDialog_to_downloadMultipleBottomSheetDialog" android:id="@+id/action_selectPlaylistItemsDialog_to_downloadMultipleBottomSheetDialog"
app:destination="@id/downloadMultipleBottomSheetDialog" /> app:destination="@id/downloadMultipleBottomSheetDialog"
app:launchSingleTop="true"
app:popUpTo="@id/selectPlaylistItemsDialog"
app:popUpToInclusive="true" />
<action
android:id="@+id/action_selectPlaylistItemsDialog_to_downloadBottomSheetDialog"
app:destination="@id/downloadBottomSheetDialog"
app:launchSingleTop="true"
app:popUpTo="@id/selectPlaylistItemsDialog"
app:popUpToInclusive="true" />
</dialog> </dialog>
<dialog <dialog
android:id="@+id/downloadMultipleBottomSheetDialog" android:id="@+id/downloadMultipleBottomSheetDialog"

View file

@ -309,7 +309,7 @@
<string name="remember_download_type_summary">تعيين نوع التحميل اللاحق بناءً على آخر نوع تحميل تم الوصول اليه</string> <string name="remember_download_type_summary">تعيين نوع التحميل اللاحق بناءً على آخر نوع تحميل تم الوصول اليه</string>
<string name="schedule">توقيت</string> <string name="schedule">توقيت</string>
<string name="search_command_hint">بحث في قوالب الأوامر</string> <string name="search_command_hint">بحث في قوالب الأوامر</string>
<string name="skip">تخطي</string> <string name="ignore">تخطي</string>
<string name="license">الترخيص</string> <string name="license">الترخيص</string>
<string name="download_already_exists_summary">المضي بالتحميل على أي حال؟</string> <string name="download_already_exists_summary">المضي بالتحميل على أي حال؟</string>
<string name="modify_download_card">تحرير بطاقة التحميل</string> <string name="modify_download_card">تحرير بطاقة التحميل</string>

View file

@ -309,7 +309,7 @@
<string name="remember_download_type_summary">Selecciona el último tipo de la descarga abierto para el elemento actual</string> <string name="remember_download_type_summary">Selecciona el último tipo de la descarga abierto para el elemento actual</string>
<string name="schedule">Horario</string> <string name="schedule">Horario</string>
<string name="search_command_hint">Búsqueda a partir de comandos</string> <string name="search_command_hint">Búsqueda a partir de comandos</string>
<string name="skip">Omitir</string> <string name="ignore">Omitir</string>
<string name="license">Licencia</string> <string name="license">Licencia</string>
<string name="download_already_exists_summary">¿Todavía quieres descargar\?</string> <string name="download_already_exists_summary">¿Todavía quieres descargar\?</string>
<string name="modify_download_card">Modificar la tarjeta de descarga</string> <string name="modify_download_card">Modificar la tarjeta de descarga</string>

View file

@ -309,7 +309,7 @@
<string name="remember_download_type_summary">वर्तमान आइटम के लिए अंतिम बार खोला गया डाउनलोड टाईप चुनें</string> <string name="remember_download_type_summary">वर्तमान आइटम के लिए अंतिम बार खोला गया डाउनलोड टाईप चुनें</string>
<string name="schedule">शडिऊल</string> <string name="schedule">शडिऊल</string>
<string name="search_command_hint">कमांड में से खोजें</string> <string name="search_command_hint">कमांड में से खोजें</string>
<string name="skip">छोड़ें</string> <string name="ignore">छोड़ें</string>
<string name="license">लाइसेंस</string> <string name="license">लाइसेंस</string>
<string name="download_already_exists_summary">क्या आप अब भी डाउनलोड करना चाहते हैं\?</string> <string name="download_already_exists_summary">क्या आप अब भी डाउनलोड करना चाहते हैं\?</string>
<string name="modify_download_card">डाउनलोड कार्ड संशोधित करें</string> <string name="modify_download_card">डाउनलोड कार्ड संशोधित करें</string>

View file

@ -309,7 +309,7 @@
<string name="couldnt_parse_file">לא ניתן לנתח את הקובץ</string> <string name="couldnt_parse_file">לא ניתן לנתח את הקובץ</string>
<string name="schedule">תזמון</string> <string name="schedule">תזמון</string>
<string name="search_command_hint">חפש מתוך פקודות</string> <string name="search_command_hint">חפש מתוך פקודות</string>
<string name="skip">דילוג</string> <string name="ignore">דילוג</string>
<string name="license">רישיון</string> <string name="license">רישיון</string>
<string name="download_already_exists_summary">האם תרצה להמשיך להוריד\?</string> <string name="download_already_exists_summary">האם תרצה להמשיך להוריד\?</string>
<string name="modify_download_card">התאמת כרטיס הורדה</string> <string name="modify_download_card">התאמת כרטיס הורדה</string>

View file

@ -309,7 +309,7 @@
<string name="remember_download_type_summary">ਮੌਜੂਦਾ ਆਈਟਮ ਲਈ ਆਖਰੀ ਵਾਰ ਖੋਲ੍ਹੀ ਗਈ ਡਾਊਨਲੋਡ ਕਿਸਮ ਚੁਣੋ</string> <string name="remember_download_type_summary">ਮੌਜੂਦਾ ਆਈਟਮ ਲਈ ਆਖਰੀ ਵਾਰ ਖੋਲ੍ਹੀ ਗਈ ਡਾਊਨਲੋਡ ਕਿਸਮ ਚੁਣੋ</string>
<string name="schedule">ਸ਼ਡਿਊਲ</string> <string name="schedule">ਸ਼ਡਿਊਲ</string>
<string name="search_command_hint">ਕਮਾਂਡਾਂ ਤੋਂ ਖੋਜ ਕਰੋ</string> <string name="search_command_hint">ਕਮਾਂਡਾਂ ਤੋਂ ਖੋਜ ਕਰੋ</string>
<string name="skip">ਸਕਿੱਪ ਕਰੋ</string> <string name="ignore">ਸਕਿੱਪ ਕਰੋ</string>
<string name="license">ਲਾਈਸੈਂਸ</string> <string name="license">ਲਾਈਸੈਂਸ</string>
<string name="download_already_exists_summary">ਕੀ ਤੁਸੀਂ ਅਜੇ ਵੀ ਡਾਊਨਲੋਡ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ\?</string> <string name="download_already_exists_summary">ਕੀ ਤੁਸੀਂ ਅਜੇ ਵੀ ਡਾਊਨਲੋਡ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ\?</string>
<string name="modify_download_card">ਡਾਊਨਲੋਡ ਕਾਰਡ ਨੂੰ ਸੋਧੋ</string> <string name="modify_download_card">ਡਾਊਨਲੋਡ ਕਾਰਡ ਨੂੰ ਸੋਧੋ</string>

View file

@ -309,7 +309,7 @@
<string name="remember_download_type_summary">Selecione o último tipo de download aberto para o item atual</string> <string name="remember_download_type_summary">Selecione o último tipo de download aberto para o item atual</string>
<string name="schedule">Agendar</string> <string name="schedule">Agendar</string>
<string name="search_command_hint">Pesquisar a partir dos comandos</string> <string name="search_command_hint">Pesquisar a partir dos comandos</string>
<string name="skip">Pular</string> <string name="ignore">Pular</string>
<string name="license">Licença</string> <string name="license">Licença</string>
<string name="download_already_exists_summary">Você ainda quer fazer o download\?</string> <string name="download_already_exists_summary">Você ainda quer fazer o download\?</string>
<string name="modify_download_card">Modificar cartão de download</string> <string name="modify_download_card">Modificar cartão de download</string>

View file

@ -309,7 +309,7 @@
<string name="remember_download_type_summary">Selecionar o último tipo de download aberto para o item atual</string> <string name="remember_download_type_summary">Selecionar o último tipo de download aberto para o item atual</string>
<string name="schedule">Agendar</string> <string name="schedule">Agendar</string>
<string name="search_command_hint">Pesquisar a partir de comandos</string> <string name="search_command_hint">Pesquisar a partir de comandos</string>
<string name="skip">Saltar</string> <string name="ignore">Saltar</string>
<string name="license">Licença</string> <string name="license">Licença</string>
<string name="download_already_exists_summary">Ainda queres descarregar\?</string> <string name="download_already_exists_summary">Ainda queres descarregar\?</string>
<string name="modify_download_card">Modificar o cartão de descarregamento</string> <string name="modify_download_card">Modificar o cartão de descarregamento</string>

View file

@ -307,7 +307,7 @@
<string name="schedule">Расписание</string> <string name="schedule">Расписание</string>
<string name="user_agent_header">Заголовок User-Agent</string> <string name="user_agent_header">Заголовок User-Agent</string>
<string name="search_command_hint">Поиск по командам</string> <string name="search_command_hint">Поиск по командам</string>
<string name="skip">Пропустить</string> <string name="ignore">Пропустить</string>
<string name="license">Лицензия</string> <string name="license">Лицензия</string>
<string name="download_already_exists_summary">Вы все еще хотите скачать\?</string> <string name="download_already_exists_summary">Вы все еще хотите скачать\?</string>
<string name="modify_download_card">Изменить карту загрузки</string> <string name="modify_download_card">Изменить карту загрузки</string>

View file

@ -310,7 +310,7 @@
<string name="remember_download_type_summary">Zgjidh llojin e fundit të shkarkimit për shkarkimin aktual</string> <string name="remember_download_type_summary">Zgjidh llojin e fundit të shkarkimit për shkarkimin aktual</string>
<string name="length">Gjatësia</string> <string name="length">Gjatësia</string>
<string name="schedule">Orar</string> <string name="schedule">Orar</string>
<string name="skip">Injoro</string> <string name="ignore">Injoro</string>
<string name="license">Liçensa</string> <string name="license">Liçensa</string>
<string name="download_already_exists_summary">Do sërisht ta shkarkosh\?</string> <string name="download_already_exists_summary">Do sërisht ta shkarkosh\?</string>
<string name="modify_download_card">Modifiko Kartën e Shkarkimit</string> <string name="modify_download_card">Modifiko Kartën e Shkarkimit</string>

View file

@ -307,7 +307,7 @@
<string name="schedule">Розклад</string> <string name="schedule">Розклад</string>
<string name="user_agent_header">Заголовок User-Agent</string> <string name="user_agent_header">Заголовок User-Agent</string>
<string name="search_command_hint">Пошук з команд</string> <string name="search_command_hint">Пошук з команд</string>
<string name="skip">Пропустити</string> <string name="ignore">Пропустити</string>
<string name="license">Ліцензія</string> <string name="license">Ліцензія</string>
<string name="download_already_exists_summary">Ви все ще хочете завантажити\?</string> <string name="download_already_exists_summary">Ви все ще хочете завантажити\?</string>
<string name="modify_download_card">Змінити картку завантаження</string> <string name="modify_download_card">Змінити картку завантаження</string>

View file

@ -307,7 +307,7 @@
<string name="schedule">Lịch trình</string> <string name="schedule">Lịch trình</string>
<string name="user_agent_header">Tiêu đề tác nhân người dùng</string> <string name="user_agent_header">Tiêu đề tác nhân người dùng</string>
<string name="search_command_hint">Tìm kiếm từ lệnh</string> <string name="search_command_hint">Tìm kiếm từ lệnh</string>
<string name="skip">Bỏ qua</string> <string name="ignore">Bỏ qua</string>
<string name="license">Giấy phép</string> <string name="license">Giấy phép</string>
<string name="download_already_exists_summary">Bạn vẫn muốn tải xuống\?</string> <string name="download_already_exists_summary">Bạn vẫn muốn tải xuống\?</string>
<string name="modify_download_card">Sửa đổi thẻ tải xuống</string> <string name="modify_download_card">Sửa đổi thẻ tải xuống</string>

View file

@ -309,7 +309,7 @@
<string name="remember_download_type_summary">为当前项目选择上次打开的下载类型</string> <string name="remember_download_type_summary">为当前项目选择上次打开的下载类型</string>
<string name="schedule">安排时间</string> <string name="schedule">安排时间</string>
<string name="search_command_hint">搜索命令行模板</string> <string name="search_command_hint">搜索命令行模板</string>
<string name="skip">跳过</string> <string name="ignore">跳过</string>
<string name="license">许可证</string> <string name="license">许可证</string>
<string name="download_already_exists_summary">你仍要下载吗?</string> <string name="download_already_exists_summary">你仍要下载吗?</string>
<string name="modify_download_card">修改下载卡片</string> <string name="modify_download_card">修改下载卡片</string>

View file

@ -309,7 +309,7 @@
<string name="remember_download_type_summary">使用上次的下載類型</string> <string name="remember_download_type_summary">使用上次的下載類型</string>
<string name="schedule">排程設定</string> <string name="schedule">排程設定</string>
<string name="search_command_hint">指令搜尋</string> <string name="search_command_hint">指令搜尋</string>
<string name="skip">略過此項</string> <string name="ignore">略過此項</string>
<string name="license">使用授權</string> <string name="license">使用授權</string>
<string name="download_already_exists_summary">是否繼續進行下載?</string> <string name="download_already_exists_summary">是否繼續進行下載?</string>
<string name="modify_download_card">編輯下載資訊卡片</string> <string name="modify_download_card">編輯下載資訊卡片</string>

View file

@ -293,7 +293,7 @@
<string name="extra_command">Extra Command</string> <string name="extra_command">Extra Command</string>
<string name="use_cookies">Use Cookies</string> <string name="use_cookies">Use Cookies</string>
<string name="embed_metadata">Embed Metadata</string> <string name="embed_metadata">Embed Metadata</string>
<string name="embed_metadata_summary">Embed and parse metadata to the audio file</string> <string name="embed_metadata_summary">Parse and embed metadata to the audio file</string>
<string name="update_formats_background">Update Formats in Background</string> <string name="update_formats_background">Update Formats in Background</string>
<string name="auto_update_ytdlp">Auto-Update yt-dlp</string> <string name="auto_update_ytdlp">Auto-Update yt-dlp</string>
<string name="use_sponsorblock">Use SponsorBlock</string> <string name="use_sponsorblock">Use SponsorBlock</string>
@ -316,7 +316,7 @@
<string name="license">License</string> <string name="license">License</string>
<string name="download_already_exists_summary">Do you still want to download?</string> <string name="download_already_exists_summary">Do you still want to download?</string>
<string name="schedule">Schedule</string> <string name="schedule">Schedule</string>
<string name="skip">Skip</string> <string name="ignore">Ignore</string>
<string name="modify_download_card">Modify Download Card</string> <string name="modify_download_card">Modify Download Card</string>
<string name="export_file">Export File</string> <string name="export_file">Export File</string>
</resources> </resources>

View file

@ -39,6 +39,7 @@ plugins {
id 'com.android.library' version '8.1.1' apply false id 'com.android.library' version '8.1.1' apply false
id 'org.jetbrains.kotlin.android' version '1.8.10' apply false id 'org.jetbrains.kotlin.android' version '1.8.10' apply false
id "org.jetbrains.kotlin.plugin.serialization" version "1.8.10" apply false id "org.jetbrains.kotlin.plugin.serialization" version "1.8.10" apply false
id "org.jetbrains.kotlin.plugin.parcelize" version "1.8.10" apply false
id 'com.android.test' version '8.1.1' apply false id 'com.android.test' version '8.1.1' apply false
id 'com.google.devtools.ksp' version '1.8.10-1.0.9' apply false id 'com.google.devtools.ksp' version '1.8.10-1.0.9' apply false
} }