From 17d6a87e49beae7213dabd771d0d3214b8857277 Mon Sep 17 00:00:00 2001 From: zaednasr <75589932+zaednasr@users.noreply.github.com> Date: Sun, 12 Nov 2023 15:59:55 +0100 Subject: [PATCH] stuff - 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 --- .../12.json | 572 ++++++++++++++++++ .../com/deniscerri/ytdlnis/MainActivity.kt | 19 +- .../deniscerri/ytdlnis/database/DBManager.kt | 15 +- .../database/dao/CommandTemplateDao.kt | 6 + .../ytdlnis/database/dao/ResultDao.kt | 3 + .../database/models/CommandTemplate.kt | 6 +- .../database/repository/DownloadRepository.kt | 3 +- .../database/repository/ResultRepository.kt | 20 +- .../database/viewmodel/DownloadViewModel.kt | 30 +- .../database/viewmodel/ResultViewModel.kt | 70 +++ .../com/deniscerri/ytdlnis/ui/HomeFragment.kt | 27 +- .../ui/adapter/ActiveDownloadAdapter.kt | 37 +- .../ytdlnis/ui/adapter/TemplatesAdapter.kt | 23 +- .../ui/downloadcard/AddExtraCommandsDialog.kt | 2 +- .../downloadcard/CutVideoBottomSheetDialog.kt | 2 +- .../ui/downloadcard/DownloadAudioFragment.kt | 2 +- .../downloadcard/DownloadBottomSheetDialog.kt | 288 ++++----- .../downloadcard/DownloadCommandFragment.kt | 6 +- .../DownloadMultipleBottomSheetDialog.kt | 2 +- .../ui/downloadcard/DownloadVideoFragment.kt | 2 +- .../downloadcard/ResultCardDetailsDialog.kt | 26 +- .../downloadcard/SelectPlaylistItemsDialog.kt | 4 +- .../ui/downloads/ActiveDownloadsFragment.kt | 43 +- .../downloads/CancelledDownloadsFragment.kt | 7 +- .../ui/downloads/ErroredDownloadsFragment.kt | 7 +- .../ytdlnis/ui/downloads/HistoryFragment.kt | 5 +- .../ui/downloads/QueuedDownloadsFragment.kt | 7 +- .../ui/downloads/SavedDownloadsFragment.kt | 7 +- .../ui/more/CommandTemplatesFragment.kt | 3 +- .../ytdlnis/ui/more/CookiesFragment.kt | 3 +- .../more/downloadLogs/DownloadLogFragment.kt | 2 +- .../downloadLogs/DownloadLogListFragment.kt | 4 +- .../ui/more/settings/BaseSettingsFragment.kt | 5 +- .../more/settings/GeneralSettingsFragment.kt | 3 +- .../terminal/TerminalDownloadsListFragment.kt | 2 +- .../ui/more/terminal/TerminalFragment.kt | 2 +- .../com/deniscerri/ytdlnis/util/Extensions.kt | 94 +++ .../com/deniscerri/ytdlnis/util/InfoUtil.kt | 2 +- .../com/deniscerri/ytdlnis/util/UiUtil.kt | 148 +++-- .../main/res/drawable/baseline_share2_24.xml | 5 + .../main/res/layout-land/fragment_home.xml | 1 + .../res/layout-sw600dp-land/fragment_home.xml | 1 + .../main/res/layout-sw600dp/fragment_home.xml | 1 + .../res/layout-sw950dp-land/fragment_home.xml | 1 + .../main/res/layout/active_download_card.xml | 70 ++- .../main/res/layout/command_template_item.xml | 1 - .../res/layout/create_command_template.xml | 57 +- app/src/main/res/layout/format_item.xml | 3 + app/src/main/res/layout/fragment_home.xml | 1 + .../main/res/menu/command_templates_menu.xml | 2 +- app/src/main/res/menu/history_menu.xml | 4 +- app/src/main/res/values/arrays.xml | 20 + app/src/main/res/xml/general_preferences.xml | 15 +- build.gradle | 2 +- 54 files changed, 1304 insertions(+), 389 deletions(-) create mode 100644 app/schemas/com.deniscerri.ytdlnis.database.DBManager/12.json create mode 100644 app/src/main/java/com/deniscerri/ytdlnis/util/Extensions.kt create mode 100644 app/src/main/res/drawable/baseline_share2_24.xml diff --git a/app/schemas/com.deniscerri.ytdlnis.database.DBManager/12.json b/app/schemas/com.deniscerri.ytdlnis.database.DBManager/12.json new file mode 100644 index 00000000..9636e209 --- /dev/null +++ b/app/schemas/com.deniscerri.ytdlnis.database.DBManager/12.json @@ -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')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.kt b/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.kt index fd918e02..ab3a0b47 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.kt @@ -59,6 +59,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import java.io.BufferedReader @@ -146,14 +147,16 @@ class MainActivity : BaseActivity() { } val activeDownloadsBadge = (navigationView as NavigationBarView).getOrCreateBadge(R.id.historyFragment) - downloadViewModel.activeDownloadsCount.observe(this){ - if (it == 0) { - activeDownloadsBadge.isVisible = false - activeDownloadsBadge.clearNumber() - } - else { - activeDownloadsBadge.isVisible = true - activeDownloadsBadge.number = it + lifecycleScope.launch { + downloadViewModel.activeDownloadsCount.collectLatest { + if (it == 0) { + activeDownloadsBadge.isVisible = false + activeDownloadsBadge.clearNumber() + } + else { + activeDownloadsBadge.isVisible = true + activeDownloadsBadge.number = it + } } } } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/DBManager.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/DBManager.kt index 101a84e0..0b36f4db 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/DBManager.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/DBManager.kt @@ -2,6 +2,11 @@ package com.deniscerri.ytdlnis.database import android.content.Context 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.models.* @@ -18,7 +23,7 @@ import com.deniscerri.ytdlnis.database.models.* LogItem::class, TerminalItem::class ], - version = 11, + version = 12, autoMigrations = [ AutoMigration (from = 1, to = 2), AutoMigration (from = 2, to = 3), @@ -29,7 +34,8 @@ import com.deniscerri.ytdlnis.database.models.* AutoMigration (from = 7, to = 8), AutoMigration (from = 8, to = 9), AutoMigration (from = 9, to = 10), - AutoMigration (from = 10, to = 11) + AutoMigration (from = 10, to = 11), + AutoMigration (from = 11, to = 12) ] ) abstract class DBManager : RoomDatabase(){ @@ -53,15 +59,18 @@ abstract class DBManager : RoomDatabase(){ //if its not null return it, otherwise create db fun getInstance(context: Context) : DBManager { return instance ?: synchronized(this){ + val dbInstance = Room.databaseBuilder( context.applicationContext, DBManager::class.java, "YTDLnisDatabase" ) - .build() + .build() instance = dbInstance dbInstance } } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CommandTemplateDao.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CommandTemplateDao.kt index b3b1b8b3..94888975 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CommandTemplateDao.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CommandTemplateDao.kt @@ -34,6 +34,12 @@ interface CommandTemplateDao { @Query("SELECT content FROM commandTemplates WHERE useAsExtraCommand is 1") fun getAllTemplatesAsExtraCommands() : List + @Query("SELECT content FROM commandTemplates WHERE useAsExtraCommand is 1 AND useAsExtraCommandAudio = 1") + fun getAllTemplatesAsExtraCommandsForAudio() : List + + @Query("SELECT content FROM commandTemplates WHERE useAsExtraCommand is 1 AND useAsExtraCommandVideo = 1") + fun getAllTemplatesAsExtraCommandsForVideo() : List + @Query("SELECT * FROM templateShortcuts ORDER BY id DESC") fun getAllShortcutsFlow() : Flow> diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ResultDao.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ResultDao.kt index 5bebde4f..e4796154 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ResultDao.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ResultDao.kt @@ -24,6 +24,9 @@ interface ResultDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insert(item: ResultItem) : Long + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertMultiple(items: List) : List + @Update(onConflict = OnConflictStrategy.REPLACE) suspend fun update(item: ResultItem) diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/models/CommandTemplate.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/models/CommandTemplate.kt index d7f9c01c..c87c9002 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/models/CommandTemplate.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/models/CommandTemplate.kt @@ -13,5 +13,9 @@ data class CommandTemplate( var title: String, var content: String, @ColumnInfo(defaultValue = "0") - var useAsExtraCommand: Boolean + var useAsExtraCommand: Boolean, + @ColumnInfo(defaultValue = "1") + var useAsExtraCommandAudio: Boolean, + @ColumnInfo(defaultValue = "1") + var useAsExtraCommandVideo: Boolean ) \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt index 3cee20a1..658fd24f 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt @@ -8,6 +8,7 @@ import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.DownloadItemSimple import com.deniscerri.ytdlnis.util.FileUtil import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged import java.io.File @@ -16,7 +17,7 @@ class DownloadRepository(private val downloadDao: DownloadDao) { config = PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 1), pagingSourceFactory = {downloadDao.getAllDownloads()} ) - val activeDownloads : Flow> = downloadDao.getActiveDownloads() + val activeDownloads : Flow> = downloadDao.getActiveDownloads().distinctUntilChanged() val queuedDownloads : Pager = Pager( config = PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 1), pagingSourceFactory = {downloadDao.getQueuedDownloads()} diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt index 5f147926..82a65f74 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt @@ -25,9 +25,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co val infoUtil = InfoUtil(context) val items = infoUtil.getTrending() itemCount.value = items.size - for (i in items){ - resultDao.insert(i!!) - } + resultDao.insertMultiple(items) } suspend fun search(inputQuery: String, resetResults: Boolean) : ArrayList{ @@ -35,9 +33,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co if (resetResults) deleteAll() val res = infoUtil.search(inputQuery) itemCount.value = res.size - res.forEach { - resultDao.insert(it!!) - } + resultDao.insertMultiple(res) return res } @@ -50,9 +46,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co }else{ v.forEach { it?.playlistTitle = "ytdlnis-Search" } } - v.forEach { - resultDao.insert(it!!) - } + resultDao.insertMultiple(v) return ArrayList(v) } @@ -72,9 +66,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co nextPageToken = tmpToken } while (true) itemCount.value = items.size - items.forEach { - resultDao.insert(it!!) - } + resultDao.insertMultiple(items.toList()) return items } @@ -87,9 +79,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co }else{ items.forEach { it!!.playlistTitle = "ytdlnis-Search" } } - items.forEach { - resultDao.insert(it!!) - } + resultDao.insertMultiple(items.toList()) return items } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt index 7e8f4b6b..f412fc3b 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt @@ -64,8 +64,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application private val infoUtil : InfoUtil val allDownloads : Flow> val queuedDownloads : Flow> - val activeDownloads : LiveData> - val activeDownloadsCount : LiveData + val activeDownloads : Flow> + val activeDownloadsCount : Flow val cancelledDownloads : Flow> val erroredDownloads : Flow> val savedDownloads : Flow> @@ -78,7 +78,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application private val formatIDPreference: List private val audioFormatIDPreference: List private val resources : Resources - private var extraCommands: String = "" + private var extraCommandsForAudio: String = "" + private var extraCommandsForVideo: String = "" private var audioContainer: String? private var videoContainer: String? @@ -123,14 +124,15 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application allDownloads = repository.allDownloads.flow queuedDownloads = repository.queuedDownloads.flow - activeDownloads = repository.activeDownloads.asLiveData() - activeDownloadsCount = repository.activeDownloadsCount.asLiveData() + activeDownloads = repository.activeDownloads + activeDownloadsCount = repository.activeDownloadsCount savedDownloads = repository.savedDownloads.flow cancelledDownloads = repository.cancelledDownloads.flow erroredDownloads = repository.erroredDownloads.flow viewModelScope.launch(Dispatchers.IO){ 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 ) + val extraCommands = when(type){ + Type.audio -> extraCommandsForAudio + Type.video -> extraCommandsForVideo + else -> "" + } + return DownloadItem(0, resultItem.url, resultItem.title, @@ -404,6 +412,12 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application 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 videoPreferences = VideoPreferences(embedSubs, addChapters, false, ArrayList(sponsorblock), saveSubs) val downloadPath = File(historyItem.downloadPath) @@ -467,9 +481,9 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application else -> { val lastUsedCommandTemplate = sharedPreferences.getString("lastCommandTemplateUsed", "")!! val c = if (lastUsedCommandTemplate.isBlank()){ - commandTemplateDao.getFirst() ?: CommandTemplate(0,"","", false) + commandTemplateDao.getFirst() ?: CommandTemplate(0,"","", useAsExtraCommand = false, useAsExtraCommandAudio = false, useAsExtraCommandVideo = false) }else{ - commandTemplateDao.getTemplateByContent(lastUsedCommandTemplate) ?: CommandTemplate(0, "", lastUsedCommandTemplate, false) + commandTemplateDao.getTemplateByContent(lastUsedCommandTemplate) ?: CommandTemplate(0, "", lastUsedCommandTemplate, useAsExtraCommand = false, useAsExtraCommandAudio = false, useAsExtraCommandVideo = false) } return generateCommandFormat(c) } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt index 4e1bb084..7cb0b3e0 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt @@ -4,19 +4,29 @@ import android.app.Application import android.content.SharedPreferences import android.util.Log 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.LiveData import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope +import androidx.navigation.fragment.findNavController import androidx.preference.PreferenceManager import com.deniscerri.ytdlnis.App import com.deniscerri.ytdlnis.R 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.SearchHistoryItem import com.deniscerri.ytdlnis.database.repository.ResultRepository 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.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch @@ -28,6 +38,7 @@ class ResultViewModel(application: Application) : AndroidViewModel(application) val repository : ResultRepository private val searchHistoryRepository : SearchHistoryRepository val items : LiveData> + private val infoUtil: InfoUtil data class ResultsUiState( var processing: Boolean, var errorMessage: Pair?, @@ -51,6 +62,7 @@ class ResultViewModel(application: Application) : AndroidViewModel(application) searchHistoryRepository = SearchHistoryRepository(DBManager.getInstance(application).searchHistoryDao) items = repository.allResults.asLiveData() sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application) + infoUtil = InfoUtil(application) } fun checkTrending() = viewModelScope.launch(Dispatchers.IO){ @@ -171,4 +183,62 @@ class ResultViewModel(application: Application) : AndroidViewModel(application) repository.delete(it) } } + + val updatingData: MutableStateFlow = MutableStateFlow(false) + var updateResultData: MutableStateFlow?> = MutableStateFlow(null) + private var updateResultDataJob : Job? = null + + val updatingFormats: MutableStateFlow = MutableStateFlow(false) + var updateFormatsResultData: MutableStateFlow?> = 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() + } } \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt index 6882f1a5..1a85ae53 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt @@ -33,18 +33,15 @@ import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.deniscerri.ytdlnis.MainActivity 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.ResultItem import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel -import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog -import com.deniscerri.ytdlnis.ui.downloadcard.DownloadMultipleBottomSheetDialog -import com.deniscerri.ytdlnis.ui.downloadcard.ResultCardDetailsDialog +import com.deniscerri.ytdlnis.ui.adapter.HomeAdapter +import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll import com.deniscerri.ytdlnis.util.InfoUtil import com.deniscerri.ytdlnis.util.ThemeUtil import com.deniscerri.ytdlnis.util.UiUtil -import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll import com.facebook.shimmer.ShimmerFrameLayout import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.MaterialToolbar @@ -316,6 +313,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene providersChipGroup!!.addView(tmp) } + val chipGroupDivider : View? = requireView().findViewById(R.id.chipGroupDivider) searchView!!.addTransitionListener { _, _, newState -> 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{ val clipboard = 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 - if (regex.containsMatchIn(clip.toString())) { + if (Patterns.WEB_URL.matcher(clip.toString()).matches()) { linkYouCopied!!.visibility = VISIBLE val textView = linkYouCopied!!.findViewById(R.id.suggestion_text) textView.text = getString(R.string.link_you_copied) @@ -381,6 +385,13 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene lifecycleScope.launch { 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 -> diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/ActiveDownloadAdapter.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/ActiveDownloadAdapter.kt index 8deaa5a6..b9cefd30 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/ActiveDownloadAdapter.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/ActiveDownloadAdapter.kt @@ -1,5 +1,6 @@ package com.deniscerri.ytdlnis.ui.adapter +import android.animation.ValueAnimator import android.app.Activity import android.content.SharedPreferences import android.graphics.Color @@ -9,6 +10,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView +import android.widget.LinearLayout import android.widget.TextView import androidx.core.content.ContextCompat 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.repository.DownloadRepository import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel +import com.deniscerri.ytdlnis.util.Extensions.dp import com.deniscerri.ytdlnis.util.FileUtil import com.google.android.material.button.MaterialButton 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.squareup.picasso.Picasso + class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter(AsyncDifferConfig.Builder( DIFF_CALLBACK ).build()) { @@ -137,16 +141,37 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity: when(DownloadRepository.Status.valueOf(item.status)){ DownloadRepository.Status.Active -> { 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 - pauseButton.isEnabled = true - pauseButton.tag = ActiveDownloadAction.Pause } DownloadRepository.Status.ActivePaused -> { progressBar.isIndeterminate = false - pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_play_arrow_white) - pauseButton.tag = ActiveDownloadAction.Resume - pauseButton.isEnabled = true + + val fromRadius: Int = dp(activity.resources, 15f) + 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 } DownloadRepository.Status.PausedReQueued -> { diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/TemplatesAdapter.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/TemplatesAdapter.kt index 170a0b94..0b87fb90 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/TemplatesAdapter.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/TemplatesAdapter.kt @@ -6,6 +6,8 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView +import androidx.compose.ui.util.fastJoinToString +import androidx.core.view.isVisible import androidx.recyclerview.widget.AsyncDifferConfig import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter @@ -51,11 +53,18 @@ class TemplatesAdapter(onItemClickListener: OnItemClickListener, activity: Activ val content = card.findViewById(R.id.content) content.text = item?.content - val useInExtraCommands = card.findViewById(R.id.useInExtraCommands) - if (item!!.useAsExtraCommand) useInExtraCommands.visibility = View.VISIBLE - else useInExtraCommands.visibility = View.GONE + card.findViewById(R.id.useInExtraCommands).apply { + isVisible = item!!.useAsExtraCommand + 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.strokeWidth = 5 } else { @@ -132,7 +141,11 @@ class TemplatesAdapter(onItemClickListener: OnItemClickListener, activity: Activ } 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 } } } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/AddExtraCommandsDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/AddExtraCommandsDialog.kt index 00936e27..43552868 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/AddExtraCommandsDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/AddExtraCommandsDialog.kt @@ -21,7 +21,7 @@ import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel import com.deniscerri.ytdlnis.util.InfoUtil 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.BottomSheetDialogFragment import kotlinx.coroutines.Dispatchers diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/CutVideoBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/CutVideoBottomSheetDialog.kt index 6c4bd510..28e7b1ea 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/CutVideoBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/CutVideoBottomSheetDialog.kt @@ -30,7 +30,7 @@ import com.deniscerri.ytdlnis.database.models.ChapterItem import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.util.InfoUtil 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.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialogFragment diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt index 49d70648..ee8b7150 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt @@ -69,7 +69,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private infoUtil = InfoUtil(requireContext()) genericAudioFormats = infoUtil.getGenericAudioFormats(requireContext().resources) 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 } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadBottomSheetDialog.kt index 28bc98da..cde3a5a3 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadBottomSheetDialog.kt @@ -9,6 +9,7 @@ import android.os.Build import android.os.Bundle import android.text.format.DateFormat import android.util.DisplayMetrics +import android.util.Log import android.util.Patterns import android.view.LayoutInflater import android.view.View @@ -20,8 +21,6 @@ import androidx.core.content.edit import androidx.core.os.bundleOf import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope -import androidx.navigation.NavController -import androidx.navigation.NavDestination import androidx.navigation.fragment.findNavController import androidx.preference.PreferenceManager 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.dialog.MaterialAlertDialogBuilder import com.google.android.material.progressindicator.LinearProgressIndicator +import com.google.android.material.snackbar.Snackbar import com.google.android.material.tabs.TabLayout import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.update @@ -78,9 +77,6 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() { private lateinit var shimmerLoadingSubtitle : ShimmerFrameLayout private lateinit var subtitle : View - private var updateJob : Job? = null - private var updateFormatsJob : Job? = null - private lateinit var result: ResultItem private lateinit var type: Type @@ -88,9 +84,9 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] - historyViewModel = ViewModelProvider(this)[HistoryViewModel::class.java] - resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java] + downloadViewModel = ViewModelProvider(requireActivity())[DownloadViewModel::class.java] + historyViewModel = ViewModelProvider(requireActivity())[HistoryViewModel::class.java] + resultViewModel = ViewModelProvider(requireActivity())[ResultViewModel::class.java] commandTemplateDao = DBManager.getInstance(requireContext()).commandTemplateDao infoUtil = InfoUtil(requireContext()) sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) @@ -143,6 +139,16 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() { shimmerLoadingSubtitle = view.findViewById(R.id.shimmer_loading_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 { isNestedScrollingEnabled = false overScrollMode = View.OVER_SCROLL_NEVER @@ -259,7 +265,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() { 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(R.id.bottomsheet_schedule_button) scheduleBtn.visibility = if(shownFields.contains("schedule")){ @@ -272,8 +278,11 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() { scheduleBtn.setOnClickListener{ UiUtil.showDatePicker(fragmentManager) { - updateJob?.cancel() - updateFormatsJob?.cancel() + lifecycleScope.launch { + resultViewModel.cancelUpdateItemData() + resultViewModel.cancelUpdateFormatsItemData() + } + scheduleBtn.isEnabled = false download.isEnabled = false val item: DownloadItem = getDownloadItem() @@ -287,8 +296,10 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() { } } download!!.setOnClickListener { - updateJob?.cancel() - updateFormatsJob?.cancel() + lifecycleScope.launch { + resultViewModel.cancelUpdateItemData() + resultViewModel.cancelUpdateFormatsItemData() + } scheduleBtn.isEnabled = false download.isEnabled = false val item: DownloadItem = getDownloadItem() @@ -356,7 +367,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() { }else { 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)){ - 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(R.id.format_loading_progress)?.visibility = View.VISIBLE + } + runCatching { + val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment + f1.view?.findViewById(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(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(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{ return when(selectedTabPosition){ 0 -> { @@ -430,139 +549,22 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() { } private fun initUpdateData() { - updateJob = CoroutineScope(SupervisorJob()).launch(Dispatchers.IO){ - findNavController().saveState() - kotlin.runCatching { - 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() - } - } - } + if (result.url.isBlank()) { + dismiss() + return } - shimmerLoading.setOnClickListener { - updateJob?.cancel() - (updateItem.parent as LinearLayout).visibility = View.VISIBLE + if (resultViewModel.updatingData.value) return + + 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){ - updateFormatsJob = CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) { - withContext(Dispatchers.Main){ - runCatching { - val f1 = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment - f1.view?.findViewById(R.id.format_loading_progress)?.visibility = View.VISIBLE - } - runCatching { - val f1 = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment - f1.view?.findViewById(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(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(R.id.format_loading_progress)?.visibility = View.GONE - } - } - - if (formats.isNotEmpty()){ - result.formats = formats - resultViewModel.update(result) - } - + private fun initUpdateFormats(res: ResultItem){ + if (resultViewModel.updatingFormats.value) return + CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) { + resultViewModel.updateFormatItemData(res) } - updateFormatsJob?.start() } } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadCommandFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadCommandFragment.kt index 994aa46c..5496708a 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadCommandFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadCommandFragment.kt @@ -31,7 +31,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.util.FileUtil 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.google.android.material.card.MaterialCardView 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() 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 = "" } @@ -231,7 +231,7 @@ class DownloadCommandFragment(private val resultItem: ResultItem? = null, privat 0, "", chosenCommandView.editText!!.text.toString(), - false + useAsExtraCommand = false, useAsExtraCommandAudio = false, useAsExtraCommandVideo = false ) UiUtil.showCommandTemplateCreationOrUpdatingSheet(current, requireActivity(), viewLifecycleOwner, commandTemplateViewModel) { templates.add(it) diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt index 37dc6da7..426d88ce 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt @@ -48,7 +48,7 @@ import com.deniscerri.ytdlnis.receiver.ShareActivity import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.InfoUtil 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.google.android.material.bottomappbar.BottomAppBar import com.google.android.material.bottomsheet.BottomSheetBehavior diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt index 4784ed6d..37e927bb 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt @@ -74,7 +74,7 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private infoUtil = InfoUtil(requireContext()) genericVideoFormats = infoUtil.getGenericVideoFormats(requireContext().resources) 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 } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/ResultCardDetailsDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/ResultCardDetailsDialog.kt index 62c45088..38840150 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/ResultCardDetailsDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/ResultCardDetailsDialog.kt @@ -62,6 +62,7 @@ import com.google.android.material.snackbar.Snackbar import com.yausername.youtubedl_android.YoutubeDL import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @@ -181,14 +182,17 @@ class ResultCardDetailsDialog : BottomSheetDialogFragment(), GenericDownloadAdap queuedRecycler.adapter = queuedAdapter queuedRecycler.layoutManager = GridLayoutManager(context, resources.getInteger(R.integer.grid_size)) - downloadViewModel.activeDownloads.map { it.filter { d -> d.url == item.url } }.observe(viewLifecycleOwner) { - activeAdapter.submitList(it) - if (it.isEmpty()){ - running.visibility = View.GONE - runningRecycler.visibility = View.GONE - }else{ - running.visibility = View.VISIBLE - runningRecycler.visibility = View.VISIBLE + lifecycleScope.launch { + downloadViewModel.activeDownloads.map { it.filter { d -> d.url == item.url } }.collectLatest { + delay(500) + activeAdapter.submitList(it) + if (it.isEmpty()){ + running.visibility = View.GONE + runningRecycler.visibility = View.GONE + }else{ + running.visibility = View.VISIBLE + runningRecycler.visibility = View.VISIBLE + } } } @@ -231,8 +235,10 @@ class ResultCardDetailsDialog : BottomSheetDialogFragment(), GenericDownloadAdap } } - downloadViewModel.activeDownloadsCount.observe(viewLifecycleOwner){ - activeCount = it + lifecycleScope.launch { + downloadViewModel.activeDownloadsCount.collectLatest { + activeCount = it + } } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/SelectPlaylistItemsDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/SelectPlaylistItemsDialog.kt index 2e4d8908..8181e370 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/SelectPlaylistItemsDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/SelectPlaylistItemsDialog.kt @@ -25,8 +25,8 @@ import com.deniscerri.ytdlnis.database.models.ResultItem import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel import com.deniscerri.ytdlnis.receiver.ShareActivity -import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll -import com.deniscerri.ytdlnis.util.UiUtil.setTextAndRecalculateWidth +import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll +import com.deniscerri.ytdlnis.util.Extensions.setTextAndRecalculateWidth import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.bottomappbar.BottomAppBar import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/ActiveDownloadsFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/ActiveDownloadsFragment.kt index 285c41ac..08ddb2df 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/ActiveDownloadsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/ActiveDownloadsFragment.kt @@ -23,11 +23,13 @@ import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.repository.DownloadRepository import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.util.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.progressindicator.LinearProgressIndicator import com.yausername.youtubedl_android.YoutubeDL import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext @@ -157,21 +159,30 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis } } - downloadViewModel.activeDownloads.observe(viewLifecycleOwner) { - 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{ - activeDownloads.notifyDataSetChanged() - pauseResume.visibility = View.GONE + lifecycleScope.launch { + downloadViewModel.activeDownloadsCount.collectLatest { + delay(200) + noResults.visibility = if (it == 0) View.VISIBLE else 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){ downloadViewModel.updateDownload(item) } - activeDownloads.notifyItemChanged(position) } } ActiveDownloadAdapter.ActiveDownloadAction.Resume -> { @@ -221,7 +231,6 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis withContext(Dispatchers.IO){ downloadViewModel.updateDownload(item) } - activeDownloads.notifyItemChanged(position) runBlocking { downloadViewModel.queueDownloads(listOf(item)) diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/CancelledDownloadsFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/CancelledDownloadsFragment.kt index f09a7001..24055443 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/CancelledDownloadsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/CancelledDownloadsFragment.kt @@ -23,6 +23,7 @@ import androidx.preference.PreferenceManager import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView +import com.afollestad.materialdialogs.utils.MDUtil.getStringArray import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter 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.ui.downloadcard.DownloadBottomSheetDialog import com.deniscerri.ytdlnis.util.UiUtil -import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll -import com.deniscerri.ytdlnis.util.UiUtil.forceFastScrollMode +import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll +import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.color.MaterialColors import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -80,7 +81,7 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic cancelledRecyclerView.adapter = adapter cancelledRecyclerView.enableFastScroll() 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) itemTouchHelper.attachToRecyclerView(cancelledRecyclerView) } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/ErroredDownloadsFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/ErroredDownloadsFragment.kt index c284af96..5d31deab 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/ErroredDownloadsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/ErroredDownloadsFragment.kt @@ -24,6 +24,7 @@ import androidx.preference.PreferenceManager import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView +import com.afollestad.materialdialogs.utils.MDUtil.getStringArray import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter 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.ui.downloadcard.DownloadBottomSheetDialog import com.deniscerri.ytdlnis.util.UiUtil -import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll -import com.deniscerri.ytdlnis.util.UiUtil.forceFastScrollMode +import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll +import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.color.MaterialColors import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -80,7 +81,7 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL erroredRecyclerView.adapter = adapter erroredRecyclerView.enableFastScroll() 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) itemTouchHelper.attachToRecyclerView(erroredRecyclerView) } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/HistoryFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/HistoryFragment.kt index d2ccb095..f14e7547 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/HistoryFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/HistoryFragment.kt @@ -36,6 +36,7 @@ import androidx.preference.PreferenceManager import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView +import com.afollestad.materialdialogs.utils.MDUtil.getStringArray import com.deniscerri.ytdlnis.MainActivity import com.deniscerri.ytdlnis.R 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.ui.downloadcard.DownloadBottomSheetDialog 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.MaterialToolbar import com.google.android.material.bottomsheet.BottomSheetDialog @@ -122,7 +123,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{ recyclerView?.enableFastScroll() 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) itemTouchHelper.attachToRecyclerView(recyclerView) } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/QueuedDownloadsFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/QueuedDownloadsFragment.kt index 7d9c4498..57d54b4e 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/QueuedDownloadsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/QueuedDownloadsFragment.kt @@ -26,6 +26,7 @@ import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import androidx.work.WorkManager +import com.afollestad.materialdialogs.utils.MDUtil.getStringArray import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter 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.NotificationUtil import com.deniscerri.ytdlnis.util.UiUtil -import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll -import com.deniscerri.ytdlnis.util.UiUtil.forceFastScrollMode +import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll +import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.color.MaterialColors import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -91,7 +92,7 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi queuedRecyclerView.enableFastScroll() 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) itemTouchHelper.attachToRecyclerView(queuedRecyclerView) } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/SavedDownloadsFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/SavedDownloadsFragment.kt index 152abbaa..c767d9c7 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/SavedDownloadsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/SavedDownloadsFragment.kt @@ -23,6 +23,7 @@ import androidx.preference.PreferenceManager import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView +import com.afollestad.materialdialogs.utils.MDUtil.getStringArray import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter 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.ui.downloadcard.DownloadBottomSheetDialog import com.deniscerri.ytdlnis.util.UiUtil -import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll -import com.deniscerri.ytdlnis.util.UiUtil.forceFastScrollMode +import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll +import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.color.MaterialColors import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -79,7 +80,7 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis savedRecyclerView.enableFastScroll() savedRecyclerView.adapter = adapter 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) itemTouchHelper.attachToRecyclerView(savedRecyclerView) } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/CommandTemplatesFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/CommandTemplatesFragment.kt index 572cc46a..91b1d0ed 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/CommandTemplatesFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/CommandTemplatesFragment.kt @@ -29,6 +29,7 @@ import androidx.preference.PreferenceManager import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.afollestad.materialdialogs.utils.MDUtil.getStringArray import com.deniscerri.ytdlnis.MainActivity import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.ui.adapter.TemplatesAdapter @@ -94,7 +95,7 @@ class CommandTemplatesFragment : Fragment(), TemplatesAdapter.OnItemClickListene recyclerView.layoutManager = LinearLayoutManager(context) recyclerView.adapter = templatesAdapter 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) itemTouchHelper.attachToRecyclerView(recyclerView) } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/CookiesFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/CookiesFragment.kt index 583d95eb..d2edf317 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/CookiesFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/CookiesFragment.kt @@ -23,6 +23,7 @@ import androidx.preference.PreferenceManager import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.afollestad.materialdialogs.utils.MDUtil.getStringArray import com.deniscerri.ytdlnis.MainActivity import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.ui.adapter.CookieAdapter @@ -79,7 +80,7 @@ class CookiesFragment : Fragment(), CookieAdapter.OnItemClickListener { recyclerView.layoutManager = LinearLayoutManager(context) recyclerView.adapter = listAdapter 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) itemTouchHelper.attachToRecyclerView(recyclerView) } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/downloadLogs/DownloadLogFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/downloadLogs/DownloadLogFragment.kt index 9bfbce0b..c0f300ae 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/downloadLogs/DownloadLogFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/downloadLogs/DownloadLogFragment.kt @@ -27,7 +27,7 @@ import androidx.lifecycle.lifecycleScope import com.deniscerri.ytdlnis.MainActivity import com.deniscerri.ytdlnis.R 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.bottomappbar.BottomAppBar import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/downloadLogs/DownloadLogListFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/downloadLogs/DownloadLogListFragment.kt index 2edfb271..ffd5629e 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/downloadLogs/DownloadLogListFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/downloadLogs/DownloadLogListFragment.kt @@ -26,8 +26,8 @@ import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.ui.adapter.DownloadLogsAdapter import com.deniscerri.ytdlnis.database.models.LogItem import com.deniscerri.ytdlnis.database.viewmodel.LogViewModel -import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll -import com.deniscerri.ytdlnis.util.UiUtil.forceFastScrollMode +import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll +import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/BaseSettingsFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/BaseSettingsFragment.kt index b6b372c7..1071862b 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/BaseSettingsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/BaseSettingsFragment.kt @@ -51,7 +51,10 @@ abstract class BaseSettingsFragment : PreferenceFragmentCompat() { }.toBooleanArray() MaterialAlertDialogBuilder(requireContext()) .setTitle(preference.title) - .setMultiChoiceItems(preference.entries, selectedItems) { _, _, _ -> + .setMultiChoiceItems(preference.entries, selectedItems) { _, which, isChecked -> + selectedItems[which] = isChecked + } + .setPositiveButton(R.string.ok) { _, _ -> val newValues = preference.entryValues .filterIndexed { index, _ -> selectedItems[index] } .map { it.toString() } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/GeneralSettingsFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/GeneralSettingsFragment.kt index 1256e8c3..d9860574 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/GeneralSettingsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/GeneralSettingsFragment.kt @@ -6,6 +6,7 @@ import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.net.Uri +import android.os.Build import android.os.Bundle import android.os.PowerManager import android.provider.Settings @@ -66,11 +67,9 @@ class GeneralSettingsFragment : BaseSettingsFragment() { language!!.entries = entries.toTypedArray() if(language!!.value == null) language!!.value = Locale.getDefault().language - language!!.summary = Locale(language!!.value).getDisplayLanguage(Locale(language!!.value)) language!!.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any -> - language!!.summary = Locale(newValue.toString()).getDisplayLanguage(Locale(newValue.toString())) AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(newValue.toString())) true } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/terminal/TerminalDownloadsListFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/terminal/TerminalDownloadsListFragment.kt index 2f43cc9d..f6564e26 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/terminal/TerminalDownloadsListFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/terminal/TerminalDownloadsListFragment.kt @@ -20,7 +20,7 @@ import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.ui.adapter.TerminalDownloadsAdapter import com.deniscerri.ytdlnis.database.models.TerminalItem 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.progressindicator.LinearProgressIndicator import kotlinx.coroutines.flow.collectLatest diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/terminal/TerminalFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/terminal/TerminalFragment.kt index 00bf8f43..e25ca8f6 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/terminal/TerminalFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/terminal/TerminalFragment.kt @@ -37,7 +37,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.TerminalViewModel import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.NotificationUtil 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.bottomappbar.BottomAppBar import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton diff --git a/app/src/main/java/com/deniscerri/ytdlnis/util/Extensions.kt b/app/src/main/java/com/deniscerri/ytdlnis/util/Extensions.kt new file mode 100644 index 00000000..1b8a5efa --- /dev/null +++ b/app/src/main/java/com/deniscerri/ytdlnis/util/Extensions.kt @@ -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() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt b/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt index 7460351c..b148851d 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt @@ -638,7 +638,7 @@ class InfoUtil(private val context: Context) { val website = jsonObject.getString(listOf("ie_key", "extractor_key", "extractor").first { jsonObject.has(it) }) var playlistTitle: String? = "" 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){ playlistTitle += "[${jsonObject.getString("playlist_index")}]" diff --git a/app/src/main/java/com/deniscerri/ytdlnis/util/UiUtil.kt b/app/src/main/java/com/deniscerri/ytdlnis/util/UiUtil.kt index fae27a58..4394590b 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/util/UiUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/util/UiUtil.kt @@ -25,6 +25,7 @@ import android.view.ViewGroup import android.view.Window import android.view.inputmethod.InputMethodManager import android.widget.Button +import android.widget.CheckBox import android.widget.EditText import android.widget.LinearLayout import android.widget.TextView @@ -100,14 +101,24 @@ object UiUtil { formatCard.findViewById(R.id.container).text = container.uppercase() formatCard.findViewById(R.id.format_note).text = formatNote.uppercase() - val audioFormatsTextView = formatCard.findViewById(R.id.audio_formats) - if (!audioFormats.isNullOrEmpty()) { - audioFormatsTextView.text = "id: " + audioFormats.joinToString("+") { it.format_id } - audioFormatsTextView.visibility = View.VISIBLE - }else{ - audioFormatsTextView.visibility = View.GONE + formatCard.findViewById(R.id.audio_formats).apply { + if (!audioFormats.isNullOrEmpty()){ + text = "id: " + audioFormats.joinToString("+") { it.format_id } + visibility = View.VISIBLE + }else{ + visibility = View.GONE + } + setOnClickListener { + formatCard.callOnClick() + } + } + + formatCard.findViewById(R.id.format_id).apply { + text = "id: ${chosenFormat.format_id}" + setOnClickListener { + formatCard.callOnClick() + } } - formatCard.findViewById(R.id.format_id).text = "id: ${chosenFormat.format_id}" val codec = if (chosenFormat.encoding != "") { chosenFormat.encoding.uppercase() @@ -116,15 +127,28 @@ object UiUtil { } else { chosenFormat.acodec.uppercase() } - if (codec == "" || codec == "none"){ - formatCard.findViewById(R.id.codec).visibility = View.GONE - }else{ - formatCard.findViewById(R.id.codec).visibility = View.VISIBLE - formatCard.findViewById(R.id.codec).text = codec + + formatCard.findViewById(R.id.codec).apply { + if (codec == "" || codec == "none"){ + visibility = View.GONE + }else{ + visibility = View.VISIBLE + text = codec + } + setOnClickListener { + formatCard.callOnClick() + } } + + var filesize = chosenFormat.filesize if (!audioFormats.isNullOrEmpty()) filesize += audioFormats.sumOf { it.filesize } - formatCard.findViewById(R.id.file_size).text = FileUtil.convertFileSize(filesize) + formatCard.findViewById(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 content : TextInputLayout = bottomSheet.findViewById(R.id.content)!! 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 editShortcuts : Button = bottomSheet.findViewById(R.id.edit_shortcuts)!! @@ -194,8 +220,34 @@ object UiUtil { if (item != null){ 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){ shortcutsChipGroup.removeAllViews() it.forEach {shortcut -> @@ -214,13 +266,15 @@ object UiUtil { ok.setOnClickListener { 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) newTemplate(t) }else{ item.title = title.editText!!.text.toString() item.content = content.editText!!.text.toString() item.useAsExtraCommand = extraCommandsSwitch.isChecked + item.useAsExtraCommandAudio = extraCommandsAudio.isChecked + item.useAsExtraCommandVideo = extraCommandsVideo.isChecked Log.e("aa", item.toString()) commandTemplateViewModel.update(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){ val title = context.getString(it.errorMessage!!.first) val message = it.errorMessage!!.second diff --git a/app/src/main/res/drawable/baseline_share2_24.xml b/app/src/main/res/drawable/baseline_share2_24.xml new file mode 100644 index 00000000..66aa3204 --- /dev/null +++ b/app/src/main/res/drawable/baseline_share2_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout-land/fragment_home.xml b/app/src/main/res/layout-land/fragment_home.xml index 6df5c18d..762e23b8 100644 --- a/app/src/main/res/layout-land/fragment_home.xml +++ b/app/src/main/res/layout-land/fragment_home.xml @@ -46,6 +46,7 @@ diff --git a/app/src/main/res/layout-sw600dp-land/fragment_home.xml b/app/src/main/res/layout-sw600dp-land/fragment_home.xml index 6806c2a5..ba1294b5 100644 --- a/app/src/main/res/layout-sw600dp-land/fragment_home.xml +++ b/app/src/main/res/layout-sw600dp-land/fragment_home.xml @@ -46,6 +46,7 @@ diff --git a/app/src/main/res/layout-sw600dp/fragment_home.xml b/app/src/main/res/layout-sw600dp/fragment_home.xml index 6df5c18d..762e23b8 100644 --- a/app/src/main/res/layout-sw600dp/fragment_home.xml +++ b/app/src/main/res/layout-sw600dp/fragment_home.xml @@ -46,6 +46,7 @@ diff --git a/app/src/main/res/layout-sw950dp-land/fragment_home.xml b/app/src/main/res/layout-sw950dp-land/fragment_home.xml index 4475cb91..942c212b 100644 --- a/app/src/main/res/layout-sw950dp-land/fragment_home.xml +++ b/app/src/main/res/layout-sw950dp-land/fragment_home.xml @@ -46,6 +46,7 @@ diff --git a/app/src/main/res/layout/active_download_card.xml b/app/src/main/res/layout/active_download_card.xml index 22476755..ee6625cd 100644 --- a/app/src/main/res/layout/active_download_card.xml +++ b/app/src/main/res/layout/active_download_card.xml @@ -108,7 +108,7 @@ android:dividerPadding="5dp" android:orientation="vertical" 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_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/linearlayout2_horizontalscrollview"> @@ -151,37 +151,55 @@ - - + app:layout_constraintVertical_bias="0.51" + > + + + + + + + + + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> - + android:layout_height="wrap_content" + app:cardCornerRadius="20dp"> + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/app/src/main/res/layout/format_item.xml b/app/src/main/res/layout/format_item.xml index 8f6af10f..0c7b6cd2 100644 --- a/app/src/main/res/layout/format_item.xml +++ b/app/src/main/res/layout/format_item.xml @@ -89,6 +89,8 @@ android:layout_width="wrap_content" android:layout_marginEnd="10dp" android:layout_height="wrap_content" + android:clickable="false" + android:focusable="false" android:layout_marginStart="10dp" android:scrollbars="none"> @@ -115,6 +117,7 @@ android:textStyle="bold" app:cornerRadius="10dp" app:drawableStartCompat="@drawable/ic_music" + android:textCursorDrawable="@string/audio_format" app:drawableTint="@color/white" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 6164cb01..e5612152 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -46,6 +46,7 @@ diff --git a/app/src/main/res/menu/command_templates_menu.xml b/app/src/main/res/menu/command_templates_menu.xml index a5d79e9e..a5a12a1a 100644 --- a/app/src/main/res/menu/command_templates_menu.xml +++ b/app/src/main/res/menu/command_templates_menu.xml @@ -6,7 +6,7 @@ android:id="@+id/search_command" android:title="@string/search" android:icon="@drawable/ic_search" - app:showAsAction="collapseActionView|ifRoom" + app:showAsAction="always" app:actionViewClass="androidx.appcompat.widget.SearchView"/> + app:showAsAction="always"/> ko + + @string/downloads + @string/in_queue + @string/cancelled + @string/errored + @string/saved + @string/command_templates + @string/cookies + + + + history + queued + cancelled + errored + saved + templates + cookies + + \ No newline at end of file diff --git a/app/src/main/res/xml/general_preferences.xml b/app/src/main/res/xml/general_preferences.xml index 230ae20d..94e17007 100644 --- a/app/src/main/res/xml/general_preferences.xml +++ b/app/src/main/res/xml/general_preferences.xml @@ -6,9 +6,11 @@ - diff --git a/build.gradle b/build.gradle index fd2e7300..a1769873 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,7 @@ buildscript { coroutineVer = '1.7.3' retrofitVer = "2.9.0" kodeinVer = "7.16.0" - navVer = "2.7.4" + navVer = "2.7.5" media3_version = "1.1.1" agp_version = '8.1.0' agp_version1 = '7.4.2'