- added hungarian
- added serbian
- added ability to enable/disable swipe gestures on any screen
- added ability to choose whether extra command applies to audio video or both
- hide search providers if the user has typed out an url in the searchview
This commit is contained in:
zaednasr 2023-11-12 15:59:55 +01:00
parent 715120dd59
commit 17d6a87e49
No known key found for this signature in database
GPG key ID: 92B1DE23AD3D0E9E
54 changed files with 1304 additions and 389 deletions

View file

@ -0,0 +1,572 @@
{
"formatVersion": 1,
"database": {
"version": 12,
"identityHash": "d56ec0fdb461aabd551c64b826f0f8ea",
"entities": [
{
"tableName": "results",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `website` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `formats` TEXT NOT NULL, `urls` TEXT NOT NULL DEFAULT '', `chapters` TEXT, `creationTime` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "author",
"columnName": "author",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "duration",
"columnName": "duration",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "thumb",
"columnName": "thumb",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "website",
"columnName": "website",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "playlistTitle",
"columnName": "playlistTitle",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "formats",
"columnName": "formats",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "urls",
"columnName": "urls",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "chapters",
"columnName": "chapters",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "creationTime",
"columnName": "creationTime",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "history",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `type` TEXT NOT NULL, `time` INTEGER NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `format` TEXT NOT NULL, `downloadId` INTEGER NOT NULL DEFAULT 0, `command` TEXT NOT NULL DEFAULT '')",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "author",
"columnName": "author",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "duration",
"columnName": "duration",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "thumb",
"columnName": "thumb",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "time",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "downloadPath",
"columnName": "downloadPath",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "website",
"columnName": "website",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "format",
"columnName": "format",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "downloadId",
"columnName": "downloadId",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "command",
"columnName": "command",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "downloads",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `thumb` TEXT NOT NULL, `duration` TEXT NOT NULL, `type` TEXT NOT NULL, `format` TEXT NOT NULL, `container` TEXT NOT NULL DEFAULT 'Default', `downloadSections` TEXT NOT NULL DEFAULT '', `allFormats` TEXT NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `downloadSize` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `audioPreferences` TEXT NOT NULL, `videoPreferences` TEXT NOT NULL, `extraCommands` TEXT NOT NULL DEFAULT '', `customFileNameTemplate` TEXT NOT NULL, `SaveThumb` INTEGER NOT NULL, `status` TEXT NOT NULL DEFAULT 'Queued', `downloadStartTime` INTEGER NOT NULL DEFAULT 0, `logID` INTEGER)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "author",
"columnName": "author",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "thumb",
"columnName": "thumb",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "duration",
"columnName": "duration",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "format",
"columnName": "format",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "container",
"columnName": "container",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'Default'"
},
{
"fieldPath": "downloadSections",
"columnName": "downloadSections",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "allFormats",
"columnName": "allFormats",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "downloadPath",
"columnName": "downloadPath",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "website",
"columnName": "website",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "downloadSize",
"columnName": "downloadSize",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "playlistTitle",
"columnName": "playlistTitle",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "audioPreferences",
"columnName": "audioPreferences",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "videoPreferences",
"columnName": "videoPreferences",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "extraCommands",
"columnName": "extraCommands",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "customFileNameTemplate",
"columnName": "customFileNameTemplate",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "SaveThumb",
"columnName": "SaveThumb",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'Queued'"
},
{
"fieldPath": "downloadStartTime",
"columnName": "downloadStartTime",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "logID",
"columnName": "logID",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "commandTemplates",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `useAsExtraCommand` INTEGER NOT NULL DEFAULT 0, `useAsExtraCommandAudio` INTEGER NOT NULL DEFAULT 1, `useAsExtraCommandVideo` INTEGER NOT NULL DEFAULT 1)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "content",
"columnName": "content",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "useAsExtraCommand",
"columnName": "useAsExtraCommand",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "useAsExtraCommandAudio",
"columnName": "useAsExtraCommandAudio",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "1"
},
{
"fieldPath": "useAsExtraCommandVideo",
"columnName": "useAsExtraCommandVideo",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "1"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "searchHistory",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `query` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "query",
"columnName": "query",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "templateShortcuts",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `content` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "content",
"columnName": "content",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "cookies",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `content` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "content",
"columnName": "content",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "logs",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `format` TEXT NOT NULL, `downloadType` TEXT NOT NULL, `downloadTime` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "content",
"columnName": "content",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "format",
"columnName": "format",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "downloadType",
"columnName": "downloadType",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "downloadTime",
"columnName": "downloadTime",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "terminalDownloads",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `command` TEXT NOT NULL, `log` TEXT DEFAULT '')",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "command",
"columnName": "command",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "log",
"columnName": "log",
"affinity": "TEXT",
"notNull": false,
"defaultValue": "''"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd56ec0fdb461aabd551c64b826f0f8ea')"
]
}
}

View file

@ -59,6 +59,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import java.io.BufferedReader import java.io.BufferedReader
@ -146,14 +147,16 @@ class MainActivity : BaseActivity() {
} }
val activeDownloadsBadge = (navigationView as NavigationBarView).getOrCreateBadge(R.id.historyFragment) val activeDownloadsBadge = (navigationView as NavigationBarView).getOrCreateBadge(R.id.historyFragment)
downloadViewModel.activeDownloadsCount.observe(this){ lifecycleScope.launch {
if (it == 0) { downloadViewModel.activeDownloadsCount.collectLatest {
activeDownloadsBadge.isVisible = false if (it == 0) {
activeDownloadsBadge.clearNumber() activeDownloadsBadge.isVisible = false
} activeDownloadsBadge.clearNumber()
else { }
activeDownloadsBadge.isVisible = true else {
activeDownloadsBadge.number = it activeDownloadsBadge.isVisible = true
activeDownloadsBadge.number = it
}
} }
} }
} }

View file

@ -2,6 +2,11 @@ package com.deniscerri.ytdlnis.database
import android.content.Context import android.content.Context
import androidx.room.* import androidx.room.*
import androidx.room.migration.AutoMigrationSpec
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import androidx.work.impl.Migration_11_12
import androidx.work.impl.Migration_12_13
import com.deniscerri.ytdlnis.database.dao.* import com.deniscerri.ytdlnis.database.dao.*
import com.deniscerri.ytdlnis.database.models.* import com.deniscerri.ytdlnis.database.models.*
@ -18,7 +23,7 @@ import com.deniscerri.ytdlnis.database.models.*
LogItem::class, LogItem::class,
TerminalItem::class TerminalItem::class
], ],
version = 11, version = 12,
autoMigrations = [ autoMigrations = [
AutoMigration (from = 1, to = 2), AutoMigration (from = 1, to = 2),
AutoMigration (from = 2, to = 3), AutoMigration (from = 2, to = 3),
@ -29,7 +34,8 @@ import com.deniscerri.ytdlnis.database.models.*
AutoMigration (from = 7, to = 8), AutoMigration (from = 7, to = 8),
AutoMigration (from = 8, to = 9), AutoMigration (from = 8, to = 9),
AutoMigration (from = 9, to = 10), AutoMigration (from = 9, to = 10),
AutoMigration (from = 10, to = 11) AutoMigration (from = 10, to = 11),
AutoMigration (from = 11, to = 12)
] ]
) )
abstract class DBManager : RoomDatabase(){ abstract class DBManager : RoomDatabase(){
@ -53,15 +59,18 @@ abstract class DBManager : RoomDatabase(){
//if its not null return it, otherwise create db //if its not null return it, otherwise create db
fun getInstance(context: Context) : DBManager { fun getInstance(context: Context) : DBManager {
return instance ?: synchronized(this){ return instance ?: synchronized(this){
val dbInstance = Room.databaseBuilder( val dbInstance = Room.databaseBuilder(
context.applicationContext, context.applicationContext,
DBManager::class.java, DBManager::class.java,
"YTDLnisDatabase" "YTDLnisDatabase"
) )
.build() .build()
instance = dbInstance instance = dbInstance
dbInstance dbInstance
} }
} }
} }
} }

View file

@ -34,6 +34,12 @@ interface CommandTemplateDao {
@Query("SELECT content FROM commandTemplates WHERE useAsExtraCommand is 1") @Query("SELECT content FROM commandTemplates WHERE useAsExtraCommand is 1")
fun getAllTemplatesAsExtraCommands() : List<String> fun getAllTemplatesAsExtraCommands() : List<String>
@Query("SELECT content FROM commandTemplates WHERE useAsExtraCommand is 1 AND useAsExtraCommandAudio = 1")
fun getAllTemplatesAsExtraCommandsForAudio() : List<String>
@Query("SELECT content FROM commandTemplates WHERE useAsExtraCommand is 1 AND useAsExtraCommandVideo = 1")
fun getAllTemplatesAsExtraCommandsForVideo() : List<String>
@Query("SELECT * FROM templateShortcuts ORDER BY id DESC") @Query("SELECT * FROM templateShortcuts ORDER BY id DESC")
fun getAllShortcutsFlow() : Flow<List<TemplateShortcut>> fun getAllShortcutsFlow() : Flow<List<TemplateShortcut>>

View file

@ -24,6 +24,9 @@ interface ResultDao {
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(item: ResultItem) : Long suspend fun insert(item: ResultItem) : Long
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertMultiple(items: List<ResultItem?>) : List<Long>
@Update(onConflict = OnConflictStrategy.REPLACE) @Update(onConflict = OnConflictStrategy.REPLACE)
suspend fun update(item: ResultItem) suspend fun update(item: ResultItem)

View file

@ -13,5 +13,9 @@ data class CommandTemplate(
var title: String, var title: String,
var content: String, var content: String,
@ColumnInfo(defaultValue = "0") @ColumnInfo(defaultValue = "0")
var useAsExtraCommand: Boolean var useAsExtraCommand: Boolean,
@ColumnInfo(defaultValue = "1")
var useAsExtraCommandAudio: Boolean,
@ColumnInfo(defaultValue = "1")
var useAsExtraCommandVideo: Boolean
) )

View file

@ -8,6 +8,7 @@ import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.DownloadItemSimple import com.deniscerri.ytdlnis.database.models.DownloadItemSimple
import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.FileUtil
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import java.io.File import java.io.File
@ -16,7 +17,7 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
config = PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 1), config = PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 1),
pagingSourceFactory = {downloadDao.getAllDownloads()} pagingSourceFactory = {downloadDao.getAllDownloads()}
) )
val activeDownloads : Flow<List<DownloadItem>> = downloadDao.getActiveDownloads() val activeDownloads : Flow<List<DownloadItem>> = downloadDao.getActiveDownloads().distinctUntilChanged()
val queuedDownloads : Pager<Int, DownloadItemSimple> = Pager( val queuedDownloads : Pager<Int, DownloadItemSimple> = Pager(
config = PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 1), config = PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 1),
pagingSourceFactory = {downloadDao.getQueuedDownloads()} pagingSourceFactory = {downloadDao.getQueuedDownloads()}

View file

@ -25,9 +25,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
val infoUtil = InfoUtil(context) val infoUtil = InfoUtil(context)
val items = infoUtil.getTrending() val items = infoUtil.getTrending()
itemCount.value = items.size itemCount.value = items.size
for (i in items){ resultDao.insertMultiple(items)
resultDao.insert(i!!)
}
} }
suspend fun search(inputQuery: String, resetResults: Boolean) : ArrayList<ResultItem?>{ suspend fun search(inputQuery: String, resetResults: Boolean) : ArrayList<ResultItem?>{
@ -35,9 +33,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
if (resetResults) deleteAll() if (resetResults) deleteAll()
val res = infoUtil.search(inputQuery) val res = infoUtil.search(inputQuery)
itemCount.value = res.size itemCount.value = res.size
res.forEach { resultDao.insertMultiple(res)
resultDao.insert(it!!)
}
return res return res
} }
@ -50,9 +46,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
}else{ }else{
v.forEach { it?.playlistTitle = "ytdlnis-Search" } v.forEach { it?.playlistTitle = "ytdlnis-Search" }
} }
v.forEach { resultDao.insertMultiple(v)
resultDao.insert(it!!)
}
return ArrayList(v) return ArrayList(v)
} }
@ -72,9 +66,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
nextPageToken = tmpToken nextPageToken = tmpToken
} while (true) } while (true)
itemCount.value = items.size itemCount.value = items.size
items.forEach { resultDao.insertMultiple(items.toList())
resultDao.insert(it!!)
}
return items return items
} }
@ -87,9 +79,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
}else{ }else{
items.forEach { it!!.playlistTitle = "ytdlnis-Search" } items.forEach { it!!.playlistTitle = "ytdlnis-Search" }
} }
items.forEach { resultDao.insertMultiple(items.toList())
resultDao.insert(it!!)
}
return items return items
} }

View file

@ -64,8 +64,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
private val infoUtil : InfoUtil private val infoUtil : InfoUtil
val allDownloads : Flow<PagingData<DownloadItem>> val allDownloads : Flow<PagingData<DownloadItem>>
val queuedDownloads : Flow<PagingData<DownloadItemSimple>> val queuedDownloads : Flow<PagingData<DownloadItemSimple>>
val activeDownloads : LiveData<List<DownloadItem>> val activeDownloads : Flow<List<DownloadItem>>
val activeDownloadsCount : LiveData<Int> val activeDownloadsCount : Flow<Int>
val cancelledDownloads : Flow<PagingData<DownloadItemSimple>> val cancelledDownloads : Flow<PagingData<DownloadItemSimple>>
val erroredDownloads : Flow<PagingData<DownloadItemSimple>> val erroredDownloads : Flow<PagingData<DownloadItemSimple>>
val savedDownloads : Flow<PagingData<DownloadItemSimple>> val savedDownloads : Flow<PagingData<DownloadItemSimple>>
@ -78,7 +78,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
private val formatIDPreference: List<String> private val formatIDPreference: List<String>
private val audioFormatIDPreference: List<String> private val audioFormatIDPreference: List<String>
private val resources : Resources private val resources : Resources
private var extraCommands: String = "" private var extraCommandsForAudio: String = ""
private var extraCommandsForVideo: String = ""
private var audioContainer: String? private var audioContainer: String?
private var videoContainer: String? private var videoContainer: String?
@ -123,14 +124,15 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
allDownloads = repository.allDownloads.flow allDownloads = repository.allDownloads.flow
queuedDownloads = repository.queuedDownloads.flow queuedDownloads = repository.queuedDownloads.flow
activeDownloads = repository.activeDownloads.asLiveData() activeDownloads = repository.activeDownloads
activeDownloadsCount = repository.activeDownloadsCount.asLiveData() activeDownloadsCount = repository.activeDownloadsCount
savedDownloads = repository.savedDownloads.flow savedDownloads = repository.savedDownloads.flow
cancelledDownloads = repository.cancelledDownloads.flow cancelledDownloads = repository.cancelledDownloads.flow
erroredDownloads = repository.erroredDownloads.flow erroredDownloads = repository.erroredDownloads.flow
viewModelScope.launch(Dispatchers.IO){ viewModelScope.launch(Dispatchers.IO){
if (sharedPreferences.getBoolean("use_extra_commands", false)){ if (sharedPreferences.getBoolean("use_extra_commands", false)){
extraCommands = commandTemplateDao.getAllTemplatesAsExtraCommands().joinToString(" ") extraCommandsForAudio = commandTemplateDao.getAllTemplatesAsExtraCommandsForAudio().joinToString(" ")
extraCommandsForVideo = commandTemplateDao.getAllTemplatesAsExtraCommandsForVideo().joinToString(" ")
} }
} }
@ -283,6 +285,12 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
audioFormatIDs = preferredAudioFormats audioFormatIDs = preferredAudioFormats
) )
val extraCommands = when(type){
Type.audio -> extraCommandsForAudio
Type.video -> extraCommandsForVideo
else -> ""
}
return DownloadItem(0, return DownloadItem(0,
resultItem.url, resultItem.url,
resultItem.title, resultItem.title,
@ -404,6 +412,12 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
val sponsorblock = sharedPreferences.getStringSet("sponsorblock_filters", emptySet()) val sponsorblock = sharedPreferences.getStringSet("sponsorblock_filters", emptySet())
val extraCommands = when(historyItem.type){
Type.audio -> extraCommandsForAudio
Type.video -> extraCommandsForVideo
else -> ""
}
val audioPreferences = AudioPreferences(embedThumb, false, ArrayList(sponsorblock!!)) val audioPreferences = AudioPreferences(embedThumb, false, ArrayList(sponsorblock!!))
val videoPreferences = VideoPreferences(embedSubs, addChapters, false, ArrayList(sponsorblock), saveSubs) val videoPreferences = VideoPreferences(embedSubs, addChapters, false, ArrayList(sponsorblock), saveSubs)
val downloadPath = File(historyItem.downloadPath) val downloadPath = File(historyItem.downloadPath)
@ -467,9 +481,9 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
else -> { else -> {
val lastUsedCommandTemplate = sharedPreferences.getString("lastCommandTemplateUsed", "")!! val lastUsedCommandTemplate = sharedPreferences.getString("lastCommandTemplateUsed", "")!!
val c = if (lastUsedCommandTemplate.isBlank()){ val c = if (lastUsedCommandTemplate.isBlank()){
commandTemplateDao.getFirst() ?: CommandTemplate(0,"","", false) commandTemplateDao.getFirst() ?: CommandTemplate(0,"","", useAsExtraCommand = false, useAsExtraCommandAudio = false, useAsExtraCommandVideo = false)
}else{ }else{
commandTemplateDao.getTemplateByContent(lastUsedCommandTemplate) ?: CommandTemplate(0, "", lastUsedCommandTemplate, false) commandTemplateDao.getTemplateByContent(lastUsedCommandTemplate) ?: CommandTemplate(0, "", lastUsedCommandTemplate, useAsExtraCommand = false, useAsExtraCommandAudio = false, useAsExtraCommandVideo = false)
} }
return generateCommandFormat(c) return generateCommandFormat(c)
} }

View file

@ -4,19 +4,29 @@ import android.app.Application
import android.content.SharedPreferences import android.content.SharedPreferences
import android.util.Log import android.util.Log
import android.util.Patterns import android.util.Patterns
import android.view.View
import androidx.compose.material3.formatWithSkeleton
import androidx.core.os.bundleOf
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.deniscerri.ytdlnis.App import com.deniscerri.ytdlnis.App
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.models.Format
import com.deniscerri.ytdlnis.database.models.ResultItem import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.models.SearchHistoryItem import com.deniscerri.ytdlnis.database.models.SearchHistoryItem
import com.deniscerri.ytdlnis.database.repository.ResultRepository import com.deniscerri.ytdlnis.database.repository.ResultRepository
import com.deniscerri.ytdlnis.database.repository.SearchHistoryRepository import com.deniscerri.ytdlnis.database.repository.SearchHistoryRepository
import com.deniscerri.ytdlnis.receiver.ShareActivity
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadAudioFragment
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadVideoFragment
import com.deniscerri.ytdlnis.util.InfoUtil
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -28,6 +38,7 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
val repository : ResultRepository val repository : ResultRepository
private val searchHistoryRepository : SearchHistoryRepository private val searchHistoryRepository : SearchHistoryRepository
val items : LiveData<List<ResultItem>> val items : LiveData<List<ResultItem>>
private val infoUtil: InfoUtil
data class ResultsUiState( data class ResultsUiState(
var processing: Boolean, var processing: Boolean,
var errorMessage: Pair<Int, String>?, var errorMessage: Pair<Int, String>?,
@ -51,6 +62,7 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
searchHistoryRepository = SearchHistoryRepository(DBManager.getInstance(application).searchHistoryDao) searchHistoryRepository = SearchHistoryRepository(DBManager.getInstance(application).searchHistoryDao)
items = repository.allResults.asLiveData() items = repository.allResults.asLiveData()
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application) sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application)
infoUtil = InfoUtil(application)
} }
fun checkTrending() = viewModelScope.launch(Dispatchers.IO){ fun checkTrending() = viewModelScope.launch(Dispatchers.IO){
@ -171,4 +183,62 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
repository.delete(it) repository.delete(it)
} }
} }
val updatingData: MutableStateFlow<Boolean> = MutableStateFlow(false)
var updateResultData: MutableStateFlow<List<ResultItem?>?> = MutableStateFlow(null)
private var updateResultDataJob : Job? = null
val updatingFormats: MutableStateFlow<Boolean> = MutableStateFlow(false)
var updateFormatsResultData: MutableStateFlow<MutableList<Format>?> = MutableStateFlow(null)
private var updateFormatsResultDataJob: Job? = null
suspend fun updateItemData(res: ResultItem){
if (updateResultDataJob == null || updateResultDataJob?.isCancelled == true || updateResultDataJob?.isCompleted == true){
updateResultDataJob = viewModelScope.launch(Dispatchers.IO) {
kotlin.runCatching {
updatingData.emit(true)
val result = parseQueries(listOf(res.url))
updatingData.emit(false)
updateResultData.emit(result)
}.onFailure {
updatingData.emit(false)
updateResultData.emit(mutableListOf())
}
}
}
updateResultDataJob?.start()
}
suspend fun cancelUpdateItemData(){
updatingData.emit(false)
updateResultData.emit(null)
updateResultDataJob?.cancel()
}
suspend fun cancelUpdateFormatsItemData(){
updatingFormats.emit(false)
updateFormatsResultData.emit(null)
updateFormatsResultDataJob?.cancel()
}
suspend fun updateFormatItemData(result: ResultItem){
updateFormatsResultDataJob = viewModelScope.launch(Dispatchers.IO) {
kotlin.runCatching {
updatingFormats.emit(true)
val formats = infoUtil.getFormats(result.url)
updatingFormats.emit(false)
if (formats.isNotEmpty()){
val res = getItemByURL(result.url)
res.formats = formats.toMutableList()
update(res)
}
updateFormatsResultData.emit(formats.toMutableList())
}.onFailure {
updatingFormats.emit(false)
updateFormatsResultData.emit(mutableListOf())
}
}
updateFormatsResultDataJob?.start()
}
} }

View file

@ -33,18 +33,15 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.MainActivity import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.ui.adapter.HomeAdapter
import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.ResultItem import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog import com.deniscerri.ytdlnis.ui.adapter.HomeAdapter
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadMultipleBottomSheetDialog import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
import com.deniscerri.ytdlnis.ui.downloadcard.ResultCardDetailsDialog
import com.deniscerri.ytdlnis.util.InfoUtil import com.deniscerri.ytdlnis.util.InfoUtil
import com.deniscerri.ytdlnis.util.ThemeUtil import com.deniscerri.ytdlnis.util.ThemeUtil
import com.deniscerri.ytdlnis.util.UiUtil import com.deniscerri.ytdlnis.util.UiUtil
import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll
import com.facebook.shimmer.ShimmerFrameLayout import com.facebook.shimmer.ShimmerFrameLayout
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.appbar.MaterialToolbar
@ -316,6 +313,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
providersChipGroup!!.addView(tmp) providersChipGroup!!.addView(tmp)
} }
val chipGroupDivider : View? = requireView().findViewById(R.id.chipGroupDivider)
searchView!!.addTransitionListener { _, _, newState -> searchView!!.addTransitionListener { _, _, newState ->
if (newState == SearchView.TransitionState.SHOWN) { if (newState == SearchView.TransitionState.SHOWN) {
@ -328,13 +326,19 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
} }
} }
if (Patterns.WEB_URL.matcher(searchBar!!.text.toString()).matches() && searchBar!!.text.isNotBlank()){
providersChipGroup.visibility = GONE
chipGroupDivider?.visibility = GONE
}else{
providersChipGroup.visibility = VISIBLE
chipGroupDivider?.visibility = VISIBLE
}
try{ try{
val clipboard = val clipboard =
requireContext().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager requireContext().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val regex =
"(https?://(?:www\\.|(?!www))[a-zA-Z\\d][a-zA-Z\\d-]+[a-zA-Z\\d]\\.\\S{2,}|www\\.[a-zA-Z\\d][a-zA-Z\\d-]+[a-zA-Z\\d]\\.\\S{2,}|https?://(?:www\\.|(?!www))[a-zA-Z\\d]+\\.\\S{2,}|www\\.[a-zA-Z\\d]+\\.\\S{2,})".toRegex()
val clip = clipboard.primaryClip!!.getItemAt(0).text val clip = clipboard.primaryClip!!.getItemAt(0).text
if (regex.containsMatchIn(clip.toString())) { if (Patterns.WEB_URL.matcher(clip.toString()).matches()) {
linkYouCopied!!.visibility = VISIBLE linkYouCopied!!.visibility = VISIBLE
val textView = linkYouCopied!!.findViewById<TextView>(R.id.suggestion_text) val textView = linkYouCopied!!.findViewById<TextView>(R.id.suggestion_text)
textView.text = getString(R.string.link_you_copied) textView.text = getString(R.string.link_you_copied)
@ -381,6 +385,13 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
lifecycleScope.launch { lifecycleScope.launch {
updateSearchViewItems(it, linkYouCopied) updateSearchViewItems(it, linkYouCopied)
} }
if (Patterns.WEB_URL.matcher(it.toString()).matches()){
providersChipGroup.visibility = GONE
chipGroupDivider?.visibility = GONE
}else{
providersChipGroup.visibility = VISIBLE
chipGroupDivider?.visibility = VISIBLE
}
} }
searchView!!.editText.setOnTouchListener(OnTouchListener { _, event -> searchView!!.editText.setOnTouchListener(OnTouchListener { _, event ->

View file

@ -1,5 +1,6 @@
package com.deniscerri.ytdlnis.ui.adapter package com.deniscerri.ytdlnis.ui.adapter
import android.animation.ValueAnimator
import android.app.Activity import android.app.Activity
import android.content.SharedPreferences import android.content.SharedPreferences
import android.graphics.Color import android.graphics.Color
@ -9,6 +10,7 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
@ -20,6 +22,7 @@ import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.repository.DownloadRepository import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.util.Extensions.dp
import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.FileUtil
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
@ -27,6 +30,7 @@ import com.google.android.material.chip.Chip
import com.google.android.material.progressindicator.LinearProgressIndicator import com.google.android.material.progressindicator.LinearProgressIndicator
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<DownloadItem?, ActiveDownloadAdapter.ViewHolder>(AsyncDifferConfig.Builder( class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<DownloadItem?, ActiveDownloadAdapter.ViewHolder>(AsyncDifferConfig.Builder(
DIFF_CALLBACK DIFF_CALLBACK
).build()) { ).build()) {
@ -137,16 +141,37 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
when(DownloadRepository.Status.valueOf(item.status)){ when(DownloadRepository.Status.valueOf(item.status)){
DownloadRepository.Status.Active -> { DownloadRepository.Status.Active -> {
progressBar.isIndeterminate = true progressBar.isIndeterminate = true
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_pause_white)
val fromRadius: Int = dp(activity.resources, 30f)
val toRadius: Int = dp(activity.resources, 15f)
val animator = ValueAnimator.ofInt(fromRadius, toRadius)
animator.setDuration(500)
.addUpdateListener { animation ->
val value = animation.animatedValue as Int
pauseButton.cornerRadius = value
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_pause_white)
pauseButton.isEnabled = true
pauseButton.tag = ActiveDownloadAction.Pause
}
animator.start()
cancelButton.visibility = View.GONE cancelButton.visibility = View.GONE
pauseButton.isEnabled = true
pauseButton.tag = ActiveDownloadAction.Pause
} }
DownloadRepository.Status.ActivePaused -> { DownloadRepository.Status.ActivePaused -> {
progressBar.isIndeterminate = false progressBar.isIndeterminate = false
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_play_arrow_white)
pauseButton.tag = ActiveDownloadAction.Resume val fromRadius: Int = dp(activity.resources, 15f)
pauseButton.isEnabled = true val toRadius: Int = dp(activity.resources, 30f)
val animator = ValueAnimator.ofInt(fromRadius, toRadius)
animator.setDuration(500)
.addUpdateListener { animation ->
val value = animation.animatedValue as Int
pauseButton.cornerRadius = value
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_play_arrow_white)
pauseButton.tag = ActiveDownloadAction.Resume
pauseButton.isEnabled = true
}
animator.start()
cancelButton.visibility = View.VISIBLE cancelButton.visibility = View.VISIBLE
} }
DownloadRepository.Status.PausedReQueued -> { DownloadRepository.Status.PausedReQueued -> {

View file

@ -6,6 +6,8 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import androidx.compose.ui.util.fastJoinToString
import androidx.core.view.isVisible
import androidx.recyclerview.widget.AsyncDifferConfig import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
@ -51,11 +53,18 @@ class TemplatesAdapter(onItemClickListener: OnItemClickListener, activity: Activ
val content = card.findViewById<TextView>(R.id.content) val content = card.findViewById<TextView>(R.id.content)
content.text = item?.content content.text = item?.content
val useInExtraCommands = card.findViewById<TextView>(R.id.useInExtraCommands) card.findViewById<TextView>(R.id.useInExtraCommands).apply {
if (item!!.useAsExtraCommand) useInExtraCommands.visibility = View.VISIBLE isVisible = item!!.useAsExtraCommand
else useInExtraCommands.visibility = View.GONE val extraAudio = if (item.useAsExtraCommandAudio) context.getString(R.string.audio) else null
val extraVideo = if (item.useAsExtraCommandVideo) context.getString(R.string.video) else null
val finalText = context.getString(R.string.extra_command) + " " + listOfNotNull(
extraAudio,
extraVideo
).joinToString(" ", "[", "]", -1)
text = finalText
}
if (checkedItems.contains(item.id)) { if (checkedItems.contains(item!!.id)) {
card.isChecked = true card.isChecked = true
card.strokeWidth = 5 card.strokeWidth = 5
} else { } else {
@ -132,7 +141,11 @@ class TemplatesAdapter(onItemClickListener: OnItemClickListener, activity: Activ
} }
override fun areContentsTheSame(oldItem: CommandTemplate, newItem: CommandTemplate): Boolean { override fun areContentsTheSame(oldItem: CommandTemplate, newItem: CommandTemplate): Boolean {
return oldItem.title == newItem.title && oldItem.content == newItem.content && oldItem.useAsExtraCommand == newItem.useAsExtraCommand return oldItem.title == newItem.title &&
oldItem.content == newItem.content &&
oldItem.useAsExtraCommand == newItem.useAsExtraCommand &&
oldItem.useAsExtraCommandAudio == newItem.useAsExtraCommandAudio &&
oldItem.useAsExtraCommandVideo == newItem.useAsExtraCommandVideo
} }
} }
} }

View file

@ -21,7 +21,7 @@ import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel
import com.deniscerri.ytdlnis.util.InfoUtil import com.deniscerri.ytdlnis.util.InfoUtil
import com.deniscerri.ytdlnis.util.UiUtil import com.deniscerri.ytdlnis.util.UiUtil
import com.deniscerri.ytdlnis.util.UiUtil.enableTextHighlight import com.deniscerri.ytdlnis.util.Extensions.enableTextHighlight
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers

View file

@ -30,7 +30,7 @@ import com.deniscerri.ytdlnis.database.models.ChapterItem
import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.util.InfoUtil import com.deniscerri.ytdlnis.util.InfoUtil
import com.deniscerri.ytdlnis.util.UiUtil import com.deniscerri.ytdlnis.util.UiUtil
import com.deniscerri.ytdlnis.util.UiUtil.setTextAndRecalculateWidth import com.deniscerri.ytdlnis.util.Extensions.setTextAndRecalculateWidth
import com.deniscerri.ytdlnis.util.VideoPlayerUtil import com.deniscerri.ytdlnis.util.VideoPlayerUtil
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment

View file

@ -69,7 +69,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private
infoUtil = InfoUtil(requireContext()) infoUtil = InfoUtil(requireContext())
genericAudioFormats = infoUtil.getGenericAudioFormats(requireContext().resources) genericAudioFormats = infoUtil.getGenericAudioFormats(requireContext().resources)
preferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
shownFields = preferences.getStringSet("modify_download_card", setOf())!!.toList().ifEmpty { requireContext().getStringArray(R.array.modify_download_card_values).toList() } shownFields = preferences.getStringSet("modify_download_card", requireContext().getStringArray(R.array.modify_download_card_values).toSet())!!.toList()
return fragmentView return fragmentView
} }

View file

@ -9,6 +9,7 @@ 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
import android.util.Log
import android.util.Patterns import android.util.Patterns
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -20,8 +21,6 @@ import androidx.core.content.edit
import androidx.core.os.bundleOf 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.navigation.fragment.findNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -46,10 +45,10 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.button.MaterialButton 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.snackbar.Snackbar
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
@ -78,9 +77,6 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
private lateinit var shimmerLoadingSubtitle : ShimmerFrameLayout private lateinit var shimmerLoadingSubtitle : ShimmerFrameLayout
private lateinit var subtitle : View private lateinit var subtitle : View
private var updateJob : Job? = null
private var updateFormatsJob : Job? = null
private lateinit var result: ResultItem private lateinit var result: ResultItem
private lateinit var type: Type private lateinit var type: Type
@ -88,9 +84,9 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] downloadViewModel = ViewModelProvider(requireActivity())[DownloadViewModel::class.java]
historyViewModel = ViewModelProvider(this)[HistoryViewModel::class.java] historyViewModel = ViewModelProvider(requireActivity())[HistoryViewModel::class.java]
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java] resultViewModel = ViewModelProvider(requireActivity())[ResultViewModel::class.java]
commandTemplateDao = DBManager.getInstance(requireContext()).commandTemplateDao commandTemplateDao = DBManager.getInstance(requireContext()).commandTemplateDao
infoUtil = InfoUtil(requireContext()) infoUtil = InfoUtil(requireContext())
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
@ -143,6 +139,16 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
shimmerLoadingSubtitle = view.findViewById(R.id.shimmer_loading_subtitle) shimmerLoadingSubtitle = view.findViewById(R.id.shimmer_loading_subtitle)
subtitle = view.findViewById(R.id.bottom_sheet_subtitle) subtitle = view.findViewById(R.id.bottom_sheet_subtitle)
shimmerLoading.setOnClickListener {
lifecycleScope.launch {
withContext(Dispatchers.IO){
resultViewModel.cancelUpdateItemData()
}
(updateItem.parent as LinearLayout).visibility = View.VISIBLE
}
}
(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
@ -259,7 +265,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
viewPager2.setPageTransformer(BackgroundToForegroundPageTransformer()) viewPager2.setPageTransformer(BackgroundToForegroundPageTransformer())
val shownFields = sharedPreferences.getStringSet("modify_download_card", setOf())!!.toList().ifEmpty { requireContext().getStringArray(R.array.modify_download_card_values).toList() } val shownFields = sharedPreferences.getStringSet("modify_download_card", requireContext().getStringArray(R.array.modify_download_card_values).toSet())!!.toList()
val scheduleBtn = view.findViewById<MaterialButton>(R.id.bottomsheet_schedule_button) val scheduleBtn = view.findViewById<MaterialButton>(R.id.bottomsheet_schedule_button)
scheduleBtn.visibility = if(shownFields.contains("schedule")){ scheduleBtn.visibility = if(shownFields.contains("schedule")){
@ -272,8 +278,11 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
scheduleBtn.setOnClickListener{ scheduleBtn.setOnClickListener{
UiUtil.showDatePicker(fragmentManager) { UiUtil.showDatePicker(fragmentManager) {
updateJob?.cancel() lifecycleScope.launch {
updateFormatsJob?.cancel() resultViewModel.cancelUpdateItemData()
resultViewModel.cancelUpdateFormatsItemData()
}
scheduleBtn.isEnabled = false scheduleBtn.isEnabled = false
download.isEnabled = false download.isEnabled = false
val item: DownloadItem = getDownloadItem() val item: DownloadItem = getDownloadItem()
@ -287,8 +296,10 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
} }
} }
download!!.setOnClickListener { download!!.setOnClickListener {
updateJob?.cancel() lifecycleScope.launch {
updateFormatsJob?.cancel() resultViewModel.cancelUpdateItemData()
resultViewModel.cancelUpdateFormatsItemData()
}
scheduleBtn.isEnabled = false scheduleBtn.isEnabled = false
download.isEnabled = false download.isEnabled = false
val item: DownloadItem = getDownloadItem() val item: DownloadItem = getDownloadItem()
@ -356,7 +367,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
}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)){
initUpdateFormats(result.url) initUpdateFormats(result)
} }
} }
@ -392,7 +403,115 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
} }
} }
} }
lifecycleScope.launch {
resultViewModel.updatingData.collectLatest {
if (it){
title.visibility = View.GONE
subtitle.visibility = View.GONE
shimmerLoading.visibility = View.VISIBLE
shimmerLoadingSubtitle.visibility = View.VISIBLE
shimmerLoading.startShimmer()
shimmerLoadingSubtitle.startShimmer()
}
}
}
lifecycleScope.launch {
resultViewModel.updatingFormats.collectLatest {
if (it){
runCatching {
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.VISIBLE
}
runCatching {
val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.VISIBLE
}
}
}
}
lifecycleScope.launch {
resultViewModel.updateResultData.collectLatest { result ->
if (result == null) return@collectLatest
lifecycleScope.launch(Dispatchers.Main) {
if (result.isNotEmpty()){
if (result.size == 1 && result[0] != null){
fragmentAdapter.setResultItem(result[0]!!)
runCatching {
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
f.updateUI(result[0])
}
runCatching {
val f1 = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
f1.updateUI(result[0])
}
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") }
fragmentAdapter.setResultItem(result[0]!!)
arguments?.putParcelable("result", result[0]!!)
if (usingGenericFormatsOrEmpty && sharedPreferences.getBoolean("update_formats", false)){
initUpdateFormats(result[0]!!)
}
}else{
//open multi download card instead
if (activity is ShareActivity){
val preferredType = DownloadViewModel.Type.valueOf(sharedPreferences.getString("preferred_download_type", "video")!!)
findNavController().navigate(R.id.action_downloadBottomSheetDialog_to_selectPlaylistItemsDialog, bundleOf(
Pair("results", result),
Pair("type", preferredType),
))
}else{
dismiss()
}
}
}
resultViewModel.updateResultData.emit(null)
}
}
}
lifecycleScope.launch {
resultViewModel.updateFormatsResultData.collectLatest { formats ->
if (formats == null) return@collectLatest
lifecycleScope.launch {
withContext(Dispatchers.Main){
runCatching {
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
val resultItem = downloadViewModel.createResultItemFromDownload(f1.downloadItem)
resultItem.formats = formats
fragmentAdapter.setResultItem(resultItem)
f1.updateUI(resultItem)
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.GONE
}
runCatching {
val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
val resultItem = downloadViewModel.createResultItemFromDownload(f1.downloadItem)
resultItem.formats = formats
fragmentAdapter.setResultItem(resultItem)
f1.updateUI(resultItem)
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.GONE
}
}
if (formats.isNotEmpty()){
result.formats = formats
}
resultViewModel.updateFormatsResultData.emit(null)
}
}
}
} }
private fun getDownloadItem(selectedTabPosition: Int = tabLayout.selectedTabPosition) : DownloadItem{ private fun getDownloadItem(selectedTabPosition: Int = tabLayout.selectedTabPosition) : DownloadItem{
return when(selectedTabPosition){ return when(selectedTabPosition){
0 -> { 0 -> {
@ -430,139 +549,22 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
} }
private fun initUpdateData() { private fun initUpdateData() {
updateJob = CoroutineScope(SupervisorJob()).launch(Dispatchers.IO){ if (result.url.isBlank()) {
findNavController().saveState() dismiss()
kotlin.runCatching { return
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){
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 { if (resultViewModel.updatingData.value) return
updateJob?.cancel()
(updateItem.parent as LinearLayout).visibility = View.VISIBLE CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) {
resultViewModel.updateItemData(result)
} }
updateJob?.invokeOnCompletion {
kotlin.runCatching {
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(res: ResultItem){
updateFormatsJob = CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) { if (resultViewModel.updatingFormats.value) return
withContext(Dispatchers.Main){ CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) {
runCatching { resultViewModel.updateFormatItemData(res)
val f1 = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.VISIBLE
}
runCatching {
val f1 = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.VISIBLE
}
}
val formats = runCatching {
infoUtil.getFormats(url).toMutableList()
}.getOrElse {
mutableListOf()
}
withContext(Dispatchers.Main){
runCatching {
val f1 = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
val resultItem = downloadViewModel.createResultItemFromDownload(f1.downloadItem)
resultItem.formats = formats
fragmentAdapter.setResultItem(resultItem)
f1.updateUI(resultItem)
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.GONE
}
runCatching {
val f1 = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
val resultItem = downloadViewModel.createResultItemFromDownload(f1.downloadItem)
resultItem.formats = formats
fragmentAdapter.setResultItem(resultItem)
f1.updateUI(resultItem)
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.GONE
}
}
if (formats.isNotEmpty()){
result.formats = formats
resultViewModel.update(result)
}
} }
updateFormatsJob?.start()
} }
} }

View file

@ -31,7 +31,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.UiUtil import com.deniscerri.ytdlnis.util.UiUtil
import com.deniscerri.ytdlnis.util.UiUtil.enableTextHighlight import com.deniscerri.ytdlnis.util.Extensions.enableTextHighlight
import com.deniscerri.ytdlnis.util.UiUtil.populateCommandCard import com.deniscerri.ytdlnis.util.UiUtil.populateCommandCard
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
import com.google.android.material.chip.Chip import com.google.android.material.chip.Chip
@ -87,7 +87,7 @@ class DownloadCommandFragment(private val resultItem: ResultItem? = null, privat
preferences.edit().putString("lastCommandTemplateUsed", downloadItem.format.format_note).apply() preferences.edit().putString("lastCommandTemplateUsed", downloadItem.format.format_note).apply()
if (!Patterns.WEB_URL.matcher(downloadItem.url).matches()){ if (!Patterns.WEB_URL.matcher(downloadItem.url).matches()){
downloadItem.format = downloadViewModel.generateCommandFormat(CommandTemplate(0,"txt", "-a \"${downloadItem.url}\"", false)) downloadItem.format = downloadViewModel.generateCommandFormat(CommandTemplate(0,"txt", "-a \"${downloadItem.url}\"", useAsExtraCommand = false, useAsExtraCommandAudio = false, useAsExtraCommandVideo = false))
downloadItem.url = "" downloadItem.url = ""
} }
@ -231,7 +231,7 @@ class DownloadCommandFragment(private val resultItem: ResultItem? = null, privat
0, 0,
"", "",
chosenCommandView.editText!!.text.toString(), chosenCommandView.editText!!.text.toString(),
false useAsExtraCommand = false, useAsExtraCommandAudio = false, useAsExtraCommandVideo = false
) )
UiUtil.showCommandTemplateCreationOrUpdatingSheet(current, requireActivity(), viewLifecycleOwner, commandTemplateViewModel) { UiUtil.showCommandTemplateCreationOrUpdatingSheet(current, requireActivity(), viewLifecycleOwner, commandTemplateViewModel) {
templates.add(it) templates.add(it)

View file

@ -48,7 +48,7 @@ import com.deniscerri.ytdlnis.receiver.ShareActivity
import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.InfoUtil import com.deniscerri.ytdlnis.util.InfoUtil
import com.deniscerri.ytdlnis.util.UiUtil import com.deniscerri.ytdlnis.util.UiUtil
import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
import com.deniscerri.ytdlnis.work.UpdatePlaylistFormatsWorker import com.deniscerri.ytdlnis.work.UpdatePlaylistFormatsWorker
import com.google.android.material.bottomappbar.BottomAppBar import com.google.android.material.bottomappbar.BottomAppBar
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior

View file

@ -74,7 +74,7 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
infoUtil = InfoUtil(requireContext()) infoUtil = InfoUtil(requireContext())
genericVideoFormats = infoUtil.getGenericVideoFormats(requireContext().resources) genericVideoFormats = infoUtil.getGenericVideoFormats(requireContext().resources)
preferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
shownFields = preferences.getStringSet("modify_download_card", setOf())!!.toList().ifEmpty { requireContext().getStringArray(R.array.modify_download_card_values).toList() } shownFields = preferences.getStringSet("modify_download_card", requireContext().getStringArray(R.array.modify_download_card_values).toSet())!!.toList()
return fragmentView return fragmentView
} }

View file

@ -62,6 +62,7 @@ import com.google.android.material.snackbar.Snackbar
import com.yausername.youtubedl_android.YoutubeDL import com.yausername.youtubedl_android.YoutubeDL
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -181,14 +182,17 @@ class ResultCardDetailsDialog : BottomSheetDialogFragment(), GenericDownloadAdap
queuedRecycler.adapter = queuedAdapter queuedRecycler.adapter = queuedAdapter
queuedRecycler.layoutManager = GridLayoutManager(context, resources.getInteger(R.integer.grid_size)) queuedRecycler.layoutManager = GridLayoutManager(context, resources.getInteger(R.integer.grid_size))
downloadViewModel.activeDownloads.map { it.filter { d -> d.url == item.url } }.observe(viewLifecycleOwner) { lifecycleScope.launch {
activeAdapter.submitList(it) downloadViewModel.activeDownloads.map { it.filter { d -> d.url == item.url } }.collectLatest {
if (it.isEmpty()){ delay(500)
running.visibility = View.GONE activeAdapter.submitList(it)
runningRecycler.visibility = View.GONE if (it.isEmpty()){
}else{ running.visibility = View.GONE
running.visibility = View.VISIBLE runningRecycler.visibility = View.GONE
runningRecycler.visibility = View.VISIBLE }else{
running.visibility = View.VISIBLE
runningRecycler.visibility = View.VISIBLE
}
} }
} }
@ -231,8 +235,10 @@ class ResultCardDetailsDialog : BottomSheetDialogFragment(), GenericDownloadAdap
} }
} }
downloadViewModel.activeDownloadsCount.observe(viewLifecycleOwner){ lifecycleScope.launch {
activeCount = it downloadViewModel.activeDownloadsCount.collectLatest {
activeCount = it
}
} }

View file

@ -25,8 +25,8 @@ import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.receiver.ShareActivity import com.deniscerri.ytdlnis.receiver.ShareActivity
import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
import com.deniscerri.ytdlnis.util.UiUtil.setTextAndRecalculateWidth import com.deniscerri.ytdlnis.util.Extensions.setTextAndRecalculateWidth
import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.bottomappbar.BottomAppBar import com.google.android.material.bottomappbar.BottomAppBar
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton

View file

@ -23,11 +23,13 @@ import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.repository.DownloadRepository import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.util.NotificationUtil import com.deniscerri.ytdlnis.util.NotificationUtil
import com.deniscerri.ytdlnis.util.UiUtil.forceFastScrollMode import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.progressindicator.LinearProgressIndicator import com.google.android.material.progressindicator.LinearProgressIndicator
import com.yausername.youtubedl_android.YoutubeDL import com.yausername.youtubedl_android.YoutubeDL
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -157,21 +159,30 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
} }
} }
downloadViewModel.activeDownloads.observe(viewLifecycleOwner) { lifecycleScope.launch {
activeDownloads.submitList(it) downloadViewModel.activeDownloadsCount.collectLatest {
delay(200)
if (it.size > 1){ noResults.visibility = if (it == 0) View.VISIBLE else View.GONE
pauseResume.visibility = View.VISIBLE
if (it.all { l -> l.status == DownloadRepository.Status.ActivePaused.toString() }){
pauseResume.text = requireContext().getString(R.string.resume)
}else{
pauseResume.text = requireContext().getString(R.string.pause)
}
}else{
activeDownloads.notifyDataSetChanged()
pauseResume.visibility = View.GONE
} }
noResults.visibility = if (it.isEmpty()) View.VISIBLE else View.GONE }
lifecycleScope.launch {
downloadViewModel.activeDownloads.collectLatest {
delay(100)
activeDownloads.submitList(it)
if (it.size > 1){
pauseResume.visibility = View.VISIBLE
if (it.all { l -> l.status == DownloadRepository.Status.ActivePaused.toString() }){
pauseResume.text = requireContext().getString(R.string.resume)
}else{
pauseResume.text = requireContext().getString(R.string.pause)
}
}else{
pauseResume.visibility = View.GONE
}
}
} }
} }
@ -212,7 +223,6 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
withContext(Dispatchers.IO){ withContext(Dispatchers.IO){
downloadViewModel.updateDownload(item) downloadViewModel.updateDownload(item)
} }
activeDownloads.notifyItemChanged(position)
} }
} }
ActiveDownloadAdapter.ActiveDownloadAction.Resume -> { ActiveDownloadAdapter.ActiveDownloadAction.Resume -> {
@ -221,7 +231,6 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
withContext(Dispatchers.IO){ withContext(Dispatchers.IO){
downloadViewModel.updateDownload(item) downloadViewModel.updateDownload(item)
} }
activeDownloads.notifyItemChanged(position)
runBlocking { runBlocking {
downloadViewModel.queueDownloads(listOf(item)) downloadViewModel.queueDownloads(listOf(item))

View file

@ -23,6 +23,7 @@ import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter
import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.DownloadItem
@ -30,8 +31,8 @@ import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog
import com.deniscerri.ytdlnis.util.UiUtil import com.deniscerri.ytdlnis.util.UiUtil
import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
import com.deniscerri.ytdlnis.util.UiUtil.forceFastScrollMode import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -80,7 +81,7 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
cancelledRecyclerView.adapter = adapter cancelledRecyclerView.adapter = adapter
cancelledRecyclerView.enableFastScroll() cancelledRecyclerView.enableFastScroll()
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
if (preferences.getBoolean("swipe_gestures", true)){ if (preferences.getStringSet("swipe_gesture", requireContext().getStringArray(R.array.swipe_gestures_values).toSet())!!.toList().contains("cancelled")){
val itemTouchHelper = ItemTouchHelper(simpleCallback) val itemTouchHelper = ItemTouchHelper(simpleCallback)
itemTouchHelper.attachToRecyclerView(cancelledRecyclerView) itemTouchHelper.attachToRecyclerView(cancelledRecyclerView)
} }

View file

@ -24,6 +24,7 @@ import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter
import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.DownloadItem
@ -31,8 +32,8 @@ import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog
import com.deniscerri.ytdlnis.util.UiUtil import com.deniscerri.ytdlnis.util.UiUtil
import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
import com.deniscerri.ytdlnis.util.UiUtil.forceFastScrollMode import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -80,7 +81,7 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
erroredRecyclerView.adapter = adapter erroredRecyclerView.adapter = adapter
erroredRecyclerView.enableFastScroll() erroredRecyclerView.enableFastScroll()
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
if (preferences.getBoolean("swipe_gestures", true)){ if (preferences.getStringSet("swipe_gesture", requireContext().getStringArray(R.array.swipe_gestures_values).toSet())!!.toList().contains("errored")){
val itemTouchHelper = ItemTouchHelper(simpleCallback) val itemTouchHelper = ItemTouchHelper(simpleCallback)
itemTouchHelper.attachToRecyclerView(erroredRecyclerView) itemTouchHelper.attachToRecyclerView(erroredRecyclerView)
} }

View file

@ -36,6 +36,7 @@ import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.deniscerri.ytdlnis.MainActivity import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.ui.adapter.HistoryAdapter import com.deniscerri.ytdlnis.ui.adapter.HistoryAdapter
@ -47,7 +48,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.HistoryViewModel
import com.deniscerri.ytdlnis.databinding.FragmentHistoryBinding import com.deniscerri.ytdlnis.databinding.FragmentHistoryBinding
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog
import com.deniscerri.ytdlnis.util.UiUtil import com.deniscerri.ytdlnis.util.UiUtil
import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
@ -122,7 +123,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
recyclerView?.enableFastScroll() recyclerView?.enableFastScroll()
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
if (preferences.getBoolean("swipe_gestures", true)){ if (preferences.getStringSet("swipe_gesture", requireContext().getStringArray(R.array.swipe_gestures_values).toSet())!!.toList().contains("history")){
val itemTouchHelper = ItemTouchHelper(simpleCallback) val itemTouchHelper = ItemTouchHelper(simpleCallback)
itemTouchHelper.attachToRecyclerView(recyclerView) itemTouchHelper.attachToRecyclerView(recyclerView)
} }

View file

@ -26,6 +26,7 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.work.WorkManager import androidx.work.WorkManager
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter
import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.DownloadItem
@ -35,8 +36,8 @@ import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog
import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.NotificationUtil import com.deniscerri.ytdlnis.util.NotificationUtil
import com.deniscerri.ytdlnis.util.UiUtil import com.deniscerri.ytdlnis.util.UiUtil
import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
import com.deniscerri.ytdlnis.util.UiUtil.forceFastScrollMode import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -91,7 +92,7 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
queuedRecyclerView.enableFastScroll() queuedRecyclerView.enableFastScroll()
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
if (preferences.getBoolean("swipe_gestures", true)){ if (preferences.getStringSet("swipe_gesture", requireContext().getStringArray(R.array.swipe_gestures_values).toSet())!!.toList().contains("queued")){
val itemTouchHelper = ItemTouchHelper(simpleCallback) val itemTouchHelper = ItemTouchHelper(simpleCallback)
itemTouchHelper.attachToRecyclerView(queuedRecyclerView) itemTouchHelper.attachToRecyclerView(queuedRecyclerView)
} }

View file

@ -23,6 +23,7 @@ import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter
import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.DownloadItem
@ -30,8 +31,8 @@ import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog
import com.deniscerri.ytdlnis.util.UiUtil import com.deniscerri.ytdlnis.util.UiUtil
import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
import com.deniscerri.ytdlnis.util.UiUtil.forceFastScrollMode import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -79,7 +80,7 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis
savedRecyclerView.enableFastScroll() savedRecyclerView.enableFastScroll()
savedRecyclerView.adapter = adapter savedRecyclerView.adapter = adapter
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
if (preferences.getBoolean("swipe_gestures", true)){ if (preferences.getStringSet("swipe_gesture", requireContext().getStringArray(R.array.swipe_gestures_values).toSet())!!.toList().contains("saved")){
val itemTouchHelper = ItemTouchHelper(simpleCallback) val itemTouchHelper = ItemTouchHelper(simpleCallback)
itemTouchHelper.attachToRecyclerView(savedRecyclerView) itemTouchHelper.attachToRecyclerView(savedRecyclerView)
} }

View file

@ -29,6 +29,7 @@ import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.deniscerri.ytdlnis.MainActivity import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.ui.adapter.TemplatesAdapter import com.deniscerri.ytdlnis.ui.adapter.TemplatesAdapter
@ -94,7 +95,7 @@ class CommandTemplatesFragment : Fragment(), TemplatesAdapter.OnItemClickListene
recyclerView.layoutManager = LinearLayoutManager(context) recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.adapter = templatesAdapter recyclerView.adapter = templatesAdapter
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
if (preferences.getBoolean("swipe_gestures", true)){ if (preferences.getStringSet("swipe_gesture", requireContext().getStringArray(R.array.swipe_gestures_values).toSet())!!.toList().contains("templates")){
val itemTouchHelper = ItemTouchHelper(simpleCallback) val itemTouchHelper = ItemTouchHelper(simpleCallback)
itemTouchHelper.attachToRecyclerView(recyclerView) itemTouchHelper.attachToRecyclerView(recyclerView)
} }

View file

@ -23,6 +23,7 @@ import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.deniscerri.ytdlnis.MainActivity import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.ui.adapter.CookieAdapter import com.deniscerri.ytdlnis.ui.adapter.CookieAdapter
@ -79,7 +80,7 @@ class CookiesFragment : Fragment(), CookieAdapter.OnItemClickListener {
recyclerView.layoutManager = LinearLayoutManager(context) recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.adapter = listAdapter recyclerView.adapter = listAdapter
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
if (preferences.getBoolean("swipe_gestures", true)){ if (preferences.getStringSet("swipe_gesture", requireContext().getStringArray(R.array.swipe_gestures_values).toSet())!!.toList().contains("cookies")){
val itemTouchHelper = ItemTouchHelper(simpleCallback) val itemTouchHelper = ItemTouchHelper(simpleCallback)
itemTouchHelper.attachToRecyclerView(recyclerView) itemTouchHelper.attachToRecyclerView(recyclerView)
} }

View file

@ -27,7 +27,7 @@ import androidx.lifecycle.lifecycleScope
import com.deniscerri.ytdlnis.MainActivity import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.viewmodel.LogViewModel import com.deniscerri.ytdlnis.database.viewmodel.LogViewModel
import com.deniscerri.ytdlnis.util.UiUtil.enableTextHighlight import com.deniscerri.ytdlnis.util.Extensions.enableTextHighlight
import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.bottomappbar.BottomAppBar import com.google.android.material.bottomappbar.BottomAppBar
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton

View file

@ -26,8 +26,8 @@ import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.ui.adapter.DownloadLogsAdapter import com.deniscerri.ytdlnis.ui.adapter.DownloadLogsAdapter
import com.deniscerri.ytdlnis.database.models.LogItem import com.deniscerri.ytdlnis.database.models.LogItem
import com.deniscerri.ytdlnis.database.viewmodel.LogViewModel import com.deniscerri.ytdlnis.database.viewmodel.LogViewModel
import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
import com.deniscerri.ytdlnis.util.UiUtil.forceFastScrollMode import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode
import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar

View file

@ -51,7 +51,10 @@ abstract class BaseSettingsFragment : PreferenceFragmentCompat() {
}.toBooleanArray() }.toBooleanArray()
MaterialAlertDialogBuilder(requireContext()) MaterialAlertDialogBuilder(requireContext())
.setTitle(preference.title) .setTitle(preference.title)
.setMultiChoiceItems(preference.entries, selectedItems) { _, _, _ -> .setMultiChoiceItems(preference.entries, selectedItems) { _, which, isChecked ->
selectedItems[which] = isChecked
}
.setPositiveButton(R.string.ok) { _, _ ->
val newValues = preference.entryValues val newValues = preference.entryValues
.filterIndexed { index, _ -> selectedItems[index] } .filterIndexed { index, _ -> selectedItems[index] }
.map { it.toString() } .map { it.toString() }

View file

@ -6,6 +6,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.PowerManager import android.os.PowerManager
import android.provider.Settings import android.provider.Settings
@ -66,11 +67,9 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
language!!.entries = entries.toTypedArray() language!!.entries = entries.toTypedArray()
if(language!!.value == null) language!!.value = Locale.getDefault().language if(language!!.value == null) language!!.value = Locale.getDefault().language
language!!.summary = Locale(language!!.value).getDisplayLanguage(Locale(language!!.value))
language!!.onPreferenceChangeListener = language!!.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any -> Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
language!!.summary = Locale(newValue.toString()).getDisplayLanguage(Locale(newValue.toString()))
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(newValue.toString())) AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(newValue.toString()))
true true
} }

View file

@ -20,7 +20,7 @@ import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.ui.adapter.TerminalDownloadsAdapter import com.deniscerri.ytdlnis.ui.adapter.TerminalDownloadsAdapter
import com.deniscerri.ytdlnis.database.models.TerminalItem import com.deniscerri.ytdlnis.database.models.TerminalItem
import com.deniscerri.ytdlnis.database.viewmodel.TerminalViewModel import com.deniscerri.ytdlnis.database.viewmodel.TerminalViewModel
import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.progressindicator.LinearProgressIndicator import com.google.android.material.progressindicator.LinearProgressIndicator
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest

View file

@ -37,7 +37,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.TerminalViewModel
import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.NotificationUtil import com.deniscerri.ytdlnis.util.NotificationUtil
import com.deniscerri.ytdlnis.util.UiUtil import com.deniscerri.ytdlnis.util.UiUtil
import com.deniscerri.ytdlnis.util.UiUtil.enableTextHighlight import com.deniscerri.ytdlnis.util.Extensions.enableTextHighlight
import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.bottomappbar.BottomAppBar import com.google.android.material.bottomappbar.BottomAppBar
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton

View file

@ -0,0 +1,94 @@
package com.deniscerri.ytdlnis.util
import android.annotation.SuppressLint
import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.shapes.OvalShape
import android.util.DisplayMetrics
import android.view.MotionEvent
import android.view.View
import android.widget.EditText
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.neo.highlight.core.Highlight
import com.neo.highlight.util.listener.HighlightTextWatcher
import com.neo.highlight.util.scheme.ColorScheme
import me.zhanghai.android.fastscroll.FastScrollerBuilder
import java.util.regex.Pattern
object Extensions {
fun dp(resources: Resources, px: Float) : Int {
val metrics: DisplayMetrics = resources.displayMetrics
return (metrics.density * px).toInt()
}
private var textHighLightSchemes = listOf(
ColorScheme(Pattern.compile("([\"'])(?:\\\\1|.)*?\\1"), Color.parseColor("#FC8500")),
ColorScheme(Pattern.compile("yt-dlp"), Color.parseColor("#77eb09")),
ColorScheme(Pattern.compile("(https?://(?:www\\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|www\\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|https?://(?:www\\.|(?!www))[a-zA-Z0-9]+\\.[^\\s]{2,}|www\\.[a-zA-Z0-9]+\\.[^\\s]{2,})"), Color.parseColor("#b5942f")),
ColorScheme(Pattern.compile("\\d+(\\.\\d)?%"), Color.parseColor("#43a564"))
)
fun View.enableTextHighlight(){
if (this is EditText || this is TextView){
//init syntax highlighter
val highlight = Highlight()
val highlightWatcher = HighlightTextWatcher()
highlight.addScheme(
*textHighLightSchemes.map { it }.toTypedArray()
)
highlightWatcher.addScheme(
*textHighLightSchemes.map { it }.toTypedArray()
)
highlight.setSpan(this as TextView)
this.addTextChangedListener(highlightWatcher)
}
}
fun EditText.setTextAndRecalculateWidth(t : String){
val scale = context.resources.displayMetrics.density
this.setText(t)
val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
this.measure(widthMeasureSpec, heightMeasureSpec)
val requiredWidth: Int = this.measuredWidth
if (t.length < 5){
this.layoutParams.width = (70 * scale + 0.5F).toInt()
}else{
this.layoutParams.width = requiredWidth
}
this.requestLayout()
}
@SuppressLint("ClickableViewAccessibility")
fun RecyclerView.forceFastScrollMode()
{
overScrollMode = View.OVER_SCROLL_ALWAYS
scrollBarStyle = View.SCROLLBARS_INSIDE_INSET
isVerticalScrollBarEnabled = true
setOnTouchListener { view, event ->
if (event.x >= this.width - 30) {
view.parent.requestDisallowInterceptTouchEvent(true)
when (event.action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_UP -> view.parent.requestDisallowInterceptTouchEvent(false)
}
}
false
}
}
fun RecyclerView.enableFastScroll(){
val drawable = ShapeDrawable(OvalShape())
drawable.paint.color = context.getColor(android.R.color.transparent)
FastScrollerBuilder(this)
.setTrackDrawable(drawable)
.build()
}
}

View file

@ -638,7 +638,7 @@ class InfoUtil(private val context: Context) {
val website = jsonObject.getString(listOf("ie_key", "extractor_key", "extractor").first { jsonObject.has(it) }) val website = jsonObject.getString(listOf("ie_key", "extractor_key", "extractor").first { jsonObject.has(it) })
var playlistTitle: String? = "" var playlistTitle: String? = ""
if (jsonObject.has("playlist_title")) playlistTitle = jsonObject.getString("playlist_title") if (jsonObject.has("playlist_title")) playlistTitle = jsonObject.getString("playlist_title")
if(playlistTitle.equals(query)) playlistTitle = "" if(playlistTitle?.removeSurrounding("\"").equals(query)) playlistTitle = ""
if (playlistTitle?.isNotBlank() == true){ if (playlistTitle?.isNotBlank() == true){
playlistTitle += "[${jsonObject.getString("playlist_index")}]" playlistTitle += "[${jsonObject.getString("playlist_index")}]"

View file

@ -25,6 +25,7 @@ import android.view.ViewGroup
import android.view.Window import android.view.Window
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.Button import android.widget.Button
import android.widget.CheckBox
import android.widget.EditText import android.widget.EditText
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
@ -100,14 +101,24 @@ object UiUtil {
formatCard.findViewById<TextView>(R.id.container).text = container.uppercase() formatCard.findViewById<TextView>(R.id.container).text = container.uppercase()
formatCard.findViewById<TextView>(R.id.format_note).text = formatNote.uppercase() formatCard.findViewById<TextView>(R.id.format_note).text = formatNote.uppercase()
val audioFormatsTextView = formatCard.findViewById<TextView>(R.id.audio_formats) formatCard.findViewById<TextView>(R.id.audio_formats).apply {
if (!audioFormats.isNullOrEmpty()) { if (!audioFormats.isNullOrEmpty()){
audioFormatsTextView.text = "id: " + audioFormats.joinToString("+") { it.format_id } text = "id: " + audioFormats.joinToString("+") { it.format_id }
audioFormatsTextView.visibility = View.VISIBLE visibility = View.VISIBLE
}else{ }else{
audioFormatsTextView.visibility = View.GONE visibility = View.GONE
}
setOnClickListener {
formatCard.callOnClick()
}
}
formatCard.findViewById<TextView>(R.id.format_id).apply {
text = "id: ${chosenFormat.format_id}"
setOnClickListener {
formatCard.callOnClick()
}
} }
formatCard.findViewById<TextView>(R.id.format_id).text = "id: ${chosenFormat.format_id}"
val codec = val codec =
if (chosenFormat.encoding != "") { if (chosenFormat.encoding != "") {
chosenFormat.encoding.uppercase() chosenFormat.encoding.uppercase()
@ -116,15 +127,28 @@ object UiUtil {
} else { } else {
chosenFormat.acodec.uppercase() chosenFormat.acodec.uppercase()
} }
if (codec == "" || codec == "none"){
formatCard.findViewById<TextView>(R.id.codec).visibility = View.GONE formatCard.findViewById<TextView>(R.id.codec).apply {
}else{ if (codec == "" || codec == "none"){
formatCard.findViewById<TextView>(R.id.codec).visibility = View.VISIBLE visibility = View.GONE
formatCard.findViewById<TextView>(R.id.codec).text = codec }else{
visibility = View.VISIBLE
text = codec
}
setOnClickListener {
formatCard.callOnClick()
}
} }
var filesize = chosenFormat.filesize var filesize = chosenFormat.filesize
if (!audioFormats.isNullOrEmpty()) filesize += audioFormats.sumOf { it.filesize } if (!audioFormats.isNullOrEmpty()) filesize += audioFormats.sumOf { it.filesize }
formatCard.findViewById<TextView>(R.id.file_size).text = FileUtil.convertFileSize(filesize) formatCard.findViewById<TextView>(R.id.file_size).apply {
text = FileUtil.convertFileSize(filesize)
setOnClickListener {
formatCard.callOnClick()
}
}
} }
@ -144,6 +168,8 @@ object UiUtil {
val title : TextInputLayout = bottomSheet.findViewById(R.id.title)!! val title : TextInputLayout = bottomSheet.findViewById(R.id.title)!!
val content : TextInputLayout = bottomSheet.findViewById(R.id.content)!! val content : TextInputLayout = bottomSheet.findViewById(R.id.content)!!
val extraCommandsSwitch : MaterialSwitch = bottomSheet.findViewById(R.id.extraCommandsSwitch)!! val extraCommandsSwitch : MaterialSwitch = bottomSheet.findViewById(R.id.extraCommandsSwitch)!!
val extraCommandsAudio : CheckBox = bottomSheet.findViewById(R.id.checkbox_audio)!!
val extraCommandsVideo : CheckBox = bottomSheet.findViewById(R.id.checkbox_video)!!
val shortcutsChipGroup : ChipGroup = bottomSheet.findViewById(R.id.shortcutsChipGroup)!! val shortcutsChipGroup : ChipGroup = bottomSheet.findViewById(R.id.shortcutsChipGroup)!!
val editShortcuts : Button = bottomSheet.findViewById(R.id.edit_shortcuts)!! val editShortcuts : Button = bottomSheet.findViewById(R.id.edit_shortcuts)!!
@ -194,8 +220,34 @@ object UiUtil {
if (item != null){ if (item != null){
extraCommandsSwitch.isChecked = item.useAsExtraCommand extraCommandsSwitch.isChecked = item.useAsExtraCommand
if (item.useAsExtraCommand){
extraCommandsAudio.isVisible = true
extraCommandsAudio.isChecked = item.useAsExtraCommandAudio
extraCommandsVideo.isVisible = true
extraCommandsVideo.isChecked = item.useAsExtraCommandVideo
}else{
extraCommandsAudio.isVisible = false
extraCommandsAudio.isChecked = false
extraCommandsVideo.isVisible = false
extraCommandsVideo.isChecked = false
}
} }
extraCommandsSwitch.setOnCheckedChangeListener { compoundButton, b ->
extraCommandsAudio.isVisible = extraCommandsSwitch.isChecked
extraCommandsAudio.isChecked = true
extraCommandsVideo.isVisible = extraCommandsSwitch.isChecked
extraCommandsVideo.isChecked = true
}
extraCommandsAudio.setOnCheckedChangeListener { compoundButton, b ->
ok.isEnabled = extraCommandsAudio.isChecked || extraCommandsVideo.isChecked
}
extraCommandsVideo.setOnCheckedChangeListener { compoundButton, b ->
ok.isEnabled = extraCommandsAudio.isChecked || extraCommandsVideo.isChecked
}
commandTemplateViewModel.shortcuts.observe(lifeCycle){ commandTemplateViewModel.shortcuts.observe(lifeCycle){
shortcutsChipGroup.removeAllViews() shortcutsChipGroup.removeAllViews()
it.forEach {shortcut -> it.forEach {shortcut ->
@ -214,13 +266,15 @@ object UiUtil {
ok.setOnClickListener { ok.setOnClickListener {
if (item == null){ if (item == null){
val t = CommandTemplate(0, title.editText!!.text.toString(), content.editText!!.text.toString(), extraCommandsSwitch.isChecked) val t = CommandTemplate(0, title.editText!!.text.toString(), content.editText!!.text.toString(), extraCommandsSwitch.isChecked, extraCommandsAudio.isChecked, extraCommandsVideo.isChecked)
commandTemplateViewModel.insert(t) commandTemplateViewModel.insert(t)
newTemplate(t) newTemplate(t)
}else{ }else{
item.title = title.editText!!.text.toString() item.title = title.editText!!.text.toString()
item.content = content.editText!!.text.toString() item.content = content.editText!!.text.toString()
item.useAsExtraCommand = extraCommandsSwitch.isChecked item.useAsExtraCommand = extraCommandsSwitch.isChecked
item.useAsExtraCommandAudio = extraCommandsAudio.isChecked
item.useAsExtraCommandVideo = extraCommandsVideo.isChecked
Log.e("aa", item.toString()) Log.e("aa", item.toString())
commandTemplateViewModel.update(item) commandTemplateViewModel.update(item)
newTemplate(item) newTemplate(item)
@ -1311,72 +1365,6 @@ object UiUtil {
@SuppressLint("ClickableViewAccessibility")
fun RecyclerView.forceFastScrollMode()
{
overScrollMode = View.OVER_SCROLL_ALWAYS
scrollBarStyle = View.SCROLLBARS_INSIDE_INSET
isVerticalScrollBarEnabled = true
setOnTouchListener { view, event ->
if (event.x >= this.width - 30) {
view.parent.requestDisallowInterceptTouchEvent(true)
when (event.action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_UP -> view.parent.requestDisallowInterceptTouchEvent(false)
}
}
false
}
}
fun RecyclerView.enableFastScroll(){
val drawable = ShapeDrawable(OvalShape())
drawable.paint.color = context.getColor(android.R.color.transparent)
FastScrollerBuilder(this)
.setTrackDrawable(drawable)
.build()
}
private var textHighLightSchemes = listOf(
ColorScheme(Pattern.compile("([\"'])(?:\\\\1|.)*?\\1"), Color.parseColor("#FC8500")),
ColorScheme(Pattern.compile("yt-dlp"), Color.parseColor("#77eb09")),
ColorScheme(Pattern.compile("(https?://(?:www\\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|www\\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|https?://(?:www\\.|(?!www))[a-zA-Z0-9]+\\.[^\\s]{2,}|www\\.[a-zA-Z0-9]+\\.[^\\s]{2,})"), Color.parseColor("#b5942f")),
ColorScheme(Pattern.compile("\\d+(\\.\\d)?%"), Color.parseColor("#43a564"))
)
fun View.enableTextHighlight(){
if (this is EditText || this is TextView){
//init syntax highlighter
val highlight = Highlight()
val highlightWatcher = HighlightTextWatcher()
highlight.addScheme(
*textHighLightSchemes.map { it }.toTypedArray()
)
highlightWatcher.addScheme(
*textHighLightSchemes.map { it }.toTypedArray()
)
highlight.setSpan(this as TextView)
this.addTextChangedListener(highlightWatcher)
}
}
fun EditText.setTextAndRecalculateWidth(t : String){
val scale = context.resources.displayMetrics.density
this.setText(t)
val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
this.measure(widthMeasureSpec, heightMeasureSpec)
val requiredWidth: Int = this.measuredWidth
if (t.length < 5){
this.layoutParams.width = (70 * scale + 0.5F).toInt()
}else{
this.layoutParams.width = requiredWidth
}
this.requestLayout()
}
fun handleResultResponse(context: Activity, it: ResultViewModel.ResultsUiState, closed: () -> Unit){ fun handleResultResponse(context: Activity, it: ResultViewModel.ResultsUiState, closed: () -> Unit){
val title = context.getString(it.errorMessage!!.first) val title = context.getString(it.errorMessage!!.first)
val message = it.errorMessage!!.second val message = it.errorMessage!!.second

View file

@ -0,0 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="?android:colorAccent" android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
</vector>

View file

@ -46,6 +46,7 @@
</HorizontalScrollView> </HorizontalScrollView>
<View <View
android:id="@+id/chipGroupDivider"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
style="@style/Divider.Vertical"/> style="@style/Divider.Vertical"/>

View file

@ -46,6 +46,7 @@
</HorizontalScrollView> </HorizontalScrollView>
<View <View
android:id="@+id/chipGroupDivider"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
style="@style/Divider.Vertical"/> style="@style/Divider.Vertical"/>

View file

@ -46,6 +46,7 @@
</HorizontalScrollView> </HorizontalScrollView>
<View <View
android:id="@+id/chipGroupDivider"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
style="@style/Divider.Vertical"/> style="@style/Divider.Vertical"/>

View file

@ -46,6 +46,7 @@
</HorizontalScrollView> </HorizontalScrollView>
<View <View
android:id="@+id/chipGroupDivider"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
style="@style/Divider.Vertical"/> style="@style/Divider.Vertical"/>

View file

@ -108,7 +108,7 @@
android:dividerPadding="5dp" android:dividerPadding="5dp"
android:orientation="vertical" android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@+id/output" app:layout_constraintBottom_toTopOf="@+id/output"
app:layout_constraintEnd_toStartOf="@+id/active_download_pause" app:layout_constraintEnd_toStartOf="@+id/active_pause_cancel_btns"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearlayout2_horizontalscrollview"> app:layout_constraintTop_toBottomOf="@+id/linearlayout2_horizontalscrollview">
@ -151,37 +151,55 @@
</LinearLayout> </LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/active_download_pause"
style="?attr/materialIconButtonFilledStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:backgroundTint="?attr/colorSurface"
app:cornerRadius="15dp"
android:layout_marginEnd="5dp"
app:icon="@drawable/exomedia_ic_play_arrow_white"
app:iconSize="30dp"
app:iconTint="?android:textColorPrimary"
app:layout_constraintBottom_toTopOf="@+id/output"
app:layout_constraintEnd_toStartOf="@id/active_download_delete"
app:layout_constraintTop_toBottomOf="@+id/linearlayout2_horizontalscrollview"
app:layout_constraintVertical_bias="0.51" />
<com.google.android.material.button.MaterialButton
android:id="@+id/active_download_delete" <LinearLayout
style="?attr/materialIconButtonFilledStyle" android:id="@+id/active_pause_cancel_btns"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="5dp" android:orientation="horizontal"
app:backgroundTint="?attr/colorSurface" android:animateLayoutChanges="true"
app:cornerRadius="15dp"
app:icon="@drawable/baseline_delete_24"
app:iconSize="30dp"
app:iconTint="?android:textColorPrimary"
app:layout_constraintBottom_toTopOf="@+id/output" app:layout_constraintBottom_toTopOf="@+id/output"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="5dp"
app:layout_constraintTop_toBottomOf="@+id/linearlayout2_horizontalscrollview" app:layout_constraintTop_toBottomOf="@+id/linearlayout2_horizontalscrollview"
app:layout_constraintVertical_bias="0.51" /> app:layout_constraintVertical_bias="0.51"
>
<com.google.android.material.button.MaterialButton
android:id="@+id/active_download_pause"
style="?attr/materialIconButtonFilledStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layoutAnimation="@anim/popup_enter"
app:backgroundTint="?attr/colorSurface"
app:cornerRadius="15dp"
app:icon="@drawable/exomedia_ic_play_arrow_white"
app:iconSize="30dp"
app:iconTint="?android:textColorPrimary"
app:layout_constraintBottom_toTopOf="@+id/output"
app:layout_constraintEnd_toStartOf="@id/active_download_delete"
app:layout_constraintTop_toBottomOf="@+id/linearlayout2_horizontalscrollview"
app:layout_constraintVertical_bias="0.51" />
<com.google.android.material.button.MaterialButton
android:id="@+id/active_download_delete"
style="?attr/materialIconButtonFilledStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:backgroundTint="?attr/colorSurface"
app:cornerRadius="15dp"
app:icon="@drawable/baseline_delete_24"
app:iconSize="30dp"
app:iconTint="?android:textColorPrimary"
app:layout_constraintBottom_toTopOf="@+id/output"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearlayout2_horizontalscrollview"
app:layout_constraintVertical_bias="0.51" />
</LinearLayout>
<TextView <TextView
android:id="@+id/output" android:id="@+id/output"

View file

@ -54,7 +54,6 @@
android:ellipsize="end" android:ellipsize="end"
android:text="@string/extra_command" android:text="@string/extra_command"
android:gravity="center" android:gravity="center"
android:maxLength="17"
android:maxLines="1" android:maxLines="1"
android:minWidth="30dp" android:minWidth="30dp"
android:paddingHorizontal="5dp" android:paddingHorizontal="5dp"

View file

@ -1,9 +1,9 @@
<?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:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:orientation="vertical">
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
@ -103,17 +103,51 @@
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.materialswitch.MaterialSwitch <com.google.android.material.card.MaterialCardView
android:id="@+id/extraCommandsSwitch"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:checked="false" app:cardCornerRadius="20dp">
android:layout_marginHorizontal="5dp"
android:textSize="15sp"
android:text="@string/extra_command"
app:layout_constraintEnd_toEndOf="parent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:orientation="vertical">
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/extraCommandsSwitch"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:checked="false"
android:text="@string/extra_command"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox
android:id="@+id/checkbox_audio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:checked="true"
android:text="@string/audio" />
<CheckBox
android:id="@+id/checkbox_video"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/video" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<View <View
style="@style/Divider.Horizontal" style="@style/Divider.Horizontal"
@ -171,5 +205,4 @@
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -89,6 +89,8 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clickable="false"
android:focusable="false"
android:layout_marginStart="10dp" android:layout_marginStart="10dp"
android:scrollbars="none"> android:scrollbars="none">
@ -115,6 +117,7 @@
android:textStyle="bold" android:textStyle="bold"
app:cornerRadius="10dp" app:cornerRadius="10dp"
app:drawableStartCompat="@drawable/ic_music" app:drawableStartCompat="@drawable/ic_music"
android:textCursorDrawable="@string/audio_format"
app:drawableTint="@color/white" app:drawableTint="@color/white"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"

View file

@ -46,6 +46,7 @@
</HorizontalScrollView> </HorizontalScrollView>
<View <View
android:id="@+id/chipGroupDivider"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
style="@style/Divider.Vertical"/> style="@style/Divider.Vertical"/>

View file

@ -6,7 +6,7 @@
android:id="@+id/search_command" android:id="@+id/search_command"
android:title="@string/search" android:title="@string/search"
android:icon="@drawable/ic_search" android:icon="@drawable/ic_search"
app:showAsAction="collapseActionView|ifRoom" app:showAsAction="always"
app:actionViewClass="androidx.appcompat.widget.SearchView"/> app:actionViewClass="androidx.appcompat.widget.SearchView"/>
<item <item

View file

@ -7,14 +7,14 @@
android:id="@+id/search_history" android:id="@+id/search_history"
android:title="@string/search" android:title="@string/search"
android:icon="@drawable/ic_search" android:icon="@drawable/ic_search"
app:showAsAction="collapseActionView|ifRoom" app:showAsAction="collapseActionView|always"
app:actionViewClass="androidx.appcompat.widget.SearchView"/> app:actionViewClass="androidx.appcompat.widget.SearchView"/>
<item <item
android:id="@+id/download_queue" android:id="@+id/download_queue"
android:title="@string/download_queue" android:title="@string/download_queue"
android:icon="@drawable/baseline_downloading_24" android:icon="@drawable/baseline_downloading_24"
app:showAsAction="collapseActionView|ifRoom"/> app:showAsAction="always"/>
<item <item
android:id="@+id/remove_history" android:id="@+id/remove_history"

View file

@ -1017,4 +1017,24 @@
<item>ko</item> <item>ko</item>
</string-array> </string-array>
<array name="swipe_gestures">
<item>@string/downloads</item>
<item>@string/in_queue</item>
<item>@string/cancelled</item>
<item>@string/errored</item>
<item>@string/saved</item>
<item>@string/command_templates</item>
<item>@string/cookies</item>
</array>
<array name="swipe_gestures_values">
<item>history</item>
<item>queued</item>
<item>cancelled</item>
<item>errored</item>
<item>saved</item>
<item>templates</item>
<item>cookies</item>
</array>
</resources> </resources>

View file

@ -6,9 +6,11 @@
<ListPreference <ListPreference
android:entries="@array/language_values" android:entries="@array/language_values"
android:entryValues="@array/language_values" android:entryValues="@array/language_values"
android:defaultValue=""
android:icon="@drawable/baseline_translate_24" android:icon="@drawable/baseline_translate_24"
app:key="app_language" app:key="app_language"
app:summary="en" app:useSimpleSummaryProvider="true"
app:summary=""
app:title="@string/language" /> app:title="@string/language" />
<ListPreference <ListPreference
@ -110,18 +112,19 @@
app:summary="@string/search_suggestions_summary" app:summary="@string/search_suggestions_summary"
app:title="@string/search_suggestions" /> app:title="@string/search_suggestions" />
<SwitchPreferenceCompat <MultiSelectListPreference
android:widgetLayout="@layout/preferece_material_switch"
app:defaultValue="true"
android:icon="@drawable/baseline_swipe_24" android:icon="@drawable/baseline_swipe_24"
android:key="swipe_gestures" android:key="swipe_gesture"
android:entries="@array/swipe_gestures"
android:entryValues="@array/swipe_gestures_values"
android:defaultValue="@array/swipe_gestures_values"
app:summary="@string/swipe_gestures_summary" app:summary="@string/swipe_gestures_summary"
app:title="@string/swipe_gestures" /> app:title="@string/swipe_gestures" />
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:widgetLayout="@layout/preferece_material_switch" android:widgetLayout="@layout/preferece_material_switch"
app:defaultValue="false" app:defaultValue="false"
android:icon="@drawable/baseline_share_24" android:icon="@drawable/baseline_share2_24"
android:key="show_terminal" android:key="show_terminal"
app:title="@string/show_terminal" /> app:title="@string/show_terminal" />

View file

@ -21,7 +21,7 @@ buildscript {
coroutineVer = '1.7.3' coroutineVer = '1.7.3'
retrofitVer = "2.9.0" retrofitVer = "2.9.0"
kodeinVer = "7.16.0" kodeinVer = "7.16.0"
navVer = "2.7.4" navVer = "2.7.5"
media3_version = "1.1.1" media3_version = "1.1.1"
agp_version = '8.1.0' agp_version = '8.1.0'
agp_version1 = '7.4.2' agp_version1 = '7.4.2'