added multiple pages to settings & more...
Dismiss download multiple sheet when all items are dismissed Add backup for history, queued downloads, cancelled downloads, cookies, shortcuts, templates Add long click format to show details in audio video tab removed check icon from checked state in material cards Implement format fetching on playlist from invidious Invert selected items in playlist bottom sheet Make filename templates for both audio and video and add them as a chip on both tabs Fixed errors when there is no internet and u ask for the latest version of the app Fixed "default" format error
This commit is contained in:
parent
510798d492
commit
880abd503e
62 changed files with 2242 additions and 1324 deletions
|
|
@ -58,7 +58,7 @@ android {
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
minifyEnabled true
|
minifyEnabled false
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
debuggable false
|
debuggable false
|
||||||
signingConfig signingConfigs.debug
|
signingConfig signingConfigs.debug
|
||||||
|
|
@ -178,4 +178,5 @@ dependencies {
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0'
|
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0'
|
||||||
implementation "com.google.android.exoplayer:exoplayer:2.18.6"
|
implementation "com.google.android.exoplayer:exoplayer:2.18.6"
|
||||||
implementation 'it.xabaras.android:recyclerview-swipedecorator:1.4'
|
implementation 'it.xabaras.android:recyclerview-swipedecorator:1.4'
|
||||||
|
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.1"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
app/proguard-rules.pro
vendored
1
app/proguard-rules.pro
vendored
|
|
@ -61,7 +61,6 @@
|
||||||
public <init>(android.content.Context,androidx.work.WorkerParameters);
|
public <init>(android.content.Context,androidx.work.WorkerParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
-dontobfuscate
|
|
||||||
-dontwarn com.google.android.exoplayer2.**
|
-dontwarn com.google.android.exoplayer2.**
|
||||||
# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher.
|
# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher.
|
||||||
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
|
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
|
||||||
|
|
|
||||||
|
|
@ -29,15 +29,8 @@ class App : Application() {
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
instance = this
|
||||||
createNotificationChannels()
|
createNotificationChannels()
|
||||||
PreferenceManager.setDefaultValues(
|
|
||||||
this,
|
|
||||||
"root_preferences",
|
|
||||||
MODE_PRIVATE,
|
|
||||||
R.xml.root_preferences,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
|
|
||||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
|
||||||
applicationScope = CoroutineScope(SupervisorJob())
|
applicationScope = CoroutineScope(SupervisorJob())
|
||||||
|
|
@ -80,5 +73,6 @@ class App : Application() {
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "App"
|
private const val TAG = "App"
|
||||||
private lateinit var applicationScope: CoroutineScope
|
private lateinit var applicationScope: CoroutineScope
|
||||||
|
lateinit var instance: App
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -123,7 +123,6 @@ class MainActivity : BaseActivity() {
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
R.id.moreFragment -> {
|
R.id.moreFragment -> {
|
||||||
//navController.navigate(R.id.settingsFragment)
|
|
||||||
val intent = Intent(context, SettingsActivity::class.java)
|
val intent = Intent(context, SettingsActivity::class.java)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
|
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
@ -119,6 +120,16 @@ class PlaylistAdapter(onItemClickListener: OnItemClickListener, activity: Activi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun invertSelected(items: List<ResultItem?>?){
|
||||||
|
val invertedList = mutableListOf<String>()
|
||||||
|
items?.forEach {
|
||||||
|
if (!checkedItems.contains(it!!.url)) invertedList.add(it.url)
|
||||||
|
}
|
||||||
|
checkedItems.clear()
|
||||||
|
checkedItems.addAll(invertedList)
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
fun checkRange(start: Int, end: Int){
|
fun checkRange(start: Int, end: Int){
|
||||||
checkedItems.clear()
|
checkedItems.clear()
|
||||||
if (start == end ){
|
if (start == end ){
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,19 @@ package com.deniscerri.ytdlnis.database.dao
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface DownloadDao {
|
interface DownloadDao {
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads ORDER BY status")
|
@Query("SELECT * FROM downloads ORDER BY status")
|
||||||
fun getAllDownloads() : LiveData<List<DownloadItem>>
|
fun getAllDownloads() : Flow<List<DownloadItem>>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Active'")
|
@Query("SELECT * FROM downloads WHERE status='Active'")
|
||||||
fun getActiveDownloads() : LiveData<List<DownloadItem>>
|
fun getActiveDownloads() : Flow<List<DownloadItem>>
|
||||||
|
|
||||||
@Query("SELECT COUNT(*) FROM downloads WHERE status='Active'")
|
@Query("SELECT COUNT(*) FROM downloads WHERE status='Active'")
|
||||||
fun getActiveDownloadsCount() : LiveData<Int>
|
fun getActiveDownloadsCount() : Flow<Int>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Active'")
|
@Query("SELECT * FROM downloads WHERE status='Active'")
|
||||||
fun getActiveDownloadsList() : List<DownloadItem>
|
fun getActiveDownloadsList() : List<DownloadItem>
|
||||||
|
|
@ -23,16 +24,22 @@ interface DownloadDao {
|
||||||
fun getActiveAndQueuedDownloadsList() : List<DownloadItem>
|
fun getActiveAndQueuedDownloadsList() : List<DownloadItem>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Queued' ORDER BY downloadStartTime, id")
|
@Query("SELECT * FROM downloads WHERE status='Queued' ORDER BY downloadStartTime, id")
|
||||||
fun getQueuedDownloads() : LiveData<List<DownloadItem>>
|
fun getQueuedDownloads() : Flow<List<DownloadItem>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM downloads WHERE status='Queued' ORDER BY downloadStartTime, id")
|
||||||
|
fun getQueuedDownloadsList() : List<DownloadItem>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC")
|
@Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC")
|
||||||
fun getCancelledDownloads() : LiveData<List<DownloadItem>>
|
fun getCancelledDownloads() : Flow<List<DownloadItem>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC")
|
||||||
|
fun getCancelledDownloadsList() : List<DownloadItem>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC")
|
@Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC")
|
||||||
fun getErroredDownloads() : LiveData<List<DownloadItem>>
|
fun getErroredDownloads() : Flow<List<DownloadItem>>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Processing' ORDER BY id DESC")
|
@Query("SELECT * FROM downloads WHERE status='Processing' ORDER BY id DESC")
|
||||||
fun getProcessingDownloads() : LiveData<List<DownloadItem>>
|
fun getProcessingDownloads() : Flow<List<DownloadItem>>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE id=:id LIMIT 1")
|
@Query("SELECT * FROM downloads WHERE id=:id LIMIT 1")
|
||||||
fun getDownloadById(id: Long) : DownloadItem
|
fun getDownloadById(id: Long) : DownloadItem
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,9 @@ interface HistoryDao {
|
||||||
@Query("SELECT * FROM history")
|
@Query("SELECT * FROM history")
|
||||||
fun getAllHistory() : LiveData<List<HistoryItem>>
|
fun getAllHistory() : LiveData<List<HistoryItem>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM history")
|
||||||
|
fun getAllHistoryList() : List<HistoryItem>
|
||||||
|
|
||||||
@Query("SELECT * FROM history WHERE id=:id LIMIT 1")
|
@Query("SELECT * FROM history WHERE id=:id LIMIT 1")
|
||||||
suspend fun getHistoryItem(id: Int) : HistoryItem
|
suspend fun getHistoryItem(id: Int) : HistoryItem
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,15 @@ package com.deniscerri.ytdlnis.database.dao
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface ResultDao {
|
interface ResultDao {
|
||||||
@Query("SELECT * FROM results")
|
@Query("SELECT * FROM results")
|
||||||
fun getResults() : LiveData<List<ResultItem>>
|
fun getResults() : Flow<List<ResultItem>>
|
||||||
|
|
||||||
@Query("SELECT COUNT(id) FROM results")
|
@Query("SELECT COUNT(id) FROM results")
|
||||||
fun getCount() : LiveData<Int>
|
fun getCount() : Flow<Int>
|
||||||
|
|
||||||
@Query("SELECT COUNT(id) FROM results")
|
@Query("SELECT COUNT(id) FROM results")
|
||||||
fun getCountInt() :Int
|
fun getCountInt() :Int
|
||||||
|
|
|
||||||
|
|
@ -3,5 +3,5 @@ package com.deniscerri.ytdlnis.database.models
|
||||||
data class AudioPreferences(
|
data class AudioPreferences(
|
||||||
var embedThumb: Boolean = true,
|
var embedThumb: Boolean = true,
|
||||||
var splitByChapters: Boolean = false,
|
var splitByChapters: Boolean = false,
|
||||||
var sponsorBlockFilters: ArrayList<String>
|
var sponsorBlockFilters: ArrayList<String> = arrayListOf()
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ data class VideoPreferences (
|
||||||
var embedSubs: Boolean = true,
|
var embedSubs: Boolean = true,
|
||||||
var addChapters: Boolean = true,
|
var addChapters: Boolean = true,
|
||||||
var splitByChapters: Boolean = false,
|
var splitByChapters: Boolean = false,
|
||||||
var sponsorBlockFilters: ArrayList<String>,
|
var sponsorBlockFilters: ArrayList<String> = arrayListOf(),
|
||||||
var writeSubs: Boolean = false,
|
var writeSubs: Boolean = false,
|
||||||
var subsLanguages: String = "en.*,.*-orig",
|
var subsLanguages: String = "en.*,.*-orig",
|
||||||
var removeAudio: Boolean = false
|
var removeAudio: Boolean = false
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,16 @@ import com.deniscerri.ytdlnis.database.Converters
|
||||||
import com.deniscerri.ytdlnis.database.dao.DownloadDao
|
import com.deniscerri.ytdlnis.database.dao.DownloadDao
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
class DownloadRepository(private val downloadDao: DownloadDao) {
|
class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
val allDownloads : LiveData<List<DownloadItem>> = downloadDao.getAllDownloads()
|
val allDownloads : Flow<List<DownloadItem>> = downloadDao.getAllDownloads()
|
||||||
val activeDownloads : LiveData<List<DownloadItem>> = downloadDao.getActiveDownloads()
|
val activeDownloads : Flow<List<DownloadItem>> = downloadDao.getActiveDownloads()
|
||||||
val activeDownloadsCount : LiveData<Int> = downloadDao.getActiveDownloadsCount()
|
val activeDownloadsCount : Flow<Int> = downloadDao.getActiveDownloadsCount()
|
||||||
val queuedDownloads : LiveData<List<DownloadItem>> = downloadDao.getQueuedDownloads()
|
val queuedDownloads : Flow<List<DownloadItem>> = downloadDao.getQueuedDownloads()
|
||||||
val cancelledDownloads : LiveData<List<DownloadItem>> = downloadDao.getCancelledDownloads()
|
val cancelledDownloads : Flow<List<DownloadItem>> = downloadDao.getCancelledDownloads()
|
||||||
val erroredDownloads : LiveData<List<DownloadItem>> = downloadDao.getErroredDownloads()
|
val erroredDownloads : Flow<List<DownloadItem>> = downloadDao.getErroredDownloads()
|
||||||
val processingDownloads : LiveData<List<DownloadItem>> = downloadDao.getProcessingDownloads()
|
val processingDownloads : Flow<List<DownloadItem>> = downloadDao.getProcessingDownloads()
|
||||||
|
|
||||||
enum class Status {
|
enum class Status {
|
||||||
Active, Queued, Error, Processing, Cancelled
|
Active, Queued, Error, Processing, Cancelled
|
||||||
|
|
@ -53,6 +54,14 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
return downloadDao.getActiveAndQueuedDownloadsList()
|
return downloadDao.getActiveAndQueuedDownloadsList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getQueuedDownloads() : List<DownloadItem> {
|
||||||
|
return downloadDao.getQueuedDownloadsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCancelledDownloads() : List<DownloadItem> {
|
||||||
|
return downloadDao.getCancelledDownloadsList()
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun deleteCancelled(){
|
suspend fun deleteCancelled(){
|
||||||
downloadDao.deleteCancelled()
|
downloadDao.deleteCancelled()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,10 @@ class HistoryRepository(private val historyDao: HistoryDao) {
|
||||||
return historyDao.getHistoryItem(id)
|
return historyDao.getHistoryItem(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getAll() : List<HistoryItem> {
|
||||||
|
return historyDao.getAllHistoryList()
|
||||||
|
}
|
||||||
|
|
||||||
fun getFiltered(query : String, format : String, site : String, sortType: HistorySortType, sort: HistorySort) : List<HistoryItem> {
|
fun getFiltered(query : String, format : String, site : String, sortType: HistorySortType, sort: HistorySort) : List<HistoryItem> {
|
||||||
return when(sortType){
|
return when(sortType){
|
||||||
HistorySortType.DATE -> historyDao.getHistorySortedByID(query, format, site, sort.toString())
|
HistorySortType.DATE -> historyDao.getHistorySortedByID(query, format, site, sort.toString())
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,11 @@ import com.deniscerri.ytdlnis.database.dao.ResultDao
|
||||||
import com.deniscerri.ytdlnis.database.models.CommandTemplate
|
import com.deniscerri.ytdlnis.database.models.CommandTemplate
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
import com.deniscerri.ytdlnis.util.InfoUtil
|
import com.deniscerri.ytdlnis.util.InfoUtil
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
class ResultRepository(private val resultDao: ResultDao, private val commandTemplateDao: CommandTemplateDao, private val context: Context) {
|
class ResultRepository(private val resultDao: ResultDao, private val commandTemplateDao: CommandTemplateDao, private val context: Context) {
|
||||||
private val tag: String = "ResultRepository"
|
private val tag: String = "ResultRepository"
|
||||||
val allResults : LiveData<List<ResultItem>> = resultDao.getResults()
|
val allResults : Flow<List<ResultItem>> = resultDao.getResults()
|
||||||
var itemCount = MutableLiveData(-1)
|
var itemCount = MutableLiveData(-1)
|
||||||
|
|
||||||
suspend fun insert(it: ResultItem){
|
suspend fun insert(it: ResultItem){
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import android.net.ConnectivityManager
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.asLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.work.*
|
import androidx.work.*
|
||||||
|
|
@ -52,13 +53,13 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application)
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application)
|
||||||
commandTemplateDao = DBManager.getInstance(application).commandTemplateDao
|
commandTemplateDao = DBManager.getInstance(application).commandTemplateDao
|
||||||
|
|
||||||
allDownloads = repository.allDownloads
|
allDownloads = repository.allDownloads.asLiveData()
|
||||||
queuedDownloads = repository.queuedDownloads
|
queuedDownloads = repository.queuedDownloads.asLiveData()
|
||||||
activeDownloads = repository.activeDownloads
|
activeDownloads = repository.activeDownloads.asLiveData()
|
||||||
activeDownloadsCount = repository.activeDownloadsCount
|
activeDownloadsCount = repository.activeDownloadsCount.asLiveData()
|
||||||
processingDownloads = repository.processingDownloads
|
processingDownloads = repository.processingDownloads.asLiveData()
|
||||||
cancelledDownloads = repository.cancelledDownloads
|
cancelledDownloads = repository.cancelledDownloads.asLiveData()
|
||||||
erroredDownloads = repository.erroredDownloads
|
erroredDownloads = repository.erroredDownloads.asLiveData()
|
||||||
|
|
||||||
videoQualityPreference = sharedPreferences.getString("video_quality", application.getString(R.string.best_quality)).toString()
|
videoQualityPreference = sharedPreferences.getString("video_quality", application.getString(R.string.best_quality)).toString()
|
||||||
formatIDPreference = sharedPreferences.getString("format_id", "").toString()
|
formatIDPreference = sharedPreferences.getString("format_id", "").toString()
|
||||||
|
|
@ -114,7 +115,11 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
val addChapters = sharedPreferences.getBoolean("add_chapters", false)
|
val addChapters = sharedPreferences.getBoolean("add_chapters", false)
|
||||||
val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false)
|
val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false)
|
||||||
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
|
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
|
||||||
val customFileNameTemplate = sharedPreferences.getString("file_name_template", "%(uploader)s - %(title)s")
|
val customFileNameTemplate = when(type) {
|
||||||
|
Type.audio -> sharedPreferences.getString("file_name_template_audio", "%(uploader)s - %(title)s")
|
||||||
|
Type.video -> sharedPreferences.getString("file_name_template", "%(uploader)s - %(title)s")
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
|
||||||
val downloadPath = when(type){
|
val downloadPath = when(type){
|
||||||
Type.audio -> sharedPreferences.getString("music_path", getApplication<App>().resources.getString(R.string.music_path))
|
Type.audio -> sharedPreferences.getString("music_path", getApplication<App>().resources.getString(R.string.music_path))
|
||||||
|
|
@ -205,7 +210,11 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
val addChapters = sharedPreferences.getBoolean("add_chapters", false)
|
val addChapters = sharedPreferences.getBoolean("add_chapters", false)
|
||||||
val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false)
|
val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false)
|
||||||
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
|
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
|
||||||
val customFileNameTemplate = sharedPreferences.getString("file_name_template", "%(uploader)s - %(title)s")
|
val customFileNameTemplate = when(historyItem.type) {
|
||||||
|
Type.audio -> sharedPreferences.getString("file_name_template_audio", "%(uploader)s - %(title)s")
|
||||||
|
Type.video -> sharedPreferences.getString("file_name_template", "%(uploader)s - %(title)s")
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
|
||||||
val downloadPath = when(historyItem.type){
|
val downloadPath = when(historyItem.type){
|
||||||
Type.audio -> sharedPreferences.getString("music_path", getApplication<App>().resources.getString(R.string.music_path))
|
Type.audio -> sharedPreferences.getString("music_path", getApplication<App>().resources.getString(R.string.music_path))
|
||||||
|
|
@ -345,6 +354,14 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
repository.cancelQueued()
|
repository.cancelQueued()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getQueued() : List<DownloadItem> {
|
||||||
|
return repository.getQueuedDownloads()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCancelled() : List<DownloadItem> {
|
||||||
|
return repository.getCancelledDownloads()
|
||||||
|
}
|
||||||
|
|
||||||
private fun cloneFormat(item: Format) : Format {
|
private fun cloneFormat(item: Format) : Format {
|
||||||
val string = Gson().toJson(item, Format::class.java)
|
val string = Gson().toJson(item, Format::class.java)
|
||||||
return Gson().fromJson(string, Format::class.java)
|
return Gson().fromJson(string, Format::class.java)
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,6 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
|
||||||
return _items
|
return _items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun setSorting(sort: HistorySortType){
|
fun setSorting(sort: HistorySortType){
|
||||||
if (sortType.value != sort){
|
if (sortType.value != sort){
|
||||||
sortOrder.value = HistorySort.DESC
|
sortOrder.value = HistorySort.DESC
|
||||||
|
|
@ -76,6 +75,10 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
|
||||||
_items.postValue(repository.getFiltered(query, format, site, sortType, sort))
|
_items.postValue(repository.getFiltered(query, format, site, sortType, sort))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getAll() : List<HistoryItem> {
|
||||||
|
return repository.getAll()
|
||||||
|
}
|
||||||
|
|
||||||
fun insert(item: HistoryItem) = viewModelScope.launch(Dispatchers.IO){
|
fun insert(item: HistoryItem) = viewModelScope.launch(Dispatchers.IO){
|
||||||
repository.insert(item)
|
repository.insert(item)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import android.util.Log
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.asLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.deniscerri.ytdlnis.App
|
import com.deniscerri.ytdlnis.App
|
||||||
|
|
@ -35,7 +36,7 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
|
||||||
val commandDao = DBManager.getInstance(application).commandTemplateDao
|
val commandDao = DBManager.getInstance(application).commandTemplateDao
|
||||||
repository = ResultRepository(dao, commandDao, getApplication<Application>().applicationContext)
|
repository = ResultRepository(dao, commandDao, getApplication<Application>().applicationContext)
|
||||||
searchHistoryRepository = SearchHistoryRepository(DBManager.getInstance(application).searchHistoryDao)
|
searchHistoryRepository = SearchHistoryRepository(DBManager.getInstance(application).searchHistoryDao)
|
||||||
items = repository.allResults
|
items = repository.allResults.asLiveData()
|
||||||
loadingItems.postValue(false)
|
loadingItems.postValue(false)
|
||||||
itemCount = repository.itemCount
|
itemCount = repository.itemCount
|
||||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application)
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application)
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,14 @@ import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdlnis.util.FileUtil
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
import com.deniscerri.ytdlnis.util.InfoUtil
|
import com.deniscerri.ytdlnis.util.InfoUtil
|
||||||
import com.deniscerri.ytdlnis.util.UiUtil
|
import com.deniscerri.ytdlnis.util.UiUtil
|
||||||
|
import com.google.android.exoplayer2.C
|
||||||
import com.google.android.exoplayer2.DefaultLoadControl
|
import com.google.android.exoplayer2.DefaultLoadControl
|
||||||
import com.google.android.exoplayer2.DefaultRenderersFactory
|
import com.google.android.exoplayer2.DefaultRenderersFactory
|
||||||
import com.google.android.exoplayer2.DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER
|
import com.google.android.exoplayer2.DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER
|
||||||
import com.google.android.exoplayer2.ExoPlayer
|
import com.google.android.exoplayer2.ExoPlayer
|
||||||
import com.google.android.exoplayer2.MediaItem.fromUri
|
import com.google.android.exoplayer2.MediaItem.fromUri
|
||||||
import com.google.android.exoplayer2.Player
|
import com.google.android.exoplayer2.Player
|
||||||
|
import com.google.android.exoplayer2.audio.AudioAttributes
|
||||||
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo
|
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo
|
||||||
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector
|
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector
|
||||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
|
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
|
||||||
|
|
@ -131,9 +133,18 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
val trackSelector = DefaultTrackSelector(requireContext())
|
val trackSelector = DefaultTrackSelector(requireContext())
|
||||||
val loadControl = DefaultLoadControl()
|
val loadControl = DefaultLoadControl()
|
||||||
|
|
||||||
player = ExoPlayer.Builder(requireContext(), renderersFactory)
|
player = ExoPlayer.Builder(requireContext())
|
||||||
|
.setUsePlatformDiagnostics(false)
|
||||||
.setTrackSelector(trackSelector)
|
.setTrackSelector(trackSelector)
|
||||||
.setLoadControl(loadControl)
|
.setLoadControl(loadControl)
|
||||||
|
.setHandleAudioBecomingNoisy(true)
|
||||||
|
.setAudioAttributes(
|
||||||
|
AudioAttributes.Builder()
|
||||||
|
.setUsage(C.USAGE_MEDIA)
|
||||||
|
.setContentType(C.AUDIO_CONTENT_TYPE_MOVIE)
|
||||||
|
.build()
|
||||||
|
,
|
||||||
|
true)
|
||||||
.build()
|
.build()
|
||||||
val frame = view.findViewById<MaterialCardView>(R.id.frame_layout)
|
val frame = view.findViewById<MaterialCardView>(R.id.frame_layout)
|
||||||
val videoView = view.findViewById<StyledPlayerView>(R.id.video_view)
|
val videoView = view.findViewById<StyledPlayerView>(R.id.video_view)
|
||||||
|
|
@ -207,9 +218,8 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
|
||||||
progress.visibility = View.GONE
|
progress.visibility = View.GONE
|
||||||
populateSuggestedChapters()
|
populateSuggestedChapters()
|
||||||
|
|
||||||
player.prepare().apply {
|
player.prepare()
|
||||||
player.play()
|
player.play()
|
||||||
}
|
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
progress.visibility = View.GONE
|
progress.visibility = View.GONE
|
||||||
frame.visibility = View.GONE
|
frame.visibility = View.GONE
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,25 @@ package com.deniscerri.ytdlnis.ui.downloadcard
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
|
import android.view.Gravity
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.AdapterView
|
import android.widget.AdapterView
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.AutoCompleteTextView
|
import android.widget.AutoCompleteTextView
|
||||||
|
import android.widget.EditText
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.core.widget.doOnTextChanged
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
|
@ -142,10 +149,6 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
|
||||||
override fun onFormatClick(allFormats: List<List<Format>>, item: List<Format>) {
|
override fun onFormatClick(allFormats: List<List<Format>>, item: List<Format>) {
|
||||||
downloadItem.format = item.first()
|
downloadItem.format = item.first()
|
||||||
uiUtil.populateFormatCard(formatCard, item.first())
|
uiUtil.populateFormatCard(formatCard, item.first())
|
||||||
if (containers.contains(item.first().container)){
|
|
||||||
downloadItem.format.container = item.first().container
|
|
||||||
containerAutoCompleteTextView.setText(item.first().container, false)
|
|
||||||
}
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
resultItem.formats.removeAll(formats.toSet())
|
resultItem.formats.removeAll(formats.toSet())
|
||||||
|
|
@ -162,6 +165,10 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
|
||||||
bottomSheet.show(parentFragmentManager, "formatSheet")
|
bottomSheet.show(parentFragmentManager, "formatSheet")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
formatCard.setOnLongClickListener {
|
||||||
|
uiUtil.showFormatDetails(downloadItem.format, requireActivity())
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
container?.isEnabled = true
|
container?.isEnabled = true
|
||||||
|
|
@ -173,12 +180,13 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
downloadItem.format.container = containerPreference!!
|
downloadItem.format.container = if (containerPreference == getString(R.string.defaultValue)) "" else containerPreference!!
|
||||||
containerAutoCompleteTextView.setText(downloadItem.format.container, false)
|
containerAutoCompleteTextView.setText(downloadItem.format.container.ifEmpty { getString(R.string.defaultValue) }, false)
|
||||||
|
|
||||||
(container!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
|
(container!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
|
||||||
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
|
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
|
||||||
downloadItem.format.container = containers[index]
|
downloadItem.format.container = containers[index]
|
||||||
|
if (containers[index] == getString(R.string.defaultValue)) downloadItem.format.container = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
val embedThumb = view.findViewById<Chip>(R.id.embed_thumb)
|
val embedThumb = view.findViewById<Chip>(R.id.embed_thumb)
|
||||||
|
|
@ -199,6 +207,41 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
|
||||||
downloadItem.audioPreferences.splitByChapters = splitByChapters.isChecked
|
downloadItem.audioPreferences.splitByChapters = splitByChapters.isChecked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val filenameTemplate = view.findViewById<Chip>(R.id.filename_template)
|
||||||
|
filenameTemplate.setOnClickListener {
|
||||||
|
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
builder.setTitle(getString(R.string.file_name_template))
|
||||||
|
val inputLayout = layoutInflater.inflate(R.layout.textinput, null)
|
||||||
|
val editText = inputLayout.findViewById<EditText>(R.id.url_edittext)
|
||||||
|
inputLayout.findViewById<TextInputLayout>(R.id.url_textinput).hint = getString(R.string.file_name_template)
|
||||||
|
editText.setText(downloadItem.customFileNameTemplate)
|
||||||
|
editText.setSelection(editText.text.length)
|
||||||
|
builder.setView(inputLayout)
|
||||||
|
builder.setPositiveButton(
|
||||||
|
getString(R.string.ok)
|
||||||
|
) { dialog: DialogInterface?, which: Int ->
|
||||||
|
downloadItem.customFileNameTemplate = editText.text.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle the negative button of the alert dialog
|
||||||
|
builder.setNegativeButton(
|
||||||
|
getString(R.string.cancel)
|
||||||
|
) { dialog: DialogInterface?, which: Int -> }
|
||||||
|
|
||||||
|
val dialog = builder.create()
|
||||||
|
editText.doOnTextChanged { text, start, before, count ->
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = editText.text.isNotEmpty()
|
||||||
|
}
|
||||||
|
dialog.show()
|
||||||
|
val imm = context?.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
editText!!.postDelayed({
|
||||||
|
editText.requestFocus()
|
||||||
|
imm.showSoftInput(editText, 0)
|
||||||
|
}, 300)
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = editText.text.isNotEmpty()
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_NEUTRAL).gravity = Gravity.START
|
||||||
|
}
|
||||||
|
|
||||||
val sponsorblock = view.findViewById<Chip>(R.id.sponsorblock_filters)
|
val sponsorblock = view.findViewById<Chip>(R.id.sponsorblock_filters)
|
||||||
sponsorblock!!.setOnClickListener {
|
sponsorblock!!.setOnClickListener {
|
||||||
val builder = MaterialAlertDialogBuilder(requireContext())
|
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,9 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
|
||||||
behavior = BottomSheetBehavior.from(view.parent as View)
|
behavior = BottomSheetBehavior.from(view.parent as View)
|
||||||
val displayMetrics = DisplayMetrics()
|
val displayMetrics = DisplayMetrics()
|
||||||
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
|
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||||
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
if(resources.getBoolean(R.bool.isTablet)){
|
||||||
|
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,9 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
|
||||||
behavior = BottomSheetBehavior.from(view.parent as View)
|
behavior = BottomSheetBehavior.from(view.parent as View)
|
||||||
val displayMetrics = DisplayMetrics()
|
val displayMetrics = DisplayMetrics()
|
||||||
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
|
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||||
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
if(resources.getBoolean(R.bool.isTablet)){
|
||||||
|
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
listAdapter =
|
listAdapter =
|
||||||
|
|
@ -409,11 +411,16 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
|
||||||
val deletedItem = items[position]
|
val deletedItem = items[position]
|
||||||
items.remove(deletedItem)
|
items.remove(deletedItem)
|
||||||
listAdapter.submitList(items.toList())
|
listAdapter.submitList(items.toList())
|
||||||
Snackbar.make(recyclerView, getString(R.string.you_are_going_to_delete) + ": " + deletedItem.title, Snackbar.LENGTH_LONG)
|
if (items.isNotEmpty()){
|
||||||
.setAction(getString(R.string.undo)) {
|
Snackbar.make(recyclerView, getString(R.string.you_are_going_to_delete) + ": " + deletedItem.title, Snackbar.LENGTH_LONG)
|
||||||
items.add(position, deletedItem)
|
.setAction(getString(R.string.undo)) {
|
||||||
listAdapter.submitList(items.toList())
|
items.add(position, deletedItem)
|
||||||
}.show()
|
listAdapter.submitList(items.toList())
|
||||||
|
}.show()
|
||||||
|
}else{
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -148,11 +148,6 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
|
||||||
val listener = object : OnFormatClickListener {
|
val listener = object : OnFormatClickListener {
|
||||||
override fun onFormatClick(allFormats: List<List<Format>>, item: List<Format>) {
|
override fun onFormatClick(allFormats: List<List<Format>>, item: List<Format>) {
|
||||||
downloadItem.format = item.first()
|
downloadItem.format = item.first()
|
||||||
|
|
||||||
if (containers.contains(item.first().container)){
|
|
||||||
downloadItem.format.container = item.first().container
|
|
||||||
containerAutoCompleteTextView.setText(item.first().container, false)
|
|
||||||
}
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
resultItem.formats.removeAll(formats.toSet())
|
resultItem.formats.removeAll(formats.toSet())
|
||||||
|
|
@ -171,6 +166,11 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
formatCard.setOnLongClickListener {
|
||||||
|
uiUtil.showFormatDetails(downloadItem.format, requireActivity())
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
container?.isEnabled = true
|
container?.isEnabled = true
|
||||||
containerAutoCompleteTextView?.setAdapter(
|
containerAutoCompleteTextView?.setAdapter(
|
||||||
ArrayAdapter(
|
ArrayAdapter(
|
||||||
|
|
@ -187,6 +187,7 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
|
||||||
(container!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
|
(container!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
|
||||||
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
|
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
|
||||||
downloadItem.format.container = containers[index]
|
downloadItem.format.container = containers[index]
|
||||||
|
if (containers[index] == getString(R.string.defaultValue)) downloadItem.format.container = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -313,6 +314,41 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
|
||||||
cut.isEnabled = false
|
cut.isEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val filenameTemplate = view.findViewById<Chip>(R.id.filename_template)
|
||||||
|
filenameTemplate.setOnClickListener {
|
||||||
|
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
builder.setTitle(getString(R.string.file_name_template))
|
||||||
|
val inputLayout = layoutInflater.inflate(R.layout.textinput, null)
|
||||||
|
val editText = inputLayout.findViewById<EditText>(R.id.url_edittext)
|
||||||
|
inputLayout.findViewById<TextInputLayout>(R.id.url_textinput).hint = getString(R.string.file_name_template)
|
||||||
|
editText.setText(downloadItem.customFileNameTemplate)
|
||||||
|
editText.setSelection(editText.text.length)
|
||||||
|
builder.setView(inputLayout)
|
||||||
|
builder.setPositiveButton(
|
||||||
|
getString(R.string.ok)
|
||||||
|
) { dialog: DialogInterface?, which: Int ->
|
||||||
|
downloadItem.customFileNameTemplate = editText.text.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle the negative button of the alert dialog
|
||||||
|
builder.setNegativeButton(
|
||||||
|
getString(R.string.cancel)
|
||||||
|
) { dialog: DialogInterface?, which: Int -> }
|
||||||
|
|
||||||
|
val dialog = builder.create()
|
||||||
|
editText.doOnTextChanged { text, start, before, count ->
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = editText.text.isNotEmpty()
|
||||||
|
}
|
||||||
|
dialog.show()
|
||||||
|
val imm = context?.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
editText!!.postDelayed({
|
||||||
|
editText.requestFocus()
|
||||||
|
imm.showSoftInput(editText, 0)
|
||||||
|
}, 300)
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = editText.text.isNotEmpty()
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_NEUTRAL).gravity = Gravity.START
|
||||||
|
}
|
||||||
|
|
||||||
val saveSubtitles = view.findViewById<Chip>(R.id.save_subtitles)
|
val saveSubtitles = view.findViewById<Chip>(R.id.save_subtitles)
|
||||||
val subtitleLanguages = view.findViewById<Chip>(R.id.subtitle_languages)
|
val subtitleLanguages = view.findViewById<Chip>(R.id.subtitle_languages)
|
||||||
if (downloadItem.videoPreferences.writeSubs) {
|
if (downloadItem.videoPreferences.writeSubs) {
|
||||||
|
|
|
||||||
|
|
@ -210,110 +210,13 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
formatItem.setOnLongClickListener {
|
formatItem.setOnLongClickListener {
|
||||||
val bottomSheet = BottomSheetDialog(requireContext())
|
uiUtil.showFormatDetails(format, requireActivity())
|
||||||
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
|
||||||
bottomSheet.setContentView(R.layout.format_details_sheet)
|
|
||||||
|
|
||||||
val formatIdParent = bottomSheet.findViewById<LinearLayout>(R.id.format_id_parent)
|
|
||||||
val containerParent = bottomSheet.findViewById<LinearLayout>(R.id.container_parent)
|
|
||||||
val codecParent = bottomSheet.findViewById<LinearLayout>(R.id.codec_parent)
|
|
||||||
val filesizeParent = bottomSheet.findViewById<LinearLayout>(R.id.filesize_parent)
|
|
||||||
val formatnoteParent = bottomSheet.findViewById<LinearLayout>(R.id.format_note_parent)
|
|
||||||
val fpsParent = bottomSheet.findViewById<LinearLayout>(R.id.fps_parent)
|
|
||||||
val asrParent = bottomSheet.findViewById<LinearLayout>(R.id.asr_parent)
|
|
||||||
|
|
||||||
if (format.format_id.isBlank()) formatIdParent?.visibility = View.GONE
|
|
||||||
else {
|
|
||||||
formatIdParent?.findViewById<TextView>(R.id.format_id_value)?.text = format.format_id
|
|
||||||
formatIdParent?.setOnClickListener {
|
|
||||||
copyToClipboard(format.format_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (format.container.isBlank()) containerParent?.visibility = View.GONE
|
|
||||||
else {
|
|
||||||
containerParent?.findViewById<TextView>(R.id.container_value)?.text = format.container
|
|
||||||
containerParent?.setOnClickListener {
|
|
||||||
copyToClipboard(format.container)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val codecField =
|
|
||||||
if (format.encoding != "") {
|
|
||||||
format.encoding.uppercase()
|
|
||||||
}else if (format.vcodec != "none" && format.vcodec != ""){
|
|
||||||
format.vcodec.uppercase()
|
|
||||||
} else {
|
|
||||||
format.acodec.uppercase()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (codecField.isBlank()) codecParent?.visibility = View.GONE
|
|
||||||
else {
|
|
||||||
codecParent?.findViewById<TextView>(R.id.codec_value)?.text = codecField
|
|
||||||
codecParent?.setOnClickListener {
|
|
||||||
copyToClipboard(codecField)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (format.filesize != 0L) filesizeParent?.visibility = View.GONE
|
|
||||||
else {
|
|
||||||
filesizeParent?.findViewById<TextView>(R.id.filesize_value)?.text = fileUtil.convertFileSize(format.filesize)
|
|
||||||
filesizeParent?.setOnClickListener {
|
|
||||||
copyToClipboard(fileUtil.convertFileSize(format.filesize))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (format.format_note.isBlank()) formatnoteParent?.visibility = View.GONE
|
|
||||||
else {
|
|
||||||
formatnoteParent?.findViewById<TextView>(R.id.format_note_value)?.text = format.format_note
|
|
||||||
formatnoteParent?.setOnClickListener {
|
|
||||||
copyToClipboard(format.format_note)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (format.fps.isNullOrBlank()) fpsParent?.visibility = View.GONE
|
|
||||||
else {
|
|
||||||
fpsParent?.findViewById<TextView>(R.id.fps_value)?.text = format.fps
|
|
||||||
fpsParent?.setOnClickListener {
|
|
||||||
copyToClipboard(format.fps!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (format.asr.isNullOrBlank()) asrParent?.visibility = View.GONE
|
|
||||||
else {
|
|
||||||
asrParent?.findViewById<TextView>(R.id.asr_value)?.text = format.asr
|
|
||||||
asrParent?.setOnClickListener {
|
|
||||||
copyToClipboard(format.asr!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bottomSheet.show()
|
|
||||||
val displayMetrics = DisplayMetrics()
|
|
||||||
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
|
|
||||||
bottomSheet.behavior.peekHeight = displayMetrics.heightPixels
|
|
||||||
bottomSheet.window!!.setLayout(
|
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
||||||
ViewGroup.LayoutParams.MATCH_PARENT
|
|
||||||
)
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
linearLayout.addView(formatItem)
|
linearLayout.addView(formatItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun copyToClipboard(text: String){
|
|
||||||
val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
||||||
val clip = ClipData.newPlainText(text, text)
|
|
||||||
clipboard.setPrimaryClip(clip)
|
|
||||||
Toast.makeText(context, requireContext().getString(R.string.copied_to_clipboard), Toast.LENGTH_SHORT)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun onCancel(dialog: DialogInterface) {
|
override fun onCancel(dialog: DialogInterface) {
|
||||||
super.onCancel(dialog)
|
super.onCancel(dialog)
|
||||||
cleanUp()
|
cleanUp()
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,16 @@ package com.deniscerri.ytdlnis.ui.downloadcard
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
|
import android.content.ClipboardManager
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.widget.doAfterTextChanged
|
import androidx.core.widget.doAfterTextChanged
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
|
@ -21,8 +24,10 @@ import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
|
||||||
import com.deniscerri.ytdlnis.receiver.ShareActivity
|
import com.deniscerri.ytdlnis.receiver.ShareActivity
|
||||||
|
import com.google.android.material.bottomappbar.BottomAppBar
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
@ -115,12 +120,14 @@ class SelectPlaylistItemsBottomSheetDialog(private val items: List<ResultItem?>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val checkAll = view.findViewById<FloatingActionButton>(R.id.check_all)
|
val checkAll = view.findViewById<ExtendedFloatingActionButton>(R.id.check_all)
|
||||||
checkAll!!.setOnClickListener {
|
checkAll!!.setOnClickListener {
|
||||||
if (listAdapter.getCheckedItems().size != items.size){
|
if (listAdapter.getCheckedItems().size != items.size){
|
||||||
fromTextInput.editText!!.setText("1")
|
fromTextInput.editText!!.setText("1")
|
||||||
toTextInput.editText!!.setText(items.size.toString())
|
toTextInput.editText!!.setText(items.size.toString())
|
||||||
listAdapter.checkAll()
|
listAdapter.checkAll()
|
||||||
|
fromTextInput.isEnabled = true
|
||||||
|
toTextInput.isEnabled = true
|
||||||
selectedText.text = resources.getString(R.string.all_items_selected)
|
selectedText.text = resources.getString(R.string.all_items_selected)
|
||||||
}else{
|
}else{
|
||||||
reset()
|
reset()
|
||||||
|
|
@ -160,6 +167,24 @@ class SelectPlaylistItemsBottomSheetDialog(private val items: List<ResultItem?>,
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
view.findViewById<BottomAppBar>(R.id.bottomAppBar).setOnMenuItemClickListener { m: MenuItem ->
|
||||||
|
val itemId = m.itemId
|
||||||
|
if (itemId == R.id.invert_selected) {
|
||||||
|
listAdapter.invertSelected(items)
|
||||||
|
val checkedItems = listAdapter.getCheckedItems()
|
||||||
|
if (checkedItems.size == items.size){
|
||||||
|
selectedText.text = resources.getString(R.string.all_items_selected)
|
||||||
|
}else{
|
||||||
|
selectedText.text = "${checkedItems.size} ${resources.getString(R.string.selected)}"
|
||||||
|
}
|
||||||
|
if(checkedItems.isNotEmpty() && checkedItems.size < items.size){
|
||||||
|
fromTextInput.isEnabled = false
|
||||||
|
toTextInput.isEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCancel(dialog: DialogInterface) {
|
override fun onCancel(dialog: DialogInterface) {
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import com.google.android.material.chip.Chip
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
|
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -124,6 +125,7 @@ class CookiesActivity : BaseActivity(), CookieAdapter.OnItemClickListener {
|
||||||
builder.setTitle(getString(R.string.cookies))
|
builder.setTitle(getString(R.string.cookies))
|
||||||
val inputLayout = layoutInflater.inflate(R.layout.textinput, null)
|
val inputLayout = layoutInflater.inflate(R.layout.textinput, null)
|
||||||
val editText = inputLayout.findViewById<EditText>(R.id.url_edittext)
|
val editText = inputLayout.findViewById<EditText>(R.id.url_edittext)
|
||||||
|
inputLayout.findViewById<TextInputLayout>(R.id.url_textinput).hint = "URL"
|
||||||
val text = if (url.isNullOrBlank()) "https://" else url
|
val text = if (url.isNullOrBlank()) "https://" else url
|
||||||
editText.setText(text)
|
editText.setText(text)
|
||||||
editText.setSelection(editText.text.length)
|
editText.setSelection(editText.text.length)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,158 @@
|
||||||
|
package com.deniscerri.ytdlnis.ui.more.settings
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context.POWER_SERVICE
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build.VERSION
|
||||||
|
import android.os.Build.VERSION_CODES
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Environment
|
||||||
|
import android.os.PowerManager
|
||||||
|
import android.provider.Settings
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
|
import androidx.core.os.LocaleListCompat
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.preference.EditTextPreference
|
||||||
|
import androidx.preference.ListPreference
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import androidx.preference.SeekBarPreference
|
||||||
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
|
import androidx.work.WorkInfo
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import com.deniscerri.ytdlnis.BuildConfig
|
||||||
|
import com.deniscerri.ytdlnis.MainActivity
|
||||||
|
import com.deniscerri.ytdlnis.R
|
||||||
|
import com.deniscerri.ytdlnis.database.models.CommandTemplate
|
||||||
|
import com.deniscerri.ytdlnis.database.models.CookieItem
|
||||||
|
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
|
import com.deniscerri.ytdlnis.database.models.HistoryItem
|
||||||
|
import com.deniscerri.ytdlnis.database.models.SearchHistoryItem
|
||||||
|
import com.deniscerri.ytdlnis.database.models.TemplateShortcut
|
||||||
|
import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel
|
||||||
|
import com.deniscerri.ytdlnis.database.viewmodel.CookieViewModel
|
||||||
|
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
|
import com.deniscerri.ytdlnis.database.viewmodel.HistoryViewModel
|
||||||
|
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
|
||||||
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
|
import com.deniscerri.ytdlnis.util.ThemeUtil
|
||||||
|
import com.deniscerri.ytdlnis.util.UpdateUtil
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.JsonArray
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import com.google.gson.JsonParser
|
||||||
|
import com.yausername.youtubedl_android.YoutubeDL
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import java.io.BufferedReader
|
||||||
|
import java.io.File
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
|
||||||
|
class AppearanceSettingsFragment : BaseSettingsFragment() {
|
||||||
|
override val title: Int = R.string.appearance
|
||||||
|
|
||||||
|
private var language: ListPreference? = null
|
||||||
|
private var theme: ListPreference? = null
|
||||||
|
private var accent: ListPreference? = null
|
||||||
|
private var highContrast: SwitchPreferenceCompat? = null
|
||||||
|
private var locale: ListPreference? = null
|
||||||
|
|
||||||
|
private var updateUtil: UpdateUtil? = null
|
||||||
|
private var fileUtil: FileUtil? = null
|
||||||
|
private var activeDownloadCount = 0
|
||||||
|
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
setPreferencesFromResource(R.xml.appearance_preferences, rootKey)
|
||||||
|
updateUtil = UpdateUtil(requireContext())
|
||||||
|
fileUtil = FileUtil()
|
||||||
|
|
||||||
|
WorkManager.getInstance(requireContext()).getWorkInfosByTagLiveData("download").observe(this){
|
||||||
|
activeDownloadCount = 0
|
||||||
|
it.forEach {w ->
|
||||||
|
if (w.state == WorkInfo.State.RUNNING) activeDownloadCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
|
language = findPreference("app_language")
|
||||||
|
theme = findPreference("ytdlnis_theme")
|
||||||
|
accent = findPreference("theme_accent")
|
||||||
|
highContrast = findPreference("high_contrast")
|
||||||
|
locale = findPreference("locale")
|
||||||
|
|
||||||
|
if(VERSION.SDK_INT < VERSION_CODES.TIRAMISU){
|
||||||
|
val values = resources.getStringArray(R.array.language_values)
|
||||||
|
val entries = mutableListOf<String>()
|
||||||
|
values.forEach {
|
||||||
|
entries.add(Locale(it).getDisplayName(Locale(it)))
|
||||||
|
}
|
||||||
|
language!!.entries = entries.toTypedArray()
|
||||||
|
}else{
|
||||||
|
language!!.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
theme!!.summary = theme!!.entry
|
||||||
|
theme!!.onPreferenceChangeListener =
|
||||||
|
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
|
||||||
|
when(newValue){
|
||||||
|
"System" -> {
|
||||||
|
theme!!.summary = getString(R.string.system)
|
||||||
|
}
|
||||||
|
"Dark" -> {
|
||||||
|
theme!!.summary = getString(R.string.dark)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
theme!!.summary = getString(R.string.light)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ThemeUtil.updateTheme(requireActivity() as AppCompatActivity)
|
||||||
|
val intent = Intent(requireContext(), MainActivity::class.java)
|
||||||
|
this.startActivity(intent)
|
||||||
|
requireActivity().finishAffinity()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
accent!!.summary = accent!!.entry
|
||||||
|
accent!!.onPreferenceChangeListener =
|
||||||
|
Preference.OnPreferenceChangeListener { _: Preference?, _: Any ->
|
||||||
|
ThemeUtil.updateTheme(requireActivity() as AppCompatActivity)
|
||||||
|
val intent = Intent(requireContext(), MainActivity::class.java)
|
||||||
|
this.startActivity(intent)
|
||||||
|
requireActivity().finishAffinity()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
highContrast!!.onPreferenceChangeListener =
|
||||||
|
Preference.OnPreferenceChangeListener { _: Preference?, _: Any ->
|
||||||
|
ThemeUtil.updateTheme(requireActivity() as AppCompatActivity)
|
||||||
|
val intent = Intent(requireContext(), MainActivity::class.java)
|
||||||
|
this.startActivity(intent)
|
||||||
|
requireActivity().finishAffinity()
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.deniscerri.ytdlnis.ui.more.settings
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
|
||||||
|
|
||||||
|
abstract class BaseSettingsFragment : PreferenceFragmentCompat() {
|
||||||
|
abstract val title: Int
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
(activity as? SettingsActivity)?.changeTopAppbarTitle(getString(title))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
package com.deniscerri.ytdlnis.ui.more.settings
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context.POWER_SERVICE
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.PowerManager
|
||||||
|
import android.provider.Settings
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.SeekBarPreference
|
||||||
|
import com.deniscerri.ytdlnis.MainActivity
|
||||||
|
import com.deniscerri.ytdlnis.R
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
|
||||||
|
|
||||||
|
class DownloadSettingsFragment : BaseSettingsFragment() {
|
||||||
|
override val title: Int = R.string.downloading
|
||||||
|
|
||||||
|
private var concurrentDownloads: SeekBarPreference? = null
|
||||||
|
private var ignoreBatteryOptimization: Preference? = null
|
||||||
|
|
||||||
|
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
setPreferencesFromResource(R.xml.downloading_preferences, rootKey)
|
||||||
|
|
||||||
|
ignoreBatteryOptimization = findPreference("ignore_battery")
|
||||||
|
val packageName: String = requireContext().packageName
|
||||||
|
val pm = requireContext().applicationContext.getSystemService(POWER_SERVICE) as PowerManager
|
||||||
|
if (pm.isIgnoringBatteryOptimizations(packageName)) {
|
||||||
|
ignoreBatteryOptimization!!.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
concurrentDownloads = findPreference("concurrent_downloads")
|
||||||
|
concurrentDownloads!!.onPreferenceChangeListener =
|
||||||
|
Preference.OnPreferenceChangeListener { _: Preference?, _: Any ->
|
||||||
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(resources.getString(R.string.warning))
|
||||||
|
.setMessage(resources.getString(R.string.workmanager_updated))
|
||||||
|
.setNegativeButton(resources.getString(R.string.cancel)) { dialog, _ ->
|
||||||
|
dialog.cancel()
|
||||||
|
}
|
||||||
|
.setPositiveButton(resources.getString(R.string.restart)) { _, _ ->
|
||||||
|
val intent = Intent(context, MainActivity::class.java)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
requireContext().startActivity(intent)
|
||||||
|
if (context is Activity) {
|
||||||
|
(context as Activity).finish()
|
||||||
|
}
|
||||||
|
Runtime.getRuntime().exit(0)
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
ignoreBatteryOptimization = findPreference("ignore_battery")
|
||||||
|
ignoreBatteryOptimization!!.onPreferenceClickListener =
|
||||||
|
Preference.OnPreferenceClickListener {
|
||||||
|
val intent = Intent()
|
||||||
|
intent.action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
|
||||||
|
intent.data = Uri.parse("package:" + requireContext().packageName)
|
||||||
|
startActivity(intent)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
package com.deniscerri.ytdlnis.ui.more.settings
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build.VERSION
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Environment
|
||||||
|
import android.provider.Settings
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.deniscerri.ytdlnis.R
|
||||||
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
|
import com.deniscerri.ytdlnis.util.UpdateUtil
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
|
class FolderSettingsFragment : BaseSettingsFragment() {
|
||||||
|
override val title: Int = R.string.directories
|
||||||
|
|
||||||
|
private var musicPath: Preference? = null
|
||||||
|
private var videoPath: Preference? = null
|
||||||
|
private var commandPath: Preference? = null
|
||||||
|
private var accessAllFiles : Preference? = null
|
||||||
|
private var clearCache: Preference? = null
|
||||||
|
|
||||||
|
private var fileUtil: FileUtil? = null
|
||||||
|
private var activeDownloadCount = 0
|
||||||
|
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
setPreferencesFromResource(R.xml.folders_preference, rootKey)
|
||||||
|
|
||||||
|
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
|
val editor = preferences.edit()
|
||||||
|
fileUtil = FileUtil()
|
||||||
|
|
||||||
|
musicPath = findPreference("music_path")
|
||||||
|
videoPath = findPreference("video_path")
|
||||||
|
commandPath = findPreference("command_path")
|
||||||
|
accessAllFiles = findPreference("access_all_files")
|
||||||
|
clearCache = findPreference("clear_cache")
|
||||||
|
|
||||||
|
if (preferences.getString("music_path", "")!!.isEmpty()) {
|
||||||
|
editor.putString("music_path", getString(R.string.music_path))
|
||||||
|
}
|
||||||
|
if (preferences.getString("video_path", "")!!.isEmpty()) {
|
||||||
|
editor.putString("video_path", getString(R.string.video_path))
|
||||||
|
}
|
||||||
|
if (preferences.getString("command_path", "")!!.isEmpty()) {
|
||||||
|
editor.putString("command_path", getString(R.string.command_path))
|
||||||
|
}
|
||||||
|
|
||||||
|
if((VERSION.SDK_INT >= 30 && Environment.isExternalStorageManager()) ||
|
||||||
|
VERSION.SDK_INT < 30) {
|
||||||
|
accessAllFiles!!.isVisible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.apply()
|
||||||
|
|
||||||
|
musicPath!!.summary = fileUtil?.formatPath(preferences.getString("music_path", "")!!)
|
||||||
|
musicPath!!.onPreferenceClickListener =
|
||||||
|
Preference.OnPreferenceClickListener {
|
||||||
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||||
|
musicPathResultLauncher.launch(intent)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
videoPath!!.summary = fileUtil?.formatPath(preferences.getString("video_path", "")!!)
|
||||||
|
videoPath!!.onPreferenceClickListener =
|
||||||
|
Preference.OnPreferenceClickListener {
|
||||||
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||||
|
videoPathResultLauncher.launch(intent)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
commandPath!!.summary = fileUtil?.formatPath(preferences.getString("command_path", "")!!)
|
||||||
|
commandPath!!.onPreferenceClickListener =
|
||||||
|
Preference.OnPreferenceClickListener {
|
||||||
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||||
|
commandPathResultLauncher.launch(intent)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
accessAllFiles!!.onPreferenceClickListener =
|
||||||
|
Preference.OnPreferenceClickListener {
|
||||||
|
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
|
||||||
|
val uri = Uri.parse("package:" + requireContext().packageName)
|
||||||
|
intent.data = uri
|
||||||
|
startActivity(intent)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
var cacheSize = File(requireContext().cacheDir.absolutePath + "/downloads").walkBottomUp().fold(0L) { acc, file -> acc + file.length() }
|
||||||
|
clearCache!!.summary = "(${fileUtil!!.convertFileSize(cacheSize)}) ${resources.getString(R.string.clear_temporary_files_summary)}"
|
||||||
|
clearCache!!.onPreferenceClickListener =
|
||||||
|
Preference.OnPreferenceClickListener {
|
||||||
|
if (activeDownloadCount == 0){
|
||||||
|
File(requireContext().cacheDir.absolutePath + "/downloads").deleteRecursively()
|
||||||
|
Snackbar.make(requireView(), getString(R.string.cache_cleared), Snackbar.LENGTH_SHORT).show()
|
||||||
|
cacheSize = File(requireContext().cacheDir.absolutePath + "/downloads").walkBottomUp().fold(0L) { acc, file -> acc + file.length() }
|
||||||
|
clearCache!!.summary = "(${fileUtil!!.convertFileSize(cacheSize)}) ${resources.getString(R.string.clear_temporary_files_summary)}"
|
||||||
|
}else{
|
||||||
|
Snackbar.make(requireView(), getString(R.string.downloads_running_try_later), Snackbar.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private var musicPathResultLauncher = registerForActivityResult(
|
||||||
|
ActivityResultContracts.StartActivityForResult()
|
||||||
|
) { result ->
|
||||||
|
if (result.resultCode == Activity.RESULT_OK) {
|
||||||
|
result.data?.data?.let {
|
||||||
|
activity?.contentResolver?.takePersistableUriPermission(
|
||||||
|
it,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
||||||
|
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||||
|
)
|
||||||
|
}
|
||||||
|
changePath(musicPath, result.data, MUSIC_PATH_CODE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private var videoPathResultLauncher = registerForActivityResult(
|
||||||
|
ActivityResultContracts.StartActivityForResult()
|
||||||
|
) { result ->
|
||||||
|
if (result.resultCode == Activity.RESULT_OK) {
|
||||||
|
result.data?.data?.let {
|
||||||
|
activity?.contentResolver?.takePersistableUriPermission(
|
||||||
|
it,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
||||||
|
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||||
|
)
|
||||||
|
}
|
||||||
|
changePath(videoPath, result.data, VIDEO_PATH_CODE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private var commandPathResultLauncher = registerForActivityResult(
|
||||||
|
ActivityResultContracts.StartActivityForResult()
|
||||||
|
) { result ->
|
||||||
|
if (result.resultCode == Activity.RESULT_OK) {
|
||||||
|
result.data?.data?.let {
|
||||||
|
activity?.contentResolver?.takePersistableUriPermission(
|
||||||
|
it,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
||||||
|
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||||
|
)
|
||||||
|
}
|
||||||
|
changePath(commandPath, result.data, COMMAND_PATH_CODE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun changePath(p: Preference?, data: Intent?, requestCode: Int) {
|
||||||
|
val path = data!!.data.toString()
|
||||||
|
p!!.summary = fileUtil?.formatPath(data.data.toString())
|
||||||
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
|
val editor = sharedPreferences.edit()
|
||||||
|
when (requestCode) {
|
||||||
|
MUSIC_PATH_CODE -> editor.putString("music_path", path)
|
||||||
|
VIDEO_PATH_CODE -> editor.putString("video_path", path)
|
||||||
|
COMMAND_PATH_CODE -> editor.putString("command_path", path)
|
||||||
|
}
|
||||||
|
editor.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val MUSIC_PATH_CODE = 33333
|
||||||
|
const val VIDEO_PATH_CODE = 55555
|
||||||
|
const val COMMAND_PATH_CODE = 77777
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,501 @@
|
||||||
|
package com.deniscerri.ytdlnis.ui.more.settings
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Environment
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import androidx.work.WorkInfo
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import com.deniscerri.ytdlnis.BuildConfig
|
||||||
|
import com.deniscerri.ytdlnis.MainActivity
|
||||||
|
import com.deniscerri.ytdlnis.R
|
||||||
|
import com.deniscerri.ytdlnis.database.models.CommandTemplate
|
||||||
|
import com.deniscerri.ytdlnis.database.models.CookieItem
|
||||||
|
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
|
import com.deniscerri.ytdlnis.database.models.HistoryItem
|
||||||
|
import com.deniscerri.ytdlnis.database.models.SearchHistoryItem
|
||||||
|
import com.deniscerri.ytdlnis.database.models.TemplateShortcut
|
||||||
|
import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel
|
||||||
|
import com.deniscerri.ytdlnis.database.viewmodel.CookieViewModel
|
||||||
|
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
|
import com.deniscerri.ytdlnis.database.viewmodel.HistoryViewModel
|
||||||
|
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
|
||||||
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
|
import com.deniscerri.ytdlnis.util.UpdateUtil
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.JsonArray
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
import com.google.gson.JsonParser
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.io.BufferedReader
|
||||||
|
import java.io.File
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
import java.util.Calendar
|
||||||
|
|
||||||
|
|
||||||
|
class MainSettingsFragment : PreferenceFragmentCompat() {
|
||||||
|
private var backup : Preference? = null
|
||||||
|
private var restore : Preference? = null
|
||||||
|
|
||||||
|
private var updateUtil: UpdateUtil? = null
|
||||||
|
private var fileUtil: FileUtil? = null
|
||||||
|
private var activeDownloadCount = 0
|
||||||
|
|
||||||
|
private lateinit var resultViewModel: ResultViewModel
|
||||||
|
private lateinit var historyViewModel: HistoryViewModel
|
||||||
|
private lateinit var downloadViewModel: DownloadViewModel
|
||||||
|
private lateinit var cookieViewModel: CookieViewModel
|
||||||
|
private lateinit var commandTemplateViewModel: CommandTemplateViewModel
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
setPreferencesFromResource(R.xml.root_preferences, rootKey)
|
||||||
|
val navController = findNavController()
|
||||||
|
val appearance = findPreference<Preference>("appearance")
|
||||||
|
appearance?.summary = "${getString(R.string.language)}, ${getString(R.string.Theme)}, ${getString(R.string.accents)}"
|
||||||
|
appearance?.setOnPreferenceClickListener {
|
||||||
|
navController.navigate(R.id.action_mainSettingsFragment_to_appearanceSettingsFragment)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
val folders = findPreference<Preference>("folders")
|
||||||
|
folders?.summary = "${getString(R.string.music_directory)}, ${getString(R.string.video_directory)}, ${getString(R.string.command_directory)}"
|
||||||
|
folders?.setOnPreferenceClickListener {
|
||||||
|
navController.navigate(R.id.action_mainSettingsFragment_to_folderSettingsFragment)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
val downloading = findPreference<Preference>("downloading")
|
||||||
|
downloading?.summary = "${getString(R.string.quick_download)}, ${getString(R.string.concurrent_downloads)}, ${getString(R.string.limit_rate)}"
|
||||||
|
downloading?.setOnPreferenceClickListener {
|
||||||
|
navController.navigate(R.id.action_mainSettingsFragment_to_downloadSettingsFragment)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
val processing = findPreference<Preference>("processing")
|
||||||
|
processing?.summary = "${getString(R.string.sponsorblock)}, ${getString(R.string.embed_subtitles)}, ${getString(R.string.add_chapters)}"
|
||||||
|
processing?.setOnPreferenceClickListener {
|
||||||
|
navController.navigate(R.id.action_mainSettingsFragment_to_processingSettingsFragment)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
val updating = findPreference<Preference>("updating")
|
||||||
|
updating?.summary = "${getString(R.string.update_ytdl)}, ${getString(R.string.format_source)}, ${getString(R.string.update_app)}"
|
||||||
|
updating?.setOnPreferenceClickListener {
|
||||||
|
navController.navigate(R.id.action_mainSettingsFragment_to_updateSettingsFragment)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
//about section -------------------------
|
||||||
|
updateUtil = UpdateUtil(requireContext())
|
||||||
|
fileUtil = FileUtil()
|
||||||
|
|
||||||
|
WorkManager.getInstance(requireContext()).getWorkInfosByTagLiveData("download").observe(this){
|
||||||
|
activeDownloadCount = 0
|
||||||
|
it.forEach {w ->
|
||||||
|
if (w.state == WorkInfo.State.RUNNING) activeDownloadCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
|
||||||
|
historyViewModel = ViewModelProvider(this)[HistoryViewModel::class.java]
|
||||||
|
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
||||||
|
cookieViewModel = ViewModelProvider(this)[CookieViewModel::class.java]
|
||||||
|
commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java]
|
||||||
|
|
||||||
|
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
|
backup = findPreference("backup")
|
||||||
|
restore = findPreference("restore")
|
||||||
|
|
||||||
|
backup!!.onPreferenceClickListener =
|
||||||
|
Preference.OnPreferenceClickListener {
|
||||||
|
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
builder.setTitle(getString(R.string.select_backup_categories))
|
||||||
|
val values = resources.getStringArray(R.array.backup_category_values)
|
||||||
|
val entries = resources.getStringArray(R.array.backup_category_entries)
|
||||||
|
val checkedItems : ArrayList<Boolean> = arrayListOf()
|
||||||
|
values.forEach { _ ->
|
||||||
|
checkedItems.add(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.setMultiChoiceItems(
|
||||||
|
entries,
|
||||||
|
checkedItems.toBooleanArray()
|
||||||
|
) { _, which, isChecked ->
|
||||||
|
checkedItems[which] = isChecked
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.setPositiveButton(
|
||||||
|
getString(R.string.ok)
|
||||||
|
) { _: DialogInterface?, _: Int ->
|
||||||
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
val json = JsonObject()
|
||||||
|
json.addProperty("app", "YTDLnis_backup")
|
||||||
|
for (i in 0 until checkedItems.size) {
|
||||||
|
if (checkedItems[i]) {
|
||||||
|
when(values[i]){
|
||||||
|
"settings" -> json.add("settings", backupSettings(preferences))
|
||||||
|
"downloads" -> json.add("downloads", backupHistory())
|
||||||
|
"queued" -> json.add("queued", backupQueuedDownloads() )
|
||||||
|
"cancelled" -> json.add("cancelled", backupCancelledDownloads() )
|
||||||
|
"cookies" -> json.add("cookies", backupCookies() )
|
||||||
|
"templates" -> json.add("templates", backupCommandTemplates() )
|
||||||
|
"shortcuts" -> json.add("shortcuts", backupShortcuts() )
|
||||||
|
"searchHistory" -> json.add("search_history", backupSearchHistory() )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val currentTime = Calendar.getInstance()
|
||||||
|
val saveFile = File(
|
||||||
|
Environment.getExternalStoragePublicDirectory(
|
||||||
|
Environment.DIRECTORY_DOWNLOADS), "YTDLnis_${BuildConfig.VERSION_NAME}_${currentTime.get(
|
||||||
|
Calendar.YEAR)}-${currentTime.get(Calendar.MONTH) + 1}-${currentTime.get(
|
||||||
|
Calendar.DAY_OF_MONTH)}.json")
|
||||||
|
saveFile.delete()
|
||||||
|
saveFile.createNewFile()
|
||||||
|
saveFile.writeText(Gson().toJson(json))
|
||||||
|
Snackbar.make(requireView(), getString(R.string.backup_created_successfully), Snackbar.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle the negative button of the alert dialog
|
||||||
|
builder.setNegativeButton(
|
||||||
|
getString(R.string.cancel)
|
||||||
|
) { _: DialogInterface?, _: Int -> }
|
||||||
|
|
||||||
|
|
||||||
|
val dialog = builder.create()
|
||||||
|
dialog.show()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
restore!!.onPreferenceClickListener =
|
||||||
|
Preference.OnPreferenceClickListener {
|
||||||
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||||
|
addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
type = "application/json"
|
||||||
|
}
|
||||||
|
appRestoreResultLauncher.launch(intent)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun backupSettings(preferences: SharedPreferences) : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val prefs = preferences.all
|
||||||
|
val arr = JsonArray()
|
||||||
|
prefs.forEach {
|
||||||
|
val obj = JsonObject()
|
||||||
|
obj.addProperty("key", it.key)
|
||||||
|
obj.addProperty("value", it.value.toString())
|
||||||
|
obj.addProperty("type", it.value!!::class.simpleName)
|
||||||
|
arr.add(obj)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun backupHistory() : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val historyItems = withContext(Dispatchers.IO) {
|
||||||
|
historyViewModel.getAll()
|
||||||
|
}
|
||||||
|
val arr = JsonArray()
|
||||||
|
historyItems.forEach {
|
||||||
|
arr.add(JsonParser().parse(Gson().toJson(it)).asJsonObject)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun backupQueuedDownloads() : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val items = withContext(Dispatchers.IO) {
|
||||||
|
downloadViewModel.getQueued()
|
||||||
|
}
|
||||||
|
val arr = JsonArray()
|
||||||
|
items.forEach {
|
||||||
|
arr.add(JsonParser().parse(Gson().toJson(it)).asJsonObject)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun backupCancelledDownloads() : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val items = withContext(Dispatchers.IO) {
|
||||||
|
downloadViewModel.getCancelled()
|
||||||
|
}
|
||||||
|
val arr = JsonArray()
|
||||||
|
items.forEach {
|
||||||
|
arr.add(JsonParser().parse(Gson().toJson(it)).asJsonObject)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun backupCookies() : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val items = withContext(Dispatchers.IO) {
|
||||||
|
cookieViewModel.getAll()
|
||||||
|
}
|
||||||
|
val arr = JsonArray()
|
||||||
|
items.forEach {
|
||||||
|
arr.add(JsonParser().parse(Gson().toJson(it)).asJsonObject)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun backupCommandTemplates() : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val items = withContext(Dispatchers.IO) {
|
||||||
|
commandTemplateViewModel.getAll()
|
||||||
|
}
|
||||||
|
val arr = JsonArray()
|
||||||
|
items.forEach {
|
||||||
|
arr.add(JsonParser().parse(Gson().toJson(it)).asJsonObject)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun backupShortcuts() : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val items = withContext(Dispatchers.IO) {
|
||||||
|
commandTemplateViewModel.getAllShortcuts()
|
||||||
|
}
|
||||||
|
val arr = JsonArray()
|
||||||
|
items.forEach {
|
||||||
|
arr.add(JsonParser().parse(Gson().toJson(it)).asJsonObject)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun backupSearchHistory() : JsonArray {
|
||||||
|
runCatching {
|
||||||
|
val historyItems = withContext(Dispatchers.IO) {
|
||||||
|
resultViewModel.getSearchHistory()
|
||||||
|
}
|
||||||
|
val arr = JsonArray()
|
||||||
|
historyItems.forEach {
|
||||||
|
arr.add(JsonParser().parse(Gson().toJson(it)).asJsonObject)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
return JsonArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var appRestoreResultLauncher = registerForActivityResult(
|
||||||
|
ActivityResultContracts.StartActivityForResult()
|
||||||
|
) { result ->
|
||||||
|
if (result.resultCode == Activity.RESULT_OK) {
|
||||||
|
result.data?.data?.let {
|
||||||
|
activity?.contentResolver?.takePersistableUriPermission(
|
||||||
|
it,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
||||||
|
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val preferences =
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
|
val editor = preferences.edit()
|
||||||
|
|
||||||
|
runCatching {
|
||||||
|
val ip = requireContext().contentResolver.openInputStream(result.data!!.data!!)
|
||||||
|
val r = BufferedReader(InputStreamReader(ip))
|
||||||
|
val total: java.lang.StringBuilder = java.lang.StringBuilder()
|
||||||
|
var line: String?
|
||||||
|
while (r.readLine().also { line = it } != null) {
|
||||||
|
total.append(line).append('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
val json = Gson().fromJson(total.toString(), JsonObject::class.java)
|
||||||
|
val finalMessage = StringBuilder("${getString(R.string.restore_complete)}\n")
|
||||||
|
|
||||||
|
//preference restore
|
||||||
|
if(json.has("settings")){
|
||||||
|
val prefs = json.getAsJsonArray("settings")
|
||||||
|
val preferencesKeys = preferences.all.map { it.key }
|
||||||
|
prefs.forEach {
|
||||||
|
val key : String = it.asJsonObject.get("key").toString().replace("\"", "")
|
||||||
|
if (preferencesKeys.contains(key)){
|
||||||
|
when(it.asJsonObject.get("type").toString().replace("\"", "")){
|
||||||
|
"String" -> {
|
||||||
|
val value = it.asJsonObject.get("value").toString().replace("\"", "")
|
||||||
|
editor.putString(key, value)
|
||||||
|
}
|
||||||
|
"Boolean" -> {
|
||||||
|
val value = it.asJsonObject.get("value").toString().replace("\"", "").toBoolean()
|
||||||
|
editor.putBoolean(key, value)
|
||||||
|
}
|
||||||
|
"Int" -> {
|
||||||
|
val value = it.asJsonObject.get("value").toString().replace("\"", "").toInt()
|
||||||
|
editor.putInt(key, value)
|
||||||
|
}
|
||||||
|
"HashSet" -> {
|
||||||
|
val value = hashSetOf(it.asJsonObject.get("value").toString().replace("(\")|(\\[)|(])".toRegex(), ""))
|
||||||
|
editor.putStringSet(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finalMessage.append("${getString(R.string.settings)}: ${prefs.count()}\n")
|
||||||
|
}
|
||||||
|
//history restore
|
||||||
|
if(json.has("downloads")){
|
||||||
|
val items = json.getAsJsonArray("downloads")
|
||||||
|
items.forEach {
|
||||||
|
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), HistoryItem::class.java)
|
||||||
|
item.id = 0L
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
historyViewModel.insert(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finalMessage.append("${getString(R.string.downloads)}: ${items.count()}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
//queued downloads restore
|
||||||
|
if(json.has("queued")){
|
||||||
|
val items = json.getAsJsonArray("queued")
|
||||||
|
val queued = mutableListOf<DownloadItem>()
|
||||||
|
items.forEach {
|
||||||
|
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), DownloadItem::class.java)
|
||||||
|
item.id = 0L
|
||||||
|
queued.add(item)
|
||||||
|
}
|
||||||
|
if (queued.isNotEmpty()) {
|
||||||
|
finalMessage.append("${getString(R.string.in_queue)}: ${queued.count()}\n")
|
||||||
|
withContext(Dispatchers.IO){downloadViewModel.queueDownloads(queued)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//cancelled downloads restore
|
||||||
|
if(json.has("cancelled")){
|
||||||
|
val items = json.getAsJsonArray("cancelled")
|
||||||
|
val cancelled = mutableListOf<DownloadItem>()
|
||||||
|
items.forEach {
|
||||||
|
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), DownloadItem::class.java)
|
||||||
|
item.id = 0L
|
||||||
|
cancelled.add(item)
|
||||||
|
}
|
||||||
|
if(cancelled.isNotEmpty()){
|
||||||
|
finalMessage.append("${getString(R.string.cancelled)}: ${cancelled.count()}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//cookies restore
|
||||||
|
if(json.has("cookies")){
|
||||||
|
val items = json.getAsJsonArray("cookies")
|
||||||
|
items.forEach {
|
||||||
|
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), CookieItem::class.java)
|
||||||
|
item.id = 0L
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
cookieViewModel.insert(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(items.count() > 0){
|
||||||
|
finalMessage.append("${getString(R.string.cookies)}: ${items.count()}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//command template restore
|
||||||
|
if(json.has("templates")){
|
||||||
|
val items = json.getAsJsonArray("templates")
|
||||||
|
items.forEach {
|
||||||
|
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), CommandTemplate::class.java)
|
||||||
|
item.id = 0L
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
commandTemplateViewModel.insert(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(items.count() > 0){
|
||||||
|
finalMessage.append("${getString(R.string.command_templates)}: ${items.count()}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//shortcuts restore
|
||||||
|
if(json.has("shortcuts")){
|
||||||
|
val items = json.getAsJsonArray("shortcuts")
|
||||||
|
items.forEach {
|
||||||
|
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), TemplateShortcut::class.java)
|
||||||
|
item.id = 0L
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
commandTemplateViewModel.insertShortcut(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(items.count() > 0){
|
||||||
|
finalMessage.append("${getString(R.string.shortcuts)}: ${items.count()}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//search history restore
|
||||||
|
if(json.has("search_history")){
|
||||||
|
val items = json.getAsJsonArray("search_history")
|
||||||
|
items.forEach {
|
||||||
|
val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), SearchHistoryItem::class.java)
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
resultViewModel.addSearchQueryToHistory(item.query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(items.count() > 0){
|
||||||
|
finalMessage.append("${getString(R.string.search_history)}: ${items.count()}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finalMessage.append("${getString(R.string.restart)}?")
|
||||||
|
|
||||||
|
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
builder.setTitle(getString(R.string.restore))
|
||||||
|
builder.setMessage(finalMessage)
|
||||||
|
builder.setPositiveButton(
|
||||||
|
getString(R.string.restart)
|
||||||
|
) { _: DialogInterface?, _: Int ->
|
||||||
|
val intent = Intent(context, MainActivity::class.java)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
requireContext().startActivity(intent)
|
||||||
|
if (context is Activity) {
|
||||||
|
(context as Activity).finish()
|
||||||
|
}
|
||||||
|
Runtime.getRuntime().exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle the negative button of the alert dialog
|
||||||
|
builder.setNegativeButton(
|
||||||
|
getString(R.string.cancel)
|
||||||
|
) { _: DialogInterface?, _: Int -> }
|
||||||
|
|
||||||
|
val dialog = builder.create()
|
||||||
|
dialog.show()
|
||||||
|
}.onFailure {
|
||||||
|
it.printStackTrace()
|
||||||
|
Snackbar.make(requireView(), getString(R.string.couldnt_parse_file), Snackbar.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.deniscerri.ytdlnis.ui.more.settings
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.preference.EditTextPreference
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import com.deniscerri.ytdlnis.R
|
||||||
|
|
||||||
|
|
||||||
|
class ProcessingSettingsFragment : BaseSettingsFragment() {
|
||||||
|
override val title: Int = R.string.processing
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
setPreferencesFromResource(R.xml.processing_preferences, rootKey)
|
||||||
|
findPreference<EditTextPreference>("file_name_template")?.title = "${getString(R.string.file_name_template)} [${getString(R.string.video)}]"
|
||||||
|
findPreference<EditTextPreference>("file_name_template")?.dialogTitle = "${getString(R.string.file_name_template)} [${getString(R.string.video)}]"
|
||||||
|
|
||||||
|
findPreference<EditTextPreference>("file_name_template_audio")?.title = "${getString(R.string.file_name_template)} [${getString(R.string.audio)}]"
|
||||||
|
findPreference<EditTextPreference>("file_name_template_audio")?.dialogTitle = "${getString(R.string.file_name_template)} [${getString(R.string.audio)}]"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -3,28 +3,56 @@ package com.deniscerri.ytdlnis.ui.more.settings
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.activity.addCallback
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import androidx.fragment.app.commit
|
||||||
|
import androidx.fragment.app.replace
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.fragment.NavHostFragment
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import com.deniscerri.ytdlnis.MainActivity
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
|
import com.deniscerri.ytdlnis.databinding.ActivitySettingsBinding
|
||||||
import com.deniscerri.ytdlnis.ui.BaseActivity
|
import com.deniscerri.ytdlnis.ui.BaseActivity
|
||||||
import com.google.android.material.appbar.MaterialToolbar
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
|
|
||||||
|
|
||||||
class SettingsActivity : BaseActivity() {
|
class SettingsActivity : BaseActivity() {
|
||||||
private var fm: FragmentManager? = null
|
|
||||||
private var topAppBar: MaterialToolbar? = null
|
|
||||||
private lateinit var sharedPreferences: SharedPreferences
|
|
||||||
private lateinit var editor: SharedPreferences.Editor
|
|
||||||
var context: Context? = null
|
var context: Context? = null
|
||||||
|
lateinit var binding: ActivitySettingsBinding
|
||||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_settings)
|
|
||||||
context = baseContext
|
context = baseContext
|
||||||
topAppBar = findViewById(R.id.settings_toolbar)
|
binding = ActivitySettingsBinding.inflate(layoutInflater)
|
||||||
topAppBar!!.setNavigationOnClickListener { onBackPressedDispatcher.onBackPressed() }
|
setContentView(binding.root)
|
||||||
fm = supportFragmentManager
|
|
||||||
fm!!.beginTransaction()
|
val navHostFragment = supportFragmentManager.findFragmentById(R.id.frame_layout) as NavHostFragment
|
||||||
.replace(R.id.settings_frame_layout, SettingsFragment())
|
val navController = navHostFragment.findNavController()
|
||||||
.commit()
|
|
||||||
|
val listener = NavController.OnDestinationChangedListener { controller, destination, arguments ->
|
||||||
|
if (destination.id == R.id.mainSettingsFragment){
|
||||||
|
changeTopAppbarTitle(getString(R.string.settings))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
navController.addOnDestinationChangedListener(listener)
|
||||||
|
binding.settingsToolbar.setNavigationOnClickListener {
|
||||||
|
onBackPressedDispatcher.onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
onBackPressedDispatcher.addCallback(this) {
|
||||||
|
if (navController.currentDestination?.id == R.id.mainSettingsFragment) {
|
||||||
|
navController.popBackStack()
|
||||||
|
finishAndRemoveTask()
|
||||||
|
}else{
|
||||||
|
navController.navigateUp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
navController.navigate(R.id.mainSettingsFragment)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun changeTopAppbarTitle(text: String) {
|
||||||
|
if (this::binding.isInitialized) binding.collapsingToolbar.title = text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,522 +0,0 @@
|
||||||
package com.deniscerri.ytdlnis.ui.more.settings
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.ClipboardManager
|
|
||||||
import android.content.Context.POWER_SERVICE
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build.VERSION
|
|
||||||
import android.os.Build.VERSION_CODES
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.os.Environment
|
|
||||||
import android.os.PowerManager
|
|
||||||
import android.provider.Settings
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
|
||||||
import androidx.core.content.edit
|
|
||||||
import androidx.core.os.LocaleListCompat
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.preference.EditTextPreference
|
|
||||||
import androidx.preference.ListPreference
|
|
||||||
import androidx.preference.Preference
|
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import androidx.preference.SeekBarPreference
|
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
|
||||||
import androidx.work.WorkInfo
|
|
||||||
import androidx.work.WorkManager
|
|
||||||
import com.deniscerri.ytdlnis.BuildConfig
|
|
||||||
import com.deniscerri.ytdlnis.MainActivity
|
|
||||||
import com.deniscerri.ytdlnis.R
|
|
||||||
import com.deniscerri.ytdlnis.util.FileUtil
|
|
||||||
import com.deniscerri.ytdlnis.util.ThemeUtil
|
|
||||||
import com.deniscerri.ytdlnis.util.UpdateUtil
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
import com.yausername.youtubedl_android.YoutubeDL
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import kotlinx.serialization.decodeFromString
|
|
||||||
import kotlinx.serialization.encodeToString
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import kotlinx.serialization.json.JsonObject
|
|
||||||
import kotlinx.serialization.json.buildJsonObject
|
|
||||||
import kotlinx.serialization.json.jsonArray
|
|
||||||
import kotlinx.serialization.json.jsonObject
|
|
||||||
import kotlinx.serialization.json.put
|
|
||||||
import kotlinx.serialization.json.putJsonArray
|
|
||||||
import java.io.File
|
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
|
|
||||||
class SettingsFragment : PreferenceFragmentCompat() {
|
|
||||||
private var language: ListPreference? = null
|
|
||||||
private var theme: ListPreference? = null
|
|
||||||
private var accent: ListPreference? = null
|
|
||||||
private var highContrast: SwitchPreferenceCompat? = null
|
|
||||||
private var ignoreBatteryOptimization: Preference? = null
|
|
||||||
private var musicPath: Preference? = null
|
|
||||||
private var videoPath: Preference? = null
|
|
||||||
private var commandPath: Preference? = null
|
|
||||||
private var accessAllFiles : Preference? = null
|
|
||||||
private var clearCache: Preference? = null
|
|
||||||
private var locale: ListPreference? = null
|
|
||||||
private var concurrentFragments: SeekBarPreference? = null
|
|
||||||
private var concurrentDownloads: SeekBarPreference? = null
|
|
||||||
private var audioFormat: ListPreference? = null
|
|
||||||
private var videoFormat: ListPreference? = null
|
|
||||||
private var videoQuality: ListPreference? = null
|
|
||||||
private var formatID: EditTextPreference? = null
|
|
||||||
private var updateYTDL: Preference? = null
|
|
||||||
private var formatSource: ListPreference? = null
|
|
||||||
private var exportPreferences : Preference? = null
|
|
||||||
private var importPreferences : Preference? = null
|
|
||||||
private var ytdlVersion: Preference? = null
|
|
||||||
private var version: Preference? = null
|
|
||||||
|
|
||||||
|
|
||||||
private var updateUtil: UpdateUtil? = null
|
|
||||||
private var fileUtil: FileUtil? = null
|
|
||||||
private var activeDownloadCount = 0
|
|
||||||
|
|
||||||
private val jsonFormat = Json { prettyPrint = true }
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
||||||
setPreferencesFromResource(R.xml.root_preferences, rootKey)
|
|
||||||
updateUtil = UpdateUtil(requireContext())
|
|
||||||
fileUtil = FileUtil()
|
|
||||||
|
|
||||||
WorkManager.getInstance(requireContext()).getWorkInfosByTagLiveData("download").observe(this){
|
|
||||||
activeDownloadCount = 0
|
|
||||||
it.forEach {w ->
|
|
||||||
if (w.state == WorkInfo.State.RUNNING) activeDownloadCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
initPreferences()
|
|
||||||
initListeners()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initPreferences() {
|
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
|
||||||
val editor = preferences.edit()
|
|
||||||
language = findPreference("app_language")
|
|
||||||
theme = findPreference("ytdlnis_theme")
|
|
||||||
accent = findPreference("theme_accent")
|
|
||||||
highContrast = findPreference("high_contrast")
|
|
||||||
ignoreBatteryOptimization = findPreference("ignore_battery")
|
|
||||||
musicPath = findPreference("music_path")
|
|
||||||
videoPath = findPreference("video_path")
|
|
||||||
commandPath = findPreference("command_path")
|
|
||||||
accessAllFiles = findPreference("access_all_files")
|
|
||||||
clearCache = findPreference("clear_cache")
|
|
||||||
|
|
||||||
locale = findPreference("locale")
|
|
||||||
concurrentFragments = findPreference("concurrent_fragments")
|
|
||||||
concurrentDownloads = findPreference("concurrent_downloads")
|
|
||||||
|
|
||||||
audioFormat = findPreference("audio_format")
|
|
||||||
videoFormat = findPreference("video_format")
|
|
||||||
videoQuality = findPreference("video_quality")
|
|
||||||
formatID = findPreference("format_id")
|
|
||||||
updateYTDL = findPreference("update_ytdl")
|
|
||||||
formatSource = findPreference("formats_source")
|
|
||||||
exportPreferences = findPreference("export_preferences")
|
|
||||||
importPreferences = findPreference("import_preferences")
|
|
||||||
ytdlVersion = findPreference("ytdl-version")
|
|
||||||
ytdlVersion!!.summary = preferences.getString("ytdl-version", "NULL")
|
|
||||||
version = findPreference("version")
|
|
||||||
version!!.summary = BuildConfig.VERSION_NAME
|
|
||||||
|
|
||||||
if(VERSION.SDK_INT < VERSION_CODES.TIRAMISU){
|
|
||||||
val values = resources.getStringArray(R.array.language_values)
|
|
||||||
val entries = mutableListOf<String>()
|
|
||||||
values.forEach {
|
|
||||||
entries.add(Locale(it).getDisplayName(Locale(it)))
|
|
||||||
}
|
|
||||||
language!!.entries = entries.toTypedArray()
|
|
||||||
}else{
|
|
||||||
language!!.isVisible = false
|
|
||||||
}
|
|
||||||
|
|
||||||
val packageName: String = requireContext().packageName
|
|
||||||
val pm = requireContext().applicationContext.getSystemService(POWER_SERVICE) as PowerManager
|
|
||||||
if (pm.isIgnoringBatteryOptimizations(packageName)) {
|
|
||||||
ignoreBatteryOptimization!!.isVisible = false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (preferences.getString("music_path", "")!!.isEmpty()) {
|
|
||||||
editor.putString("music_path", getString(R.string.music_path))
|
|
||||||
}
|
|
||||||
if (preferences.getString("video_path", "")!!.isEmpty()) {
|
|
||||||
editor.putString("video_path", getString(R.string.video_path))
|
|
||||||
}
|
|
||||||
if (preferences.getString("command_path", "")!!.isEmpty()) {
|
|
||||||
editor.putString("command_path", getString(R.string.command_path))
|
|
||||||
}
|
|
||||||
|
|
||||||
if((VERSION.SDK_INT >= 30 && Environment.isExternalStorageManager()) ||
|
|
||||||
VERSION.SDK_INT < 30) {
|
|
||||||
accessAllFiles!!.isVisible = false
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("BatteryLife", "InlinedApi")
|
|
||||||
private fun initListeners() {
|
|
||||||
val preferences =
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
|
||||||
val editor = preferences.edit()
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
theme!!.summary = theme!!.entry
|
|
||||||
theme!!.onPreferenceChangeListener =
|
|
||||||
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
|
|
||||||
when(newValue){
|
|
||||||
"System" -> {
|
|
||||||
theme!!.summary = getString(R.string.system)
|
|
||||||
}
|
|
||||||
"Dark" -> {
|
|
||||||
theme!!.summary = getString(R.string.dark)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
theme!!.summary = getString(R.string.light)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ThemeUtil.updateTheme(requireActivity() as AppCompatActivity)
|
|
||||||
val intent = Intent(requireContext(), MainActivity::class.java)
|
|
||||||
this.startActivity(intent)
|
|
||||||
requireActivity().finishAffinity()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
accent!!.summary = accent!!.entry
|
|
||||||
accent!!.onPreferenceChangeListener =
|
|
||||||
Preference.OnPreferenceChangeListener { _: Preference?, _: Any ->
|
|
||||||
ThemeUtil.updateTheme(requireActivity() as AppCompatActivity)
|
|
||||||
val intent = Intent(requireContext(), MainActivity::class.java)
|
|
||||||
this.startActivity(intent)
|
|
||||||
requireActivity().finishAffinity()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
highContrast!!.onPreferenceChangeListener =
|
|
||||||
Preference.OnPreferenceChangeListener { _: Preference?, _: Any ->
|
|
||||||
ThemeUtil.updateTheme(requireActivity() as AppCompatActivity)
|
|
||||||
val intent = Intent(requireContext(), MainActivity::class.java)
|
|
||||||
this.startActivity(intent)
|
|
||||||
requireActivity().finishAffinity()
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
ignoreBatteryOptimization!!.onPreferenceClickListener =
|
|
||||||
Preference.OnPreferenceClickListener {
|
|
||||||
val intent = Intent()
|
|
||||||
intent.action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
|
|
||||||
intent.data = Uri.parse("package:" + requireContext().packageName)
|
|
||||||
startActivity(intent)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
musicPath!!.summary = fileUtil?.formatPath(preferences.getString("music_path", "")!!)
|
|
||||||
musicPath!!.onPreferenceClickListener =
|
|
||||||
Preference.OnPreferenceClickListener {
|
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
|
||||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
|
||||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
||||||
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
|
||||||
musicPathResultLauncher.launch(intent)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
videoPath!!.summary = fileUtil?.formatPath(preferences.getString("video_path", "")!!)
|
|
||||||
videoPath!!.onPreferenceClickListener =
|
|
||||||
Preference.OnPreferenceClickListener {
|
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
|
||||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
|
||||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
||||||
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
|
||||||
videoPathResultLauncher.launch(intent)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
commandPath!!.summary = fileUtil?.formatPath(preferences.getString("command_path", "")!!)
|
|
||||||
commandPath!!.onPreferenceClickListener =
|
|
||||||
Preference.OnPreferenceClickListener {
|
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
|
||||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
|
||||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
||||||
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
|
||||||
commandPathResultLauncher.launch(intent)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
accessAllFiles!!.onPreferenceClickListener =
|
|
||||||
Preference.OnPreferenceClickListener {
|
|
||||||
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
|
|
||||||
val uri = Uri.parse("package:" + requireContext().packageName)
|
|
||||||
intent.data = uri
|
|
||||||
startActivity(intent)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
var cacheSize = File(requireContext().cacheDir.absolutePath + "/downloads").walkBottomUp().fold(0L) { acc, file -> acc + file.length() }
|
|
||||||
clearCache!!.summary = "(${fileUtil!!.convertFileSize(cacheSize)}) ${resources.getString(R.string.clear_temporary_files_summary)}"
|
|
||||||
clearCache!!.onPreferenceClickListener =
|
|
||||||
Preference.OnPreferenceClickListener {
|
|
||||||
if (activeDownloadCount == 0){
|
|
||||||
File(requireContext().cacheDir.absolutePath + "/downloads").deleteRecursively()
|
|
||||||
Snackbar.make(requireView(), getString(R.string.cache_cleared), Snackbar.LENGTH_SHORT).show()
|
|
||||||
cacheSize = File(requireContext().cacheDir.absolutePath + "/downloads").walkBottomUp().fold(0L) { acc, file -> acc + file.length() }
|
|
||||||
clearCache!!.summary = "(${fileUtil!!.convertFileSize(cacheSize)}) ${resources.getString(R.string.clear_temporary_files_summary)}"
|
|
||||||
}else{
|
|
||||||
Snackbar.make(requireView(), getString(R.string.downloads_running_try_later), Snackbar.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
locale!!.summary = locale!!.value
|
|
||||||
concurrentDownloads!!.onPreferenceChangeListener =
|
|
||||||
Preference.OnPreferenceChangeListener { _: Preference?, _: Any ->
|
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
|
||||||
.setTitle(resources.getString(R.string.warning))
|
|
||||||
.setMessage(resources.getString(R.string.workmanager_updated))
|
|
||||||
.setNegativeButton(resources.getString(R.string.cancel)) { dialog, _ ->
|
|
||||||
dialog.cancel()
|
|
||||||
}
|
|
||||||
.setPositiveButton(resources.getString(R.string.restart)) { _, _ ->
|
|
||||||
val intent = Intent(context, MainActivity::class.java)
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
requireContext().startActivity(intent)
|
|
||||||
if (context is Activity) {
|
|
||||||
(context as Activity).finish()
|
|
||||||
}
|
|
||||||
Runtime.getRuntime().exit(0)
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
audioFormat!!.summary = preferences.getString("audio_format", "")!!.replace("Default", getString(R.string.defaultValue))
|
|
||||||
audioFormat!!.onPreferenceChangeListener =
|
|
||||||
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
|
|
||||||
audioFormat!!.summary = newValue.toString().replace("Default", getString(R.string.defaultValue))
|
|
||||||
true
|
|
||||||
}
|
|
||||||
videoFormat!!.summary = preferences.getString("video_format", "")!!.replace("Default", getString(R.string.defaultValue))
|
|
||||||
videoFormat!!.onPreferenceChangeListener =
|
|
||||||
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
|
|
||||||
videoFormat!!.summary = newValue.toString().replace("Default", getString(R.string.defaultValue))
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
videoQuality!!.summary = preferences.getString("video_quality", "")
|
|
||||||
videoQuality!!.onPreferenceChangeListener =
|
|
||||||
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
|
|
||||||
videoQuality!!.summary = newValue.toString()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
formatID!!.summary = formatID!!.text
|
|
||||||
formatID!!.onPreferenceChangeListener =
|
|
||||||
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
|
|
||||||
formatID!!.summary = newValue.toString()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
YoutubeDL.getInstance().version(context)?.let {
|
|
||||||
editor.putString("ytdl-version", it)
|
|
||||||
editor.apply()
|
|
||||||
ytdlVersion!!.summary = it
|
|
||||||
}
|
|
||||||
updateYTDL!!.onPreferenceClickListener =
|
|
||||||
Preference.OnPreferenceClickListener {
|
|
||||||
lifecycleScope.launch {
|
|
||||||
Snackbar.make(requireView(),
|
|
||||||
requireContext().getString(R.string.ytdl_updating_started),
|
|
||||||
Snackbar.LENGTH_LONG).show()
|
|
||||||
when (updateUtil!!.updateYoutubeDL()) {
|
|
||||||
YoutubeDL.UpdateStatus.DONE -> {
|
|
||||||
Snackbar.make(requireView(),
|
|
||||||
requireContext().getString(R.string.ytld_update_success),
|
|
||||||
Snackbar.LENGTH_LONG).show()
|
|
||||||
|
|
||||||
YoutubeDL.getInstance().version(context)?.let {
|
|
||||||
editor.putString("ytdl-version", it)
|
|
||||||
editor.apply()
|
|
||||||
ytdlVersion!!.summary = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
YoutubeDL.UpdateStatus.ALREADY_UP_TO_DATE -> Snackbar.make(requireView(),
|
|
||||||
requireContext().getString(R.string.you_are_in_latest_version),
|
|
||||||
Snackbar.LENGTH_LONG).show()
|
|
||||||
else -> {
|
|
||||||
Snackbar.make(requireView(),
|
|
||||||
requireContext().getString(R.string.errored),
|
|
||||||
Snackbar.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
formatSource!!.summary = preferences.getString("formats_source", "")!!.replace("Default", getString(R.string.defaultValue))
|
|
||||||
formatSource!!.onPreferenceChangeListener =
|
|
||||||
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
|
|
||||||
formatSource!!.summary = newValue.toString().replace("Default", getString(R.string.defaultValue))
|
|
||||||
true
|
|
||||||
}
|
|
||||||
exportPreferences!!.onPreferenceClickListener =
|
|
||||||
Preference.OnPreferenceClickListener {
|
|
||||||
val prefs = preferences.all
|
|
||||||
val json = buildJsonObject {
|
|
||||||
putJsonArray("YTDLnis_Preferences") {
|
|
||||||
prefs.forEach {
|
|
||||||
add(buildJsonObject {
|
|
||||||
put("key", it.key)
|
|
||||||
put("value", it.value.toString())
|
|
||||||
put("type", it.value!!::class.simpleName)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val string = jsonFormat.encodeToString(json)
|
|
||||||
val clipboard: ClipboardManager =
|
|
||||||
requireContext().getSystemService(AppCompatActivity.CLIPBOARD_SERVICE) as ClipboardManager
|
|
||||||
clipboard.setText(string)
|
|
||||||
Snackbar.make(requireView(), getString(R.string.copied_to_clipboard), Snackbar.LENGTH_LONG).show()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
importPreferences!!.onPreferenceClickListener =
|
|
||||||
Preference.OnPreferenceClickListener {
|
|
||||||
val clipboard = requireContext().getSystemService(AppCompatActivity.CLIPBOARD_SERVICE) as ClipboardManager
|
|
||||||
val clip = clipboard.primaryClip!!.getItemAt(0).text.toString()
|
|
||||||
try{
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(requireContext()).edit(commit = true) {
|
|
||||||
clear()
|
|
||||||
|
|
||||||
val preferencesKeys = preferences.all.map { it.key }
|
|
||||||
jsonFormat.decodeFromString<JsonObject>(clip).run {
|
|
||||||
val prefs = this.jsonObject["YTDLnis_Preferences"]!!.jsonArray
|
|
||||||
prefs.forEach {
|
|
||||||
val key : String = it.jsonObject["key"].toString().replace("\"", "")
|
|
||||||
if (preferencesKeys.contains(key)){
|
|
||||||
when(it.jsonObject["type"].toString().replace("\"", "")){
|
|
||||||
"String" -> {
|
|
||||||
val value = it.jsonObject["value"].toString().replace("\"", "")
|
|
||||||
putString(key, value)
|
|
||||||
}
|
|
||||||
"Boolean" -> {
|
|
||||||
val value = it.jsonObject["value"].toString().replace("\"", "").toBoolean()
|
|
||||||
putBoolean(key, value)
|
|
||||||
}
|
|
||||||
"Int" -> {
|
|
||||||
val value = it.jsonObject["value"].toString().replace("\"", "").toInt()
|
|
||||||
putInt(key, value)
|
|
||||||
}
|
|
||||||
"HashSet" -> {
|
|
||||||
val value = hashSetOf(it.jsonObject["value"].toString().replace("(\")|(\\[)|(])".toRegex(), ""))
|
|
||||||
putStringSet(key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val intent = Intent(context, MainActivity::class.java)
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
requireContext().startActivity(intent)
|
|
||||||
if (context is Activity) {
|
|
||||||
(context as Activity).finish()
|
|
||||||
}
|
|
||||||
Runtime.getRuntime().exit(0)
|
|
||||||
}catch (e: Exception){
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
version!!.onPreferenceClickListener =
|
|
||||||
Preference.OnPreferenceClickListener {
|
|
||||||
lifecycleScope.launch{
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
updateUtil!!.updateApp{ msg ->
|
|
||||||
lifecycleScope.launch(Dispatchers.Main){
|
|
||||||
Snackbar.make(requireView(), msg, Snackbar.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var musicPathResultLauncher = registerForActivityResult(
|
|
||||||
ActivityResultContracts.StartActivityForResult()
|
|
||||||
) { result ->
|
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
|
||||||
result.data?.data?.let {
|
|
||||||
activity?.contentResolver?.takePersistableUriPermission(
|
|
||||||
it,
|
|
||||||
Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
|
||||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
|
||||||
)
|
|
||||||
}
|
|
||||||
changePath(musicPath, result.data, MUSIC_PATH_CODE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private var videoPathResultLauncher = registerForActivityResult(
|
|
||||||
ActivityResultContracts.StartActivityForResult()
|
|
||||||
) { result ->
|
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
|
||||||
result.data?.data?.let {
|
|
||||||
activity?.contentResolver?.takePersistableUriPermission(
|
|
||||||
it,
|
|
||||||
Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
|
||||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
|
||||||
)
|
|
||||||
}
|
|
||||||
changePath(videoPath, result.data, VIDEO_PATH_CODE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private var commandPathResultLauncher = registerForActivityResult(
|
|
||||||
ActivityResultContracts.StartActivityForResult()
|
|
||||||
) { result ->
|
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
|
||||||
result.data?.data?.let {
|
|
||||||
activity?.contentResolver?.takePersistableUriPermission(
|
|
||||||
it,
|
|
||||||
Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
|
||||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
|
||||||
)
|
|
||||||
}
|
|
||||||
changePath(commandPath, result.data, COMMAND_PATH_CODE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun changePath(p: Preference?, data: Intent?, requestCode: Int) {
|
|
||||||
val path = data!!.data.toString()
|
|
||||||
p!!.summary = fileUtil?.formatPath(data.data.toString())
|
|
||||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
|
||||||
val editor = sharedPreferences.edit()
|
|
||||||
when (requestCode) {
|
|
||||||
MUSIC_PATH_CODE -> editor.putString("music_path", path)
|
|
||||||
VIDEO_PATH_CODE -> editor.putString("video_path", path)
|
|
||||||
COMMAND_PATH_CODE -> editor.putString("command_path", path)
|
|
||||||
}
|
|
||||||
editor.apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val MUSIC_PATH_CODE = 33333
|
|
||||||
const val VIDEO_PATH_CODE = 55555
|
|
||||||
const val COMMAND_PATH_CODE = 77777
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
package com.deniscerri.ytdlnis.ui.more.settings
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.deniscerri.ytdlnis.BuildConfig
|
||||||
|
import com.deniscerri.ytdlnis.R
|
||||||
|
import com.deniscerri.ytdlnis.util.UpdateUtil
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import com.yausername.youtubedl_android.YoutubeDL
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateSettingsFragment : BaseSettingsFragment() {
|
||||||
|
override val title: Int = R.string.updating
|
||||||
|
private var updateYTDL: Preference? = null
|
||||||
|
private var ytdlVersion: Preference? = null
|
||||||
|
private var version: Preference? = null
|
||||||
|
private var updateUtil: UpdateUtil? = null
|
||||||
|
|
||||||
|
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
setPreferencesFromResource(R.xml.updating_preferences, rootKey)
|
||||||
|
updateUtil = UpdateUtil(requireContext())
|
||||||
|
val preferences =
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
|
val editor = preferences.edit()
|
||||||
|
updateYTDL = findPreference("update_ytdl")
|
||||||
|
ytdlVersion = findPreference("ytdl-version")
|
||||||
|
version = findPreference("version")
|
||||||
|
version!!.summary = BuildConfig.VERSION_NAME
|
||||||
|
|
||||||
|
YoutubeDL.getInstance().version(context)?.let {
|
||||||
|
editor.putString("ytdl-version", it)
|
||||||
|
editor.apply()
|
||||||
|
ytdlVersion!!.summary = it
|
||||||
|
}
|
||||||
|
updateYTDL!!.onPreferenceClickListener =
|
||||||
|
Preference.OnPreferenceClickListener {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
Snackbar.make(requireView(),
|
||||||
|
requireContext().getString(R.string.ytdl_updating_started),
|
||||||
|
Snackbar.LENGTH_LONG).show()
|
||||||
|
when (updateUtil!!.updateYoutubeDL()) {
|
||||||
|
YoutubeDL.UpdateStatus.DONE -> {
|
||||||
|
Snackbar.make(requireView(),
|
||||||
|
requireContext().getString(R.string.ytld_update_success),
|
||||||
|
Snackbar.LENGTH_LONG).show()
|
||||||
|
|
||||||
|
YoutubeDL.getInstance().version(context)?.let {
|
||||||
|
editor.putString("ytdl-version", it)
|
||||||
|
editor.apply()
|
||||||
|
ytdlVersion!!.summary = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
YoutubeDL.UpdateStatus.ALREADY_UP_TO_DATE -> Snackbar.make(requireView(),
|
||||||
|
requireContext().getString(R.string.you_are_in_latest_version),
|
||||||
|
Snackbar.LENGTH_LONG).show()
|
||||||
|
else -> {
|
||||||
|
Snackbar.make(requireView(),
|
||||||
|
requireContext().getString(R.string.errored),
|
||||||
|
Snackbar.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
version!!.onPreferenceClickListener =
|
||||||
|
Preference.OnPreferenceClickListener {
|
||||||
|
lifecycleScope.launch{
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
updateUtil!!.updateApp{ msg ->
|
||||||
|
lifecycleScope.launch(Dispatchers.Main){
|
||||||
|
Snackbar.make(requireView(), msg, Snackbar.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -525,49 +525,63 @@ class InfoUtil(private val context: Context) {
|
||||||
urls.forEach {
|
urls.forEach {
|
||||||
urlsFile.appendText(it+"\n")
|
urlsFile.appendText(it+"\n")
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
val request = YoutubeDLRequest(emptyList())
|
|
||||||
request.addOption("--print", "%(formats)s")
|
|
||||||
request.addOption("-a", urlsFile.absolutePath)
|
|
||||||
request.addOption("--skip-download")
|
|
||||||
request.addOption("-R", "1")
|
|
||||||
request.addOption("--socket-timeout", "5")
|
|
||||||
val cookiesFile = File(context.cacheDir, "cookies.txt")
|
|
||||||
if (cookiesFile.exists()){
|
|
||||||
request.addOption("--cookies", cookiesFile.absolutePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
YoutubeDL.getInstance().execute(request){ progress, _, line ->
|
val formatSource = sharedPreferences.getString("formats_source", "yt-dlp")
|
||||||
try{
|
val p = Pattern.compile("^(https?)://(www.)?youtu(.be)?")
|
||||||
val formats = mutableListOf<Format>()
|
val allYoutubeLinks = urls.any {p.matcher(it).find() }
|
||||||
val listOfStrings = JSONArray(line)
|
if (formatSource == "yt-dlp" || !allYoutubeLinks){
|
||||||
for (f in 0 until listOfStrings.length()){
|
try {
|
||||||
val format = listOfStrings.get(f) as JSONObject
|
val request = YoutubeDLRequest(emptyList())
|
||||||
try{
|
request.addOption("--print", "%(formats)s")
|
||||||
if (format.getString("filesize") == "None") continue
|
request.addOption("-a", urlsFile.absolutePath)
|
||||||
}catch (e: Exception) { continue }
|
request.addOption("--skip-download")
|
||||||
val formatProper = Gson().fromJson(format.toString(), Format::class.java)
|
request.addOption("-R", "1")
|
||||||
if (formatProper.filesize > 0){
|
request.addOption("--socket-timeout", "5")
|
||||||
if ( !formatProper!!.format_note!!.contains("audio only", true)) {
|
val cookiesFile = File(context.cacheDir, "cookies.txt")
|
||||||
formatProper.format_note = format.getString("format_note")
|
if (cookiesFile.exists()){
|
||||||
}else{
|
request.addOption("--cookies", cookiesFile.absolutePath)
|
||||||
formatProper.format_note = "${format.getString("format_note")} audio"
|
|
||||||
}
|
|
||||||
formatProper.container = format.getString("ext")
|
|
||||||
formats.add(formatProper)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
progress(formats)
|
|
||||||
}catch (e: Exception){
|
|
||||||
progress(emptyList())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
YoutubeDL.getInstance().execute(request){ progress, _, line ->
|
||||||
|
try{
|
||||||
|
val formats = mutableListOf<Format>()
|
||||||
|
val listOfStrings = JSONArray(line)
|
||||||
|
for (f in 0 until listOfStrings.length()){
|
||||||
|
val format = listOfStrings.get(f) as JSONObject
|
||||||
|
try{
|
||||||
|
if (format.getString("filesize") == "None") continue
|
||||||
|
}catch (e: Exception) { continue }
|
||||||
|
val formatProper = Gson().fromJson(format.toString(), Format::class.java)
|
||||||
|
if (formatProper.filesize > 0){
|
||||||
|
if ( !formatProper!!.format_note!!.contains("audio only", true)) {
|
||||||
|
formatProper.format_note = format.getString("format_note")
|
||||||
|
}else{
|
||||||
|
formatProper.format_note = "${format.getString("format_note")} audio"
|
||||||
|
}
|
||||||
|
formatProper.container = format.getString("ext")
|
||||||
|
formats.add(formatProper)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
progress(formats)
|
||||||
|
}catch (e: Exception){
|
||||||
|
progress(emptyList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Looper.prepare().run {
|
||||||
|
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
}else{
|
||||||
Looper.prepare().run {
|
urls.forEach {
|
||||||
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
|
val id = getIDFromYoutubeURL(it)
|
||||||
|
val res = genericRequest(invidousURL + "videos/" + id)
|
||||||
|
val vid = createVideoFromInvidiousJSON(res)
|
||||||
|
progress(vid!!.formats)
|
||||||
}
|
}
|
||||||
e.printStackTrace()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
urlsFile.delete()
|
urlsFile.delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -741,6 +755,7 @@ class InfoUtil(private val context: Context) {
|
||||||
val url = "https://suggestqueries.google.com/complete/search?client=youtube&ds=yt&client=firefox&q=$query"
|
val url = "https://suggestqueries.google.com/complete/search?client=youtube&ds=yt&client=firefox&q=$query"
|
||||||
// invidousURL + "search/suggestions?q=" + query
|
// invidousURL + "search/suggestions?q=" + query
|
||||||
val res = genericArrayRequest(url)
|
val res = genericArrayRequest(url)
|
||||||
|
Log.e("aa", res.toString())
|
||||||
if (res.length() == 0) return ArrayList()
|
if (res.length() == 0) return ArrayList()
|
||||||
val suggestionList = ArrayList<String>()
|
val suggestionList = ArrayList<String>()
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -303,6 +303,107 @@ class UiUtil(private val fileUtil: FileUtil) {
|
||||||
datePicker.show(fragmentManager, "datepicker")
|
datePicker.show(fragmentManager, "datepicker")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showFormatDetails(format: Format, activity: Activity){
|
||||||
|
val bottomSheet = BottomSheetDialog(activity)
|
||||||
|
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||||
|
bottomSheet.setContentView(R.layout.format_details_sheet)
|
||||||
|
|
||||||
|
val formatIdParent = bottomSheet.findViewById<LinearLayout>(R.id.format_id_parent)
|
||||||
|
val containerParent = bottomSheet.findViewById<LinearLayout>(R.id.container_parent)
|
||||||
|
val codecParent = bottomSheet.findViewById<LinearLayout>(R.id.codec_parent)
|
||||||
|
val filesizeParent = bottomSheet.findViewById<LinearLayout>(R.id.filesize_parent)
|
||||||
|
val formatnoteParent = bottomSheet.findViewById<LinearLayout>(R.id.format_note_parent)
|
||||||
|
val fpsParent = bottomSheet.findViewById<LinearLayout>(R.id.fps_parent)
|
||||||
|
val asrParent = bottomSheet.findViewById<LinearLayout>(R.id.asr_parent)
|
||||||
|
|
||||||
|
if (format.format_id.isBlank()) formatIdParent?.visibility = View.GONE
|
||||||
|
else {
|
||||||
|
formatIdParent?.findViewById<TextView>(R.id.format_id_value)?.text = format.format_id
|
||||||
|
formatIdParent?.setOnClickListener {
|
||||||
|
copyToClipboard(format.format_id, activity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (format.container.isBlank()) containerParent?.visibility = View.GONE
|
||||||
|
else {
|
||||||
|
containerParent?.findViewById<TextView>(R.id.container_value)?.text = format.container
|
||||||
|
containerParent?.setOnClickListener {
|
||||||
|
copyToClipboard(format.container, activity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val codecField =
|
||||||
|
if (format.encoding != "") {
|
||||||
|
format.encoding.uppercase()
|
||||||
|
}else if (format.vcodec != "none" && format.vcodec != ""){
|
||||||
|
format.vcodec.uppercase()
|
||||||
|
} else {
|
||||||
|
format.acodec.uppercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codecField.isBlank()) codecParent?.visibility = View.GONE
|
||||||
|
else {
|
||||||
|
codecParent?.findViewById<TextView>(R.id.codec_value)?.text = codecField
|
||||||
|
codecParent?.setOnClickListener {
|
||||||
|
copyToClipboard(codecField, activity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format.filesize != 0L) filesizeParent?.visibility = View.GONE
|
||||||
|
else {
|
||||||
|
filesizeParent?.findViewById<TextView>(R.id.filesize_value)?.text = fileUtil.convertFileSize(format.filesize)
|
||||||
|
filesizeParent?.setOnClickListener {
|
||||||
|
copyToClipboard(fileUtil.convertFileSize(format.filesize), activity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format.format_note.isBlank()) formatnoteParent?.visibility = View.GONE
|
||||||
|
else {
|
||||||
|
formatnoteParent?.findViewById<TextView>(R.id.format_note_value)?.text = format.format_note
|
||||||
|
formatnoteParent?.setOnClickListener {
|
||||||
|
copyToClipboard(format.format_note, activity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format.fps.isNullOrBlank()) fpsParent?.visibility = View.GONE
|
||||||
|
else {
|
||||||
|
fpsParent?.findViewById<TextView>(R.id.fps_value)?.text = format.fps
|
||||||
|
fpsParent?.setOnClickListener {
|
||||||
|
copyToClipboard(format.fps!!, activity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format.asr.isNullOrBlank()) asrParent?.visibility = View.GONE
|
||||||
|
else {
|
||||||
|
asrParent?.findViewById<TextView>(R.id.asr_value)?.text = format.asr
|
||||||
|
asrParent?.setOnClickListener {
|
||||||
|
copyToClipboard(format.asr!!, activity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bottomSheet.show()
|
||||||
|
val displayMetrics = DisplayMetrics()
|
||||||
|
activity.windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||||
|
bottomSheet.behavior.peekHeight = displayMetrics.heightPixels
|
||||||
|
bottomSheet.window!!.setLayout(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun copyToClipboard(text: String, activity: Activity){
|
||||||
|
val clipboard = activity.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
|
val clip = ClipData.newPlainText(text, text)
|
||||||
|
clipboard.setPrimaryClip(clip)
|
||||||
|
Toast.makeText(activity, activity.getString(R.string.copied_to_clipboard), Toast.LENGTH_SHORT)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
suspend fun showCommandTemplates(activity: Activity, commandTemplateViewModel: CommandTemplateViewModel, itemSelected: (itemSelected: CommandTemplate) -> Unit) {
|
suspend fun showCommandTemplates(activity: Activity, commandTemplateViewModel: CommandTemplateViewModel, itemSelected: (itemSelected: CommandTemplate) -> Unit) {
|
||||||
val bottomSheet = BottomSheetDialog(activity)
|
val bottomSheet = BottomSheetDialog(activity)
|
||||||
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ class UpdateUtil(var context: Context) {
|
||||||
version = res.getString("tag_name")
|
version = res.getString("tag_name")
|
||||||
body = res.getString("body")
|
body = res.getString("body")
|
||||||
} catch (e: JSONException) {
|
} catch (e: JSONException) {
|
||||||
result(e.message.toString())
|
result(context.getString(R.string.network_error))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val versionNameInt = version.split("v")[1].replace(".","").toInt()
|
val versionNameInt = version.split("v")[1].replace(".","").toInt()
|
||||||
|
|
|
||||||
|
|
@ -178,8 +178,8 @@ class DownloadWorker(
|
||||||
when(type){
|
when(type){
|
||||||
DownloadViewModel.Type.audio -> {
|
DownloadViewModel.Type.audio -> {
|
||||||
var audioQualityId : String = downloadItem.format.format_id
|
var audioQualityId : String = downloadItem.format.format_id
|
||||||
if (audioQualityId.isBlank() || audioQualityId == "0" || audioQualityId == context.getString(R.string.best_quality)) audioQualityId = ""
|
if (audioQualityId.isBlank() || audioQualityId == "0" || audioQualityId == context.getString(R.string.best_quality) || audioQualityId == "best") audioQualityId = ""
|
||||||
else if (audioQualityId == context.getString(R.string.worst_quality)) audioQualityId = "worstaudio"
|
else if (audioQualityId == context.getString(R.string.worst_quality) || audioQualityId == "worst") audioQualityId = "worstaudio"
|
||||||
|
|
||||||
val ext = downloadItem.format.container
|
val ext = downloadItem.format.container
|
||||||
if (audioQualityId.isNotBlank()) request.addOption("-f", audioQualityId)
|
if (audioQualityId.isNotBlank()) request.addOption("-f", audioQualityId)
|
||||||
|
|
@ -236,15 +236,15 @@ class DownloadWorker(
|
||||||
Log.e(TAG, videoFormatID)
|
Log.e(TAG, videoFormatID)
|
||||||
var formatArgument = if (downloadItem.videoPreferences.removeAudio) "bestvideo" else "bestvideo+bestaudio/best"
|
var formatArgument = if (downloadItem.videoPreferences.removeAudio) "bestvideo" else "bestvideo+bestaudio/best"
|
||||||
if (videoFormatID.isNotEmpty()) {
|
if (videoFormatID.isNotEmpty()) {
|
||||||
if (videoFormatID == context.resources.getString(R.string.best_quality)) videoFormatID = "bestvideo"
|
if (videoFormatID == context.resources.getString(R.string.best_quality) || videoFormatID == "best") videoFormatID = "bestvideo"
|
||||||
else if (videoFormatID == context.resources.getString(R.string.worst_quality)) videoFormatID = "worst"
|
else if (videoFormatID == context.resources.getString(R.string.worst_quality) || videoFormatID == "worst") videoFormatID = "worst"
|
||||||
else if (defaultFormats.contains(videoFormatID)) videoFormatID = "bestvideo[height<="+videoFormatID.substring(0, videoFormatID.length -1)+"]"
|
else if (defaultFormats.contains(videoFormatID)) videoFormatID = "bestvideo[height<="+videoFormatID.substring(0, videoFormatID.length -1)+"]"
|
||||||
if (!downloadItem.videoPreferences.removeAudio) formatArgument = "$videoFormatID+bestaudio/best/$videoFormatID"
|
if (!downloadItem.videoPreferences.removeAudio) formatArgument = "$videoFormatID+bestaudio/best/$videoFormatID"
|
||||||
}
|
}
|
||||||
Log.e(TAG, formatArgument)
|
Log.e(TAG, formatArgument)
|
||||||
request.addOption("-f", formatArgument)
|
request.addOption("-f", formatArgument)
|
||||||
val format = downloadItem.format.container
|
val format = downloadItem.format.container
|
||||||
if(format.isNotEmpty()){
|
if(format.isNotEmpty() && format != "Default" && format != context.getString(R.string.defaultValue)){
|
||||||
request.addOption("--merge-output-format", format)
|
request.addOption("--merge-output-format", format)
|
||||||
if (format != "webm") {
|
if (format != "webm") {
|
||||||
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
|
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="Incognito"
|
android:text="@string/incognito"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:translationZ="20dp"
|
android:translationZ="20dp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="Incognito"
|
android:text="@string/incognito"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:translationZ="20dp"
|
android:translationZ="20dp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
android:id="@+id/logs_recyclerview"
|
android:id="@+id/logs_recyclerview"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
android:scrollbars="vertical"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="Incognito"
|
android:text="@string/incognito"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:padding="5dp"
|
android:padding="5dp"
|
||||||
|
|
|
||||||
|
|
@ -6,32 +6,44 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context="com.deniscerri.ytdlnis.ui.more.settings.SettingsActivity">
|
tools:context="com.deniscerri.ytdlnis.ui.more.settings.SettingsActivity">
|
||||||
|
|
||||||
<FrameLayout
|
<androidx.fragment.app.FragmentContainerView
|
||||||
android:id="@+id/settings_frame_layout"
|
android:id="@+id/frame_layout"
|
||||||
android:layout_width="match_parent"
|
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||||
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:defaultNavHost="true"
|
||||||
|
app:navGraph="@navigation/settings_nav"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toBottomOf="@+id/incognito_header"
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:liftOnScroll="true"
|
android:fitsSystemWindows="true">
|
||||||
android:background="@android:color/transparent"
|
|
||||||
android:fitsSystemWindows="true">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
android:id="@+id/settings_toolbar"
|
android:id="@+id/collapsing_toolbar"
|
||||||
android:elevation="0dp"
|
style="?attr/collapsingToolbarLayoutLargeStyle"
|
||||||
app:layout_scrollFlags="scroll|enterAlways|snap"
|
|
||||||
app:title="@string/settings"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
app:navigationIcon="@drawable/ic_back"
|
app:title="@string/settings"
|
||||||
android:layout_height="match_parent"/>
|
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
|
||||||
|
android:layout_height="?attr/collapsingToolbarLayoutLargeSize">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/settings_toolbar"
|
||||||
|
android:elevation="0dp"
|
||||||
|
app:layout_collapseMode="pin"
|
||||||
|
app:layout_scrollFlags="scroll"
|
||||||
|
app:navigationIcon="@drawable/ic_back"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"/>
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||||
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
|
||||||
|
|
@ -1,154 +1,147 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.core.widget.NestedScrollView android:layout_width="match_parent"
|
||||||
|
android:scrollbars="none"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fillViewport="true"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:scrollbars="none"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:orientation="vertical">
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="20dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingTop="20dp">
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bottom_sheet_title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:singleLine="false"
|
||||||
|
android:text="@string/download"
|
||||||
|
android:textSize="25sp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/bottomsheet_schedule_button"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bottom_sheet_subtitle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:singleLine="false"
|
||||||
|
android:text="@string/configure_download"
|
||||||
|
android:textSize="12sp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/bottomsheet_schedule_button"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/bottom_sheet_title" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/bottomsheet_schedule_button"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton.Filled"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
|
app:icon="@drawable/ic_clock"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/bottomsheet_download_button"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/bottomsheet_download_button"
|
||||||
|
style="@style/Widget.Material3.Button.ElevatedButton.Icon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:autoLink="all"
|
||||||
|
android:text="@string/download"
|
||||||
|
app:icon="@drawable/ic_down"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
android:id="@+id/download_tablayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:backgroundTint="@android:color/transparent"
|
||||||
|
app:tabGravity="start"
|
||||||
|
app:tabMode="scrollable">
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabItem
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="@string/audio" />
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabItem
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="@string/video" />
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabItem
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:text="@string/command" />
|
||||||
|
|
||||||
|
|
||||||
|
</com.google.android.material.tabs.TabLayout>
|
||||||
|
|
||||||
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
|
android:id="@+id/download_viewpager"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/bottom_sheet_link"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton.Icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:textAlignment="textStart"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textSize="15sp"
|
||||||
|
app:icon="@drawable/ic_link" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:gravity="end"
|
||||||
android:orientation="vertical">
|
android:visibility="gone"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
android:paddingHorizontal="20dp"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="wrap_content">
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="20dp"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingTop="20dp">
|
|
||||||
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/bottom_sheet_title"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:maxLines="2"
|
|
||||||
android:singleLine="false"
|
|
||||||
android:text="@string/download"
|
|
||||||
android:textSize="25sp"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/bottomsheet_schedule_button"
|
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/bottom_sheet_subtitle"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:maxLines="2"
|
|
||||||
android:singleLine="false"
|
|
||||||
android:text="@string/configure_download"
|
|
||||||
android:textSize="12sp"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/bottomsheet_schedule_button"
|
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/bottom_sheet_title" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/bottomsheet_schedule_button"
|
|
||||||
style="?attr/materialIconButtonFilledTonalStyle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="10dp"
|
|
||||||
app:icon="@drawable/ic_clock"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/bottomsheet_download_button"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/bottomsheet_download_button"
|
|
||||||
style="@style/Widget.Material3.Button.ElevatedButton.Icon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:autoLink="all"
|
|
||||||
android:text="@string/download"
|
|
||||||
app:icon="@drawable/ic_down"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.tabs.TabLayout
|
|
||||||
android:id="@+id/download_tablayout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="10dp"
|
|
||||||
android:backgroundTint="@android:color/transparent"
|
|
||||||
app:tabGravity="fill"
|
|
||||||
app:tabMode="scrollable">
|
|
||||||
|
|
||||||
<com.google.android.material.tabs.TabItem
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:text="@string/audio" />
|
|
||||||
|
|
||||||
<com.google.android.material.tabs.TabItem
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:text="@string/video" />
|
|
||||||
|
|
||||||
<com.google.android.material.tabs.TabItem
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:text="@string/command" />
|
|
||||||
|
|
||||||
|
|
||||||
</com.google.android.material.tabs.TabLayout>
|
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
|
||||||
android:id="@+id/download_viewpager"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/bottom_sheet_link"
|
android:id="@+id/update_item"
|
||||||
style="@style/Widget.Material3.Button.TextButton.Icon"
|
style="@style/Widget.Material3.Button.ElevatedButton.Icon"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="10dp"
|
android:autoLink="all"
|
||||||
android:textAlignment="textStart"
|
android:text="@string/update"
|
||||||
android:singleLine="true"
|
app:icon="@drawable/ic_update"
|
||||||
android:text="@string/app_name"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
android:textSize="15sp"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:icon="@drawable/ic_link" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:gravity="end"
|
|
||||||
android:visibility="gone"
|
|
||||||
android:paddingBottom="10dp"
|
|
||||||
android:paddingHorizontal="20dp"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/update_item"
|
|
||||||
style="@style/Widget.Material3.Button.ElevatedButton.Icon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:autoLink="all"
|
|
||||||
android:text="@string/update"
|
|
||||||
app:icon="@drawable/ic_update"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
android:checkable="true"
|
android:checkable="true"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
app:checkedIcon="@null"
|
||||||
app:cardPreventCornerOverlap="true"
|
app:cardPreventCornerOverlap="true"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,21 @@
|
||||||
android:checked="false"
|
android:checked="false"
|
||||||
android:text="@string/split_by_chapters"/>
|
android:text="@string/split_by_chapters"/>
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
android:id="@+id/cut"
|
||||||
|
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
app:chipIconVisible="true"
|
||||||
|
app:chipIcon="@drawable/ic_cut"
|
||||||
|
android:text="@string/cut"
|
||||||
|
android:minWidth="30dp"
|
||||||
|
app:cornerRadius="10dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
</com.google.android.material.chip.ChipGroup>
|
</com.google.android.material.chip.ChipGroup>
|
||||||
|
|
||||||
|
|
@ -180,7 +195,7 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="SponsorBlock"
|
android:text="@string/sponsorblock"
|
||||||
app:chipIconVisible="true"
|
app:chipIconVisible="true"
|
||||||
app:chipIcon="@drawable/ic_money"
|
app:chipIcon="@drawable/ic_money"
|
||||||
android:minWidth="30dp"
|
android:minWidth="30dp"
|
||||||
|
|
@ -190,14 +205,14 @@
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<com.google.android.material.chip.Chip
|
<com.google.android.material.chip.Chip
|
||||||
android:id="@+id/cut"
|
android:id="@+id/filename_template"
|
||||||
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
|
android:text="@string/file_name_template"
|
||||||
app:chipIconVisible="true"
|
app:chipIconVisible="true"
|
||||||
app:chipIcon="@drawable/ic_cut"
|
app:chipIcon="@drawable/ic_edit"
|
||||||
android:text="@string/cut"
|
|
||||||
android:minWidth="30dp"
|
android:minWidth="30dp"
|
||||||
app:cornerRadius="10dp"
|
app:cornerRadius="10dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
|
@ -205,7 +220,6 @@
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</com.google.android.material.chip.ChipGroup>
|
</com.google.android.material.chip.ChipGroup>
|
||||||
|
|
||||||
</HorizontalScrollView>
|
</HorizontalScrollView>
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,8 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:paddingHorizontal="10dp">
|
android:paddingHorizontal="10dp"
|
||||||
|
android:baselineAligned="false">
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/author_textinput"
|
android:id="@+id/author_textinput"
|
||||||
|
|
@ -57,8 +58,10 @@
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="45"
|
android:layout_weight="45"
|
||||||
|
android:labelFor="@id/container_textview"
|
||||||
android:hint="@string/container"
|
android:hint="@string/container"
|
||||||
android:paddingStart="10dp">
|
android:paddingStart="10dp"
|
||||||
|
android:paddingEnd="0dp">
|
||||||
|
|
||||||
<AutoCompleteTextView
|
<AutoCompleteTextView
|
||||||
android:id="@+id/container_textview"
|
android:id="@+id/container_textview"
|
||||||
|
|
@ -175,14 +178,12 @@
|
||||||
app:singleLine="true">
|
app:singleLine="true">
|
||||||
|
|
||||||
<com.google.android.material.chip.Chip
|
<com.google.android.material.chip.Chip
|
||||||
android:id="@+id/sponsorblock_filters"
|
android:id="@+id/remove_audio"
|
||||||
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="SponsorBlock"
|
android:text="@string/remove_audio"
|
||||||
app:chipIconVisible="true"
|
|
||||||
app:chipIcon="@drawable/ic_money"
|
|
||||||
android:minWidth="30dp"
|
android:minWidth="30dp"
|
||||||
app:cornerRadius="10dp"
|
app:cornerRadius="10dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
|
@ -227,6 +228,36 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:singleLine="true">
|
app:singleLine="true">
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
android:id="@+id/sponsorblock_filters"
|
||||||
|
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/sponsorblock"
|
||||||
|
app:chipIconVisible="true"
|
||||||
|
app:chipIcon="@drawable/ic_money"
|
||||||
|
android:minWidth="30dp"
|
||||||
|
app:cornerRadius="10dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
android:id="@+id/filename_template"
|
||||||
|
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/file_name_template"
|
||||||
|
app:chipIconVisible="true"
|
||||||
|
app:chipIcon="@drawable/ic_edit"
|
||||||
|
android:minWidth="30dp"
|
||||||
|
app:cornerRadius="10dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<com.google.android.material.chip.Chip
|
<com.google.android.material.chip.Chip
|
||||||
android:id="@+id/save_subtitles"
|
android:id="@+id/save_subtitles"
|
||||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||||
|
|
@ -249,19 +280,6 @@
|
||||||
android:checked="false"
|
android:checked="false"
|
||||||
android:text="@string/subtitle_languages"/>
|
android:text="@string/subtitle_languages"/>
|
||||||
|
|
||||||
<com.google.android.material.chip.Chip
|
|
||||||
android:id="@+id/remove_audio"
|
|
||||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="@string/remove_audio"
|
|
||||||
android:minWidth="30dp"
|
|
||||||
app:cornerRadius="10dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</com.google.android.material.chip.ChipGroup>
|
</com.google.android.material.chip.ChipGroup>
|
||||||
|
|
||||||
</HorizontalScrollView>
|
</HorizontalScrollView>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
app:liftOnScroll="true"
|
app:liftOnScroll="true"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:elevation="0dp"
|
android:elevation="0dp"
|
||||||
|
app:liftOnScrollTargetViewId="@id/recyclerviewhistorys"
|
||||||
app:elevation="0dp"
|
app:elevation="0dp"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:cardCornerRadius="10dp"
|
app:cardCornerRadius="10dp"
|
||||||
app:cardElevation="5dp"
|
app:cardElevation="5dp"
|
||||||
|
app:checkedIcon="@null"
|
||||||
app:cardMaxElevation="12dp"
|
app:cardMaxElevation="12dp"
|
||||||
android:checkable="true"
|
android:checkable="true"
|
||||||
app:strokeWidth="0dp"
|
app:strokeWidth="0dp"
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:cardCornerRadius="20dp"
|
app:cardCornerRadius="20dp"
|
||||||
|
app:checkedIcon="@null"
|
||||||
app:cardElevation="5dp"
|
app:cardElevation="5dp"
|
||||||
app:cardMaxElevation="12dp"
|
app:cardMaxElevation="12dp"
|
||||||
app:cardPreventCornerOverlap="true"
|
app:cardPreventCornerOverlap="true"
|
||||||
|
|
|
||||||
|
|
@ -20,23 +20,26 @@
|
||||||
style="@style/Widget.Material3.BottomAppBar"
|
style="@style/Widget.Material3.BottomAppBar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
app:menu="@menu/select_playlist_menu"
|
||||||
android:layout_gravity="bottom">
|
android:layout_gravity="bottom">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/selected"
|
android:id="@+id/selected"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
|
android:layout_marginHorizontal="30dp"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
|
||||||
</com.google.android.material.bottomappbar.BottomAppBar>
|
</com.google.android.material.bottomappbar.BottomAppBar>
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
android:id="@+id/check_all"
|
android:id="@+id/check_all"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
android:text="@string/select_all"
|
||||||
|
app:icon="@drawable/ic_select_all"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_anchor="@id/bottomAppBar"
|
app:layout_anchor="@id/bottomAppBar"/>
|
||||||
app:srcCompat="@drawable/ic_select_all" />
|
|
||||||
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
@ -83,6 +86,7 @@
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox"
|
style="@style/Widget.Material3.TextInputLayout.FilledBox"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="URL"
|
|
||||||
android:padding="10dp"
|
android:padding="10dp"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
|
|
||||||
10
app/src/main/res/menu/select_playlist_menu.xml
Normal file
10
app/src/main/res/menu/select_playlist_menu.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:theme="@style/BaseTheme">
|
||||||
|
|
||||||
|
<item android:title="@string/invert_selected"
|
||||||
|
android:icon="@drawable/ic_terminal"
|
||||||
|
app:showAsAction="never"
|
||||||
|
android:id="@+id/invert_selected"/>
|
||||||
|
</menu>
|
||||||
|
|
@ -26,13 +26,21 @@
|
||||||
android:label="More"
|
android:label="More"
|
||||||
tools:layout="@layout/fragment_more" >
|
tools:layout="@layout/fragment_more" >
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_moreFragment_to_settingsFragment"
|
android:id="@+id/action_moreFragment_to_terminalActivity"
|
||||||
app:destination="@id/settingsFragment" />
|
app:destination="@id/terminalActivity" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_moreFragment_to_cookiesActivity"
|
||||||
|
app:destination="@id/cookiesActivity" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_moreFragment_to_downloadQueueActivity"
|
||||||
|
app:destination="@id/downloadQueueActivity" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_moreFragment_to_downloadLogListActivity"
|
||||||
|
app:destination="@id/downloadLogListActivity" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_moreFragment_to_commandTemplatesActivity"
|
||||||
|
app:destination="@id/commandTemplatesActivity" />
|
||||||
</fragment>
|
</fragment>
|
||||||
<fragment
|
|
||||||
android:id="@+id/settingsFragment"
|
|
||||||
android:name="com.deniscerri.ytdlnis.ui.more.settings.SettingsFragment"
|
|
||||||
android:label="SettingsFragment" />
|
|
||||||
<activity
|
<activity
|
||||||
android:id="@+id/downloadQueueActivity"
|
android:id="@+id/downloadQueueActivity"
|
||||||
android:name="com.deniscerri.ytdlnis.ui.downloads.DownloadQueueActivity"
|
android:name="com.deniscerri.ytdlnis.ui.downloads.DownloadQueueActivity"
|
||||||
|
|
@ -50,11 +58,6 @@
|
||||||
android:id="@+id/cookiesActivity"
|
android:id="@+id/cookiesActivity"
|
||||||
android:name="com.deniscerri.ytdlnis.ui.more.CookiesActivity"
|
android:name="com.deniscerri.ytdlnis.ui.more.CookiesActivity"
|
||||||
android:label="CookiesActivity" />
|
android:label="CookiesActivity" />
|
||||||
<activity
|
|
||||||
android:id="@+id/settingsActivity"
|
|
||||||
android:name="com.deniscerri.ytdlnis.ui.more.settings.SettingsActivity"
|
|
||||||
android:label="activity_settings"
|
|
||||||
tools:layout="@layout/activity_settings" />
|
|
||||||
<activity
|
<activity
|
||||||
android:id="@+id/terminalActivity"
|
android:id="@+id/terminalActivity"
|
||||||
android:name="com.deniscerri.ytdlnis.ui.more.TerminalActivity"
|
android:name="com.deniscerri.ytdlnis.ui.more.TerminalActivity"
|
||||||
|
|
|
||||||
47
app/src/main/res/navigation/settings_nav.xml
Normal file
47
app/src/main/res/navigation/settings_nav.xml
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/settings_nav_graph"
|
||||||
|
app:startDestination="@id/mainSettingsFragment">
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/appearanceSettingsFragment"
|
||||||
|
android:name="com.deniscerri.ytdlnis.ui.more.settings.AppearanceSettingsFragment"
|
||||||
|
android:label="AppearanceSettingsFragment" />
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/processingSettingsFragment"
|
||||||
|
android:name="com.deniscerri.ytdlnis.ui.more.settings.ProcessingSettingsFragment"
|
||||||
|
android:label="ProcessingSettingsFragment" />
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/folderSettingsFragment"
|
||||||
|
android:name="com.deniscerri.ytdlnis.ui.more.settings.FolderSettingsFragment"
|
||||||
|
android:label="FolderSettingsFragment" />
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/updateSettingsFragment"
|
||||||
|
android:name="com.deniscerri.ytdlnis.ui.more.settings.UpdateSettingsFragment"
|
||||||
|
android:label="UpdateSettingsFragment" />
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/downloadSettingsFragment"
|
||||||
|
android:name="com.deniscerri.ytdlnis.ui.more.settings.DownloadSettingsFragment"
|
||||||
|
android:label="DownloadSettingsFragment" />
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/mainSettingsFragment"
|
||||||
|
android:name="com.deniscerri.ytdlnis.ui.more.settings.MainSettingsFragment"
|
||||||
|
android:label="MainSettingsFragment" >
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_mainSettingsFragment_to_appearanceSettingsFragment"
|
||||||
|
app:destination="@id/appearanceSettingsFragment" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_mainSettingsFragment_to_folderSettingsFragment"
|
||||||
|
app:destination="@id/folderSettingsFragment" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_mainSettingsFragment_to_downloadSettingsFragment"
|
||||||
|
app:destination="@id/downloadSettingsFragment" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_mainSettingsFragment_to_processingSettingsFragment"
|
||||||
|
app:destination="@id/processingSettingsFragment" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_mainSettingsFragment_to_updateSettingsFragment"
|
||||||
|
app:destination="@id/updateSettingsFragment" />
|
||||||
|
</fragment>
|
||||||
|
</navigation>
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="audio_containers_values">
|
<string-array name="audio_containers_values">
|
||||||
<item />
|
<item>Default</item>
|
||||||
<item>mp3</item>
|
<item>mp3</item>
|
||||||
<item>m4a</item>
|
<item>m4a</item>
|
||||||
<item>aac</item>
|
<item>aac</item>
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="video_containers_values">
|
<string-array name="video_containers_values">
|
||||||
<item />
|
<item>Default</item>
|
||||||
<item>mp4</item>
|
<item>mp4</item>
|
||||||
<item>mkv</item>
|
<item>mkv</item>
|
||||||
<item>webm</item>
|
<item>webm</item>
|
||||||
|
|
@ -46,6 +46,17 @@
|
||||||
<item>@string/best_quality</item>
|
<item>@string/best_quality</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="video_formats_values">
|
||||||
|
<item>worst</item>
|
||||||
|
<item>360p</item>
|
||||||
|
<item>480p</item>
|
||||||
|
<item>720p</item>
|
||||||
|
<item>1080p</item>
|
||||||
|
<item>1440p</item>
|
||||||
|
<item>2160p</item>
|
||||||
|
<item>best</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<string-array name="audio_formats">
|
<string-array name="audio_formats">
|
||||||
<item>@string/worst_quality</item>
|
<item>@string/worst_quality</item>
|
||||||
<item>@string/best_quality</item>
|
<item>@string/best_quality</item>
|
||||||
|
|
@ -683,4 +694,28 @@
|
||||||
<item>ZW</item>
|
<item>ZW</item>
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<array name="backup_category_entries">
|
||||||
|
<item>@string/settings</item>
|
||||||
|
<item>@string/downloads</item>
|
||||||
|
<item>@string/in_queue</item>
|
||||||
|
<item>@string/cancelled</item>
|
||||||
|
<item>@string/cookies</item>
|
||||||
|
<item>@string/command_templates</item>
|
||||||
|
<item>@string/shortcuts</item>
|
||||||
|
<item>@string/search_history</item>
|
||||||
|
</array>
|
||||||
|
|
||||||
|
<array name="backup_category_values">
|
||||||
|
<item>settings</item>
|
||||||
|
<item>downloads</item>
|
||||||
|
<item>queued</item>
|
||||||
|
<item>cancelled</item>
|
||||||
|
<item>cookies</item>
|
||||||
|
<item>templates</item>
|
||||||
|
<item>shortcuts</item>
|
||||||
|
<item>searchHistory</item>
|
||||||
|
</array>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -255,4 +255,13 @@
|
||||||
<string name="keep_cache_summary">Don\'t delete temporary files after the download is finished</string>
|
<string name="keep_cache_summary">Don\'t delete temporary files after the download is finished</string>
|
||||||
<string name="backup">Backup</string>
|
<string name="backup">Backup</string>
|
||||||
<string name="restore">Restore</string>
|
<string name="restore">Restore</string>
|
||||||
|
<string name="network_error">Network Error</string>
|
||||||
|
<string name="incognito">Incognito</string>
|
||||||
|
<string name="sponsorblock">SponsorBlock</string>
|
||||||
|
<string name="couldnt_parse_file">Couldn\'t parse file</string>
|
||||||
|
<string name="restore_complete">App Restore Complete. The following items have been restored:</string>
|
||||||
|
<string name="select_backup_categories">Select backup categories</string>
|
||||||
|
<string name="search_history">Search History</string>
|
||||||
|
<string name="backup_created_successfully">Backup file saved in the downloads folder</string>
|
||||||
|
<string name="appearance">Appearance</string>
|
||||||
</resources>
|
</resources>
|
||||||
39
app/src/main/res/xml/appearance_preferences.xml
Normal file
39
app/src/main/res/xml/appearance_preferences.xml
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:entries="@array/language_values"
|
||||||
|
android:entryValues="@array/language_values"
|
||||||
|
android:icon="@drawable/ic_language"
|
||||||
|
app:key="app_language"
|
||||||
|
app:summary="en"
|
||||||
|
app:title="@string/language" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:entries="@array/themes"
|
||||||
|
android:entryValues="@array/themes_values"
|
||||||
|
android:icon="@drawable/baseline_format_paint_24"
|
||||||
|
app:defaultValue="System"
|
||||||
|
app:key="ytdlnis_theme"
|
||||||
|
app:summary="@string/defaultValue"
|
||||||
|
app:title="@string/Theme" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:entries="@array/accents"
|
||||||
|
android:entryValues="@array/accents_values"
|
||||||
|
android:icon="@drawable/baseline_color_lens_24"
|
||||||
|
app:defaultValue="blue"
|
||||||
|
app:key="theme_accent"
|
||||||
|
app:summary="Blue"
|
||||||
|
app:title="@string/accents" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="false"
|
||||||
|
android:icon="@drawable/baseline_invert_colors_24"
|
||||||
|
android:key="high_contrast"
|
||||||
|
app:summary="@string/pure_theme_summary"
|
||||||
|
app:title="@string/high_contrast" />
|
||||||
|
</PreferenceScreen>
|
||||||
140
app/src/main/res/xml/downloading_preferences.xml
Normal file
140
app/src/main/res/xml/downloading_preferences.xml
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="false"
|
||||||
|
android:icon="@drawable/ic_incognito"
|
||||||
|
android:key="incognito"
|
||||||
|
app:summary="@string/incognito_summary"
|
||||||
|
app:title="@string/incognito" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="true"
|
||||||
|
android:icon="@drawable/ic_card"
|
||||||
|
android:key="download_card"
|
||||||
|
app:summary="@string/download_card_summary"
|
||||||
|
app:title="@string/show_download_card" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="false"
|
||||||
|
android:icon="@drawable/ic_speed"
|
||||||
|
android:key="quick_download"
|
||||||
|
app:summary="@string/quick_download_summary"
|
||||||
|
app:title="@string/quick_download" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="true"
|
||||||
|
android:icon="@drawable/metered_networks"
|
||||||
|
android:key="metered_networks"
|
||||||
|
app:summary="@string/download_over_metered_networks_summary"
|
||||||
|
app:title="@string/download_over_metered_networks" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:dependency="download_card"
|
||||||
|
android:defaultValue="video"
|
||||||
|
android:entries="@array/download_types"
|
||||||
|
android:entryValues="@array/download_types_values"
|
||||||
|
android:icon="@drawable/ic_download_type"
|
||||||
|
app:key="preferred_download_type"
|
||||||
|
app:summary="@string/preferred_download_type_summary"
|
||||||
|
app:title="@string/preferred_download_type" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="ytsearch"
|
||||||
|
android:entries="@array/search_engines"
|
||||||
|
android:entryValues="@array/search_engines_values"
|
||||||
|
android:icon="@drawable/baseline_manage_search_24"
|
||||||
|
app:key="search_engine"
|
||||||
|
app:summary="@string/preferred_search_engine_summary"
|
||||||
|
app:title="@string/preferred_search_engine" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="Home"
|
||||||
|
android:entries="@array/start_destination"
|
||||||
|
android:entryValues="@array/start_destination_values"
|
||||||
|
app:icon="@drawable/baseline_add_to_home_screen_24"
|
||||||
|
app:key="start_destination"
|
||||||
|
app:title="@string/preferred_home_screen" />
|
||||||
|
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="false"
|
||||||
|
android:icon="@drawable/baseline_recommend_24"
|
||||||
|
android:key="home_recommendations"
|
||||||
|
app:summary="@string/video_recommendations_summary"
|
||||||
|
app:title="@string/video_recommendations" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue=""
|
||||||
|
android:entries="@array/countries"
|
||||||
|
android:entryValues="@array/countries_values"
|
||||||
|
app:icon="@drawable/ic_language"
|
||||||
|
app:dependency="home_recommendations"
|
||||||
|
app:key="locale"
|
||||||
|
app:title="@string/preferred_locale" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:icon="@drawable/ic_key"
|
||||||
|
app:key="api_key"
|
||||||
|
app:defaultValue=""
|
||||||
|
app:summary="@string/api_key_summary"
|
||||||
|
app:title="@string/api_key" />
|
||||||
|
|
||||||
|
<SeekBarPreference
|
||||||
|
app:dependency="aria2"
|
||||||
|
android:defaultValue="3"
|
||||||
|
android:icon="@drawable/ic_lines"
|
||||||
|
android:max="25"
|
||||||
|
app:key="concurrent_fragments"
|
||||||
|
app:min="1"
|
||||||
|
app:showSeekBarValue="true"
|
||||||
|
android:dependency="aria2"
|
||||||
|
app:summary="@string/concurrent_fragments_summary"
|
||||||
|
app:title="@string/concurrent_fragments" />
|
||||||
|
|
||||||
|
<SeekBarPreference
|
||||||
|
android:defaultValue="1"
|
||||||
|
android:icon="@drawable/ic_concurrent_downloads"
|
||||||
|
android:max="10"
|
||||||
|
app:key="concurrent_downloads"
|
||||||
|
app:min="1"
|
||||||
|
app:showSeekBarValue="true"
|
||||||
|
app:summary="@string/concurrent_downloads_summary"
|
||||||
|
app:title="@string/concurrent_downloads" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:icon="@drawable/ic_down"
|
||||||
|
app:key="limit_rate"
|
||||||
|
app:summary="@string/limit_rate_summary"
|
||||||
|
app:title="@string/limit_rate" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="false"
|
||||||
|
android:icon="@drawable/ic_speed"
|
||||||
|
android:key="aria2"
|
||||||
|
app:summary="@string/aria2_summary"
|
||||||
|
android:disableDependentsState="true"
|
||||||
|
app:title="Aria2" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="false"
|
||||||
|
android:icon="@drawable/ic_baseline_file_open_24"
|
||||||
|
android:key="log_downloads"
|
||||||
|
app:summary="@string/log_downloads_summary"
|
||||||
|
app:title="@string/log_downloads" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
app:icon="@drawable/ic_battery"
|
||||||
|
app:key="ignore_battery"
|
||||||
|
app:title="@string/ignore_battery_optimization" />
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
||||||
43
app/src/main/res/xml/folders_preference.xml
Normal file
43
app/src/main/res/xml/folders_preference.xml
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
app:icon="@drawable/ic_music_downloaded"
|
||||||
|
app:key="music_path"
|
||||||
|
app:defaultValue="@string/music_path"
|
||||||
|
app:title="@string/music_directory" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
app:icon="@drawable/ic_video_downloaded"
|
||||||
|
app:key="video_path"
|
||||||
|
app:defaultValue="@string/video_path"
|
||||||
|
app:title="@string/video_directory" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
app:icon="@drawable/ic_terminal"
|
||||||
|
app:key="command_path"
|
||||||
|
app:defaultValue="@string/command_path"
|
||||||
|
app:title="@string/command_directory" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
app:icon="@drawable/ic_access_folder"
|
||||||
|
app:key="access_all_files"
|
||||||
|
android:summary="@string/access_all_directories_summary"
|
||||||
|
app:title="@string/access_all_directories" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
app:icon="@drawable/ic_folder_delete"
|
||||||
|
app:key="clear_cache"
|
||||||
|
android:summary="@string/clear_temporary_files_summary"
|
||||||
|
app:title="@string/clear_temporary_files" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="false"
|
||||||
|
android:icon="@drawable/baseline_save_24"
|
||||||
|
android:key="keep_cache"
|
||||||
|
app:summary="@string/keep_cache_summary"
|
||||||
|
app:title="@string/keep_cache" />
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
||||||
127
app/src/main/res/xml/processing_preferences.xml
Normal file
127
app/src/main/res/xml/processing_preferences.xml
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<MultiSelectListPreference
|
||||||
|
app:defaultValue="@array/sponsorblock_settings_default"
|
||||||
|
app:icon="@drawable/ic_money"
|
||||||
|
app:dialogTitle="@string/select_sponsorblock_filtering"
|
||||||
|
app:entries="@array/sponsorblock_settings_entries"
|
||||||
|
app:entryValues="@array/sponsorblock_settings_values"
|
||||||
|
app:key="sponsorblock_filters"
|
||||||
|
app:summary="@string/select_sponsorblock_filtering"
|
||||||
|
app:title="SponsorBlock" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:icon="@drawable/ic_textformat"
|
||||||
|
app:key="file_name_template"
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
app:defaultValue="%(uploader)s - %(title)s"
|
||||||
|
app:title="@string/file_name_template" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:icon="@drawable/ic_textformat"
|
||||||
|
app:key="file_name_template_audio"
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
app:defaultValue="%(uploader)s - %(title)s"
|
||||||
|
app:title="@string/file_name_template" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="false"
|
||||||
|
app:icon="@drawable/if_file_rename"
|
||||||
|
app:key="restrict_filenames"
|
||||||
|
app:summary="@string/restrict_filenames_summary"
|
||||||
|
app:title="@string/restrict_filenames" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="false"
|
||||||
|
app:icon="@drawable/ic_clock"
|
||||||
|
app:key="mtime"
|
||||||
|
app:summary="@string/enable_mtime_summary"
|
||||||
|
app:title="@string/enable_mtime" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="true"
|
||||||
|
app:icon="@drawable/ic_subtitles"
|
||||||
|
app:key="embed_subtitles"
|
||||||
|
app:summary="@string/embed_subs_summary"
|
||||||
|
app:title="@string/embed_subtitles" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="false"
|
||||||
|
app:icon="@drawable/ic_subtitles_alternative"
|
||||||
|
app:key="write_subtitles"
|
||||||
|
app:summary="@string/save_subs_desc"
|
||||||
|
app:title="@string/save_subs" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="true"
|
||||||
|
app:icon="@drawable/ic_image"
|
||||||
|
app:key="embed_thumbnail"
|
||||||
|
app:summary="@string/embed_thumb_summary"
|
||||||
|
app:title="@string/embed_thumb" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="true"
|
||||||
|
app:icon="@drawable/ic_chapters"
|
||||||
|
app:key="add_chapters"
|
||||||
|
app:summary="@string/add_chapters_summary"
|
||||||
|
app:title="@string/add_chapters" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="false"
|
||||||
|
app:icon="@drawable/ic_image"
|
||||||
|
app:key="write_thumbnail"
|
||||||
|
app:summary="@string/save_thumb_summary"
|
||||||
|
app:title="@string/save_thumb" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="Default"
|
||||||
|
android:entries="@array/audio_containers"
|
||||||
|
android:entryValues="@array/audio_containers_values"
|
||||||
|
android:icon="@drawable/ic_code"
|
||||||
|
app:key="audio_format"
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
app:title="@string/audio_format" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="Default"
|
||||||
|
android:entries="@array/video_containers"
|
||||||
|
android:entryValues="@array/video_containers_values"
|
||||||
|
android:icon="@drawable/ic_code"
|
||||||
|
app:key="video_format"
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
app:title="@string/video_format" />
|
||||||
|
|
||||||
|
|
||||||
|
<SeekBarPreference
|
||||||
|
android:defaultValue="0"
|
||||||
|
android:icon="@drawable/ic_music"
|
||||||
|
android:max="10"
|
||||||
|
app:key="audio_quality"
|
||||||
|
app:min="0"
|
||||||
|
app:showSeekBarValue="true"
|
||||||
|
app:summary="@string/audio_quality_summary"
|
||||||
|
app:title="@string/audio_quality" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="best"
|
||||||
|
android:entries="@array/video_formats"
|
||||||
|
android:entryValues="@array/video_formats_values"
|
||||||
|
android:icon="@drawable/ic_video"
|
||||||
|
app:key="video_quality"
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
app:title="@string/video_quality" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
app:key="format_id"
|
||||||
|
app:defaultValue=""
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
app:title="@string/preferred_format_id" />
|
||||||
|
</PreferenceScreen>
|
||||||
|
|
@ -1,398 +1,44 @@
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<ListPreference
|
|
||||||
android:entries="@array/language_values"
|
|
||||||
android:entryValues="@array/language_values"
|
|
||||||
android:icon="@drawable/ic_language"
|
|
||||||
app:key="app_language"
|
|
||||||
app:summary="en"
|
|
||||||
app:title="@string/language" />
|
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:entries="@array/themes"
|
|
||||||
android:entryValues="@array/themes_values"
|
|
||||||
android:icon="@drawable/baseline_format_paint_24"
|
|
||||||
app:defaultValue="System"
|
|
||||||
app:key="ytdlnis_theme"
|
|
||||||
app:summary="@string/defaultValue"
|
|
||||||
app:title="@string/Theme" />
|
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:entries="@array/accents"
|
|
||||||
android:entryValues="@array/accents_values"
|
|
||||||
android:icon="@drawable/baseline_color_lens_24"
|
|
||||||
app:defaultValue="blue"
|
|
||||||
app:key="theme_accent"
|
|
||||||
app:summary="Blue"
|
|
||||||
app:title="@string/accents" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="false"
|
|
||||||
android:icon="@drawable/baseline_invert_colors_24"
|
|
||||||
android:key="high_contrast"
|
|
||||||
app:summary="@string/pure_theme_summary"
|
|
||||||
app:title="@string/high_contrast" />
|
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
app:icon="@drawable/ic_battery"
|
android:key="appearance"
|
||||||
app:key="ignore_battery"
|
app:icon="@drawable/baseline_format_paint_24"
|
||||||
app:title="@string/ignore_battery_optimization" />
|
android:title="@string/appearance"/>
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/directories">
|
<Preference
|
||||||
|
android:key="folders"
|
||||||
|
app:icon="@drawable/baseline_folder_24"
|
||||||
|
android:title="@string/directories"/>
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="downloading"
|
||||||
|
app:icon="@drawable/baseline_download_24"
|
||||||
|
android:title="@string/downloading"/>
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
app:icon="@drawable/ic_music_downloaded"
|
android:key="processing"
|
||||||
app:key="music_path"
|
app:icon="@drawable/ic_code"
|
||||||
app:defaultValue="@string/music_path"
|
android:title="@string/processing"/>
|
||||||
app:title="@string/music_directory" />
|
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
app:icon="@drawable/ic_video_downloaded"
|
android:key="updating"
|
||||||
app:key="video_path"
|
app:icon="@drawable/ic_update"
|
||||||
app:defaultValue="@string/video_path"
|
android:title="@string/updating"/>
|
||||||
app:title="@string/video_directory" />
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
app:icon="@drawable/ic_terminal"
|
|
||||||
app:key="command_path"
|
|
||||||
app:defaultValue="@string/command_path"
|
|
||||||
app:title="@string/command_directory" />
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
app:icon="@drawable/ic_access_folder"
|
|
||||||
app:key="access_all_files"
|
|
||||||
android:summary="@string/access_all_directories_summary"
|
|
||||||
app:title="@string/access_all_directories" />
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
app:icon="@drawable/ic_folder_delete"
|
|
||||||
app:key="clear_cache"
|
|
||||||
android:summary="@string/clear_temporary_files_summary"
|
|
||||||
app:title="@string/clear_temporary_files" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="false"
|
|
||||||
android:icon="@drawable/baseline_save_24"
|
|
||||||
android:key="keep_cache"
|
|
||||||
app:summary="@string/keep_cache_summary"
|
|
||||||
app:title="@string/keep_cache" />
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
<PreferenceCategory app:title="@string/downloading">
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="false"
|
|
||||||
android:icon="@drawable/ic_incognito"
|
|
||||||
android:key="incognito"
|
|
||||||
app:summary="@string/incognito_summary"
|
|
||||||
app:title="Incognito" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="true"
|
|
||||||
android:icon="@drawable/ic_card"
|
|
||||||
android:key="download_card"
|
|
||||||
app:summary="@string/download_card_summary"
|
|
||||||
app:title="@string/show_download_card" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="false"
|
|
||||||
android:icon="@drawable/ic_speed"
|
|
||||||
android:key="quick_download"
|
|
||||||
app:summary="@string/quick_download_summary"
|
|
||||||
app:title="@string/quick_download" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="true"
|
|
||||||
android:icon="@drawable/metered_networks"
|
|
||||||
android:key="metered_networks"
|
|
||||||
app:summary="@string/download_over_metered_networks_summary"
|
|
||||||
app:title="@string/download_over_metered_networks" />
|
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:dependency="download_card"
|
|
||||||
android:defaultValue="video"
|
|
||||||
android:entries="@array/download_types"
|
|
||||||
android:entryValues="@array/download_types_values"
|
|
||||||
android:icon="@drawable/ic_download_type"
|
|
||||||
app:key="preferred_download_type"
|
|
||||||
app:summary="@string/preferred_download_type_summary"
|
|
||||||
app:title="@string/preferred_download_type" />
|
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:defaultValue="ytsearch"
|
|
||||||
android:entries="@array/search_engines"
|
|
||||||
android:entryValues="@array/search_engines_values"
|
|
||||||
android:icon="@drawable/baseline_manage_search_24"
|
|
||||||
app:key="search_engine"
|
|
||||||
app:summary="@string/preferred_search_engine_summary"
|
|
||||||
app:title="@string/preferred_search_engine" />
|
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:defaultValue="Home"
|
|
||||||
android:entries="@array/start_destination"
|
|
||||||
android:entryValues="@array/start_destination_values"
|
|
||||||
app:icon="@drawable/baseline_add_to_home_screen_24"
|
|
||||||
app:key="start_destination"
|
|
||||||
app:title="@string/preferred_home_screen" />
|
|
||||||
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="false"
|
|
||||||
android:icon="@drawable/baseline_recommend_24"
|
|
||||||
android:key="home_recommendations"
|
|
||||||
app:summary="@string/video_recommendations_summary"
|
|
||||||
app:title="@string/video_recommendations" />
|
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:defaultValue=""
|
|
||||||
android:entries="@array/countries"
|
|
||||||
android:entryValues="@array/countries_values"
|
|
||||||
app:icon="@drawable/ic_language"
|
|
||||||
app:dependency="home_recommendations"
|
|
||||||
app:key="locale"
|
|
||||||
app:title="@string/preferred_locale" />
|
|
||||||
|
|
||||||
<EditTextPreference
|
|
||||||
android:icon="@drawable/ic_key"
|
|
||||||
app:key="api_key"
|
|
||||||
app:defaultValue=""
|
|
||||||
app:summary="@string/api_key_summary"
|
|
||||||
app:title="@string/api_key" />
|
|
||||||
|
|
||||||
<SeekBarPreference
|
|
||||||
app:dependency="aria2"
|
|
||||||
android:defaultValue="3"
|
|
||||||
android:icon="@drawable/ic_lines"
|
|
||||||
android:max="25"
|
|
||||||
app:key="concurrent_fragments"
|
|
||||||
app:min="1"
|
|
||||||
app:showSeekBarValue="true"
|
|
||||||
android:dependency="aria2"
|
|
||||||
app:summary="@string/concurrent_fragments_summary"
|
|
||||||
app:title="@string/concurrent_fragments" />
|
|
||||||
|
|
||||||
<SeekBarPreference
|
|
||||||
android:defaultValue="1"
|
|
||||||
android:icon="@drawable/ic_concurrent_downloads"
|
|
||||||
android:max="10"
|
|
||||||
app:key="concurrent_downloads"
|
|
||||||
app:min="1"
|
|
||||||
app:showSeekBarValue="true"
|
|
||||||
app:summary="@string/concurrent_downloads_summary"
|
|
||||||
app:title="@string/concurrent_downloads" />
|
|
||||||
|
|
||||||
<EditTextPreference
|
|
||||||
android:icon="@drawable/ic_down"
|
|
||||||
app:key="limit_rate"
|
|
||||||
app:summary="@string/limit_rate_summary"
|
|
||||||
app:title="@string/limit_rate" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="false"
|
|
||||||
android:icon="@drawable/ic_speed"
|
|
||||||
android:key="aria2"
|
|
||||||
app:summary="@string/aria2_summary"
|
|
||||||
android:disableDependentsState="true"
|
|
||||||
app:title="Aria2" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="false"
|
|
||||||
android:icon="@drawable/ic_baseline_file_open_24"
|
|
||||||
android:key="log_downloads"
|
|
||||||
app:summary="@string/log_downloads_summary"
|
|
||||||
app:title="@string/log_downloads" />
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/processing">
|
|
||||||
<MultiSelectListPreference
|
|
||||||
app:defaultValue="@array/sponsorblock_settings_default"
|
|
||||||
app:icon="@drawable/ic_money"
|
|
||||||
app:dialogTitle="@string/select_sponsorblock_filtering"
|
|
||||||
app:entries="@array/sponsorblock_settings_entries"
|
|
||||||
app:entryValues="@array/sponsorblock_settings_values"
|
|
||||||
app:key="sponsorblock_filters"
|
|
||||||
app:summary="@string/select_sponsorblock_filtering"
|
|
||||||
app:title="SponsorBlock" />
|
|
||||||
|
|
||||||
<EditTextPreference
|
|
||||||
android:icon="@drawable/ic_textformat"
|
|
||||||
app:key="file_name_template"
|
|
||||||
app:defaultValue="%(uploader)s - %(title)s"
|
|
||||||
app:title="@string/file_name_template" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="false"
|
|
||||||
app:icon="@drawable/if_file_rename"
|
|
||||||
app:key="restrict_filenames"
|
|
||||||
app:summary="@string/restrict_filenames_summary"
|
|
||||||
app:title="@string/restrict_filenames" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="false"
|
|
||||||
app:icon="@drawable/ic_clock"
|
|
||||||
app:key="mtime"
|
|
||||||
app:summary="@string/enable_mtime_summary"
|
|
||||||
app:title="@string/enable_mtime" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="true"
|
|
||||||
app:icon="@drawable/ic_subtitles"
|
|
||||||
app:key="embed_subtitles"
|
|
||||||
app:summary="@string/embed_subs_summary"
|
|
||||||
app:title="@string/embed_subtitles" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="false"
|
|
||||||
app:icon="@drawable/ic_subtitles_alternative"
|
|
||||||
app:key="write_subtitles"
|
|
||||||
app:summary="@string/save_subs_desc"
|
|
||||||
app:title="@string/save_subs" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="true"
|
|
||||||
app:icon="@drawable/ic_image"
|
|
||||||
app:key="embed_thumbnail"
|
|
||||||
app:summary="@string/embed_thumb_summary"
|
|
||||||
app:title="@string/embed_thumb" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="true"
|
|
||||||
app:icon="@drawable/ic_chapters"
|
|
||||||
app:key="add_chapters"
|
|
||||||
app:summary="@string/add_chapters_summary"
|
|
||||||
app:title="@string/add_chapters" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="false"
|
|
||||||
app:icon="@drawable/ic_image"
|
|
||||||
app:key="write_thumbnail"
|
|
||||||
app:summary="@string/save_thumb_summary"
|
|
||||||
app:title="@string/save_thumb" />
|
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:defaultValue="Default"
|
|
||||||
android:entries="@array/audio_containers"
|
|
||||||
android:entryValues="@array/audio_containers_values"
|
|
||||||
android:icon="@drawable/ic_code"
|
|
||||||
app:key="audio_format"
|
|
||||||
app:summary="@string/defaultValue"
|
|
||||||
app:title="@string/audio_format" />
|
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:defaultValue="Default"
|
|
||||||
android:entries="@array/video_containers"
|
|
||||||
android:entryValues="@array/video_containers_values"
|
|
||||||
android:icon="@drawable/ic_code"
|
|
||||||
app:key="video_format"
|
|
||||||
app:summary="@string/defaultValue"
|
|
||||||
app:title="@string/video_format" />
|
|
||||||
|
|
||||||
|
|
||||||
<SeekBarPreference
|
|
||||||
android:defaultValue="0"
|
|
||||||
android:icon="@drawable/ic_music"
|
|
||||||
android:max="10"
|
|
||||||
app:key="audio_quality"
|
|
||||||
app:min="0"
|
|
||||||
app:showSeekBarValue="true"
|
|
||||||
app:summary="@string/audio_quality_summary"
|
|
||||||
app:title="@string/audio_quality" />
|
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:defaultValue="@string/best_quality"
|
|
||||||
android:entries="@array/video_formats"
|
|
||||||
android:entryValues="@array/video_formats"
|
|
||||||
android:icon="@drawable/ic_video"
|
|
||||||
app:key="video_quality"
|
|
||||||
app:summary="@string/best_quality"
|
|
||||||
app:title="@string/video_quality" />
|
|
||||||
|
|
||||||
<EditTextPreference
|
|
||||||
app:key="format_id"
|
|
||||||
app:defaultValue=""
|
|
||||||
app:title="@string/preferred_format_id" />
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/updating">
|
|
||||||
<Preference
|
|
||||||
app:icon="@drawable/ic_update"
|
|
||||||
app:key="update_ytdl"
|
|
||||||
app:summary="@string/ytdl_update_hint"
|
|
||||||
app:title="@string/update_ytdl" />
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
app:icon="@drawable/ic_info"
|
|
||||||
app:key="ytdl-version"
|
|
||||||
app:title="@string/ytdl_version"/>
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
android:defaultValue="false"
|
|
||||||
app:icon="@drawable/ic_nightly"
|
|
||||||
app:key="nightly_ytdl"
|
|
||||||
android:title="@string/update_ytdl_nightly"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="false"
|
|
||||||
android:icon="@drawable/ic_format"
|
|
||||||
android:key="update_formats"
|
|
||||||
app:summary="@string/update_formats_summary"
|
|
||||||
app:title="@string/update_formats" />
|
|
||||||
|
|
||||||
<ListPreference
|
|
||||||
android:defaultValue="yt-dlp"
|
|
||||||
android:entries="@array/formats_source"
|
|
||||||
android:entryValues="@array/formats_source_values"
|
|
||||||
android:icon="@drawable/baseline_manage_search_24"
|
|
||||||
app:key="formats_source"
|
|
||||||
app:summary="yt-dlp"
|
|
||||||
app:title="@string/format_source" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
|
||||||
app:defaultValue="true"
|
|
||||||
android:icon="@drawable/ic_update_app"
|
|
||||||
android:key="update_app"
|
|
||||||
app:summary="@string/update_app_summary"
|
|
||||||
app:title="@string/update_app" />
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/about">
|
|
||||||
|
|
||||||
|
<PreferenceCategory android:title="@string/backup">
|
||||||
<Preference
|
<Preference
|
||||||
app:icon="@drawable/baseline_save_alt_24"
|
app:icon="@drawable/baseline_save_alt_24"
|
||||||
app:key="export_preferences"
|
app:key="backup"
|
||||||
app:title="@string/backup"/>
|
app:title="@string/backup"/>
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
app:icon="@drawable/baseline_restore_page_24"
|
app:icon="@drawable/baseline_restore_page_24"
|
||||||
app:key="import_preferences"
|
app:key="restore"
|
||||||
app:title="@string/restore"/>
|
app:title="@string/restore"/>
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory android:title="@string/about" >
|
||||||
<Preference
|
<Preference
|
||||||
app:icon="@drawable/ic_discord"
|
app:icon="@drawable/ic_discord"
|
||||||
app:key="discord"
|
app:key="discord"
|
||||||
|
|
@ -434,11 +80,6 @@
|
||||||
android:data="https://github.com/deniscerri/ytdlnis" />
|
android:data="https://github.com/deniscerri/ytdlnis" />
|
||||||
</Preference>
|
</Preference>
|
||||||
|
|
||||||
<Preference
|
|
||||||
app:icon="@drawable/ic_info"
|
|
||||||
app:key="version"
|
|
||||||
app:title="@string/version"/>
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
52
app/src/main/res/xml/updating_preferences.xml
Normal file
52
app/src/main/res/xml/updating_preferences.xml
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
app:icon="@drawable/ic_update"
|
||||||
|
app:key="update_ytdl"
|
||||||
|
app:summary="@string/ytdl_update_hint"
|
||||||
|
app:title="@string/update_ytdl" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
app:icon="@drawable/ic_info"
|
||||||
|
app:key="ytdl-version"
|
||||||
|
app:title="@string/ytdl_version"/>
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
android:defaultValue="false"
|
||||||
|
app:icon="@drawable/ic_nightly"
|
||||||
|
app:key="nightly_ytdl"
|
||||||
|
android:title="@string/update_ytdl_nightly"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="false"
|
||||||
|
android:icon="@drawable/ic_format"
|
||||||
|
android:key="update_formats"
|
||||||
|
app:summary="@string/update_formats_summary"
|
||||||
|
app:title="@string/update_formats" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="yt-dlp"
|
||||||
|
android:entries="@array/formats_source"
|
||||||
|
android:entryValues="@array/formats_source_values"
|
||||||
|
android:icon="@drawable/baseline_manage_search_24"
|
||||||
|
app:key="formats_source"
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
app:title="@string/format_source" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
|
app:defaultValue="true"
|
||||||
|
android:icon="@drawable/ic_update_app"
|
||||||
|
android:key="update_app"
|
||||||
|
app:summary="@string/update_app_summary"
|
||||||
|
app:title="@string/update_app" />
|
||||||
|
<Preference
|
||||||
|
app:icon="@drawable/ic_info"
|
||||||
|
app:key="version"
|
||||||
|
app:title="@string/version"/>
|
||||||
|
</PreferenceScreen>
|
||||||
Loading…
Reference in a new issue